http_parser 0.1.0 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/lib/http/native_parser.rb +34 -7
- data/spec/http_parser_spec.rb +68 -3
- metadata +3 -3
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.3
|
data/lib/http/native_parser.rb
CHANGED
@@ -63,16 +63,16 @@ module Http
|
|
63
63
|
# Regex used to match a header line. Lines suspected of
|
64
64
|
# being headers are also checked against the HeaderContinueMatch
|
65
65
|
# to deal with multiline headers
|
66
|
-
HeaderLineMatch = %r{^([a-zA-Z-]+):[ \t]*([[:print:]]
|
67
|
-
HeaderContinueMatch = %r{^[ \t]+([[:print:]]
|
66
|
+
HeaderLineMatch = %r{^([a-zA-Z-]+):[ \t]*([[:print:]]+?)\r?\n}
|
67
|
+
HeaderContinueMatch = %r{^[ \t]+([[:print:]]+?)\r?\n}
|
68
68
|
EmptyLineMatch = %r{^\r?\n}
|
69
69
|
|
70
70
|
# Regex used to match a size specification for a chunked segment
|
71
|
-
ChunkSizeLineMatch = %r{^[0-
|
71
|
+
ChunkSizeLineMatch = %r{^[0-9a-fA-F]+\r?\n}
|
72
72
|
|
73
73
|
# Used as a fallback in error detection for a malformed request line or header.
|
74
74
|
AnyLineMatch = %r{^.+?\r?\n}
|
75
|
-
|
75
|
+
|
76
76
|
def initialize(options = DefaultOptions)
|
77
77
|
@method = nil
|
78
78
|
@path = nil
|
@@ -163,7 +163,7 @@ module Http
|
|
163
163
|
if (can_have_body?)
|
164
164
|
if (@body_length >= @options[:min_tempfile_size])
|
165
165
|
@body = @options[:tempfile_class].new("http_parser")
|
166
|
-
@body.
|
166
|
+
File.unlink(@body.to_path) rescue nil # ruby 1.9.1 does Tempfile.unlink wrong, so we do it ourselves.
|
167
167
|
else
|
168
168
|
@body = StringIO.new
|
169
169
|
end
|
@@ -213,7 +213,7 @@ module Http
|
|
213
213
|
if (@body.length >= @options[:min_tempfile_size] && @body.kind_of?(StringIO))
|
214
214
|
@body_str = @body.string
|
215
215
|
@body = @options[:tempfile_class].new("http_parser")
|
216
|
-
@body.
|
216
|
+
File.unlink(@body.to_path) rescue nil # ruby 1.9.1 does Tempfile.unlink wrong, so we do it ourselves.
|
217
217
|
@body << @body_str
|
218
218
|
end
|
219
219
|
else
|
@@ -227,7 +227,7 @@ module Http
|
|
227
227
|
end
|
228
228
|
end
|
229
229
|
elsif (scanner.scan(ChunkSizeLineMatch))
|
230
|
-
@chunk_remain = scanner[0].to_i
|
230
|
+
@chunk_remain = scanner[0].to_i(16)
|
231
231
|
if (@chunk_remain < 1)
|
232
232
|
@state = :body_chunked_tail
|
233
233
|
end
|
@@ -286,6 +286,33 @@ module Http
|
|
286
286
|
end
|
287
287
|
private :normalize_header
|
288
288
|
|
289
|
+
# Given a basic rack environment, will properly fill it in
|
290
|
+
# with the information gleaned from the parsed request. Note that
|
291
|
+
# this only fills the subset that can be determined by the parser
|
292
|
+
# library. Namely, the only rack. variable set is rack.input. You should also
|
293
|
+
# have defaults in place for SERVER_NAME and SERVER_PORT, as they
|
294
|
+
# are required.
|
295
|
+
def fill_rack_env(env = {})
|
296
|
+
env["rack.input"] = @body || StringIO.new
|
297
|
+
env["REQUEST_METHOD"] = @method
|
298
|
+
env["SCRIPT_NAME"] = ""
|
299
|
+
env["REQUEST_URI"] = @path
|
300
|
+
env["PATH_INFO"], query = @path.split("?", 2)
|
301
|
+
env["QUERY_STRING"] = query || ""
|
302
|
+
if (@headers["HOST"] && !env["SERVER_NAME"])
|
303
|
+
env["SERVER_NAME"], port = @headers["HOST"].split(":", 2)
|
304
|
+
env["SERVER_PORT"] = port if port
|
305
|
+
end
|
306
|
+
@headers.each do |key, val|
|
307
|
+
if (key == 'CONTENT_LENGTH' || key == 'CONTENT_TYPE')
|
308
|
+
env[key] = val
|
309
|
+
else
|
310
|
+
env["HTTP_#{key}"] = val
|
311
|
+
end
|
312
|
+
end
|
313
|
+
return env
|
314
|
+
end
|
315
|
+
|
289
316
|
# Returns true if the request is completely done.
|
290
317
|
def done?
|
291
318
|
@state == :done
|
data/spec/http_parser_spec.rb
CHANGED
@@ -63,6 +63,71 @@ test_parsers.each do |parser|
|
|
63
63
|
p.body.read.should == "stuff"
|
64
64
|
end
|
65
65
|
|
66
|
+
describe "fill_rack_env" do
|
67
|
+
it "should fill in a simple request correctly" do
|
68
|
+
p = parser.new
|
69
|
+
p.parse("GET /blah HTTP/1.1\r\nHost: blorp\r\n\r\n")
|
70
|
+
p.done?.should be_true
|
71
|
+
env = p.fill_rack_env
|
72
|
+
env["rack.input"].should be_kind_of(StringIO)
|
73
|
+
env["REQUEST_METHOD"].should == "GET"
|
74
|
+
env["SCRIPT_NAME"].should == ""
|
75
|
+
env["REQUEST_URI"].should == "/blah"
|
76
|
+
env["PATH_INFO"].should == "/blah"
|
77
|
+
env["QUERY_STRING"].should == ""
|
78
|
+
env["SERVER_NAME"].should == "blorp"
|
79
|
+
env["SERVER_PORT"].should be_nil
|
80
|
+
env["HTTP_HOST"].should == "blorp"
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should give Content-Type and Content-Length as CONTENT_* rather than HTTP_CONTENT_*" do
|
84
|
+
p = parser.new
|
85
|
+
p.parse("POST /blah HTTP/1.1\r\nContent-Type: text/text\r\nContent-Length: 4\r\n\r\ntest")
|
86
|
+
p.done?.should be_true
|
87
|
+
env = p.fill_rack_env
|
88
|
+
env["CONTENT_LENGTH"].should == "4"
|
89
|
+
env["CONTENT_TYPE"].should == "text/text"
|
90
|
+
env["HTTP_CONTENT_LENGTH"].should be_nil
|
91
|
+
env["HTTP_CONTENT_TYPE"].should be_nil
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should split the query string from the request uri" do
|
95
|
+
p = parser.new
|
96
|
+
p.parse("GET /blah?blorp HTTP/1.1\r\nHost: blorp\r\n\r\n")
|
97
|
+
p.done?.should be_true
|
98
|
+
env = p.fill_rack_env
|
99
|
+
env["REQUEST_URI"].should == "/blah?blorp"
|
100
|
+
env["PATH_INFO"].should == "/blah"
|
101
|
+
env["QUERY_STRING"].should == "blorp"
|
102
|
+
end
|
103
|
+
it "should split the query string from the path only once" do
|
104
|
+
p = parser.new
|
105
|
+
p.parse("GET /blah?blorp?bloop HTTP/1.1\r\nHost:blorp\r\n\r\n")
|
106
|
+
p.done?.should be_true
|
107
|
+
env = p.fill_rack_env
|
108
|
+
env["REQUEST_URI"].should == "/blah?blorp?bloop"
|
109
|
+
env["PATH_INFO"].should == "/blah"
|
110
|
+
env["QUERY_STRING"].should == "blorp?bloop"
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should split the host from the port when doing SERVER_NAME/SERVER_PORT" do
|
114
|
+
p = parser.new
|
115
|
+
p.parse("GET /blah HTTP/1.1\r\nHost: blorp.com:1234\r\n\r\n")
|
116
|
+
p.done?.should be_true
|
117
|
+
env = p.fill_rack_env
|
118
|
+
env["SERVER_NAME"].should == "blorp.com"
|
119
|
+
env["SERVER_PORT"].should == "1234"
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should not fill in SERVER_NAME and SERVER_PORT if SERVER_NAME is already set" do
|
123
|
+
p = parser.new
|
124
|
+
p.parse("GET /blah HTTP/1.1\r\nHost: blah.com:324\r\n\r\n")
|
125
|
+
p.done?.should be_true
|
126
|
+
env = p.fill_rack_env({"SERVER_NAME"=>"woop.com"})
|
127
|
+
env["SERVER_NAME"].should == "woop.com"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
66
131
|
it "should be able to parse two simple requests from the same string" do
|
67
132
|
req = <<REQ
|
68
133
|
GET /first HTTP/1.1\r
|
@@ -170,7 +235,7 @@ POST / HTTP/1.1\r
|
|
170
235
|
Host: blah.com\r
|
171
236
|
Transfer-Encoding: chunked\r
|
172
237
|
\r
|
173
|
-
|
238
|
+
A\r
|
174
239
|
stuffstuff\r
|
175
240
|
0\r
|
176
241
|
\r
|
@@ -214,7 +279,7 @@ Transfer-Encoding: chunked\r
|
|
214
279
|
\r
|
215
280
|
REQ
|
216
281
|
1.upto(200) do
|
217
|
-
p.parse("
|
282
|
+
p.parse("A\r\n")
|
218
283
|
p.parse("x"*10 + "\r\n")
|
219
284
|
end
|
220
285
|
p.parse("0\r\n\r\n")
|
@@ -405,7 +470,7 @@ POST / HTTP/1.1\r
|
|
405
470
|
Content-Length: 5
|
406
471
|
Transfer-Encoding: chunked
|
407
472
|
|
408
|
-
|
473
|
+
A
|
409
474
|
stuffstuff
|
410
475
|
0
|
411
476
|
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
version: 0.1.
|
8
|
+
- 3
|
9
|
+
version: 0.1.3
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Graham Batty
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-03-
|
17
|
+
date: 2010-03-17 00:00:00 -06:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|