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 CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.1.3
@@ -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:]]+)\r?\n}
67
- HeaderContinueMatch = %r{^[ \t]+([[:print:]]+)\r?\n}
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-9]+\r?\n}
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.unlink # unlink immediately so we don't rely on the caller to do it.
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.unlink # unlink immediately so we don't rely on the caller to do it.
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
@@ -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
- 10\r
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("10\r\n")
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
- 10
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
- - 0
9
- version: 0.1.0
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-07 00:00:00 -07:00
17
+ date: 2010-03-17 00:00:00 -06:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency