fast_http 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,90 @@
1
+ require 'stringio'
2
+
3
+ module FastHttp
4
+ # A simple class that using a StringIO object internally to allow for faster
5
+ # and simpler "push back" semantics. It basically lets you read a random
6
+ # amount from a secondary IO object, parse what is needed, and then anything
7
+ # remaining can be quickly pushed back in one chunk for the next read.
8
+ class PushBackIO
9
+ attr_accessor :secondary
10
+
11
+ def initialize(secondary)
12
+ @secondary = secondary
13
+ @buffer = StringIO.new
14
+ end
15
+
16
+ # Pushes the given string content back onto the stream for the
17
+ # next read to handle.
18
+ def push(content)
19
+ if content.length > 0
20
+ @buffer.write(content)
21
+ end
22
+ end
23
+
24
+ def pop(n)
25
+ @buffer.rewind
26
+ @buffer.read(n) || ""
27
+ end
28
+
29
+ def reset
30
+ @buffer.string = @buffer.read # reset out internal buffer
31
+ end
32
+
33
+ # First does a read from the internal buffer, and then appends anything
34
+ # needed from the secondary IO to complete the request. The return
35
+ # value is guaranteed to be a String, and never nil. If it returns
36
+ # a string of length 0 then there is nothing to read from the buffer (most
37
+ # likely closed). It will also avoid reading from a secondary that's closed.
38
+ #
39
+ # If partial==true then readpartial is used instead.
40
+ def read(n, partial=false)
41
+ r = pop(n)
42
+ needs = n - r.length
43
+
44
+ if needs > 0
45
+ sec = ""
46
+ if partial
47
+ begin
48
+ protect do
49
+ sec = @secondary.readpartial(needs)
50
+ end
51
+ rescue EOFError
52
+ close
53
+ end
54
+ else
55
+ protect { sec = @secondary.read(needs)}
56
+ end
57
+
58
+ r << (sec || "")
59
+
60
+ # finally, if there's nothing at all returned then this is bad
61
+ if r.length == 0
62
+ raise HttpClientError.new("Server returned empty response.")
63
+ end
64
+ end
65
+
66
+ reset
67
+ return r
68
+ end
69
+
70
+ def flush
71
+ protect { @secondary.flush }
72
+ end
73
+
74
+ def write(content)
75
+ protect { @secondary.write(content) }
76
+ end
77
+
78
+ def close
79
+ @secondary.close rescue nil
80
+ end
81
+
82
+ def protect
83
+ if !@secondary.closed?
84
+ yield
85
+ else
86
+ raise HttpClientError.new("Socket closed.")
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,47 @@
1
+
2
+ # Copyright (c) 2006 Zed A. Shaw
3
+ # You can redistribute it and/or modify it under the same terms as Ruby.
4
+
5
+ require 'test/unit'
6
+ require 'fast_http'
7
+ require 'socket'
8
+
9
+ class HttpClientParserTest < Test::Unit::TestCase
10
+ include FastHttp
11
+
12
+ def test_parse_simple
13
+ parser = HttpClientParser.new
14
+ req = HttpResponse.new
15
+ http = "HTTP/1.1 200 OK\r\nContent-Length: 20\r\n\r\n01234567890123456789"
16
+ nread = parser.execute(req, http, 0)
17
+ assert_equal 39, nread, "Failed to parse the full HTTP request after #{nread}"
18
+ assert parser.finished?, "Parser didn't finish"
19
+ assert !parser.error?, "Parser had error"
20
+ assert nread == parser.nread, "Number read returned from execute does not match"
21
+ assert_equal "20", req["CONTENT_LENGTH"], "Wrong content length header"
22
+ parser.reset
23
+ assert parser.nread == 0, "Number read after reset should be 0"
24
+ end
25
+
26
+ def parse(body, size, expect_req)
27
+ parser = HttpClientParser.new
28
+ req = HttpResponse.new
29
+ nread = parser.execute(req, body, 0)
30
+ assert_equal nread, body.length
31
+ assert !parser.error?
32
+ assert parser.finished?
33
+
34
+ # check data results
35
+ assert_equal size, req.http_chunk_size.to_i
36
+ expect_req.each {|k,v|assert_not_nil k; assert_equal req[k.upcase], v}
37
+ end
38
+
39
+ def test_http_parser
40
+ parse "3;test=stuff;lone\r\n", 3, {"test" => "stuff", "lone" => ""}
41
+ parse "0\r\n",0,{}
42
+ parse "\r\n",0,{}
43
+ parse ";test;test2=test2\r\n",0,{"test" => "", "test2" => "test2"}
44
+ parse "0;test;test2=test2\r\n",0,{"test" => "", "test2" => "test2"}
45
+ end
46
+
47
+ end
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fast_http
3
+ version: !ruby/object:Gem::Version
4
+ hash: 25
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 1
10
+ version: 0.1.1
11
+ platform: ruby
12
+ authors:
13
+ - Tom Lea
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-05-30 00:00:00 +01:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: ""
23
+ email:
24
+ executables: []
25
+
26
+ extensions:
27
+ - ext/http11_client/extconf.rb
28
+ extra_rdoc_files: []
29
+
30
+ files:
31
+ - ext/http11_client/MANIFEST
32
+ - ext/http11_client/Makefile
33
+ - ext/http11_client/conftest.dSYM/Contents/Info.plist
34
+ - ext/http11_client/conftest.dSYM/Contents/Resources/DWARF/conftest
35
+ - ext/http11_client/ext_help.h
36
+ - ext/http11_client/extconf.rb
37
+ - ext/http11_client/http11_client.bundle
38
+ - ext/http11_client/http11_client.c
39
+ - ext/http11_client/http11_client.o
40
+ - ext/http11_client/http11_parser.c
41
+ - ext/http11_client/http11_parser.h
42
+ - ext/http11_client/http11_parser.o
43
+ - ext/http11_client/http11_parser.rl
44
+ - ext/http11_client/mkmf.log
45
+ - lib/fast_http/client.rb
46
+ - lib/fast_http/pushbackio.rb
47
+ - lib/fast_http.rb
48
+ - test/test_httpparser.rb
49
+ has_rdoc: true
50
+ homepage:
51
+ licenses: []
52
+
53
+ post_install_message:
54
+ rdoc_options: []
55
+
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ hash: 3
64
+ segments:
65
+ - 0
66
+ version: "0"
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ hash: 3
73
+ segments:
74
+ - 0
75
+ version: "0"
76
+ requirements: []
77
+
78
+ rubyforge_project:
79
+ rubygems_version: 1.3.7
80
+ signing_key:
81
+ specification_version: 3
82
+ summary: The http client from rfuzz extracted into its own gem.
83
+ test_files:
84
+ - test/test_httpparser.rb