http_tools 0.4.3 → 0.4.4
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/README.rdoc +2 -2
- data/bench/parser/large_response_bench.rb +6 -5
- data/bench/parser/request_bench.rb +7 -32
- data/bench/parser/response_bench.rb +5 -5
- data/bench/transfer_encoding_chunked_bench.rb +6 -5
- data/example/http_server.rb +23 -24
- data/lib/http_tools.rb +6 -4
- data/lib/http_tools/parser.rb +45 -18
- data/profile/parser/request_profile.rb +5 -13
- data/test/cover.rb +8 -15
- data/test/parser/request_test.rb +132 -14
- data/test/parser/response_test.rb +149 -5
- metadata +41 -22
data/README.rdoc
CHANGED
@@ -13,8 +13,8 @@ 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
15
|
Tests are currently run on travis-ci[http://travis-ci.org/matsadler/http_tools]
|
16
|
-
against Ruby 1.8.
|
17
|
-
Enterprise Edition. Additionally tests are run against MacRuby
|
16
|
+
against Ruby 1.8.7, 1.9.2, 1.9.3, JRuby, Rubinius, and Ruby
|
17
|
+
Enterprise Edition. Additionally tests are run against 1.8.6 and MacRuby
|
18
18
|
|
19
19
|
Performance tuning is mainly aimed at Ruby 1.9, with Ruby 1.8 and JRuby taken in
|
20
20
|
to consideration. JRuby is generally fastest.
|
@@ -1,7 +1,8 @@
|
|
1
|
-
|
2
|
-
require base + '/http_tools'
|
1
|
+
require File.expand_path('../../../lib/http_tools', __FILE__)
|
3
2
|
require 'benchmark'
|
4
3
|
|
4
|
+
repeats = 200
|
5
|
+
|
5
6
|
Benchmark.bm(26) do |x|
|
6
7
|
body = "x" * 1024 * 1024
|
7
8
|
chunks = []
|
@@ -9,7 +10,7 @@ Benchmark.bm(26) do |x|
|
|
9
10
|
|
10
11
|
header = "HTTP/1.1 200 OK\r\nDate: Mon, 06 Jun 2011 14:55:51 GMT\r\nServer: Apache/2.2.17 (Unix) mod_ssl/2.2.17 OpenSSL/0.9.8l DAV/2 mod_fastcgi/2.4.2\r\nLast-Modified: Mon, 06 Jun 2011 14:55:49 GMT\r\nETag: \"3f18045-400-4a50c4c87c740\"\r\nAccept-Ranges: bytes\r\nContent-Length: #{body.length}\r\nContent-Type: text/plain\r\n\r\n"
|
11
12
|
x.report("content_length") do
|
12
|
-
|
13
|
+
repeats.times do
|
13
14
|
parser = HTTPTools::Parser.new
|
14
15
|
parser << header
|
15
16
|
chunks.each {|chunk| parser << chunk}
|
@@ -18,7 +19,7 @@ Benchmark.bm(26) do |x|
|
|
18
19
|
|
19
20
|
header = "HTTP/1.1 200 OK\r\nDate: Mon, 06 Jun 2011 14:55:51 GMT\r\nServer: Apache/2.2.17 (Unix) mod_ssl/2.2.17 OpenSSL/0.9.8l DAV/2 mod_fastcgi/2.4.2\r\nLast-Modified: Mon, 06 Jun 2011 14:55:49 GMT\r\nETag: \"3f18045-400-4a50c4c87c740\"\r\nAccept-Ranges: bytes\r\nConnection: close\r\nContent-Type: text/plain\r\n\r\n"
|
20
21
|
x.report("close") do
|
21
|
-
|
22
|
+
repeats.times do
|
22
23
|
parser = HTTPTools::Parser.new
|
23
24
|
parser << header
|
24
25
|
chunks.each {|chunk| parser << chunk}
|
@@ -30,7 +31,7 @@ Benchmark.bm(26) do |x|
|
|
30
31
|
chunks << nil
|
31
32
|
chunks.map!(&HTTPTools::Encoding.method(:transfer_encoding_chunked_encode))
|
32
33
|
x.report("chunked") do
|
33
|
-
|
34
|
+
repeats.times do
|
34
35
|
parser = HTTPTools::Parser.new
|
35
36
|
parser << header
|
36
37
|
chunks.each {|chunk| parser << chunk}
|
@@ -1,19 +1,19 @@
|
|
1
|
-
|
2
|
-
require base + '/http_tools'
|
1
|
+
require File.expand_path('../../../lib/http_tools', __FILE__)
|
3
2
|
require 'benchmark'
|
4
3
|
|
5
4
|
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"
|
5
|
+
repeats = 10_000
|
6
6
|
|
7
7
|
Benchmark.bm(41) do |x|
|
8
8
|
x.report("HTTPTools::Parser") do
|
9
|
-
|
9
|
+
repeats.times do
|
10
10
|
HTTPTools::Parser.new << request
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
14
|
x.report("HTTPTools::Parser (reset)") do
|
15
15
|
parser = HTTPTools::Parser.new
|
16
|
-
|
16
|
+
repeats.times do
|
17
17
|
parser << request
|
18
18
|
parser.reset
|
19
19
|
end
|
@@ -23,7 +23,7 @@ Benchmark.bm(41) do |x|
|
|
23
23
|
parser = HTTPTools::Parser.new
|
24
24
|
parser.on(:header) {}
|
25
25
|
parser.on(:finish) {}
|
26
|
-
|
26
|
+
repeats.times do
|
27
27
|
parser << request
|
28
28
|
parser.reset
|
29
29
|
end
|
@@ -31,35 +31,10 @@ Benchmark.bm(41) do |x|
|
|
31
31
|
|
32
32
|
x.report("HTTPTools::Parser (reset, with env)") do
|
33
33
|
parser = HTTPTools::Parser.new
|
34
|
-
|
34
|
+
repeats.times do
|
35
35
|
parser << request
|
36
36
|
parser.env
|
37
37
|
parser.reset
|
38
38
|
end
|
39
39
|
end
|
40
|
-
|
41
|
-
begin
|
42
|
-
require 'rubygems'
|
43
|
-
require 'http/parser'
|
44
|
-
x.report("Http::Parser") do
|
45
|
-
10_000.times do
|
46
|
-
parser = Http::Parser.new
|
47
|
-
parser.on_headers_complete = Proc.new {}
|
48
|
-
parser.on_message_complete = Proc.new {}
|
49
|
-
parser << request
|
50
|
-
end
|
51
|
-
end
|
52
|
-
rescue LoadError
|
53
|
-
end
|
54
|
-
|
55
|
-
begin
|
56
|
-
require 'rubygems'
|
57
|
-
require 'http11'
|
58
|
-
x.report("Mongrel::HttpParser") do
|
59
|
-
10_000.times do
|
60
|
-
Mongrel::HttpParser.new.execute({}, request.dup, 0)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
rescue LoadError
|
64
|
-
end
|
65
|
-
end
|
40
|
+
end
|
@@ -1,19 +1,19 @@
|
|
1
|
-
|
2
|
-
require base + '/http_tools'
|
1
|
+
require File.expand_path('../../../lib/http_tools', __FILE__)
|
3
2
|
require 'benchmark'
|
4
3
|
|
5
4
|
response = "HTTP/1.1 200 OK\r\nServer: Apache/2.2.3 (CentOS)\r\nLast-Modified: Thu, 03 Jun 2010 17:40:12 GMT\r\nETag: \"4d2c-23e-48823b2cf3700\"\r\nAccept-Ranges: bytes\r\nContent-Type: text/html; charset=UTF-8\r\nConnection: Keep-Alive\r\nDate: Wed, 21 Jul 2010 16:26:04 GMT\r\nAge: 7985 \r\nContent-Length: 574\r\n\r\n<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\r\n<HTML>\r\n<HEAD>\r\n <META http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\r\n <TITLE>Example Web Page</TITLE>\r\n</HEAD> \r\n<body> \r\n<p>You have reached this web page by typing "example.com",\r\n"example.net",\r\n or "example.org" into your web browser.</p>\r\n<p>These domain names are reserved for use in documentation and are not available \r\n for registration. See <a href=\"http://www.rfc-editor.org/rfc/rfc2606.txt\">RFC \r\n 2606</a>, Section 3.</p>\r\n</BODY>\r\n</HTML>\r\n\r\n"
|
5
|
+
repeats = 10_000
|
6
6
|
|
7
7
|
Benchmark.bm(25) do |x|
|
8
8
|
x.report("HTTPTools::Parser") do
|
9
|
-
|
9
|
+
repeats.times do
|
10
10
|
HTTPTools::Parser.new << response
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
14
|
x.report("HTTPTools::Parser (reset)") do
|
15
15
|
parser = HTTPTools::Parser.new
|
16
|
-
|
16
|
+
repeats.times do
|
17
17
|
parser << response
|
18
18
|
parser.reset
|
19
19
|
end
|
@@ -23,7 +23,7 @@ Benchmark.bm(25) do |x|
|
|
23
23
|
require 'rubygems'
|
24
24
|
require 'http/parser'
|
25
25
|
x.report("Http::Parser") do
|
26
|
-
|
26
|
+
repeats.times do
|
27
27
|
parser = Http::Parser.new
|
28
28
|
parser << response
|
29
29
|
end
|
@@ -1,25 +1,26 @@
|
|
1
|
-
|
2
|
-
require base + '/http_tools'
|
1
|
+
require File.expand_path('../../lib/http_tools', __FILE__)
|
3
2
|
require 'benchmark'
|
4
3
|
|
4
|
+
repeats = 1_000
|
5
|
+
|
5
6
|
Benchmark.bm(36) do |x|
|
6
7
|
encoded = "1\r\na\r\n" * 100 + "0\r\n"
|
7
8
|
x.report("lots of very short chunks") do
|
8
|
-
|
9
|
+
repeats.times do
|
9
10
|
HTTPTools::Encoding.transfer_encoding_chunked_decode(encoded)
|
10
11
|
end
|
11
12
|
end
|
12
13
|
|
13
14
|
encoded = "16\r\n<h1>Hello world</h1>\r\n\r\n12\r\n<p>Lorem ipsum</p>\r\n" * 50 + "0\r\n"
|
14
15
|
x.report("slightly less slightly longer chunks") do
|
15
|
-
|
16
|
+
repeats.times do
|
16
17
|
HTTPTools::Encoding.transfer_encoding_chunked_decode(encoded)
|
17
18
|
end
|
18
19
|
end
|
19
20
|
|
20
21
|
encoded = "2710\r\n#{"a" * 10000}\r\n" * 2 + "0\r\n"
|
21
22
|
x.report("a couple of big chunks") do
|
22
|
-
|
23
|
+
repeats.times do
|
23
24
|
HTTPTools::Encoding.transfer_encoding_chunked_decode(encoded)
|
24
25
|
end
|
25
26
|
end
|
data/example/http_server.rb
CHANGED
@@ -3,19 +3,20 @@ require 'rubygems'
|
|
3
3
|
require 'http_tools'
|
4
4
|
|
5
5
|
module HTTP
|
6
|
+
|
7
|
+
# Basic Rack HTTP server.
|
8
|
+
#
|
9
|
+
# Usage:
|
10
|
+
#
|
11
|
+
# app = lambda {|env| [200, {"Content-Length" => "3"}, ["Hi\n"]]}
|
12
|
+
# HTTP::Server.run(app)
|
13
|
+
#
|
6
14
|
class Server
|
7
|
-
CONNECTION = "Connection".freeze
|
8
|
-
KEEP_ALIVE = "Keep-Alive".freeze
|
9
|
-
CLOSE = "close".freeze
|
10
|
-
ONE_ONE = "1.1".freeze
|
11
15
|
|
12
16
|
def initialize(app, options={})
|
13
|
-
host = options[:host] || options[:Host] || "0.0.0.0"
|
14
|
-
port = (options[:port] || options[:Port] || 9292).to_s
|
17
|
+
@host = options[:host] || options[:Host] || "0.0.0.0"
|
18
|
+
@port = (options[:port] || options[:Port] || 9292).to_s
|
15
19
|
@app = app
|
16
|
-
@instance_env = {"rack.multithread" => true}
|
17
|
-
@server = TCPServer.new(host, port)
|
18
|
-
@server.listen(1024)
|
19
20
|
end
|
20
21
|
|
21
22
|
def self.run(app, options={})
|
@@ -23,25 +24,23 @@ module HTTP
|
|
23
24
|
end
|
24
25
|
|
25
26
|
def listen
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
on_connection(socket)
|
30
|
-
rescue StandardError, LoadError, SyntaxError => e
|
31
|
-
STDERR.puts("#{e.class}: #{e.message} #{e.backtrace.join("\n")}")
|
32
|
-
end
|
33
|
-
end
|
27
|
+
server = TCPServer.new(@host, @port)
|
28
|
+
while socket = server.accept
|
29
|
+
Thread.new {on_connection(socket)}
|
34
30
|
end
|
35
31
|
end
|
36
32
|
|
37
33
|
private
|
34
|
+
|
38
35
|
def on_connection(socket)
|
39
36
|
parser = HTTPTools::Parser.new
|
40
37
|
|
41
38
|
parser.on(:finish) do
|
42
|
-
|
43
|
-
|
44
|
-
|
39
|
+
env = parser.env.merge!("rack.multithread" => true)
|
40
|
+
status, header, body = @app.call(env)
|
41
|
+
|
42
|
+
keep_alive = parser.header["Connection"] != "close"
|
43
|
+
header["Connection"] = keep_alive ? "Keep-Alive" : "close"
|
45
44
|
socket << HTTPTools::Builder.response(status, header)
|
46
45
|
body.each {|chunk| socket << chunk}
|
47
46
|
body.close if body.respond_to?(:close)
|
@@ -56,12 +55,12 @@ module HTTP
|
|
56
55
|
rescue EOFError
|
57
56
|
break
|
58
57
|
end until parser.finished?
|
58
|
+
|
59
|
+
rescue StandardError, LoadError, SyntaxError => e
|
60
|
+
STDERR.puts("#{e.class}: #{e.message} #{e.backtrace.join("\n")}")
|
61
|
+
ensure
|
59
62
|
socket.close
|
60
63
|
end
|
61
64
|
|
62
|
-
def keep_alive?(http_version, connection)
|
63
|
-
http_version == ONE_ONE && connection != CLOSE || connection == KEEP_ALIVE
|
64
|
-
end
|
65
|
-
|
66
65
|
end
|
67
66
|
end
|
data/lib/http_tools.rb
CHANGED
@@ -108,11 +108,13 @@ module HTTPTools
|
|
108
108
|
STATUS_DESCRIPTIONS.values.each {|val| val.freeze}
|
109
109
|
|
110
110
|
# :stopdoc: hide from rdoc as it makes a mess
|
111
|
-
STATUS_LINES =
|
112
|
-
|
113
|
-
|
114
|
-
|
111
|
+
STATUS_LINES = {}
|
112
|
+
STATUS_CODES.each do |name, code|
|
113
|
+
line = "#{code} #{STATUS_DESCRIPTIONS[code]}"
|
114
|
+
STATUS_LINES[name] = line
|
115
|
+
STATUS_LINES[code] = line
|
115
116
|
end
|
117
|
+
STATUS_LINES.freeze
|
116
118
|
# :startdoc:
|
117
119
|
|
118
120
|
METHODS = %W{GET POST HEAD PUT DELETE OPTIONS TRACE CONNECT}.freeze
|
data/lib/http_tools/parser.rb
CHANGED
@@ -297,7 +297,7 @@ module HTTPTools
|
|
297
297
|
alias on add_listener
|
298
298
|
|
299
299
|
def inspect # :nodoc:
|
300
|
-
super.sub(/ .*>$/, " #{posstr} #{state}>")
|
300
|
+
super.sub(/ .*>$/, " #{posstr(false)} #{state}>")
|
301
301
|
end
|
302
302
|
|
303
303
|
private
|
@@ -313,6 +313,7 @@ module HTTPTools
|
|
313
313
|
elsif @allow_html_without_header && @buffer.check(/\s*</i)
|
314
314
|
skip_header
|
315
315
|
else
|
316
|
+
@buffer.skip(/H(T(TP?)?)?/i) || @buffer.skip(/[a-z]+/i)
|
316
317
|
raise ParseError.new("Protocol or method not recognised at " + posstr)
|
317
318
|
end
|
318
319
|
end
|
@@ -325,9 +326,10 @@ module HTTPTools
|
|
325
326
|
@query_string = @path_info.slice!(/\?[a-z0-9;\/?:@&=+$,%_.!~*')(-]*/i)
|
326
327
|
@query_string ? @query_string[0] = EMPTY : @query_string = ""
|
327
328
|
request_http_version
|
328
|
-
elsif @buffer.check(/[a-z0-9;\/?:@&=+$,%_.!~*')(
|
329
|
+
elsif @buffer.check(/[a-z0-9;\/?:@&=+$,%_.!~*')(-]+\Z/i)
|
329
330
|
:uri
|
330
331
|
else
|
332
|
+
@buffer.skip(/[a-z0-9;\/?:@&=+$,%_.!~*')(-]+/i)
|
331
333
|
raise ParseError.new("URI or path not recognised at " + posstr)
|
332
334
|
end
|
333
335
|
end
|
@@ -343,6 +345,7 @@ module HTTPTools
|
|
343
345
|
@buffer.check(/ (H(T(T(P(\/(\d+(\.(\d+\r?)?)?)?)?)?)?)?)?\Z/i)
|
344
346
|
:request_http_version
|
345
347
|
else
|
348
|
+
@buffer.skip(/ (H(T(T(P(\/(\d+(\.(\d+\r?)?)?)?)?)?)?)?)?/i)
|
346
349
|
raise ParseError.new("Invalid version specifier at " + posstr)
|
347
350
|
end
|
348
351
|
end
|
@@ -356,6 +359,7 @@ module HTTPTools
|
|
356
359
|
@buffer.check(/H(T(T(P(\/(\d+(\.(\d+\r?)?)?)?)?)?)?)?\Z/i)
|
357
360
|
:response_http_version
|
358
361
|
else
|
362
|
+
@buffer.skip(/H(T(T(P(\/(\d+(\.(\d+\r?)?)?)?)?)?)?)?/i)
|
359
363
|
raise ParseError.new("Invalid version specifier at " + posstr)
|
360
364
|
end
|
361
365
|
end
|
@@ -379,6 +383,7 @@ module HTTPTools
|
|
379
383
|
@buffer.check(/\d(\d(\d( ([^\x00-\x1f\x7f]+\r?)?)?)?)?\Z/i)
|
380
384
|
:status
|
381
385
|
else
|
386
|
+
@buffer.skip(/\d(\d(\d( ([^\x00-\x1f\x7f]+\r?)?)?)?)?/i)
|
382
387
|
raise ParseError.new("Invalid status line at " + posstr)
|
383
388
|
end
|
384
389
|
end
|
@@ -397,6 +402,9 @@ module HTTPTools
|
|
397
402
|
elsif @last_key = @buffer.scan(/[ -9;-~]+:(?=[^ ])/i)
|
398
403
|
@last_key.chomp!(COLON)
|
399
404
|
value
|
405
|
+
elsif @request_method
|
406
|
+
@buffer.skip(/[ -9;-~]+/i)
|
407
|
+
raise ParseError.new("Illegal character in field name at " + posstr)
|
400
408
|
else
|
401
409
|
skip_bad_header
|
402
410
|
end
|
@@ -408,6 +416,7 @@ module HTTPTools
|
|
408
416
|
elsif @buffer.check(/[^\x00\n\x7f]+\Z/)
|
409
417
|
:skip_bad_header
|
410
418
|
else
|
419
|
+
@buffer.skip(/[ -9;-~]+/i)
|
411
420
|
raise ParseError.new("Illegal character in field name at " + posstr)
|
412
421
|
end
|
413
422
|
end
|
@@ -430,20 +439,24 @@ module HTTPTools
|
|
430
439
|
elsif @buffer.eos? || @buffer.check(/[^\x00\n\x7f]+\Z/i)
|
431
440
|
:value
|
432
441
|
else
|
442
|
+
@buffer.skip(/[^\x00\n\x7f]+/i)
|
433
443
|
raise ParseError.new("Illegal character in field body at " + posstr)
|
434
444
|
end
|
435
445
|
end
|
436
446
|
|
437
447
|
def value_extention
|
438
|
-
if @buffer.check(/[^ \t]/)
|
448
|
+
if @buffer.check(/[^ \t]/i)
|
439
449
|
key_or_newline
|
440
|
-
elsif value_extra = @buffer.scan(/[ \t]+[^\x00\n\x7f]*\n/)
|
441
|
-
value_extra.sub!(/^[ \t]
|
450
|
+
elsif value_extra = @buffer.scan(/[ \t]+[^\x00\n\x7f]*\n/i)
|
451
|
+
value_extra.sub!(/^[ \t]+/i, SPACE)
|
442
452
|
value_extra.chop!
|
443
453
|
(@header[@last_key] << value_extra).strip!
|
444
454
|
value_extention
|
445
|
-
|
455
|
+
elsif @buffer.eos? || @buffer.check(/[ \t]+[^\x00\n\x7f]*\Z/i)
|
446
456
|
:value_extention
|
457
|
+
else
|
458
|
+
@buffer.skip(/[ \t]+[^\x00\n\x7f]*/i)
|
459
|
+
raise ParseError.new("Illegal character in field body at " + posstr)
|
447
460
|
end
|
448
461
|
end
|
449
462
|
|
@@ -544,6 +557,7 @@ module HTTPTools
|
|
544
557
|
@last_key.chomp!(COLON)
|
545
558
|
trailer_value
|
546
559
|
else
|
560
|
+
@buffer.skip(/[ -9;-~]+/i)
|
547
561
|
raise ParseError.new("Illegal character in field name at " + posstr)
|
548
562
|
end
|
549
563
|
end
|
@@ -561,20 +575,24 @@ module HTTPTools
|
|
561
575
|
elsif @buffer.eos? || @buffer.check(/[^\x00\n\x7f]+\Z/i)
|
562
576
|
:trailer_value
|
563
577
|
else
|
578
|
+
@buffer.skip(/[^\x00\n\x7f]+/i)
|
564
579
|
raise ParseError.new("Illegal character in field body at " + posstr)
|
565
580
|
end
|
566
581
|
end
|
567
582
|
|
568
583
|
def trailer_value_extention
|
569
|
-
if @buffer.check(/[^ \t]/)
|
584
|
+
if @buffer.check(/[^ \t]/i)
|
570
585
|
trailer_key_or_newline
|
571
|
-
elsif value_extra = @buffer.scan(/[ \t]+[^\x00\n\x7f]*\n/)
|
572
|
-
value_extra.sub!(/^[ \t]
|
586
|
+
elsif value_extra = @buffer.scan(/[ \t]+[^\x00\n\x7f]*\n/i)
|
587
|
+
value_extra.sub!(/^[ \t]+/i, SPACE)
|
573
588
|
value_extra.chop!
|
574
589
|
(@trailer[@last_key] << value_extra).strip!
|
575
590
|
trailer_value_extention
|
576
|
-
|
591
|
+
elsif @buffer.eos? || @buffer.check(/[ \t]+[^\x00\n\x7f]*\Z/i)
|
577
592
|
:trailer_value_extention
|
593
|
+
else
|
594
|
+
@buffer.skip(/[ \t]+[^\x00\n\x7f]*/i)
|
595
|
+
raise ParseError.new("Illegal character in field body at " + posstr)
|
578
596
|
end
|
579
597
|
end
|
580
598
|
|
@@ -603,19 +621,28 @@ module HTTPTools
|
|
603
621
|
end
|
604
622
|
|
605
623
|
def line_char(string, position)
|
606
|
-
line_count =
|
624
|
+
line_count = 0
|
607
625
|
char_count = 0
|
608
626
|
string.each_line do |line|
|
609
|
-
break if line.length + char_count
|
627
|
+
break if line.length + char_count >= position
|
610
628
|
line_count += 1
|
611
629
|
char_count += line.length
|
612
630
|
end
|
613
|
-
[line_count, position
|
614
|
-
end
|
615
|
-
|
616
|
-
def posstr
|
617
|
-
|
618
|
-
"line #{
|
631
|
+
[line_count, position - char_count]
|
632
|
+
end
|
633
|
+
|
634
|
+
def posstr(visual=true)
|
635
|
+
line_num, char_num = line_char(@buffer.string, @buffer.pos)
|
636
|
+
out = "line #{line_num + 1}, char #{char_num + 1}"
|
637
|
+
return out unless visual && @buffer.string.respond_to?(:lines)
|
638
|
+
line = ""
|
639
|
+
pointer = nil
|
640
|
+
@buffer.string.lines.to_a[line_num].chars.each_with_index.map do |char, i|
|
641
|
+
line << char.dump.gsub(/(^"|"$)/, "")
|
642
|
+
pointer = "#{" " * (line.length - 1)}^" if i == char_num
|
643
|
+
end
|
644
|
+
pointer ||= " " * line.length + "^"
|
645
|
+
[out, "", line, pointer].join("\n")
|
619
646
|
end
|
620
647
|
|
621
648
|
end
|
@@ -1,20 +1,12 @@
|
|
1
1
|
base = File.expand_path(File.dirname(__FILE__) + '/../../lib')
|
2
2
|
require base + '/http_tools'
|
3
3
|
require 'rubygems'
|
4
|
+
require 'ruby-prof'
|
4
5
|
|
5
6
|
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"
|
6
7
|
parser = HTTPTools::Parser.new
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
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
|
9
|
+
result = RubyProf.profile do
|
10
|
+
parser << request
|
11
|
+
end
|
12
|
+
RubyProf::FlatPrinter.new(result).print(STDOUT, 0)
|
data/test/cover.rb
CHANGED
@@ -1,28 +1,21 @@
|
|
1
1
|
require 'coverage' # >= ruby 1.9 only
|
2
2
|
|
3
|
+
testing = Dir[File.expand_path("../../lib/**/*.rb", __FILE__)]
|
4
|
+
|
3
5
|
at_exit do
|
4
|
-
testing = Dir[File.expand_path("../../lib/**/*.rb", __FILE__)]
|
5
|
-
|
6
6
|
results = Coverage.result.select {|key, value| testing.include?(key)}
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
puts "#{total.select {|i| i > 0}.length}/#{total.length} executable lines covered"
|
11
|
-
puts
|
8
|
+
all = results.map(&:last).flatten.compact
|
9
|
+
print "\n#{all.reject(&:zero?).size}/#{all.size} executable lines covered\n\n"
|
12
10
|
|
13
11
|
results.each do |key, value|
|
14
12
|
next unless value.include?(0)
|
15
|
-
|
16
|
-
|
17
|
-
puts
|
18
|
-
File.readlines(key).zip(value).each_with_index do |(line, val), i|
|
19
|
-
print val == 0 ? "> " : " "
|
20
|
-
print "%3i %5s %s" % [(i + 1), val, line]
|
13
|
+
lines = File.readlines(key).zip(value).each_with_index.map do |(line,val),i|
|
14
|
+
"%-2s%3i %5s %s" % [(">" if val == 0), (i + 1), val, line]
|
21
15
|
end
|
22
|
-
|
23
|
-
puts
|
16
|
+
print "#{key}\n line calls code\n\n#{lines.join}\n\n"
|
24
17
|
end
|
25
18
|
end
|
26
19
|
|
27
20
|
Coverage.start
|
28
|
-
|
21
|
+
require_relative 'runner'
|
data/test/parser/request_test.rb
CHANGED
@@ -75,9 +75,17 @@ class ParserRequestTest < Test::Unit::TestCase
|
|
75
75
|
def test_invalid_path
|
76
76
|
parser = HTTPTools::Parser.new
|
77
77
|
|
78
|
-
assert_raise(HTTPTools::ParseError) do
|
78
|
+
error = assert_raise(HTTPTools::ParseError) do
|
79
79
|
parser << "GET \\ HTTP/1.1\r\n\r\n"
|
80
80
|
end
|
81
|
+
|
82
|
+
return unless "".respond_to?(:lines)
|
83
|
+
assert_equal(<<-MESSAGE.chomp, error.message)
|
84
|
+
URI or path not recognised at line 1, char 5
|
85
|
+
|
86
|
+
GET \\\\ HTTP/1.1\\r\\n
|
87
|
+
^
|
88
|
+
MESSAGE
|
81
89
|
end
|
82
90
|
|
83
91
|
def test_uri
|
@@ -119,17 +127,49 @@ class ParserRequestTest < Test::Unit::TestCase
|
|
119
127
|
def test_fragment_with_path
|
120
128
|
parser = HTTPTools::Parser.new
|
121
129
|
|
122
|
-
assert_raise(HTTPTools::ParseError) do
|
130
|
+
error = assert_raise(HTTPTools::ParseError) do
|
123
131
|
parser << "GET /foo#bar HTTP/1.1\r\n\r\n"
|
124
132
|
end
|
133
|
+
|
134
|
+
return unless "".respond_to?(:lines)
|
135
|
+
assert_equal(<<-MESSAGE.chomp, error.message)
|
136
|
+
URI or path not recognised at line 1, char 9
|
137
|
+
|
138
|
+
GET /foo#bar HTTP/1.1\\r\\n
|
139
|
+
^
|
140
|
+
MESSAGE
|
125
141
|
end
|
126
142
|
|
127
143
|
def test_fragment_with_uri
|
128
144
|
parser = HTTPTools::Parser.new
|
129
145
|
|
130
|
-
assert_raise(HTTPTools::ParseError) do
|
146
|
+
error = assert_raise(HTTPTools::ParseError) do
|
131
147
|
parser << "GET http://example.com/foo#bar HTTP/1.1\r\n\r\n"
|
132
148
|
end
|
149
|
+
|
150
|
+
return unless "".respond_to?(:lines)
|
151
|
+
assert_equal(<<-MESSAGE.chomp, error.message)
|
152
|
+
URI or path not recognised at line 1, char 27
|
153
|
+
|
154
|
+
GET http://example.com/foo#bar HTTP/1.1\\r\\n
|
155
|
+
^
|
156
|
+
MESSAGE
|
157
|
+
end
|
158
|
+
|
159
|
+
def test_fragment_with_unfinished_path
|
160
|
+
parser = HTTPTools::Parser.new
|
161
|
+
|
162
|
+
error = assert_raise(HTTPTools::ParseError) do
|
163
|
+
parser << "GET /foo#ba"
|
164
|
+
end
|
165
|
+
|
166
|
+
return unless "".respond_to?(:lines)
|
167
|
+
assert_equal(<<-MESSAGE.chomp, error.message)
|
168
|
+
URI or path not recognised at line 1, char 9
|
169
|
+
|
170
|
+
GET /foo#ba
|
171
|
+
^
|
172
|
+
MESSAGE
|
133
173
|
end
|
134
174
|
|
135
175
|
def test_with_header
|
@@ -286,7 +326,15 @@ class ParserRequestTest < Test::Unit::TestCase
|
|
286
326
|
def test_unknown_protocol
|
287
327
|
parser = HTTPTools::Parser.new
|
288
328
|
|
289
|
-
assert_raise(HTTPTools::ParseError) {parser << "GET / SPDY/1.1\r\n"}
|
329
|
+
error = assert_raise(HTTPTools::ParseError) {parser << "GET / SPDY/1.1\r\n"}
|
330
|
+
|
331
|
+
return unless "".respond_to?(:lines)
|
332
|
+
assert_equal(<<-MESSAGE.chomp, error.message)
|
333
|
+
Invalid version specifier at line 1, char 7
|
334
|
+
|
335
|
+
GET / SPDY/1.1\\r\\n
|
336
|
+
^
|
337
|
+
MESSAGE
|
290
338
|
end
|
291
339
|
|
292
340
|
def test_protocol_version
|
@@ -303,7 +351,15 @@ class ParserRequestTest < Test::Unit::TestCase
|
|
303
351
|
def test_protocol_without_version
|
304
352
|
parser = HTTPTools::Parser.new
|
305
353
|
|
306
|
-
assert_raise(HTTPTools::ParseError) {parser << "GET / HTTP\r\n\r\n"}
|
354
|
+
error = assert_raise(HTTPTools::ParseError) {parser << "GET / HTTP\r\n\r\n"}
|
355
|
+
|
356
|
+
return unless "".respond_to?(:lines)
|
357
|
+
assert_equal(<<-MESSAGE.chomp, error.message)
|
358
|
+
Invalid version specifier at line 1, char 11
|
359
|
+
|
360
|
+
GET / HTTP\\r\\n
|
361
|
+
^
|
362
|
+
MESSAGE
|
307
363
|
end
|
308
364
|
|
309
365
|
def test_one_dot_x_protocol_version
|
@@ -432,7 +488,7 @@ class ParserRequestTest < Test::Unit::TestCase
|
|
432
488
|
def test_not_a_http_request
|
433
489
|
parser = HTTPTools::Parser.new
|
434
490
|
|
435
|
-
assert_raise(HTTPTools::ParseError) {parser << "not a http request"}
|
491
|
+
assert_raise(HTTPTools::ParseError) {parser << "not a http/1.1 request"}
|
436
492
|
end
|
437
493
|
|
438
494
|
def test_data_past_end
|
@@ -458,6 +514,23 @@ class ParserRequestTest < Test::Unit::TestCase
|
|
458
514
|
assert_equal("get", result)
|
459
515
|
end
|
460
516
|
|
517
|
+
def test_invalid_method
|
518
|
+
parser = HTTPTools::Parser.new
|
519
|
+
|
520
|
+
error = assert_raise(HTTPTools::ParseError) do
|
521
|
+
parser << "G3T / HTTP/1.1\r\n\r\n"
|
522
|
+
end
|
523
|
+
|
524
|
+
return unless "".respond_to?(:lines)
|
525
|
+
assert_equal(<<-MESSAGE.chomp, error.message)
|
526
|
+
Protocol or method not recognised at line 1, char 2
|
527
|
+
|
528
|
+
G3T / HTTP/1.1\\r\\n
|
529
|
+
^
|
530
|
+
MESSAGE
|
531
|
+
end
|
532
|
+
|
533
|
+
|
461
534
|
def test_lowercase_http
|
462
535
|
parser = HTTPTools::Parser.new
|
463
536
|
version = nil
|
@@ -472,31 +545,68 @@ class ParserRequestTest < Test::Unit::TestCase
|
|
472
545
|
def test_invalid_version
|
473
546
|
parser = HTTPTools::Parser.new
|
474
547
|
|
475
|
-
assert_raise(HTTPTools::ParseError)
|
548
|
+
error = assert_raise(HTTPTools::ParseError) do
|
549
|
+
parser << "GET / HTTP/one dot one\r\n"
|
550
|
+
end
|
551
|
+
|
552
|
+
return unless "".respond_to?(:lines)
|
553
|
+
assert_equal(<<-MESSAGE.chomp, error.message)
|
554
|
+
Invalid version specifier at line 1, char 12
|
555
|
+
|
556
|
+
GET / HTTP/one dot one\\r\\n
|
557
|
+
^
|
558
|
+
MESSAGE
|
476
559
|
end
|
477
560
|
|
478
561
|
def test_invalid_header_key_with_control_character
|
479
562
|
parser = HTTPTools::Parser.new
|
480
563
|
|
481
|
-
assert_raise(HTTPTools::ParseError) do
|
564
|
+
error = assert_raise(HTTPTools::ParseError) do
|
482
565
|
parser << "GET / HTTP/1.1\r\nx-invalid\0key: text/plain\r\n"
|
483
566
|
end
|
567
|
+
|
568
|
+
return unless "".respond_to?(:lines)
|
569
|
+
null = "\000".dump.gsub(/"/, "")
|
570
|
+
assert_equal(<<-MESSAGE.chomp, error.message)
|
571
|
+
Illegal character in field name at line 2, char 10
|
572
|
+
|
573
|
+
x-invalid#{null}key: text/plain\\r\\n
|
574
|
+
^
|
575
|
+
MESSAGE
|
484
576
|
end
|
485
577
|
|
486
578
|
def test_invalid_header_key_with_non_ascii_character
|
487
579
|
parser = HTTPTools::Parser.new
|
488
580
|
|
489
|
-
assert_raise(HTTPTools::ParseError) do
|
490
|
-
parser << "GET / HTTP/1.1\r\nx-invalid\
|
581
|
+
error = assert_raise(HTTPTools::ParseError) do
|
582
|
+
parser << "GET / HTTP/1.1\r\nx-invalid\342\200\224key: text/plain\r\n"
|
491
583
|
end
|
584
|
+
|
585
|
+
em_dash = "\342\200\224".dump.gsub(/"/, "")
|
586
|
+
return unless "".respond_to?(:lines)
|
587
|
+
assert_equal(<<-MESSAGE.chomp, error.message)
|
588
|
+
Illegal character in field name at line 2, char 10
|
589
|
+
|
590
|
+
x-invalid#{em_dash}key: text/plain\\r\\n
|
591
|
+
#{" " * (em_dash.length / 4)}^
|
592
|
+
MESSAGE
|
492
593
|
end
|
493
594
|
|
494
|
-
def
|
595
|
+
def test_invalid_header_value_with_non_control_character
|
495
596
|
parser = HTTPTools::Parser.new
|
496
597
|
|
497
|
-
assert_raise(HTTPTools::ParseError) do
|
498
|
-
parser << "GET / HTTP/1.1\r\nAccept: \
|
598
|
+
error = assert_raise(HTTPTools::ParseError) do
|
599
|
+
parser << "GET / HTTP/1.1\r\nAccept: text\000plain\r\n"
|
499
600
|
end
|
601
|
+
|
602
|
+
return unless "".respond_to?(:lines)
|
603
|
+
null = "\000".dump.gsub(/"/, "")
|
604
|
+
assert_equal(<<-MESSAGE.chomp, error.message)
|
605
|
+
Illegal character in field body at line 2, char 13
|
606
|
+
|
607
|
+
Accept: text#{null}plain\\r\\n
|
608
|
+
^
|
609
|
+
MESSAGE
|
500
610
|
end
|
501
611
|
|
502
612
|
def test_error_callback
|
@@ -721,4 +831,12 @@ class ParserRequestTest < Test::Unit::TestCase
|
|
721
831
|
assert_match(/#<HTTPTools::Parser:0x[a-f0-9]+ line 1, char 1 start>/, parser.inspect)
|
722
832
|
end
|
723
833
|
|
724
|
-
|
834
|
+
def test_inspect_position
|
835
|
+
parser = HTTPTools::Parser.new
|
836
|
+
|
837
|
+
parser << "GET / HTTP/1.1\r\nHost: foo."
|
838
|
+
|
839
|
+
assert_match(/#<HTTPTools::Parser:0x[a-f0-9]+ line 2, char 7 value>/, parser.inspect)
|
840
|
+
end
|
841
|
+
|
842
|
+
end
|
@@ -290,6 +290,26 @@ class ParserResponseTest < Test::Unit::TestCase
|
|
290
290
|
assert_equal({"Content-Type" => "text/html; charset=utf-8"}, headers)
|
291
291
|
end
|
292
292
|
|
293
|
+
def test_multi_line_header_invalid_value
|
294
|
+
parser = HTTPTools::Parser.new
|
295
|
+
|
296
|
+
parser << "HTTP/1.1 200 OK\r\n"
|
297
|
+
parser << "Content-Type: text/html;\r\n"
|
298
|
+
|
299
|
+
error = assert_raise(HTTPTools::ParseError) do
|
300
|
+
parser << " charset=\0\r\n\r\n"
|
301
|
+
end
|
302
|
+
|
303
|
+
return unless "".respond_to?(:lines)
|
304
|
+
null = "\000".dump.gsub(/"/, "")
|
305
|
+
assert_equal(<<-MESSAGE.chomp, error.message)
|
306
|
+
Illegal character in field body at line 3, char 10
|
307
|
+
|
308
|
+
charset=#{null}\\r\\n
|
309
|
+
^
|
310
|
+
MESSAGE
|
311
|
+
end
|
312
|
+
|
293
313
|
def test_header_value_leading_and_trailing_whitespace_is_stripped
|
294
314
|
parser = HTTPTools::Parser.new
|
295
315
|
headers = nil
|
@@ -380,6 +400,23 @@ class ParserResponseTest < Test::Unit::TestCase
|
|
380
400
|
assert(parser.finished?, "parser should be finished")
|
381
401
|
end
|
382
402
|
|
403
|
+
def test_invalid_header_key_with_control_character
|
404
|
+
parser = HTTPTools::Parser.new
|
405
|
+
|
406
|
+
error = assert_raise(HTTPTools::ParseError) do
|
407
|
+
parser << "HTTP/1.0 200 OK\r\nx-invalid\0key: valid key\r\n"
|
408
|
+
end
|
409
|
+
|
410
|
+
return unless "".respond_to?(:lines)
|
411
|
+
null = "\000".dump.gsub(/"/, "")
|
412
|
+
assert_equal(<<-MESSAGE.chomp, error.message)
|
413
|
+
Illegal character in field name at line 2, char 10
|
414
|
+
|
415
|
+
x-invalid#{null}key: valid key\\r\\n
|
416
|
+
^
|
417
|
+
MESSAGE
|
418
|
+
end
|
419
|
+
|
383
420
|
def test_apple_dot_com
|
384
421
|
parser = HTTPTools::Parser.new
|
385
422
|
code, message, headers = nil
|
@@ -646,6 +683,29 @@ class ParserResponseTest < Test::Unit::TestCase
|
|
646
683
|
assert(parser.finished?, "parser should be finished")
|
647
684
|
end
|
648
685
|
|
686
|
+
def test_body_with_no_headers
|
687
|
+
parser = HTTPTools::Parser.new
|
688
|
+
code, message, headers = nil
|
689
|
+
body = ""
|
690
|
+
|
691
|
+
parser.add_listener(:header) do
|
692
|
+
code = parser.status_code
|
693
|
+
message = parser.message
|
694
|
+
headers = parser.header
|
695
|
+
end
|
696
|
+
parser.add_listener(:stream) {|chunk| body << chunk}
|
697
|
+
|
698
|
+
parser << "HTTP/1.1 200 OK\r\n\r\n"
|
699
|
+
parser << "<h1>hello world</h1>"
|
700
|
+
parser.finish # notify parser the connection has closed
|
701
|
+
|
702
|
+
assert_equal(200, code)
|
703
|
+
assert_equal("OK", message)
|
704
|
+
assert_equal({}, headers)
|
705
|
+
assert_equal("<h1>hello world</h1>", body)
|
706
|
+
assert(parser.finished?, "parser should be finished")
|
707
|
+
end
|
708
|
+
|
649
709
|
def test_chunked
|
650
710
|
parser = HTTPTools::Parser.new
|
651
711
|
code, message, headers = nil
|
@@ -865,9 +925,17 @@ class ParserResponseTest < Test::Unit::TestCase
|
|
865
925
|
def test_html_body_only_not_allowed
|
866
926
|
parser = HTTPTools::Parser.new
|
867
927
|
|
868
|
-
assert_raise(HTTPTools::ParseError) do
|
928
|
+
error = assert_raise(HTTPTools::ParseError) do
|
869
929
|
parser << "<html><p>HTTP is hard</p></html>"
|
870
930
|
end
|
931
|
+
|
932
|
+
return unless "".respond_to?(:lines)
|
933
|
+
assert_equal(<<-MESSAGE.chomp, error.message)
|
934
|
+
Protocol or method not recognised at line 1, char 1
|
935
|
+
|
936
|
+
<html><p>HTTP is hard</p></html>
|
937
|
+
^
|
938
|
+
MESSAGE
|
871
939
|
end
|
872
940
|
|
873
941
|
def test_html_body_only_allowed
|
@@ -1155,6 +1223,29 @@ class ParserResponseTest < Test::Unit::TestCase
|
|
1155
1223
|
assert_equal({"X-Test" => "one two"}, trailer)
|
1156
1224
|
end
|
1157
1225
|
|
1226
|
+
def test_multi_line_trailer_invalid_value
|
1227
|
+
parser = HTTPTools::Parser.new
|
1228
|
+
|
1229
|
+
parser << "HTTP/1.1 200 OK\r\n"
|
1230
|
+
parser << "Transfer-Encoding: chunked\r\n"
|
1231
|
+
parser << "Trailer: X-Test\r\n\r\n"
|
1232
|
+
parser << "14\r\n<h1>Hello world</h1>\r\n0\r\n"
|
1233
|
+
parser << "X-Test: one\r\n"
|
1234
|
+
|
1235
|
+
error = assert_raise(HTTPTools::ParseError) do
|
1236
|
+
parser << " \0two\r\n"
|
1237
|
+
end
|
1238
|
+
|
1239
|
+
return unless "".respond_to?(:lines)
|
1240
|
+
null = "\000".dump.gsub(/"/, "")
|
1241
|
+
assert_equal(<<-MESSAGE.chomp, error.message)
|
1242
|
+
Illegal character in field body at line 9, char 2
|
1243
|
+
|
1244
|
+
#{null}two\\r\\n
|
1245
|
+
^
|
1246
|
+
MESSAGE
|
1247
|
+
end
|
1248
|
+
|
1158
1249
|
def test_trailer_value_leading_and_trailing_whitespace_is_stripped
|
1159
1250
|
parser = HTTPTools::Parser.new
|
1160
1251
|
trailer = nil
|
@@ -1237,9 +1328,18 @@ class ParserResponseTest < Test::Unit::TestCase
|
|
1237
1328
|
parser << "Transfer-Encoding: chunked\r\nTrailer: X-Checksum\r\n\r\n"
|
1238
1329
|
parser << "14\r\n<h1>Hello world</h1>\r\n0\r\n"
|
1239
1330
|
|
1240
|
-
assert_raise(HTTPTools::ParseError) do
|
1331
|
+
error = assert_raise(HTTPTools::ParseError) do
|
1241
1332
|
parser << "x-invalid\0key: value\r\n\r\n"
|
1242
1333
|
end
|
1334
|
+
|
1335
|
+
return unless "".respond_to?(:lines)
|
1336
|
+
null = "\000".dump.gsub(/"/, "")
|
1337
|
+
assert_equal(<<-MESSAGE.chomp, error.message)
|
1338
|
+
Illegal character in field name at line 8, char 10
|
1339
|
+
|
1340
|
+
x-invalid#{null}key: value\\r\\n
|
1341
|
+
^
|
1342
|
+
MESSAGE
|
1243
1343
|
end
|
1244
1344
|
|
1245
1345
|
def test_invalid_trailer_value
|
@@ -1252,21 +1352,65 @@ class ParserResponseTest < Test::Unit::TestCase
|
|
1252
1352
|
parser << "Transfer-Encoding: chunked\r\nTrailer: X-Checksum\r\n\r\n"
|
1253
1353
|
parser << "14\r\n<h1>Hello world</h1>\r\n0\r\n"
|
1254
1354
|
|
1255
|
-
assert_raise(HTTPTools::ParseError) do
|
1355
|
+
error = assert_raise(HTTPTools::ParseError) do
|
1256
1356
|
parser << "x-test: inva\0lid\r\n\r\n"
|
1257
1357
|
end
|
1358
|
+
|
1359
|
+
return unless "".respond_to?(:lines)
|
1360
|
+
null = "\000".dump.gsub(/"/, "")
|
1361
|
+
assert_equal(<<-MESSAGE.chomp, error.message)
|
1362
|
+
Illegal character in field body at line 8, char 13
|
1363
|
+
|
1364
|
+
x-test: inva#{null}lid\\r\\n
|
1365
|
+
^
|
1366
|
+
MESSAGE
|
1258
1367
|
end
|
1259
1368
|
|
1369
|
+
def test_invalid_protocol
|
1370
|
+
parser = HTTPTools::Parser.new
|
1371
|
+
|
1372
|
+
error = assert_raise(HTTPTools::ParseError) do
|
1373
|
+
parser << "HTTZ/1.1 200 OX\r\n"
|
1374
|
+
end
|
1375
|
+
|
1376
|
+
return unless "".respond_to?(:lines)
|
1377
|
+
assert_equal(<<-MESSAGE.chomp, error.message)
|
1378
|
+
Protocol or method not recognised at line 1, char 4
|
1379
|
+
|
1380
|
+
HTTZ/1.1 200 OX\\r\\n
|
1381
|
+
^
|
1382
|
+
MESSAGE
|
1383
|
+
end
|
1384
|
+
|
1385
|
+
|
1260
1386
|
def test_invalid_version
|
1261
1387
|
parser = HTTPTools::Parser.new
|
1262
1388
|
|
1263
|
-
assert_raise(HTTPTools::ParseError)
|
1389
|
+
error = assert_raise(HTTPTools::ParseError) do
|
1390
|
+
parser << "HTTP/one dot one 200 OK"
|
1391
|
+
end
|
1392
|
+
|
1393
|
+
return unless "".respond_to?(:lines)
|
1394
|
+
assert_equal(<<-MESSAGE.chomp, error.message)
|
1395
|
+
Invalid version specifier at line 1, char 6
|
1396
|
+
|
1397
|
+
HTTP/one dot one 200 OK
|
1398
|
+
^
|
1399
|
+
MESSAGE
|
1264
1400
|
end
|
1265
1401
|
|
1266
1402
|
def test_invalid_status
|
1267
1403
|
parser = HTTPTools::Parser.new
|
1268
1404
|
|
1269
|
-
assert_raise(HTTPTools::ParseError) {parser << "HTTP/1.1 0 Fail"}
|
1405
|
+
error = assert_raise(HTTPTools::ParseError) {parser << "HTTP/1.1 0 Fail"}
|
1406
|
+
|
1407
|
+
return unless "".respond_to?(:lines)
|
1408
|
+
assert_equal(<<-MESSAGE.chomp, error.message)
|
1409
|
+
Invalid status line at line 1, char 11
|
1410
|
+
|
1411
|
+
HTTP/1.1 0 Fail
|
1412
|
+
^
|
1413
|
+
MESSAGE
|
1270
1414
|
end
|
1271
1415
|
|
1272
1416
|
def test_finish_early
|
metadata
CHANGED
@@ -1,24 +1,33 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: http_tools
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 7
|
5
5
|
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 4
|
9
|
+
- 4
|
10
|
+
version: 0.4.4
|
6
11
|
platform: ruby
|
7
|
-
authors:
|
12
|
+
authors:
|
8
13
|
- Matthew Sadler
|
9
14
|
autorequire:
|
10
15
|
bindir: bin
|
11
16
|
cert_chain: []
|
12
|
-
|
17
|
+
|
18
|
+
date: 2011-12-20 00:00:00 +00:00
|
19
|
+
default_executable:
|
13
20
|
dependencies: []
|
14
|
-
|
15
|
-
|
21
|
+
|
22
|
+
description: A fast-as-possible pure Ruby HTTP parser plus associated lower level utilities to aid working with HTTP and the web.
|
16
23
|
email: mat@sourcetagsandcodes.com
|
17
24
|
executables: []
|
25
|
+
|
18
26
|
extensions: []
|
19
|
-
|
27
|
+
|
28
|
+
extra_rdoc_files:
|
20
29
|
- README.rdoc
|
21
|
-
files:
|
30
|
+
files:
|
22
31
|
- lib/http_tools/builder.rb
|
23
32
|
- lib/http_tools/encoding.rb
|
24
33
|
- lib/http_tools/parser.rb
|
@@ -44,32 +53,42 @@ files:
|
|
44
53
|
- example/websocket_client.rb
|
45
54
|
- example/websocket_server.rb
|
46
55
|
- README.rdoc
|
56
|
+
has_rdoc: true
|
47
57
|
homepage: http://github.com/matsadler/http_tools
|
48
58
|
licenses: []
|
59
|
+
|
49
60
|
post_install_message:
|
50
|
-
rdoc_options:
|
61
|
+
rdoc_options:
|
51
62
|
- --main
|
52
63
|
- README.rdoc
|
53
64
|
- --charset
|
54
65
|
- utf-8
|
55
|
-
require_paths:
|
66
|
+
require_paths:
|
56
67
|
- lib
|
57
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
68
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
58
69
|
none: false
|
59
|
-
requirements:
|
60
|
-
- -
|
61
|
-
- !ruby/object:Gem::Version
|
62
|
-
|
63
|
-
|
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
|
64
78
|
none: false
|
65
|
-
requirements:
|
66
|
-
- -
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
hash: 3
|
83
|
+
segments:
|
84
|
+
- 0
|
85
|
+
version: "0"
|
69
86
|
requirements: []
|
87
|
+
|
70
88
|
rubyforge_project:
|
71
|
-
rubygems_version: 1.
|
89
|
+
rubygems_version: 1.6.2
|
72
90
|
signing_key:
|
73
91
|
specification_version: 3
|
74
92
|
summary: Pure Ruby HTTP parser and friends
|
75
93
|
test_files: []
|
94
|
+
|