ebb 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +10 -44
- data/Rakefile +123 -0
- data/ext/ebb.c +794 -0
- data/ext/ebb.h +130 -0
- data/ext/ebb_ffi.c +575 -0
- data/ext/ebb_request_parser.c +5339 -0
- data/ext/ebb_request_parser.h +97 -0
- data/ext/ebb_request_parser.rl +513 -0
- data/{src → ext}/extconf.rb +12 -8
- data/ext/rbtree.c +408 -0
- data/ext/rbtree.h +54 -0
- data/lib/ebb.rb +311 -0
- data/lib/ebb/version.rb +4 -0
- data/libev/ev++.h +803 -0
- data/libev/ev.c +24 -6
- data/libev/ev.h +4 -0
- data/libev/ev_select.c +50 -15
- data/libev/ev_vars.h +3 -0
- data/libev/ev_win32.c +3 -0
- data/libev/ev_wrap.h +2 -0
- data/libev/event.c +403 -0
- data/libev/event.h +152 -0
- metadata +26 -40
- data/benchmark/application.rb +0 -93
- data/benchmark/server_test.rb +0 -193
- data/bin/ebb_rails +0 -4
- data/ruby_lib/ebb.rb +0 -257
- data/ruby_lib/ebb/runner.rb +0 -134
- data/ruby_lib/ebb/runner/rails.rb +0 -31
- data/ruby_lib/rack/adapter/rails.rb +0 -159
- data/src/ebb.c +0 -627
- data/src/ebb.h +0 -102
- data/src/ebb_ruby.c +0 -306
- data/src/parser.c +0 -2860
- data/src/parser.h +0 -53
- data/test/basic_test.rb +0 -46
- data/test/ebb_rails_test.rb +0 -34
- data/test/env_test.rb +0 -110
- data/test/helper.rb +0 -138
data/bin/ebb_rails
DELETED
data/ruby_lib/ebb.rb
DELETED
@@ -1,257 +0,0 @@
|
|
1
|
-
# Ruby Binding to the Ebb Web Server
|
2
|
-
# Copyright (c) 2008 Ry Dahl. This software is released under the MIT License.
|
3
|
-
# See README file for details.
|
4
|
-
require 'stringio'
|
5
|
-
module Ebb
|
6
|
-
VERSION = "0.2.1"
|
7
|
-
LIBDIR = File.dirname(__FILE__)
|
8
|
-
autoload :Runner, LIBDIR + '/ebb/runner'
|
9
|
-
autoload :FFI, LIBDIR + '/../src/ebb_ext'
|
10
|
-
|
11
|
-
def self.start_server(app, options={})
|
12
|
-
if options.has_key?(:fileno)
|
13
|
-
fd = options[:fileno].to_i
|
14
|
-
FFI::server_listen_on_fd(fd)
|
15
|
-
log.puts "Ebb is listening on file descriptor #{fd}"
|
16
|
-
elsif options.has_key?(:unix_socket)
|
17
|
-
socketfile = options[:unix_socket]
|
18
|
-
FFI::server_listen_on_unix_socket(socketfile)
|
19
|
-
log.puts "Ebb is listening on unix socket #{socketfile}"
|
20
|
-
else
|
21
|
-
port = (options[:Port] || options[:port] || 4001).to_i
|
22
|
-
FFI::server_listen_on_port(port)
|
23
|
-
log.puts "Ebb is listening at http://0.0.0.0:#{port}/"
|
24
|
-
end
|
25
|
-
log.puts "Ebb PID #{Process.pid}"
|
26
|
-
|
27
|
-
@running = true
|
28
|
-
trap('INT') { stop_server }
|
29
|
-
|
30
|
-
while @running
|
31
|
-
FFI::server_process_connections()
|
32
|
-
while client = FFI::server_waiting_clients.shift
|
33
|
-
if app.respond_to?(:deferred?) and app.deferred?(client.env)
|
34
|
-
Thread.new(client) { |c| process(app, c) }
|
35
|
-
else
|
36
|
-
process(app, client)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
FFI::server_unlisten()
|
41
|
-
end
|
42
|
-
|
43
|
-
def self.running?
|
44
|
-
FFI::server_open?
|
45
|
-
end
|
46
|
-
|
47
|
-
def self.stop_server()
|
48
|
-
@running = false
|
49
|
-
end
|
50
|
-
|
51
|
-
def self.process(app, client)
|
52
|
-
#p client.env
|
53
|
-
status, headers, body = app.call(client.env)
|
54
|
-
status = status.to_i
|
55
|
-
|
56
|
-
# Write the status
|
57
|
-
client.write_status(status)
|
58
|
-
|
59
|
-
# Add Content-Length to the headers.
|
60
|
-
if !headers.has_key?('Content-Length') and
|
61
|
-
headers.respond_to?(:[]=) and
|
62
|
-
status != 304
|
63
|
-
then
|
64
|
-
# for String just use "length" method
|
65
|
-
if body.kind_of?(String)
|
66
|
-
headers['Content-Length'] = body.length.to_s
|
67
|
-
else
|
68
|
-
# for non-Array object call "each" and transform to Array
|
69
|
-
unless body.kind_of?(Array)
|
70
|
-
parts = []
|
71
|
-
body.each {|p| parts << p}
|
72
|
-
body = parts
|
73
|
-
end
|
74
|
-
# body is Array so calculate Content-Length as sum of length each part
|
75
|
-
headers['Content-Length'] = body.inject(0) {|s, p| s + p.length }.to_s
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
# Decide if we should keep the connection alive or not
|
80
|
-
unless headers.has_key?('Connection')
|
81
|
-
if headers.has_key?('Content-Length') and client.should_keep_alive?
|
82
|
-
headers['Connection'] = 'Keep-Alive'
|
83
|
-
else
|
84
|
-
headers['Connection'] = 'close'
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
# Write the headers
|
89
|
-
headers.each { |field, value| client.write_header(field, value) }
|
90
|
-
|
91
|
-
# Write the body
|
92
|
-
if body.kind_of?(String)
|
93
|
-
client.write_body(body)
|
94
|
-
else
|
95
|
-
body.each { |p| client.write_body(p) }
|
96
|
-
end
|
97
|
-
|
98
|
-
rescue => e
|
99
|
-
log.puts "Ebb Error! #{e.class} #{e.message}"
|
100
|
-
log.puts e.backtrace.join("\n")
|
101
|
-
ensure
|
102
|
-
client.release
|
103
|
-
end
|
104
|
-
|
105
|
-
@@log = STDOUT
|
106
|
-
def self.log=(output)
|
107
|
-
@@log = output
|
108
|
-
end
|
109
|
-
def self.log
|
110
|
-
@@log
|
111
|
-
end
|
112
|
-
|
113
|
-
class Client
|
114
|
-
attr_reader :fd, :body_head, :content_length
|
115
|
-
BASE_ENV = {
|
116
|
-
'SERVER_NAME' => '0.0.0.0',
|
117
|
-
'SCRIPT_NAME' => '',
|
118
|
-
'QUERY_STRING' => '',
|
119
|
-
'SERVER_SOFTWARE' => "Ebb-Ruby #{Ebb::VERSION}",
|
120
|
-
'SERVER_PROTOCOL' => 'HTTP/1.1',
|
121
|
-
'rack.version' => [0, 1],
|
122
|
-
'rack.errors' => STDERR,
|
123
|
-
'rack.url_scheme' => 'http',
|
124
|
-
'rack.multiprocess' => false,
|
125
|
-
'rack.run_once' => false
|
126
|
-
}
|
127
|
-
|
128
|
-
def env
|
129
|
-
@env ||= begin
|
130
|
-
env = FFI::client_env(self).update(BASE_ENV)
|
131
|
-
env['rack.input'] = RequestBody.new(self)
|
132
|
-
env
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
def write_status(status)
|
137
|
-
FFI::client_write_status(self, status, HTTP_STATUS_CODES[status])
|
138
|
-
end
|
139
|
-
|
140
|
-
def write_body(data)
|
141
|
-
FFI::client_write_body(self, data)
|
142
|
-
end
|
143
|
-
|
144
|
-
def write_header(field, value)
|
145
|
-
FFI::client_write_header(self, field, value)
|
146
|
-
end
|
147
|
-
|
148
|
-
def release
|
149
|
-
FFI::client_release(self)
|
150
|
-
end
|
151
|
-
|
152
|
-
def set_keep_alive
|
153
|
-
FFI::client_set_keep_alive(self)
|
154
|
-
end
|
155
|
-
|
156
|
-
def should_keep_alive?
|
157
|
-
if env['HTTP_VERSION'] == 'HTTP/1.0'
|
158
|
-
return env['HTTP_CONNECTION'] =~ /Keep-Alive/i
|
159
|
-
else
|
160
|
-
return env['HTTP_CONNECTION'] !~ /close/i
|
161
|
-
end
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
|
-
class RequestBody
|
166
|
-
def initialize(client)
|
167
|
-
@content_length = client.content_length
|
168
|
-
if client.body_head
|
169
|
-
@body_head = StringIO.new(client.body_head)
|
170
|
-
if @body_head.length < @content_length
|
171
|
-
@socket = IO.new(client.fd)
|
172
|
-
end
|
173
|
-
end
|
174
|
-
@total_read = 0
|
175
|
-
end
|
176
|
-
|
177
|
-
def read(len = nil)
|
178
|
-
to_read = len.nil? ? @content_length - @total_read : min(len, @content_length - @total_read)
|
179
|
-
return nil if to_read == 0 or @body_head.nil?
|
180
|
-
unless out = @body_head.read(to_read)
|
181
|
-
return nil if @socket.nil?
|
182
|
-
out = @socket.read(to_read)
|
183
|
-
end
|
184
|
-
@total_read += out.length
|
185
|
-
out
|
186
|
-
end
|
187
|
-
|
188
|
-
def gets
|
189
|
-
raise NotImplemented
|
190
|
-
end
|
191
|
-
|
192
|
-
def each(&block)
|
193
|
-
raise NotImplemented
|
194
|
-
end
|
195
|
-
|
196
|
-
private
|
197
|
-
|
198
|
-
# cause i don't want to create an array
|
199
|
-
def min(a,b)
|
200
|
-
a > b ? b : a
|
201
|
-
end
|
202
|
-
|
203
|
-
end
|
204
|
-
|
205
|
-
|
206
|
-
HTTP_STATUS_CODES = {
|
207
|
-
100 => 'Continue',
|
208
|
-
101 => 'Switching Protocols',
|
209
|
-
200 => 'OK',
|
210
|
-
201 => 'Created',
|
211
|
-
202 => 'Accepted',
|
212
|
-
203 => 'Non-Authoritative Information',
|
213
|
-
204 => 'No Content',
|
214
|
-
205 => 'Reset Content',
|
215
|
-
206 => 'Partial Content',
|
216
|
-
300 => 'Multiple Choices',
|
217
|
-
301 => 'Moved Permanently',
|
218
|
-
302 => 'Moved Temporarily',
|
219
|
-
303 => 'See Other',
|
220
|
-
304 => 'Not Modified',
|
221
|
-
305 => 'Use Proxy',
|
222
|
-
400 => 'Bad Request',
|
223
|
-
401 => 'Unauthorized',
|
224
|
-
402 => 'Payment Required',
|
225
|
-
403 => 'Forbidden',
|
226
|
-
404 => 'Not Found',
|
227
|
-
405 => 'Method Not Allowed',
|
228
|
-
406 => 'Not Acceptable',
|
229
|
-
407 => 'Proxy Authentication Required',
|
230
|
-
408 => 'Request Time-out',
|
231
|
-
409 => 'Conflict',
|
232
|
-
410 => 'Gone',
|
233
|
-
411 => 'Length Required',
|
234
|
-
412 => 'Precondition Failed',
|
235
|
-
413 => 'Request Entity Too Large',
|
236
|
-
414 => 'Request-URI Too Large',
|
237
|
-
415 => 'Unsupported Media Type',
|
238
|
-
500 => 'Internal Server Error',
|
239
|
-
501 => 'Not Implemented',
|
240
|
-
502 => 'Bad Gateway',
|
241
|
-
503 => 'Service Unavailable',
|
242
|
-
504 => 'Gateway Time-out',
|
243
|
-
505 => 'HTTP Version not supported'
|
244
|
-
}.freeze
|
245
|
-
end
|
246
|
-
|
247
|
-
|
248
|
-
module Rack
|
249
|
-
module Handler
|
250
|
-
module Ebb
|
251
|
-
def self.run(app, options={})
|
252
|
-
::Ebb.start_server(app, options)
|
253
|
-
end
|
254
|
-
end
|
255
|
-
end
|
256
|
-
end
|
257
|
-
|
data/ruby_lib/ebb/runner.rb
DELETED
@@ -1,134 +0,0 @@
|
|
1
|
-
require 'optparse'
|
2
|
-
|
3
|
-
module Kernel
|
4
|
-
unless respond_to? :daemonize # Already part of Ruby 1.9, yeah!
|
5
|
-
# Turns the current script into a daemon process that detaches from the console.
|
6
|
-
# It can be shut down with a TERM signal. Taken from ActiveSupport.
|
7
|
-
def daemonize
|
8
|
-
exit if fork # Parent exits, child continues.
|
9
|
-
Process.setsid # Become session leader.
|
10
|
-
exit if fork # Zap session leader. See [1].
|
11
|
-
Dir.chdir "/" # Release old working directory.
|
12
|
-
File.umask 0000 # Ensure sensible umask. Adjust as needed.
|
13
|
-
STDIN.reopen "/dev/null" # Free file descriptors and
|
14
|
-
STDOUT.reopen "/dev/null", "a" # point them somewhere sensible.
|
15
|
-
STDERR.reopen STDOUT # STDOUT/ERR should better go to a logfile.
|
16
|
-
trap("TERM") { exit }
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
module Ebb
|
22
|
-
class Runner
|
23
|
-
# Classes are modules and I hate this 'Base' class pattern. I'm putting
|
24
|
-
# other classes inside this one.
|
25
|
-
autoload :Rails, LIBDIR + '/ebb/runner/rails'
|
26
|
-
|
27
|
-
# Kill the process which PID is stored in +pid_file+.
|
28
|
-
def self.kill(pid_file, timeout=60)
|
29
|
-
raise ArgumentError, 'You must specify a pid_file to stop deamonized server' unless pid_file
|
30
|
-
|
31
|
-
if pid = File.read(pid_file)
|
32
|
-
pid = pid.to_i
|
33
|
-
|
34
|
-
Process.kill('KILL', pid)
|
35
|
-
Ebb.log.puts "stopped!"
|
36
|
-
else
|
37
|
-
Ebb.log.puts "Can't stop process, no PID found in #{@pid_file}"
|
38
|
-
end
|
39
|
-
rescue Errno::ESRCH # No such process
|
40
|
-
Ebb.log.puts "process not found!"
|
41
|
-
ensure
|
42
|
-
File.delete(pid_file) rescue nil
|
43
|
-
end
|
44
|
-
|
45
|
-
def self.remove_pid_file(file)
|
46
|
-
File.delete(file) if file && File.exists?(file) && Process.pid == File.read(file)
|
47
|
-
end
|
48
|
-
|
49
|
-
def self.write_pid_file(file)
|
50
|
-
Ebb.log.puts ">> Writing PID to #{file}"
|
51
|
-
open(file,"w+") { |f| f.write(Process.pid) }
|
52
|
-
File.chmod(0644, file)
|
53
|
-
end
|
54
|
-
|
55
|
-
attr_reader :options
|
56
|
-
|
57
|
-
def initialize
|
58
|
-
@parser = OptionParser.new
|
59
|
-
@options = {
|
60
|
-
:port => 4001,
|
61
|
-
:timeout => 60
|
62
|
-
}
|
63
|
-
end
|
64
|
-
|
65
|
-
def parse_options(argv)
|
66
|
-
@parser.banner = "Usage: #{self.class} [options] start | stop"
|
67
|
-
@parser.separator ""
|
68
|
-
extra_options if respond_to?(:extra_options)
|
69
|
-
|
70
|
-
@parser.separator ""
|
71
|
-
@parser.on("-p", "--port PORT", "(default: #{@options[:port]})") { |p| @options[:port] = p }
|
72
|
-
@parser.on("-s", "--socket SOCKET", "listen on unix domain socket") { |socket| options[:unix_socket] = socket }
|
73
|
-
@parser.on("-d", "--daemonize", "Daemonize") { @options[:daemonize] = true }
|
74
|
-
@parser.on("-l", "--log-file FILE", "File to redirect output") { |f| @options[:log_file]=f }
|
75
|
-
@parser.on("-P", "--pid-file FILE", "File to store PID") { |f| @options[:pid_file]=f }
|
76
|
-
# @parser.on("-t", "--timeout SECONDS", "(default: #{@options[:timeout]})") { |s| @options[:timeout]=s }
|
77
|
-
|
78
|
-
@parser.separator ""
|
79
|
-
@parser.on_tail("-h", "--help", "Show this message") do
|
80
|
-
Ebb.log.puts @parser
|
81
|
-
exit
|
82
|
-
end
|
83
|
-
@parser.on_tail('-v', '--version', "Show version") do
|
84
|
-
Ebb.log.puts Ebb::Client::BASE_ENV['SERVER_SOFTWARE']
|
85
|
-
exit
|
86
|
-
end
|
87
|
-
|
88
|
-
@parser.parse!(argv)
|
89
|
-
end
|
90
|
-
|
91
|
-
def run(argv)
|
92
|
-
parse_options(argv)
|
93
|
-
|
94
|
-
case argv[0]
|
95
|
-
when 'start'
|
96
|
-
Ebb.log.print("Ebb is loading the application...")
|
97
|
-
Ebb.log.flush()
|
98
|
-
@app = app(@options)
|
99
|
-
Ebb.log.puts("done")
|
100
|
-
|
101
|
-
if @options[:daemonize]
|
102
|
-
pwd = Dir.pwd # Current directory is changed during daemonization, so store it
|
103
|
-
Kernel.daemonize
|
104
|
-
Dir.chdir pwd
|
105
|
-
trap('HUP', 'IGNORE') # Don't die upon logout
|
106
|
-
end
|
107
|
-
|
108
|
-
if @options[:log_file]
|
109
|
-
[STDOUT, STDERR].each { |f| f.reopen @options[:log_file], 'a' }
|
110
|
-
end
|
111
|
-
|
112
|
-
if @options[:pid_file]
|
113
|
-
Runner.write_pid_file(@options[:pid_file])
|
114
|
-
at_exit do
|
115
|
-
Ebb.log.puts ">> Exiting!"
|
116
|
-
Runner.remove_pid_file(@options[:pid_file])
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
Ebb::start_server(@app, @options)
|
121
|
-
when 'stop'
|
122
|
-
Ebb::Runner.kill @options[:pid_file], @options[:timeout]
|
123
|
-
when nil
|
124
|
-
Ebb.log.puts "Command required"
|
125
|
-
Ebb.log.puts @parser
|
126
|
-
exit 1
|
127
|
-
else
|
128
|
-
abort "Invalid command : #{argv[0]}"
|
129
|
-
end
|
130
|
-
|
131
|
-
end
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
@@ -1,31 +0,0 @@
|
|
1
|
-
module Rack
|
2
|
-
module Adapter
|
3
|
-
autoload :Rails, Ebb::LIBDIR + '/rack/adapter/rails'
|
4
|
-
end
|
5
|
-
end
|
6
|
-
|
7
|
-
module Ebb
|
8
|
-
class Runner
|
9
|
-
class Rails < Runner
|
10
|
-
def extra_options
|
11
|
-
# defaults for ebb_rails
|
12
|
-
@options.update(
|
13
|
-
:environment => 'development',
|
14
|
-
:port => 3000
|
15
|
-
)
|
16
|
-
|
17
|
-
@parser.on("-e", "--env ENV",
|
18
|
-
"Rails environment (default: development)") do |env|
|
19
|
-
@options[:environment] = env
|
20
|
-
end
|
21
|
-
@parser.on("-c", "--chdir DIR", "RAILS_ROOT directory") do |c|
|
22
|
-
@options[:root] = c
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def app(options)
|
27
|
-
Rack::Adapter::Rails.new(options)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
@@ -1,159 +0,0 @@
|
|
1
|
-
require 'cgi'
|
2
|
-
require 'rubygems'
|
3
|
-
require 'rack'
|
4
|
-
|
5
|
-
# Adapter to run a Rails app with any supported Rack handler.
|
6
|
-
# By default it will try to load the Rails application in the
|
7
|
-
# current directory in the development environment.
|
8
|
-
# Options:
|
9
|
-
# root: Root directory of the Rails app
|
10
|
-
# env: Rails environment to run in (development, production or test)
|
11
|
-
# Based on http://fuzed.rubyforge.org/ Rails adapter
|
12
|
-
module Rack
|
13
|
-
module Adapter
|
14
|
-
class Rails
|
15
|
-
def initialize(options={})
|
16
|
-
@root = options[:root] || Dir.pwd
|
17
|
-
@env = options[:environment] || 'development'
|
18
|
-
@prefix = options[:prefix]
|
19
|
-
|
20
|
-
load_application
|
21
|
-
|
22
|
-
@file_server = Rack::File.new(::File.join(RAILS_ROOT, "public"))
|
23
|
-
end
|
24
|
-
|
25
|
-
def load_application
|
26
|
-
ENV['RAILS_ENV'] = @env
|
27
|
-
|
28
|
-
require "#{@root}/config/environment"
|
29
|
-
require 'dispatcher'
|
30
|
-
|
31
|
-
ActionController::AbstractRequest.relative_url_root = @prefix if @prefix
|
32
|
-
end
|
33
|
-
|
34
|
-
# TODO refactor this in File#can_serve?(path) ??
|
35
|
-
def file_exist?(path)
|
36
|
-
full_path = ::File.join(@file_server.root, Utils.unescape(path))
|
37
|
-
::File.file?(full_path) && ::File.readable?(full_path)
|
38
|
-
end
|
39
|
-
|
40
|
-
def serve_file(env)
|
41
|
-
@file_server.call(env)
|
42
|
-
end
|
43
|
-
|
44
|
-
def serve_rails(env)
|
45
|
-
request = Request.new(env)
|
46
|
-
response = Response.new
|
47
|
-
|
48
|
-
session_options = ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS
|
49
|
-
cgi = CGIWrapper.new(request, response)
|
50
|
-
|
51
|
-
Dispatcher.dispatch(cgi, session_options, response)
|
52
|
-
|
53
|
-
response.finish
|
54
|
-
end
|
55
|
-
|
56
|
-
def call(env)
|
57
|
-
path = env['PATH_INFO'].chomp('/')
|
58
|
-
cached_path = (path.empty? ? 'index' : path) + ActionController::Base.page_cache_extension
|
59
|
-
|
60
|
-
if file_exist?(path) # Serve the file if it's there
|
61
|
-
serve_file(env)
|
62
|
-
elsif file_exist?(cached_path) # Serve the page cache if it's there
|
63
|
-
env['PATH_INFO'] = cached_path
|
64
|
-
serve_file(env)
|
65
|
-
else # No static file, let Rails handle it
|
66
|
-
serve_rails(env)
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
# Never spawn threads for a request
|
71
|
-
def deferred?(env)
|
72
|
-
false
|
73
|
-
end
|
74
|
-
|
75
|
-
protected
|
76
|
-
|
77
|
-
class CGIWrapper < ::CGI
|
78
|
-
def initialize(request, response, *args)
|
79
|
-
@request = request
|
80
|
-
@response = response
|
81
|
-
@args = *args
|
82
|
-
@input = request.body
|
83
|
-
|
84
|
-
super *args
|
85
|
-
end
|
86
|
-
|
87
|
-
def header(options = "text/html")
|
88
|
-
if options.is_a?(String)
|
89
|
-
@response['Content-Type'] = options unless @response['Content-Type']
|
90
|
-
else
|
91
|
-
@response['Content-Length'] = options.delete('Content-Length').to_s if options['Content-Length']
|
92
|
-
|
93
|
-
@response['Content-Type'] = options.delete('type') || "text/html"
|
94
|
-
@response['Content-Type'] += "; charset=" + options.delete('charset') if options['charset']
|
95
|
-
|
96
|
-
@response['Content-Language'] = options.delete('language') if options['language']
|
97
|
-
@response['Expires'] = options.delete('expires') if options['expires']
|
98
|
-
|
99
|
-
@response.status = options.delete('Status') if options['Status']
|
100
|
-
|
101
|
-
# Convert 'cookie' header to 'Set-Cookie' headers.
|
102
|
-
# Because Set-Cookie header can appear more the once in the response body,
|
103
|
-
# we store it in a line break seperated string that will be translated to
|
104
|
-
# multiple Set-Cookie header by the handler.
|
105
|
-
if cookie = options.delete('cookie')
|
106
|
-
cookies = []
|
107
|
-
|
108
|
-
case cookie
|
109
|
-
when Array then cookie.each { |c| cookies << c.to_s }
|
110
|
-
when Hash then cookie.each { |_, c| cookies << c.to_s }
|
111
|
-
else cookies << cookie.to_s
|
112
|
-
end
|
113
|
-
|
114
|
-
@output_cookies.each { |c| cookies << c.to_s } if @output_cookies
|
115
|
-
|
116
|
-
@response['Set-Cookie'] = [@response['Set-Cookie'], cookies].compact.join("\n")
|
117
|
-
end
|
118
|
-
|
119
|
-
options.each { |k,v| @response[k] = v }
|
120
|
-
end
|
121
|
-
|
122
|
-
""
|
123
|
-
end
|
124
|
-
|
125
|
-
def params
|
126
|
-
@params ||= @request.params
|
127
|
-
end
|
128
|
-
|
129
|
-
def cookies
|
130
|
-
@request.cookies
|
131
|
-
end
|
132
|
-
|
133
|
-
def query_string
|
134
|
-
@request.query_string
|
135
|
-
end
|
136
|
-
|
137
|
-
# Used to wrap the normal args variable used inside CGI.
|
138
|
-
def args
|
139
|
-
@args
|
140
|
-
end
|
141
|
-
|
142
|
-
# Used to wrap the normal env_table variable used inside CGI.
|
143
|
-
def env_table
|
144
|
-
@request.env
|
145
|
-
end
|
146
|
-
|
147
|
-
# Used to wrap the normal stdinput variable used inside CGI.
|
148
|
-
def stdinput
|
149
|
-
@input
|
150
|
-
end
|
151
|
-
|
152
|
-
def stdoutput
|
153
|
-
STDERR.puts "stdoutput should not be used."
|
154
|
-
@response.body
|
155
|
-
end
|
156
|
-
end
|
157
|
-
end
|
158
|
-
end
|
159
|
-
end
|