http_tools 0.4.1 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,4 @@
1
- = HTTPTools
1
+ = HTTPTools {<img src=https://secure.travis-ci.org/matsadler/http_tools.png>}[http://travis-ci.org/matsadler/http_tools]
2
2
 
3
3
  HTTPTools is a collection of lower level utilities to aid working with HTTP,
4
4
  including a fast-as-possible pure Ruby HTTP parser.
@@ -12,9 +12,9 @@ Written purely in Ruby, with no dependencies outside of the standard library, it
12
12
  should run across all Ruby implementations compatible with 1.8 or later, and
13
13
  install in environments without a compiler available.
14
14
 
15
- All tests are currently run (and pass) against ruby 1.8.6, 1.8.7, 1.9.2, jruby 1.6.2, rubinius 1.2.4, MacRuby 0.10
15
+ Tests are currently run on travis-ci[http://travis-ci.org/] against Ruby 1.8.6, 1.8.7, 1.9.2, 1.9.3, JRuby, Rubinius, Rubinius 2, and Ruby Enterprise Edition. Additionally tests are run against MacRuby 0.10
16
16
 
17
- Performance tuning is mainly aimed at ruby 1.9, with ruby 1.8 and jruby taken in to consideration. jruby is generally fastest.
17
+ Performance tuning is mainly aimed at Ruby 1.9, with Ruby 1.8 and JRuby taken in to consideration. JRuby is generally fastest.
18
18
 
19
19
  == HTTPTools::Parser
20
20
 
@@ -29,6 +29,15 @@ Benchmark.bm(41) do |x|
29
29
  end
30
30
  end
31
31
 
32
+ x.report("HTTPTools::Parser (reset, with env)") do
33
+ parser = HTTPTools::Parser.new
34
+ 10_000.times do
35
+ parser << request
36
+ parser.env
37
+ parser.reset
38
+ end
39
+ end
40
+
32
41
  begin
33
42
  require 'rubygems'
34
43
  require 'http/parser'
@@ -1,3 +1,8 @@
1
+ require_base = File.expand_path('../http_tools', __FILE__)
2
+ require require_base + '/encoding'
3
+ require require_base + '/parser'
4
+ require require_base + '/builder'
5
+
1
6
  module HTTPTools
2
7
  STATUS_CODES = {
3
8
  :continue => 100,
@@ -9,6 +14,8 @@ module HTTPTools
9
14
  :no_content => 204,
10
15
  :reset_content => 205,
11
16
  :partial_content => 206,
17
+ :multi_status => 207,
18
+ :im_used => 226,
12
19
  :multiple_choices => 300,
13
20
  :moved_permanently => 301,
14
21
  :found => 302,
@@ -34,12 +41,19 @@ module HTTPTools
34
41
  :unsupported_media_type => 415,
35
42
  :requested_range_not_satisfiable => 416,
36
43
  :expectation_failed => 417,
44
+ :im_a_teapot => 418,
45
+ :unprocessable_entity => 422,
46
+ :locked => 423,
47
+ :failed_dependency => 424,
48
+ :upgrade_required => 426,
37
49
  :internal_server_error => 500,
38
50
  :not_implemented => 501,
39
51
  :bad_gateway => 502,
40
52
  :service_unavailable => 503,
41
53
  :gateway_timeout => 504,
42
- :http_version_not_supported => 505}.freeze
54
+ :http_version_not_supported => 505,
55
+ :variant_also_negotiates => 506,
56
+ :insufficient_storage => 507}.freeze
43
57
 
44
58
  STATUS_DESCRIPTIONS = {
45
59
  100 => "Continue",
@@ -51,6 +65,8 @@ module HTTPTools
51
65
  204 => "No Content",
52
66
  205 => "Reset Content",
53
67
  206 => "Partial Content",
68
+ 207 => "Multi-Status",
69
+ 226 => "IM Used",
54
70
  300 => "Multiple Choices",
55
71
  301 => "Moved Permanently",
56
72
  302 => "Found",
@@ -76,12 +92,19 @@ module HTTPTools
76
92
  415 => "Unsupported Media Type",
77
93
  416 => "Requested Range Not Satisfiable",
78
94
  417 => "Expectation Failed",
95
+ 418 => "I'm a teapot",
96
+ 422 => "Unprocessable Entity",
97
+ 423 => "Locked",
98
+ 424 => "Failed Dependency",
99
+ 426 => "Upgrade Required",
79
100
  500 => "Internal Server Error",
80
101
  501 => "Not Implemented",
81
102
  502 => "Bad Gateway",
82
103
  503 => "Service Unavailable",
83
104
  504 => "Gateway Timeout",
84
- 505 => "HTTP Version Not Supported"}.freeze
105
+ 505 => "HTTP Version Not Supported",
106
+ 506 => "Variant Also Negotiates",
107
+ 507 => "Insufficient Storage"}.freeze
85
108
  STATUS_DESCRIPTIONS.values.each {|val| val.freeze}
