ebb 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|