|
ferencd@0
|
1 require 'net/http/server/chunked_stream'
|
|
ferencd@0
|
2
|
|
ferencd@0
|
3 require 'net/protocol'
|
|
ferencd@0
|
4 require 'time'
|
|
ferencd@0
|
5
|
|
ferencd@0
|
6 module Net
|
|
ferencd@0
|
7 class HTTP < Protocol
|
|
ferencd@0
|
8 module Server
|
|
ferencd@0
|
9 module Responses
|
|
ferencd@0
|
10 # The supported HTTP Protocol.
|
|
ferencd@0
|
11 HTTP_VERSION = '1.1'
|
|
ferencd@0
|
12
|
|
ferencd@0
|
13 # The known HTTP Status codes and messages
|
|
ferencd@0
|
14 HTTP_STATUSES = {
|
|
ferencd@0
|
15 # 1xx
|
|
ferencd@0
|
16 100 => 'Continue',
|
|
ferencd@0
|
17 101 => 'Switching Protocols',
|
|
ferencd@0
|
18 102 => 'Processing',
|
|
ferencd@0
|
19 # 2xx
|
|
ferencd@0
|
20 200 => 'OK',
|
|
ferencd@0
|
21 201 => 'Created',
|
|
ferencd@0
|
22 202 => 'Accepted',
|
|
ferencd@0
|
23 203 => 'Non-Authoritative Information',
|
|
ferencd@0
|
24 204 => 'No Content',
|
|
ferencd@0
|
25 205 => 'Reset Content',
|
|
ferencd@0
|
26 206 => 'Partial Content',
|
|
ferencd@0
|
27 # 3xx
|
|
ferencd@0
|
28 300 => 'Multiple Choices',
|
|
ferencd@0
|
29 301 => 'Moved Permanently',
|
|
ferencd@0
|
30 302 => 'Found',
|
|
ferencd@0
|
31 303 => 'See Other',
|
|
ferencd@0
|
32 304 => 'Not Modified',
|
|
ferencd@0
|
33 305 => 'Use Proxy',
|
|
ferencd@0
|
34 307 => 'Temporary Redirect',
|
|
ferencd@0
|
35 # 4xx
|
|
ferencd@0
|
36 400 => 'Bad Request',
|
|
ferencd@0
|
37 401 => 'Unauthorized',
|
|
ferencd@0
|
38 402 => 'Payment Required',
|
|
ferencd@0
|
39 403 => 'Forbidden',
|
|
ferencd@0
|
40 404 => 'Not Found',
|
|
ferencd@0
|
41 405 => 'Method Not Allowed',
|
|
ferencd@0
|
42 406 => 'Not Acceptable',
|
|
ferencd@0
|
43 407 => 'Proxy Authentication Required',
|
|
ferencd@0
|
44 408 => 'Request Time-out',
|
|
ferencd@0
|
45 409 => 'Conflict',
|
|
ferencd@0
|
46 410 => 'Gone',
|
|
ferencd@0
|
47 411 => 'Length Required',
|
|
ferencd@0
|
48 412 => 'Precondition Failed',
|
|
ferencd@0
|
49 413 => 'Request Entity Too Large',
|
|
ferencd@0
|
50 414 => 'Request-URI Too Large',
|
|
ferencd@0
|
51 415 => 'Unsupported Media Type',
|
|
ferencd@0
|
52 416 => 'Requested range not satisfiable',
|
|
ferencd@0
|
53 417 => 'Expectation Failed',
|
|
ferencd@0
|
54 # 5xx
|
|
ferencd@0
|
55 500 => 'Internal Server Error',
|
|
ferencd@0
|
56 501 => 'Not Implemented',
|
|
ferencd@0
|
57 502 => 'Bad Gateway',
|
|
ferencd@0
|
58 503 => 'Service Unavailable',
|
|
ferencd@0
|
59 504 => 'Gateway Time-out',
|
|
ferencd@0
|
60 505 => 'HTTP Version not supported extension-code'
|
|
ferencd@0
|
61 }
|
|
ferencd@0
|
62
|
|
ferencd@0
|
63 # Generic Bad Request response
|
|
ferencd@0
|
64 BAD_REQUEST = [400, {}, ['Bad Request']]
|
|
ferencd@0
|
65
|
|
ferencd@0
|
66 protected
|
|
ferencd@0
|
67
|
|
ferencd@0
|
68 #
|
|
ferencd@0
|
69 # Writes the status of an HTTP Response to a stream.
|
|
ferencd@0
|
70 #
|
|
ferencd@0
|
71 # @param [IO] stream
|
|
ferencd@0
|
72 # The stream to write the headers back to.
|
|
ferencd@0
|
73 #
|
|
ferencd@0
|
74 # @param [Integer] status
|
|
ferencd@0
|
75 # The status of the HTTP Response.
|
|
ferencd@0
|
76 #
|
|
ferencd@0
|
77 def write_status(stream,status)
|
|
ferencd@0
|
78 status = status.to_i
|
|
ferencd@0
|
79
|
|
ferencd@0
|
80 reason = HTTP_STATUSES[status]
|
|
ferencd@0
|
81 stream.write("HTTP/#{HTTP_VERSION} #{status} #{reason}\r\n")
|
|
ferencd@0
|
82 end
|
|
ferencd@0
|
83
|
|
ferencd@0
|
84 #
|
|
ferencd@0
|
85 # Write the headers of an HTTP Response to a stream.
|
|
ferencd@0
|
86 #
|
|
ferencd@0
|
87 # @param [IO] stream
|
|
ferencd@0
|
88 # The stream to write the headers back to.
|
|
ferencd@0
|
89 #
|
|
ferencd@0
|
90 # @param [Hash{String => String,Time,Array<String>}] headers
|
|
ferencd@0
|
91 # The headers of the HTTP Response.
|
|
ferencd@0
|
92 #
|
|
ferencd@0
|
93 def write_headers(stream,headers)
|
|
ferencd@0
|
94 headers.each do |name,values|
|
|
ferencd@0
|
95 case values
|
|
ferencd@0
|
96 when String
|
|
ferencd@0
|
97 values.each_line("\n") do |value|
|
|
ferencd@0
|
98 stream.write("#{name}: #{value.chomp}\r\n")
|
|
ferencd@0
|
99 end
|
|
ferencd@0
|
100 when Time
|
|
ferencd@0
|
101 stream.write("#{name}: #{values.httpdate}\r\n")
|
|
ferencd@0
|
102 when Array
|
|
ferencd@0
|
103 values.each do |value|
|
|
ferencd@0
|
104 stream.write("#{name}: #{value}\r\n")
|
|
ferencd@0
|
105 end
|
|
ferencd@0
|
106 end
|
|
ferencd@0
|
107 end
|
|
ferencd@0
|
108
|
|
ferencd@0
|
109 stream.write("\r\n")
|
|
ferencd@0
|
110 stream.flush
|
|
ferencd@0
|
111 end
|
|
ferencd@0
|
112
|
|
ferencd@0
|
113 #
|
|
ferencd@0
|
114 # Writes the body of a HTTP Response to a stream.
|
|
ferencd@0
|
115 #
|
|
ferencd@0
|
116 # @param [IO] stream
|
|
ferencd@0
|
117 # The stream to write the headers back to.
|
|
ferencd@0
|
118 #
|
|
ferencd@0
|
119 # @param [#each] body
|
|
ferencd@0
|
120 # The body of the HTTP Response.
|
|
ferencd@0
|
121 #
|
|
ferencd@0
|
122 def write_body(stream,body)
|
|
ferencd@0
|
123 body.each do |chunk|
|
|
ferencd@0
|
124 stream.write(chunk)
|
|
ferencd@0
|
125 stream.flush
|
|
ferencd@0
|
126 end
|
|
ferencd@0
|
127 end
|
|
ferencd@0
|
128
|
|
ferencd@0
|
129 #
|
|
ferencd@0
|
130 # Writes the body of a HTTP Response to a stream, using Chunked
|
|
ferencd@0
|
131 # Transfer-Encoding.
|
|
ferencd@0
|
132 #
|
|
ferencd@0
|
133 # @param [IO] stream
|
|
ferencd@0
|
134 # The stream to write the headers back to.
|
|
ferencd@0
|
135 #
|
|
ferencd@0
|
136 # @param [#each] body
|
|
ferencd@0
|
137 # The body of the HTTP Response.
|
|
ferencd@0
|
138 #
|
|
ferencd@0
|
139 # @since 0.2.0
|
|
ferencd@0
|
140 #
|
|
ferencd@0
|
141 def write_body_streamed(stream,body)
|
|
ferencd@0
|
142 chunked_stream = ChunkedStream.new(stream)
|
|
ferencd@0
|
143
|
|
ferencd@0
|
144 body.each { |chunk| chunked_stream.write(chunk) }
|
|
ferencd@0
|
145
|
|
ferencd@0
|
146 chunked_stream.close
|
|
ferencd@0
|
147 end
|
|
ferencd@0
|
148
|
|
ferencd@0
|
149 #
|
|
ferencd@0
|
150 # Writes a HTTP Response to a stream.
|
|
ferencd@0
|
151 #
|
|
ferencd@0
|
152 # @param [IO] stream
|
|
ferencd@0
|
153 # The stream to write the HTTP Response to.
|
|
ferencd@0
|
154 #
|
|
ferencd@0
|
155 # @param [Integer] status
|
|
ferencd@0
|
156 # The status of the HTTP Response.
|
|
ferencd@0
|
157 #
|
|
ferencd@0
|
158 # @param [Hash{String => String,Time,Array<String>}] headers
|
|
ferencd@0
|
159 # The headers of the HTTP Response.
|
|
ferencd@0
|
160 #
|
|
ferencd@0
|
161 # @param [#each] body
|
|
ferencd@0
|
162 # The body of the HTTP Response.
|
|
ferencd@0
|
163 #
|
|
ferencd@0
|
164 def write_response(stream,status,headers,body)
|
|
ferencd@0
|
165 write_status stream, status
|
|
ferencd@0
|
166 write_headers stream, headers
|
|
ferencd@0
|
167
|
|
ferencd@0
|
168 if headers['Transfer-Encoding'] == 'chunked'
|
|
ferencd@0
|
169 write_body_streamed stream, body
|
|
ferencd@0
|
170 else
|
|
ferencd@0
|
171 write_body stream, body
|
|
ferencd@0
|
172
|
|
ferencd@0
|
173 # if neither `Content-Length` or `Transfer-Encoding`
|
|
ferencd@0
|
174 # were specified, close the stream after writing the response.
|
|
ferencd@0
|
175 stream.close unless headers['Content-Length']
|
|
ferencd@0
|
176 end
|
|
ferencd@0
|
177 end
|
|
ferencd@0
|
178
|
|
ferencd@0
|
179 end
|
|
ferencd@0
|
180 end
|
|
ferencd@0
|
181 end
|
|
ferencd@0
|
182 end
|