86
109
 
87
110
  # :stopdoc: hide from rdoc as it makes a mess
@@ -103,10 +126,4 @@ module HTTPTools
103
126
  EndOfMessageError = Class.new(ParseError)
104
127
  MessageIncompleteError = Class.new(EndOfMessageError)
105
128
  EmptyMessageError = Class.new(MessageIncompleteError)
106
-
107
- require_base = File.dirname(__FILE__) + '/http_tools/'
108
- autoload :Encoding, require_base + 'encoding'
109
- autoload :Parser, require_base + 'parser'
110
- autoload :Builder, require_base + 'builder'
111
-
112
- end
129
+ end
@@ -43,7 +43,7 @@ module HTTPTools
43
43
  def format_headers(headers, buffer="")
44
44
  headers.each do |key, value|
45
45
  if value.respond_to?(:each_line)
46
- value.each_line {|val| buffer << "#{key}: #{val.chomp}\r\n"}
46
+ value.each_line {|val| val.chomp!; buffer << "#{key}: #{val}\r\n"}
47
47
  elsif value.respond_to?(:each)
48
48
  value.each {|val| buffer << "#{key}: #{val}\r\n"}
49
49
  else
@@ -15,7 +15,6 @@ module HTTPTools
15
15
  SPACE = " ".freeze
16
16
  AMPERSAND = "&".freeze
17
17
  EQUALS = "=".freeze
18
- CHUNK_FORMAT = "%x\r\n%s\r\n".freeze
19
18
  # :startdoc:
20
19
 
21
20
  module_function
@@ -25,7 +24,7 @@ module HTTPTools
25
24
  # URL encode a string, eg "le café" becomes "le+caf%c3%a9"
26
25
  #
27
26
  def url_encode(string)
28
- string.gsub(/[^a-zA-Z0-9._~-]+/) do |match|
27
+ string.gsub(/[^a-z0-9._~-]+/i) do |match|
29
28
  length = match.respond_to?(:bytesize) ? match.bytesize : match.length
30
29
  PERCENT + match.unpack(HEX_BIG_ENDIAN_2_BYTES * length).join(PERCENT)
31
30
  end
@@ -36,7 +35,7 @@ module HTTPTools
36
35
  # URL decode a string, eg "le+caf%c3%a9" becomes "le café"
37
36
  #
38
37
  def url_decode(string)
39
- string.tr(PLUS, SPACE).gsub(/(%[0-9a-fA-F]{2})+/) do |match|
38
+ string.tr(PLUS, SPACE).gsub(/(%[0-9a-f]{2})+/i) do |match|
40
39
  r = [match.delete(PERCENT)].pack(HEX_BIG_ENDIAN_REPEATING)
41
40
  r.respond_to?(:force_encoding) ? r.force_encoding(string.encoding) : r
42
41
  end
@@ -72,15 +71,17 @@ module HTTPTools
72
71
  # an array value, eg "lang=en&lang=fr" becomes {"lang" => ["en", "fr"]}
73
72
  #
74
73
  def www_form_decode(string)
75
- string.split(AMPERSAND).inject({}) do |memo, key_value|
74
+ out = {}
75
+ string.split(AMPERSAND).each do |key_value|
76
76
  key, value = key_value.split(EQUALS)
77
77
  key, value = url_decode(key), url_decode(value)
78
- if memo.key?(key)
79
- memo.merge(key => [*memo[key]].push(value))
78
+ if out.key?(key)
79
+ out[key] = [*out[key]].push(value)
80
80
  else
