http_parser 0.1.0 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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