plezi 0.9.2 → 0.10.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +30 -0
- data/README.md +44 -31
- data/bin/plezi +3 -3
- data/lib/plezi.rb +21 -43
- data/lib/plezi/common/defer.rb +21 -0
- data/lib/plezi/common/dsl.rb +115 -91
- data/lib/plezi/common/redis.rb +44 -0
- data/lib/plezi/common/settings.rb +58 -0
- data/lib/plezi/handlers/controller_core.rb +132 -0
- data/lib/plezi/handlers/controller_magic.rb +85 -259
- data/lib/plezi/handlers/http_router.rb +139 -60
- data/lib/plezi/handlers/route.rb +9 -178
- data/lib/plezi/handlers/stubs.rb +2 -2
- data/lib/plezi/helpers/http_sender.rb +72 -0
- data/lib/plezi/helpers/magic_helpers.rb +12 -0
- data/lib/plezi/{server → helpers}/mime_types.rb +0 -0
- data/lib/plezi/version.rb +1 -1
- data/plezi.gemspec +3 -11
- data/resources/Gemfile +20 -21
- data/resources/controller.rb +2 -2
- data/resources/oauth_config.rb +1 -1
- data/resources/redis_config.rb +2 -0
- data/test/plezi_tests.rb +39 -46
- metadata +24 -33
- data/lib/plezi/common/logging.rb +0 -60
- data/lib/plezi/eventmachine/connection.rb +0 -190
- data/lib/plezi/eventmachine/em.rb +0 -98
- data/lib/plezi/eventmachine/io.rb +0 -272
- data/lib/plezi/eventmachine/protocol.rb +0 -54
- data/lib/plezi/eventmachine/queue.rb +0 -51
- data/lib/plezi/eventmachine/ssl_connection.rb +0 -144
- data/lib/plezi/eventmachine/timers.rb +0 -117
- data/lib/plezi/eventmachine/workers.rb +0 -33
- data/lib/plezi/handlers/http_echo.rb +0 -27
- data/lib/plezi/handlers/http_host.rb +0 -214
- data/lib/plezi/handlers/magic_helpers.rb +0 -32
- data/lib/plezi/server/http.rb +0 -129
- data/lib/plezi/server/http_protocol.rb +0 -319
- data/lib/plezi/server/http_request.rb +0 -146
- data/lib/plezi/server/http_response.rb +0 -319
- data/lib/plezi/server/websocket.rb +0 -251
- data/lib/plezi/server/websocket_client.rb +0 -178
- data/lib/plezi/server/ws_response.rb +0 -161
@@ -1,146 +0,0 @@
|
|
1
|
-
module Plezi
|
2
|
-
|
3
|
-
# class is the base for the HTTP server.
|
4
|
-
#
|
5
|
-
# the class is initialized with a TCP/IP connection socket and starts
|
6
|
-
# an event driven cycle using the `EventStack.push` and `EventStack.reverse_async`.
|
7
|
-
#
|
8
|
-
# to-do: fox logging.
|
9
|
-
class HTTPRequest < Hash
|
10
|
-
|
11
|
-
def initialize service
|
12
|
-
super()
|
13
|
-
self[:plezi_service] = service
|
14
|
-
end
|
15
|
-
|
16
|
-
public
|
17
|
-
|
18
|
-
# the request's headers
|
19
|
-
def headers
|
20
|
-
self.select {|k,v| k.is_a? String }
|
21
|
-
end
|
22
|
-
# the request's method (GET, POST... etc').
|
23
|
-
def request_method
|
24
|
-
self[:method]
|
25
|
-
end
|
26
|
-
# set request's method (GET, POST... etc').
|
27
|
-
def request_method= value
|
28
|
-
self[:method] = value
|
29
|
-
end
|
30
|
-
# the parameters sent by the client.
|
31
|
-
def params
|
32
|
-
self[:params]
|
33
|
-
end
|
34
|
-
# the cookies sent by the client.
|
35
|
-
def cookies
|
36
|
-
self[:cookies]
|
37
|
-
end
|
38
|
-
|
39
|
-
# the query string
|
40
|
-
def query
|
41
|
-
self[:query]
|
42
|
-
end
|
43
|
-
|
44
|
-
# the original (frozen) path (resource requested).
|
45
|
-
def original_path
|
46
|
-
self[:original_path]
|
47
|
-
end
|
48
|
-
|
49
|
-
# the requested path (rewritable).
|
50
|
-
def path
|
51
|
-
self[:path]
|
52
|
-
end
|
53
|
-
def path=(new_path)
|
54
|
-
self[:path] = new_path
|
55
|
-
end
|
56
|
-
|
57
|
-
# the base url ([http/https]://host[:port])
|
58
|
-
def base_url switch_protocol = nil
|
59
|
-
"#{switch_protocol || self[:requested_protocol]}://#{self[:host_name]}#{self[:port]? ":#{self[:port]}" : ''}"
|
60
|
-
end
|
61
|
-
|
62
|
-
# the request's url, without any GET parameters ([http/https]://host[:port]/path)
|
63
|
-
def request_url switch_protocol = nil
|
64
|
-
"#{base_url switch_protocol}#{self[:original_path]}"
|
65
|
-
end
|
66
|
-
|
67
|
-
# the service (socket wrapper) that answered this request
|
68
|
-
def service
|
69
|
-
self[:plezi_service]
|
70
|
-
end
|
71
|
-
# the protocol managing this request
|
72
|
-
def protocol
|
73
|
-
self[:requested_protocol]
|
74
|
-
end
|
75
|
-
# the handler dealing with this request
|
76
|
-
def handler
|
77
|
-
self[:plezi_service].handler
|
78
|
-
end
|
79
|
-
|
80
|
-
# method recognition
|
81
|
-
|
82
|
-
# returns true of the method == GET
|
83
|
-
def get?
|
84
|
-
self[:method] == 'GET'
|
85
|
-
end
|
86
|
-
# returns true of the method == HEAD
|
87
|
-
def head?
|
88
|
-
self[:method] == 'HEAD'
|
89
|
-
end
|
90
|
-
# returns true of the method == POST
|
91
|
-
def post?
|
92
|
-
self[:method] == 'POST'
|
93
|
-
end
|
94
|
-
# returns true of the method == PUT
|
95
|
-
def put?
|
96
|
-
self[:method] == 'PUT'
|
97
|
-
end
|
98
|
-
# returns true of the method == DELETE
|
99
|
-
def delete?
|
100
|
-
self[:method] == 'DELETE'
|
101
|
-
end
|
102
|
-
# returns true of the method == TRACE
|
103
|
-
def trace?
|
104
|
-
self[:method] == 'TRACE'
|
105
|
-
end
|
106
|
-
# returns true of the method == OPTIONS
|
107
|
-
def options?
|
108
|
-
self[:method] == 'OPTIONS'
|
109
|
-
end
|
110
|
-
# returns true of the method == CONNECT
|
111
|
-
def connect?
|
112
|
-
self[:method] == 'CONNECT'
|
113
|
-
end
|
114
|
-
# returns true of the method == PATCH
|
115
|
-
def patch?
|
116
|
-
self[:method] == 'PATCH'
|
117
|
-
end
|
118
|
-
# returns true if the request is of type JSON.
|
119
|
-
def json?
|
120
|
-
self['content-type'].match /application\/json/
|
121
|
-
end
|
122
|
-
# returns true if the request is of type XML.
|
123
|
-
def xml?
|
124
|
-
self['content-type'].match /text\/xml/
|
125
|
-
end
|
126
|
-
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
|
131
|
-
######
|
132
|
-
## example requests
|
133
|
-
|
134
|
-
# GET / HTTP/1.1
|
135
|
-
# Host: localhost:3000
|
136
|
-
# Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
|
137
|
-
# Cookie: user_token=2INa32_vDgx8Aa1qe43oILELpSdIe9xwmT8GTWjkS-w
|
138
|
-
# User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10) AppleWebKit/600.1.25 (KHTML, like Gecko) Version/8.0 Safari/600.1.25
|
139
|
-
# Accept-Language: en-us
|
140
|
-
# Accept-Encoding: gzip, deflate
|
141
|
-
# Connection: keep-alive
|
142
|
-
#
|
143
|
-
# => "GET / HTTP/1.1\n\rHost: localhost:2000\n\rAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\n\rCookie: user_token=2INa32_vDgx8Aa1qe43oILELpSdIe9xwmT8GTWjkS-w\n\rUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10) AppleWebKit/600.1.25 (KHTML, like Gecko) Version/8.0 Safari/600.1.25\n\rAccept-Language: en-us\n\rAccept-Encoding: gzip, deflate\n\rConnection: keep-alive\n\r\n\r"
|
144
|
-
# => "GET /people/are/friendly HTTP/1.1\n\rHost: localhost:2000\n\rAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\n\rCookie: user_token=2INa32_vDgx8Aa1qe43oILELpSdIe9xwmT8GTWjkS-w\n\rUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10) AppleWebKit/600.1.25 (KHTML, like Gecko) Version/8.0 Safari/600.1.25\n\rAccept-Language: en-us\n\rAccept-Encoding: gzip, deflate\n\rConnection: keep-alive\n\r\n\r"
|
145
|
-
# => "GET /girls?sexy=true HTTP/1.1\n\rHost: localhost:2000\n\rAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\n\rCookie: user_token=2INa32_vDgx8Aa1qe43oILELpSdIe9xwmT8GTWjkS-w\n\rUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10) AppleWebKit/600.1.25 (KHTML, like Gecko) Version/8.0 Safari/600.1.25\n\rAccept-Language: en-us\n\rAccept-Encoding: gzip, deflate\n\rConnection: keep-alive\n\r\n\r"
|
146
|
-
# chunked => "17d; ignored data=boaz\r\nGET / HTTP/1.1\r\nHost: localhost:3000\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nCookie: user_token=2INa32_vDgx8Aa1qe43oILELpSdIe9xwmT8GTWjkS-w\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10) AppleWebKit/600.1.25 (KHTML, like Gecko) Version/8.0 Safari/600.1.25\r\nAccept-Language: en-us\r\nAccept-Encoding: gzip, deflate\r\nConnection: keep-alive\r\nc\r\n\r\nparsed as:\r\n\r\n4f4\r\n{:raw=>\"GET / HTTP/1.1\\r\\nHost: localhost:3000\\r\\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\\r\\nCookie: user_token=2INa32_vDgx8Aa1qe43oILELpSdIe9xwmT8GTWjkS-w\\r\\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10) AppleWebKit/600.1.25 (KHTML, like Gecko) Version/8.0 Safari/600.1.25\\r\\nAccept-Language: en-us\\r\\nAccept-Encoding: gzip, deflate\\r\\nConnection: keep-alive\\r\\n\\r\\n\", :plezi_service=>#<Plezi::BasicService:0x007ff4daab5ac8 @handler=Plezi::HTTPEcho, @socket=#<TCPSocket:fd 9>, @in_que=\"\", @out_que=[], @locker=#<Mutex:0x007ff4daab5a28>, @parameters={:protocol=>Plezi::HTTPProtocol, :handler=>Plezi::HTTPEcho}, @protocol=Plezi::HTTPProtocol>, :params=>{}, :cookies=>{:user_token=>\"2INa32_vDgx8Aa1qe43oILELpSdIe9xwmT8GTWjkS-w\"}, :method=>\"GET\", :query=>\"/\", :original_path=>\"/\", :path=>\"/\", :version=>\"HTTP/1.1\", \"host\"=>\"localhost:3000\", \"accept\"=>\"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\", \"cookie\"=>\"user_token=2INa32_vDgx8Aa1qe43oILELpSdIe9xwmT8GTWjkS-w\", \"user-agent\"=>\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10) AppleWebKit/600.1.25 (KHTML, like Gecko) Version/8.0 Safari/600.1.25\", \"accept-language\"=>\"en-us\", \"accept-encoding\"=>\"gzip, deflate\", \"connection\"=>\"keep-alive\"}\r\n0\r\n\r\n"
|
@@ -1,319 +0,0 @@
|
|
1
|
-
module Plezi
|
2
|
-
|
3
|
-
# this class handles HTTP response objects.
|
4
|
-
#
|
5
|
-
# learning from rack, the basic response objects imitates the [0, {}, []] structure... with some updates.
|
6
|
-
#
|
7
|
-
# the Response's body should respond to each (and optionally to close).
|
8
|
-
#
|
9
|
-
# The response can be sent asynchronously, but headers and status cannot be changed once the response started sending data.
|
10
|
-
class HTTPResponse
|
11
|
-
|
12
|
-
#the response's status code
|
13
|
-
attr_accessor :status
|
14
|
-
#the response's headers
|
15
|
-
attr_reader :headers
|
16
|
-
#the flash cookie-jar (single-use cookies, that survive only one request)
|
17
|
-
attr_reader :flash
|
18
|
-
#the response's body container (defaults to an array, but can be replaces by any obect that supports `each` - `close` is NOT supported - call `close` as a callback block after `send` if you need to close the object).
|
19
|
-
attr_accessor :body
|
20
|
-
#bytes sent to the asynchronous que so far - excluding headers (only the body object).
|
21
|
-
attr_reader :bytes_sent
|
22
|
-
#the service through which the response will be sent.
|
23
|
-
attr_reader :service
|
24
|
-
#the request.
|
25
|
-
attr_accessor :request
|
26
|
-
#the http version header
|
27
|
-
attr_accessor :http_version
|
28
|
-
#Danger Zone! direct access to cookie headers - don't use this unless you know what you're doing!
|
29
|
-
attr_reader :cookies
|
30
|
-
|
31
|
-
# the response object responds to a specific request on a specific service.
|
32
|
-
# hence, to initialize a response object, a request must be set.
|
33
|
-
#
|
34
|
-
# use, at the very least `HTTPResponse.new request`
|
35
|
-
def initialize request, status = 200, headers = {}, body = []
|
36
|
-
@request, @status, @headers, @body, @service = request, status, headers, body, request[:plezi_service]
|
37
|
-
@http_version = 'HTTP/1.1' # request.version
|
38
|
-
@bytes_sent = 0
|
39
|
-
@finished = @streaming = false
|
40
|
-
@cookies = {}
|
41
|
-
@chunked = false
|
42
|
-
# propegate flash object
|
43
|
-
@flash = Hash.new do |hs,k|
|
44
|
-
hs["plezi_flash_#{k.to_s}".to_sym] if hs.has_key? "plezi_flash_#{k.to_s}".to_sym
|
45
|
-
end
|
46
|
-
request.cookies.each do |k,v|
|
47
|
-
@flash[k] = v if k.to_s.start_with? 'plezi_flash_'
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
# returns true if headers were already sent
|
52
|
-
def headers_sent?
|
53
|
-
@headers.frozen?
|
54
|
-
end
|
55
|
-
|
56
|
-
# returns true if the response is already finished (the client isn't expecting any more data).
|
57
|
-
def finished?
|
58
|
-
@finished
|
59
|
-
end
|
60
|
-
|
61
|
-
# returns true if the response is set to http streaming (you will need to close the response manually by calling #finish).
|
62
|
-
def streaming?
|
63
|
-
@streaming
|
64
|
-
end
|
65
|
-
|
66
|
-
# sets the http streaming flag, so that the response could be handled asynchronously.
|
67
|
-
#
|
68
|
-
# if this flag is not set, the response will try to automatically finish its job
|
69
|
-
# (send its data and close the connection) once the controllers method has finished.
|
70
|
-
#
|
71
|
-
# If HTTP streaming is set, you will need to manually call `response.finish`
|
72
|
-
# of the connection will not close properly.
|
73
|
-
def start_http_streaming
|
74
|
-
@streaming = @chunked = true
|
75
|
-
end
|
76
|
-
|
77
|
-
# pushes data to the body of the response. this is the preferred way to add data to the response.
|
78
|
-
#
|
79
|
-
# if HTTP streaming is used, remember to call #send to send the data.
|
80
|
-
# it is also possible to only use #send while streaming, although performance should be considered when streaming using #send rather then caching using #<<.
|
81
|
-
def << str
|
82
|
-
body.push str
|
83
|
-
# send if streaming?
|
84
|
-
end
|
85
|
-
|
86
|
-
# returns a response header, if set.
|
87
|
-
def [] header
|
88
|
-
headers[header] # || @cookies[header]
|
89
|
-
end
|
90
|
-
|
91
|
-
# sets a response header. response headers should be a down-case String or Symbol.
|
92
|
-
#
|
93
|
-
# this is the prefered to set a header.
|
94
|
-
#
|
95
|
-
# returns the value set for the header.
|
96
|
-
#
|
97
|
-
# see HTTP response headers for valid headers and values: http://en.wikipedia.org/wiki/List_of_HTTP_header_fields
|
98
|
-
def []= header, value
|
99
|
-
header.is_a?(String) ? header.downcase! : (header.is_a?(Symbol) ? (header = header.to_s.downcase.to_sym) : (return false))
|
100
|
-
headers[header] = value
|
101
|
-
end
|
102
|
-
|
103
|
-
# sets/deletes cookies when headers are sent.
|
104
|
-
#
|
105
|
-
# accepts:
|
106
|
-
# name:: the cookie's name
|
107
|
-
# value:: the cookie's value
|
108
|
-
# parameters:: a parameters Hash for cookie creation.
|
109
|
-
#
|
110
|
-
# parameters accept any of the following Hash keys and values:
|
111
|
-
#
|
112
|
-
# expires:: a Time object with the expiration date. defaults to 10 years in the future.
|
113
|
-
# max_age:: a Max-Age HTTP cookie string.
|
114
|
-
# path:: the path from which the cookie is acessible. defaults to '/'.
|
115
|
-
# domain:: the domain for the cookie (best used to manage subdomains). defaults to the active domain (sub-domain limitations might apply).
|
116
|
-
# secure:: if set to `true`, the cookie will only be available over secure connections. defaults to false.
|
117
|
-
# http_only:: if true, the HttpOnly flag will be set (not accessible to javascript). defaults to false.
|
118
|
-
#
|
119
|
-
def set_cookie name, value, params = {}
|
120
|
-
params[:expires] = (Time.now - 315360000) unless value
|
121
|
-
value ||= 'deleted'
|
122
|
-
params[:expires] ||= (Time.now + 315360000) unless params[:max_age]
|
123
|
-
params[:path] ||= '/'
|
124
|
-
value = HTTP.encode(value.to_s)
|
125
|
-
if params[:max_age]
|
126
|
-
value << ('; Max-Age=%s' % params[:max_age])
|
127
|
-
else
|
128
|
-
value << ('; Expires=%s' % params[:expires].httpdate)
|
129
|
-
end
|
130
|
-
value << "; Path=#{params[:path]}"
|
131
|
-
value << "; Domain=#{params[:domain]}" if params[:domain]
|
132
|
-
value << '; Secure' if params[:secure]
|
133
|
-
value << '; HttpOnly' if params[:http_only]
|
134
|
-
@cookies[HTTP.encode(name.to_s).to_sym] = value
|
135
|
-
end
|
136
|
-
|
137
|
-
# deletes a cookie (actually calls `set_cookie name, nil`)
|
138
|
-
def delete_cookie name
|
139
|
-
set_cookie name, nil
|
140
|
-
end
|
141
|
-
|
142
|
-
# clears the response object, unless headers were already sent (use `response.body.clear` to clear only the unsent body).
|
143
|
-
#
|
144
|
-
# returns false if the response was already sent.
|
145
|
-
def clear
|
146
|
-
return false if headers.frozen? || @finished
|
147
|
-
@status, @body, @headers, @cookies = 200, [], {}, {}
|
148
|
-
true
|
149
|
-
end
|
150
|
-
|
151
|
-
# sends the response object. headers will be frozen (they can only be sent at the head of the response).
|
152
|
-
#
|
153
|
-
# the response will remain open for more data to be sent through (using `response << data` and `response.send`).
|
154
|
-
def send(str = nil)
|
155
|
-
raise 'HTTPResponse SERVICE MISSING: cannot send http response without a service.' unless service
|
156
|
-
body << str if str && body.is_a?(Array)
|
157
|
-
send_headers
|
158
|
-
return if request.head?
|
159
|
-
if @chunked
|
160
|
-
body.each do |s|
|
161
|
-
service.send "#{s.bytesize.to_s(16)}\r\n"
|
162
|
-
service.send s
|
163
|
-
service.send "\r\n"
|
164
|
-
@bytes_sent += s.bytesize
|
165
|
-
end
|
166
|
-
else
|
167
|
-
body.each do |s|
|
168
|
-
service.send s
|
169
|
-
@bytes_sent += s.bytesize
|
170
|
-
end
|
171
|
-
end
|
172
|
-
@body.is_a?(Array) ? @body.clear : ( @body = [] )
|
173
|
-
end
|
174
|
-
|
175
|
-
# prep for rack response
|
176
|
-
def prep_rack
|
177
|
-
@headers['content-length'] ||= body[0].bytesize.to_s if !headers_sent? && body.is_a?(Array) && body.length == 1
|
178
|
-
fix_cookie_headers
|
179
|
-
end
|
180
|
-
|
181
|
-
|
182
|
-
if defined?(PLEZI_ON_RACK)
|
183
|
-
# does nothing.
|
184
|
-
def finish
|
185
|
-
false
|
186
|
-
end
|
187
|
-
else
|
188
|
-
# sends the response and flags the response as complete. future data should not be sent. the flag will only be enforced be the Plezi router. your code might attempt sending data (which would probbaly be ignored by the client or raise an exception).
|
189
|
-
def finish
|
190
|
-
@headers['content-length'] ||= body[0].bytesize if !headers_sent? && body.is_a?(Array) && body.length == 1
|
191
|
-
self.send
|
192
|
-
service.send( (@chunked) ? "0\r\n\r\n" : nil)
|
193
|
-
@finished = true
|
194
|
-
# service.disconnect unless headers['keep-alive']
|
195
|
-
# log
|
196
|
-
Plezi.log_raw "#{request[:client_ip]} [#{Time.now.utc}] \"#{request[:method]} #{request[:original_path]} #{request[:requested_protocol]}\/#{request[:version]}\" #{status} #{bytes_sent.to_s} #{"%0.3f" % ((Time.now - request[:time_recieved])*1000)}ms\n"
|
197
|
-
end
|
198
|
-
|
199
|
-
end
|
200
|
-
|
201
|
-
# Danger Zone (internally used method, use with care): attempts to finish the response - if it was not flaged as streaming or completed.
|
202
|
-
def try_finish
|
203
|
-
finish unless @finished || @streaming
|
204
|
-
end
|
205
|
-
|
206
|
-
# Danger Zone (internally used method, use with care): fix response's headers before sending them (date, connection and transfer-coding).
|
207
|
-
def fix_cookie_headers
|
208
|
-
# remove old flash cookies
|
209
|
-
request.cookies.keys.each do |k|
|
210
|
-
if k.to_s.start_with? 'plezi_flash_'
|
211
|
-
set_cookie k, nil
|
212
|
-
flash.delete k
|
213
|
-
end
|
214
|
-
end
|
215
|
-
#set new flash cookies
|
216
|
-
@flash.each do |k,v|
|
217
|
-
set_cookie "plezi_flash_#{k.to_s}", v
|
218
|
-
end
|
219
|
-
end
|
220
|
-
# Danger Zone (internally used method, use with care): fix response's headers before sending them (date, connection and transfer-coding).
|
221
|
-
def send_headers
|
222
|
-
return false if @headers.frozen?
|
223
|
-
fix_cookie_headers
|
224
|
-
headers['cache-control'] ||= 'no-cache'
|
225
|
-
|
226
|
-
service.send "#{@http_version} #{status} #{STATUS_CODES[status] || 'unknown'}\r\nDate: #{Time.now.httpdate}\r\n"
|
227
|
-
|
228
|
-
unless headers['connection']
|
229
|
-
service.send "Connection: Keep-Alive\r\nKeep-Alive: timeout=5\r\n"
|
230
|
-
end
|
231
|
-
|
232
|
-
if headers['content-length']
|
233
|
-
@chunked = false
|
234
|
-
else
|
235
|
-
@chunked = true
|
236
|
-
service.send "Transfer-Encoding: chunked\r\n"
|
237
|
-
end
|
238
|
-
|
239
|
-
headers.each {|k,v| service.send "#{k.to_s}: #{v}\r\n"}
|
240
|
-
@cookies.each {|k,v| service.send "Set-Cookie: #{k.to_s}=#{v.to_s}\r\n"}
|
241
|
-
service.send "\r\n"
|
242
|
-
@headers.freeze
|
243
|
-
# @cookies.freeze
|
244
|
-
end
|
245
|
-
|
246
|
-
# response status codes, as defined.
|
247
|
-
STATUS_CODES = {100=>"Continue",
|
248
|
-
101=>"Switching Protocols",
|
249
|
-
102=>"Processing",
|
250
|
-
200=>"OK",
|
251
|
-
201=>"Created",
|
252
|
-
202=>"Accepted",
|
253
|
-
203=>"Non-Authoritative Information",
|
254
|
-
204=>"No Content",
|
255
|
-
205=>"Reset Content",
|
256
|
-
206=>"Partial Content",
|
257
|
-
207=>"Multi-Status",
|
258
|
-
208=>"Already Reported",
|
259
|
-
226=>"IM Used",
|
260
|
-
300=>"Multiple Choices",
|
261
|
-
301=>"Moved Permanently",
|
262
|
-
302=>"Found",
|
263
|
-
303=>"See Other",
|
264
|
-
304=>"Not Modified",
|
265
|
-
305=>"Use Proxy",
|
266
|
-
306=>"(Unused)",
|
267
|
-
307=>"Temporary Redirect",
|
268
|
-
308=>"Permanent Redirect",
|
269
|
-
400=>"Bad Request",
|
270
|
-
401=>"Unauthorized",
|
271
|
-
402=>"Payment Required",
|
272
|
-
403=>"Forbidden",
|
273
|
-
404=>"Not Found",
|
274
|
-
405=>"Method Not Allowed",
|
275
|
-
406=>"Not Acceptable",
|
276
|
-
407=>"Proxy Authentication Required",
|
277
|
-
408=>"Request Timeout",
|
278
|
-
409=>"Conflict",
|
279
|
-
410=>"Gone",
|
280
|
-
411=>"Length Required",
|
281
|
-
412=>"Precondition Failed",
|
282
|
-
413=>"Payload Too Large",
|
283
|
-
414=>"URI Too Long",
|
284
|
-
415=>"Unsupported Media Type",
|
285
|
-
416=>"Range Not Satisfiable",
|
286
|
-
417=>"Expectation Failed",
|
287
|
-
422=>"Unprocessable Entity",
|
288
|
-
423=>"Locked",
|
289
|
-
424=>"Failed Dependency",
|
290
|
-
426=>"Upgrade Required",
|
291
|
-
428=>"Precondition Required",
|
292
|
-
429=>"Too Many Requests",
|
293
|
-
431=>"Request Header Fields Too Large",
|
294
|
-
500=>"Internal Server Error",
|
295
|
-
501=>"Not Implemented",
|
296
|
-
502=>"Bad Gateway",
|
297
|
-
503=>"Service Unavailable",
|
298
|
-
504=>"Gateway Timeout",
|
299
|
-
505=>"HTTP Version Not Supported",
|
300
|
-
506=>"Variant Also Negotiates",
|
301
|
-
507=>"Insufficient Storage",
|
302
|
-
508=>"Loop Detected",
|
303
|
-
510=>"Not Extended",
|
304
|
-
511=>"Network Authentication Required"
|
305
|
-
}
|
306
|
-
end
|
307
|
-
end
|
308
|
-
|
309
|
-
######
|
310
|
-
## example requests
|
311
|
-
|
312
|
-
# GET / HTTP/1.1
|
313
|
-
# Host: localhost:2000
|
314
|
-
# Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
|
315
|
-
# Cookie: user_token=2INa32_vDgx8Aa1qe43oILELpSdIe9xwmT8GTWjkS-w
|
316
|
-
# User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10) AppleWebKit/600.1.25 (KHTML, like Gecko) Version/8.0 Safari/600.1.25
|
317
|
-
# Accept-Language: en-us
|
318
|
-
# Accept-Encoding: gzip, deflate
|
319
|
-
# Connection: keep-alive
|