81
- memo.merge(key => value)
81
+ out[key] = value
82
82
  end
83
83
  end
84
+ out
84
85
  end
85
86
 
86
87
  # :call-seq:
@@ -94,8 +95,8 @@ module HTTPTools
94
95
  # passing an empty string or nil will generate the empty chunk.
95
96
  #
96
97
  def transfer_encoding_chunked_encode(string)
97
- if string && string.length > 0
98
- sprintf(CHUNK_FORMAT, string.length, string)
98
+ if string && (length = string.length) > 0
99
+ "#{length.to_s(16)}\r\n#{string}\r\n"
99
100
  else
100
101
  "0\r\n"
101
102
  end
@@ -29,6 +29,7 @@ module HTTPTools
29
29
  #
30
30
  class Parser
31
31
  # :stopdoc:
32
+ EMPTY = "".freeze
32
33
  COLON = ":".freeze
33
34
  KEY_TERMINATOR = ": ".freeze
34
35
  CONTENT_LENGTH = "Content-Length".freeze
@@ -92,7 +93,7 @@ module HTTPTools
92
93
  @force_trailer = nil
93
94
  @max_chunk_size = nil
94
95
  @status_code = nil
95
- @header_done = nil
96
+ @header_complete = nil
96
97
  @content_left = nil
97
98
  @chunked = nil
98
99
  @body = nil
@@ -131,15 +132,15 @@ module HTTPTools
131
132
  def env
132
133
  return unless @header_complete
133
134
  env = PROTOTYPE_ENV.dup
134
- env[REQUEST_METHOD] = @request_method
135
+ env[REQUEST_METHOD] = @request_method.upcase
135
136
  env[PATH_INFO] = @path_info
136
137
  env[QUERY_STRING] = @query_string
137
138
  @header.each do |key, value|
138
139
  upper_key = key.tr(LOWERCASE, UPPERCASE)
139
- upper_key = HTTP_ + upper_key unless NO_HTTP_.key?(upper_key)
140
- env[upper_key] = value
140
+ upper_key[0,0] = HTTP_ unless NO_HTTP_.key?(upper_key)
141
+ env[upper_key.freeze] = value
141
142
  end
142
- host, port = env[HTTP_HOST].split(COLON)
143
+ host, port = env[HTTP_HOST].split(COLON) if env.key?(HTTP_HOST)
143
144
  env[SERVER_NAME] = host
144
145
  env[SERVER_PORT] = port || "80"
145
146
  @trailer.each {|k, val| env[HTTP_ + k.tr(LOWERCASE, UPPERCASE)] = val}
@@ -207,7 +208,7 @@ module HTTPTools
207
208
  # otherwise.
208
209
  #
209
210
  def header?
210
- @header_done
211
+ @header_complete
211
212
  end
212
213
 
213
214
  # :call-seq: parser.rest -> string
@@ -241,7 +242,7 @@ module HTTPTools
241
242
  @request_uri = nil
242
243
  @version = nil
243
244
  @status_code = nil
244
- @header_done = nil
245
+ @header_complete = nil
245
246
  @header = {}
246
247
  @trailer = {}
247
248
  @last_key = nil
@@ -290,9 +291,7 @@ module HTTPTools
290
291
  alias on add_listener
291
292
 
292
293
  def inspect # :nodoc:
293
- str = to_s
294
- str[-1, 0] = " #{posstr} #{state}"
295
- str
294
+ super.sub(/ .*>$/, " #{posstr} #{state}>")
296
295
  end
297
296
 
298
297
  private
@@ -300,7 +299,6 @@ module HTTPTools
300
299
  @request_method = @buffer.scan(/[a-z]+ /i)
301
300
  if @request_method
302
301
  @request_method.chop!
303
- @request_method.upcase!
304
302
  uri
305
303
  elsif @buffer.check(/HTTP\//i)
306
304
  response_http_version
@@ -319,7 +317,7 @@ module HTTPTools
319
317
  @path_info = @request_uri.dup
320
318
  @path_info.slice!(/^([a-z0-9+.-]*:\/\/)?[^\/]+/i)
321
319
  @query_string = @path_info.slice!(/\?[a-z0-9;\/?:@&=+$,%_.!~*')(-]*/i)
322
- @query_string ? @query_string.slice!(0) : @query_string = ""
320
+ @query_string ? @query_string[0] = EMPTY : @query_string = ""
323
321
  request_http_version
324
322
  elsif @buffer.check(/[a-z0-9;\/?:@&=+$,%_.!~*')(#-]+\Z/i)
325
323
  :uri
@@ -332,7 +330,6 @@ module HTTPTools
332
330
  @version = @buffer.scan(/ HTTP\/[0-9]+\.[0-9x]+\r\n/i)
333
331
  if @version
334
332
  @version.strip!
335
- @version.upcase!
336
333
  key_or_newline
337
334
  elsif @buffer.skip(/\r\n/i)
338
335
  key_or_newline
@@ -348,7 +345,6 @@ module HTTPTools
348
345
  @version = @buffer.scan(/HTTP\/[0-9]+\.[0-9x]+ /i)
349
346
  if @version
350
347
  @version.chop!
351
- @version.upcase!
352
348
  status
353
349
  elsif @buffer.eos? ||
354
350
  @buffer.check(/H(T(T(P(\/(\d+(\.(\d+\r?)?)?)?)?)?)?)?\Z/i)
@@ -417,7 +413,7 @@ module HTTPTools
417
413
  if @header.key?(@last_key)
418
414
  @header[@last_key] << "\n#{value}"
419
415
  else
420
- @header[@last_key] = value
416
+ @header[@last_key.freeze] = value
421
417
  end
422
418
  if CONTENT_LENGTH.casecmp(@last_key) == 0
423
419
  @content_left = value.to_i
@@ -433,7 +429,6 @@ module HTTPTools
433
429
  end
434
430
 
435
431
  def start_body
436
- @header_done = true
437
432
  if @request_method && !(@content_left || @chunked) ||
438
433
  NO_BODY.key?(@status_code) || @force_no_body
439
434
  end_of_message
@@ -541,7 +536,7 @@ module HTTPTools
541
536
  if @trailer.key?(@last_key)
542
537
  @trailer[@last_key] << "\n#{value}"
543
538
  else
544
- @trailer[@last_key] = value
539
+ @trailer[@last_key.freeze] = value
545
540
  end
546
541
  trailer_key_or_newline
547
542
  elsif @buffer.eos? || @buffer.check(/[^\x00\n\x7f]+\Z/i)
@@ -1,12 +1,20 @@
1
1
  base = File.expand_path(File.dirname(__FILE__) + '/../../lib')
2
2
  require base + '/http_tools'
3
3
  require 'rubygems'
4
- require 'ruby-prof'
5
4
 
6
5
  request = "GET / HTTP/1.1\r\nHost: example.com\r\nUser-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; en-gb) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16\r\nAccept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5\r\nAccept-Language: en-gb\r\nAccept-Encoding: gzip, deflate\r\nConnection: keep-alive\r\n\r\n"
7
6
  parser = HTTPTools::Parser.new
8
7
 
9
- result = RubyProf.profile do
10
- parser << request
11
- end
12
- RubyProf::FlatPrinter.new(result).print(STDOUT, 0)
8
+ # require 'ruby-prof'
9
+ # result = RubyProf.profile do
10
+ # parser << request
11
+ # end
12
+ # RubyProf::FlatPrinter.new(result).print(STDOUT, 0)
13
+
14
+ require 'perftools'
15
+ PerfTools::CpuProfiler.start("/tmp/http_tools_parser_request_profile") do
16
+ 100_000.times do
17
+ parser << request
18
+ parser.reset
19
+ end
20
+ end
@@ -1,9 +1,7 @@
1
- #!/opt/local/bin/ruby1.9 -w
2
-
3
1
  require 'coverage' # >= ruby 1.9 only
4
2
 
5
3
  at_exit do
6
- testing = Dir["../lib/**/*.rb"].map(&File.method(:expand_path))
4
+ testing = Dir[File.expand_path("../../lib/**/*.rb", __FILE__)]
7
5
 
8
6
  results = Coverage.result.select {|key, value| testing.include?(key)}
9
7
 
@@ -15,9 +13,11 @@ at_exit do
15
13
  results.each do |key, value|
16
14
  next unless value.include?(0)
17
15
  puts key
16
+ puts " line calls code"
18
17
  puts
19
18
  File.readlines(key).zip(value).each_with_index do |(line, val), i|
20
- print "%3i %3s %s" % [(i + 1), val, line]
19
+ print val == 0 ? "> " : " "
20
+ print "%3i %5s %s" % [(i + 1), val, line]
21
21
  end
22
22
  puts
23
23
  puts
@@ -25,4 +25,4 @@ at_exit do
25
25
  end
26
26
 
27
27
  Coverage.start
28
- Dir["**/*_test.rb"].each {|test| require test}
28
+ Dir[File.expand_path("../**/*_test.rb", __FILE__)].each {|test| require test}
@@ -314,7 +314,7 @@ class ParserRequestTest < Test::Unit::TestCase
314
314
 
315
315
  parser << "GET / HTTP/1.x\r\n\r\n"
316
316
 
317
- assert_equal("HTTP/1.X", version)
317
+ assert_equal("HTTP/1.x", version)
318
318
  end
319
319
 
320
320
  def test_finish_without_body_trigger
@@ -455,7 +455,7 @@ class ParserRequestTest < Test::Unit::TestCase
455
455
 
456
456
  parser << "get / HTTP/1.1\r\n\r\n"
457
457
 
458
- assert_equal("GET", result)
458
+ assert_equal("get", result)
459
459
  end
460
460
 
461
461
  def test_lowercase_http
@@ -466,7 +466,7 @@ class ParserRequestTest < Test::Unit::TestCase
466
466
 
467
467
  parser << "GET / http/1.1\r\n\r\n"
468
468
 
469
- assert_equal("HTTP/1.1", version)
469
+ assert_equal("http/1.1", version)
470
470
  end
471
471
 
472
472
  def test_invalid_version
@@ -692,10 +692,32 @@ class ParserRequestTest < Test::Unit::TestCase
692
692
  assert_equal(nil, env["rack.input"])
693
693
  end
694
694
 
695
+ def test_env_lowercase_method
696
+ parser = HTTPTools::Parser.new
697
+
698
+ parser << "get /test?q=foo HTTP/1.1\r\n"
699
+ parser << "Host: www.example.com\r\n\r\n"
700
+
701
+ assert_equal("GET", parser.env["REQUEST_METHOD"])
702
+ end
703
+
704
+ def test_env_no_host
705
+ parser = HTTPTools::Parser.new
706
+
707
+ parser << "GET /test?q=foo HTTP/1.1\r\n\r\n"
708
+
709
+ assert_nothing_raised(NoMethodError) do
710
+ parser.env
711
+ end
712
+ assert_equal(nil, parser.env["HTTP_HOST"])
713
+ assert_equal(nil, parser.env["SERVER_NAME"])
714
+ assert_equal("80", parser.env["SERVER_PORT"])
715
+ end
716
+
695
717
  def test_inspect
696
718
  parser = HTTPTools::Parser.new
697
719
 
698
- assert(/#<HTTPTools::Parser:[a-fx0-9]+ line 1, char 1 start>/ === parser.inspect, "Inspect should return useful information")
720
+ assert_match(/#<HTTPTools::Parser:0x[a-f0-9]+ line 1, char 1 start>/, parser.inspect)
699
721
  end
700
722
 
701
723
  end
@@ -24,7 +24,7 @@ class ParserResponseTest < Test::Unit::TestCase
24
24
 
25
25
  parser << "HTTP/1.x 200 OK\r\n\r\n"
26
26
 
27
- assert_equal("HTTP/1.X", version)
27
+ assert_equal("HTTP/1.x", version)
28
28
  end
29
29
 
30
30
  def test_ok
@@ -882,7 +882,6 @@ class ParserResponseTest < Test::Unit::TestCase
882
882
  parser.add_listener(:header) do
883
883
  code = parser.status_code
884
884
  message = parser.message
885
- headers = parser.header
886
885
  end
887
886
  parser.add_listener(:stream) {|chunk| body << chunk}
888
887
  parser.add_listener(:finish) {remainder = parser.rest}
@@ -905,7 +904,6 @@ class ParserResponseTest < Test::Unit::TestCase
905
904
  parser.add_listener(:header) do
906
905
  code = parser.status_code
907
906
  message = parser.message
908
- headers = parser.header
909
907
  end
910
908
  parser.add_listener(:stream) {|chunk| body << chunk}
911
909
  parser.add_listener(:finish) {remainder = parser.rest}
@@ -928,7 +926,6 @@ class ParserResponseTest < Test::Unit::TestCase
928
926
  parser.add_listener(:header) do
929
927
  code = parser.status_code
930
928
  message = parser.message
931
- headers = parser.header
932
929
  end
933
930
  parser.add_listener(:stream) {|chunk| body << chunk}
934
931
  parser.add_listener(:finish) {remainder = parser.rest}
@@ -1,10 +1 @@
1
- puts
2
- if defined? RUBY_DESCRIPTION
3
- puts RUBY_DESCRIPTION
4
- elsif defined? RUBY_ENGINE
5
- puts "#{RUBY_ENGINE} #{RUBY_VERSION} (#{RELEASE_DATE} patchlevel #{RUBY_PATCHLEVEL}) [#{RUBY_PLATFORM}]"
6
- else
7
- puts "ruby #{RUBY_VERSION} (#{RELEASE_DATE} patchlevel #{RUBY_PATCHLEVEL}) [#{RUBY_PLATFORM}]"
8
- end
9
-
10
- Dir["**/*_test.rb"].each {|test| require File.expand_path(test)}
1
+ Dir[File.expand_path("../**/*_test.rb", __FILE__)].each {|test| require test}
metadata CHANGED
@@ -1,33 +1,24 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: http_tools
3
- version: !ruby/object:Gem::Version
4
- hash: 13
5
- prerelease: false
6
- segments:
7
- - 0
8
- - 4
9
- - 1
10
- version: 0.4.1
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.2
5
+ prerelease:
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Matthew Sadler
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2011-06-27 00:00:00 +01:00
19
- default_executable:
12
+ date: 2011-09-25 00:00:00.000000000 Z
20
13
  dependencies: []
21
-
22
- description: A fast-as-possible pure Ruby HTTP parser plus associated lower level utilities to aid working with HTTP and the web.
14
+ description: A fast-as-possible pure Ruby HTTP parser plus associated lower level
15
+ utilities to aid working with HTTP and the web.
23
16
  email: mat@sourcetagsandcodes.com
24
17
  executables: []
25
-
26
18
  extensions: []
27
-
28
- extra_rdoc_files:
19
+ extra_rdoc_files:
29
20
  - README.rdoc
30
- files:
21
+ files:
31
22
  - lib/http_tools/builder.rb
32
23
  - lib/http_tools/encoding.rb
33
24
  - lib/http_tools/parser.rb
@@ -53,42 +44,32 @@ files:
53
44
  - example/websocket_client.rb
54
45
  - example/websocket_server.rb
55
46
  - README.rdoc
56
- has_rdoc: true
57
47
  homepage: http://github.com/matsadler/http_tools
58
48
  licenses: []
59
-
60
49
  post_install_message:
61
- rdoc_options:
50
+ rdoc_options:
62
51
  - --main
63
52
  - README.rdoc
64
53
  - --charset
65
54
  - utf-8
66
- require_paths:
55
+ require_paths:
67
56
  - lib
68
- required_ruby_version: !ruby/object:Gem::Requirement
57
+ required_ruby_version: !ruby/object:Gem::Requirement
69
58
  none: false
70
- requirements:
71
- - - ">="
72
- - !ruby/object:Gem::Version
73
- hash: 3
74
- segments:
75
- - 0
76
- version: "0"
77
- required_rubygems_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ! '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
64
  none: false
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- hash: 3
83
- segments:
84
- - 0
85
- version: "0"
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
86
69
  requirements: []
87
-
88
70
  rubyforge_project:
89
- rubygems_version: 1.3.7
71
+ rubygems_version: 1.8.10
90
72
  signing_key:
91
73
  specification_version: 3
92
74
  summary: Pure Ruby HTTP parser and friends
93
75
  test_files: []
94
-