andyjeffries-eventmachine_httpserver 0.1.201

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,239 @@
1
+ require 'test/unit'
2
+ require 'evma_httpserver'
3
+
4
+ begin
5
+ once = false
6
+ require 'eventmachine'
7
+ rescue LoadError => e
8
+ raise e if once
9
+ once = true
10
+ require 'rubygems'
11
+ retry
12
+ end
13
+
14
+
15
+
16
+ #--------------------------------------
17
+
18
+ module EventMachine
19
+ module HttpServer
20
+ def process_http_request
21
+ send_data generate_response()
22
+ close_connection_after_writing
23
+ end
24
+ end
25
+ end
26
+
27
+ #--------------------------------------
28
+
29
+ require 'socket'
30
+
31
+ class TestApp < Test::Unit::TestCase
32
+
33
+ TestHost = "127.0.0.1"
34
+ TestPort = 8911
35
+
36
+ TestResponse_1 = <<EORESP
37
+ HTTP/1.0 200 ...
38
+ Content-length: 4
39
+ Content-type: text/plain
40
+ Connection: close
41
+
42
+ 1234
43
+ EORESP
44
+
45
+ Thread.abort_on_exception = true
46
+
47
+ def test_simple_get
48
+ received_response = nil
49
+
50
+ EventMachine::HttpServer.module_eval do
51
+ def generate_response
52
+ TestResponse_1
53
+ end
54
+ end
55
+
56
+
57
+ EventMachine.run do
58
+ EventMachine.start_server TestHost, TestPort, EventMachine::HttpServer
59
+ EventMachine.add_timer(1) {raise "timed out"} # make sure the test completes
60
+
61
+ cb = proc do
62
+ tcp = TCPSocket.new TestHost, TestPort
63
+ tcp.write "GET / HTTP/1.0\r\n\r\n"
64
+ received_response = tcp.read
65
+ end
66
+ eb = proc { EventMachine.stop }
67
+ EventMachine.defer cb, eb
68
+ end
69
+
70
+ assert_equal( TestResponse_1, received_response )
71
+ end
72
+
73
+
74
+
75
+
76
+ # This frowsy-looking protocol handler allows the test harness to make some
77
+ # its local variables visible, so we can set them here and they can be asserted later.
78
+ class MyTestServer < EventMachine::Connection
79
+ include EventMachine::HttpServer
80
+ def initialize *args
81
+ super
82
+ end
83
+ def generate_response
84
+ @assertions.call
85
+ TestResponse_1
86
+ end
87
+ end
88
+
89
+
90
+
91
+ def test_parameters
92
+ path_info = "/test.html"
93
+ query_string = "a=b&c=d"
94
+ cookie = "eat_me=I'm a cookie"
95
+ etag = "12345"
96
+
97
+ # collect all the stuff we want to assert outside the actual test,
98
+ # to ensure it gets asserted even if the test catches some exception.
99
+ received_response = nil
100
+ request_parms = {}
101
+
102
+
103
+ EventMachine.run do
104
+ EventMachine.start_server(TestHost, TestPort, MyTestServer) do |conn|
105
+ # In each accepted connection, set up a procedure that will copy
106
+ # the request parameters into a local variable visible here, so
107
+ # we can assert the values later.
108
+ conn.instance_eval do
109
+ @assertions = proc {
110
+ parms = %w( PATH_INFO QUERY_STRING HTTP_COOKIE IF_NONE_MATCH
111
+ CONTENT_TYPE REQUEST_METHOD REQUEST_URI )
112
+ parms.each {|parm|
113
+ # request_parms is bound to a local variable visible in this context.
114
+ request_parms[parm] = ENV[parm]
115
+ }
116
+ }
117
+ end
118
+ end
119
+ EventMachine.add_timer(1) {raise "timed out"} # make sure the test completes
120
+
121
+ cb = proc do
122
+ tcp = TCPSocket.new TestHost, TestPort
123
+ data = [
124
+ "GET #{path_info}?#{query_string} HTTP/1.1\r\n",
125
+ "Cookie: #{cookie}\r\n",
126
+ "If-none-match: #{etag}\r\n",
127
+ "\r\n"
128
+ ].join
129
+ tcp.write(data)
130
+ received_response = tcp.read
131
+ end
132
+ eb = proc { EventMachine.stop }
133
+ EventMachine.defer cb, eb
134
+ end
135
+
136
+ assert_equal( TestResponse_1, received_response )
137
+ assert_equal( path_info, request_parms["PATH_INFO"] )
138
+ assert_equal( query_string, request_parms["QUERY_STRING"] )
139
+ assert_equal( cookie, request_parms["HTTP_COOKIE"] )
140
+ assert_equal( etag, request_parms["IF_NONE_MATCH"] )
141
+ assert_equal( nil, request_parms["CONTENT_TYPE"] )
142
+ assert_equal( "GET", request_parms["REQUEST_METHOD"] )
143
+ assert_equal( path_info, request_parms["REQUEST_URI"] )
144
+ end
145
+
146
+
147
+ def test_headers
148
+ received_header_string = nil
149
+ received_header_ary = nil
150
+
151
+ EventMachine.run do
152
+ EventMachine.start_server(TestHost, TestPort, MyTestServer) do |conn|
153
+ # In each accepted connection, set up a procedure that will copy
154
+ # the request parameters into a local variable visible here, so
155
+ # we can assert the values later.
156
+ # The @http_headers is set automatically and can easily be parsed.
157
+ # It isn't automatically parsed into Ruby values because that is
158
+ # a costly operation, but we should provide an optional method that
159
+ # does the parsing so it doesn't need to be done by users.
160
+ conn.instance_eval do
161
+ @assertions = proc do
162
+ received_header_string = @http_headers
163
+ received_header_ary = @http_headers.split(/\0/).map {|line| line.split(/:\s*/, 2) }
164
+ end
165
+ end
166
+ end
167
+
168
+ cb = proc do
169
+ tcp = TCPSocket.new TestHost, TestPort
170
+ data = [
171
+ "GET / HTTP/1.1\r\n",
172
+ "aaa: 111\r\n",
173
+ "bbb: 222\r\n",
174
+ "ccc: 333\r\n",
175
+ "ddd: 444\r\n",
176
+ "\r\n"
177
+ ].join
178
+ tcp.write data
179
+ received_response = tcp.read
180
+ end
181
+ eb = proc { EventMachine.stop }
182
+ EventMachine.defer cb, eb
183
+
184
+ EventMachine.add_timer(1) {raise "timed out"} # make sure the test completes
185
+ end
186
+
187
+ assert_equal( "aaa: 111\0bbb: 222\0ccc: 333\0ddd: 444\0\0", received_header_string )
188
+ assert_equal( [["aaa","111"], ["bbb","222"], ["ccc","333"], ["ddd","444"]], received_header_ary )
189
+ end
190
+
191
+
192
+
193
+
194
+
195
+ def test_post
196
+ received_header_string = nil
197
+ post_content = "1234567890"
198
+ content_type = "text/plain"
199
+ received_post_content = ""
200
+ received_content_type = ""
201
+
202
+ EventMachine.run do
203
+ EventMachine.start_server(TestHost, TestPort, MyTestServer) do |conn|
204
+ # In each accepted connection, set up a procedure that will copy
205
+ # the request parameters into a local variable visible here, so
206
+ # we can assert the values later.
207
+ # The @http_post_content variable is set automatically.
208
+ conn.instance_eval do
209
+ @assertions = proc do
210
+ received_post_content = @http_post_content
211
+ received_content_type = ENV["CONTENT_TYPE"]
212
+ end
213
+ end
214
+ end
215
+ EventMachine.add_timer(1) {raise "timed out"} # make sure the test completes
216
+
217
+ cb = proc do
218
+ tcp = TCPSocket.new TestHost, TestPort
219
+ data = [
220
+ "POST / HTTP/1.1\r\n",
221
+ "Content-type: #{content_type}\r\n",
222
+ "Content-length: #{post_content.length}\r\n",
223
+ "\r\n",
224
+ post_content
225
+ ].join
226
+ tcp.write(data)
227
+ received_response = tcp.read
228
+ end
229
+ eb = proc do
230
+ EventMachine.stop
231
+ end
232
+ EventMachine.defer cb, eb
233
+ end
234
+
235
+ assert_equal( received_post_content, post_content )
236
+ assert_equal( received_content_type, content_type )
237
+ end
238
+
239
+ end
@@ -0,0 +1,187 @@
1
+ require 'test/unit'
2
+ require 'evma_httpserver'
3
+
4
+ begin
5
+ once = false
6
+ require 'eventmachine'
7
+ rescue LoadError => e
8
+ raise e if once
9
+ once = true
10
+ require 'rubygems'
11
+ retry
12
+ end
13
+
14
+
15
+ #--------------------------------------
16
+
17
+
18
+ class TestDelegatedHttpResponse < Test::Unit::TestCase
19
+
20
+ # This is a delegate class that (trivially) implements the
21
+ # several classes needed to work with HttpResponse.
22
+ #
23
+ class D
24
+ attr_reader :output_data
25
+ attr_reader :closed_after_writing
26
+
27
+ def send_data data
28
+ @output_data ||= ""
29
+ @output_data << data
30
+ end
31
+ def close_connection_after_writing
32
+ @closed_after_writing = true
33
+ end
34
+ end
35
+
36
+ def setup
37
+ end
38
+
39
+
40
+ def teardown
41
+ end
42
+
43
+
44
+ def test_properties
45
+ a = EM::DelegatedHttpResponse.new( D.new )
46
+ a.status = 200
47
+ a.content = "Some content"
48
+ a.headers["Content-type"] = "text/xml"
49
+ end
50
+
51
+ def test_header_sugarings
52
+ a = EM::DelegatedHttpResponse.new( D.new )
53
+ a.content_type "text/xml"
54
+ a.set_cookie "a=b"
55
+ a.headers["X-bayshore"] = "aaaa"
56
+
57
+ assert_equal({
58
+ "Content-type" => "text/xml",
59
+ "Set-cookie" => ["a=b"],
60
+ "X-bayshore" => "aaaa"
61
+ }, a.headers)
62
+ end
63
+
64
+ def test_send_response
65
+ d = D.new
66
+ a = EM::DelegatedHttpResponse.new( d )
67
+ a.status = 200
68
+ a.send_response
69
+ assert_equal([
70
+ "HTTP/1.1 200 ...\r\n",
71
+ "Content-length: 0\r\n",
72
+ "\r\n"
73
+ ].join, d.output_data)
74
+ assert_equal( true, d.closed_after_writing )
75
+ end
76
+
77
+ def test_send_response_1
78
+ d = D.new
79
+ a = EM::DelegatedHttpResponse.new( d )
80
+ a.status = 200
81
+ a.content_type "text/plain"
82
+ a.content = "ABC"
83
+ a.send_response
84
+ assert_equal([
85
+ "HTTP/1.1 200 ...\r\n",
86
+ "Content-length: 3\r\n",
87
+ "Content-type: text/plain\r\n",
88
+ "\r\n",
89
+ "ABC"
90
+ ].join, d.output_data)
91
+ assert( d.closed_after_writing )
92
+ end
93
+
94
+ def test_send_response_no_close
95
+ d = D.new
96
+ a = EM::DelegatedHttpResponse.new( d )
97
+ a.status = 200
98
+ a.content_type "text/plain"
99
+ a.content = "ABC"
100
+ a.keep_connection_open
101
+ a.send_response
102
+ assert_equal([
103
+ "HTTP/1.1 200 ...\r\n",
104
+ "Content-length: 3\r\n",
105
+ "Content-type: text/plain\r\n",
106
+ "\r\n",
107
+ "ABC"
108
+ ].join, d.output_data)
109
+ assert( ! d.closed_after_writing )
110
+ end
111
+
112
+ def test_send_response_multiple_times
113
+ a = EM::DelegatedHttpResponse.new( D.new )
114
+ a.status = 200
115
+ a.send_response
116
+ assert_raise( RuntimeError ) {
117
+ a.send_response
118
+ }
119
+ end
120
+
121
+ def test_send_headers
122
+ d = D.new
123
+ a = EM::DelegatedHttpResponse.new( d )
124
+ a.status = 200
125
+ a.send_headers
126
+ assert_equal([
127
+ "HTTP/1.1 200 ...\r\n",
128
+ "Content-length: 0\r\n",
129
+ "\r\n"
130
+ ].join, d.output_data)
131
+ assert( ! d.closed_after_writing )
132
+ assert_raise( RuntimeError ) {
133
+ a.send_headers
134
+ }
135
+ end
136
+
137
+ def test_send_chunks
138
+ d = D.new
139
+ a = EM::DelegatedHttpResponse.new( d )
140
+ a.chunk "ABC"
141
+ a.chunk "DEF"
142
+ a.chunk "GHI"
143
+ a.keep_connection_open
144
+ a.send_response
145
+ assert_equal([
146
+ "HTTP/1.1 200 ...\r\n",
147
+ "Transfer-encoding: chunked\r\n",
148
+ "\r\n",
149
+ "3\r\n",
150
+ "ABC\r\n",
151
+ "3\r\n",
152
+ "DEF\r\n",
153
+ "3\r\n",
154
+ "GHI\r\n",
155
+ "0\r\n",
156
+ "\r\n"
157
+ ].join, d.output_data)
158
+ assert( !d.closed_after_writing )
159
+ end
160
+
161
+ def test_send_chunks_with_close
162
+ d = D.new
163
+ a = EM::DelegatedHttpResponse.new( d )
164
+ a.chunk "ABC"
165
+ a.chunk "DEF"
166
+ a.chunk "GHI"
167
+ a.send_response
168
+ assert_equal([
169
+ "HTTP/1.1 200 ...\r\n",
170
+ "Transfer-encoding: chunked\r\n",
171
+ "\r\n",
172
+ "3\r\n",
173
+ "ABC\r\n",
174
+ "3\r\n",
175
+ "DEF\r\n",
176
+ "3\r\n",
177
+ "GHI\r\n",
178
+ "0\r\n",
179
+ "\r\n"
180
+ ].join, d.output_data)
181
+ assert( d.closed_after_writing )
182
+ end
183
+
184
+ end
185
+
186
+
187
+
@@ -0,0 +1,178 @@
1
+ require 'test/unit'
2
+ require 'evma_httpserver'
3
+
4
+ begin
5
+ once = false
6
+ require 'eventmachine'
7
+ rescue LoadError => e
8
+ raise e if once
9
+ once = true
10
+ require 'rubygems'
11
+ retry
12
+ end
13
+
14
+
15
+ #--------------------------------------
16
+
17
+ module EventMachine
18
+
19
+ # This is a test harness wired into the HttpResponse class so we
20
+ # can test it without requiring any actual network communication.
21
+ #
22
+ class HttpResponse
23
+ attr_reader :output_data
24
+ attr_reader :closed_after_writing
25
+
26
+ def send_data data
27
+ @output_data ||= ""
28
+ @output_data << data
29
+ end
30
+ def close_connection_after_writing
31
+ @closed_after_writing = true
32
+ end
33
+ end
34
+ end
35
+
36
+ #--------------------------------------
37
+
38
+
39
+ class TestHttpResponse < Test::Unit::TestCase
40
+
41
+ def test_properties
42
+ a = EventMachine::HttpResponse.new
43
+ a.status = 200
44
+ a.content = "Some content"
45
+ a.headers["Content-type"] = "text/xml"
46
+ end
47
+
48
+ def test_header_sugarings
49
+ a = EventMachine::HttpResponse.new
50
+ a.content_type "text/xml"
51
+ a.set_cookie "a=b"
52
+ a.headers["X-bayshore"] = "aaaa"
53
+
54
+ assert_equal({
55
+ "Content-type" => "text/xml",
56
+ "Set-cookie" => ["a=b"],
57
+ "X-bayshore" => "aaaa"
58
+ }, a.headers)
59
+ end
60
+
61
+ def test_send_response
62
+ a = EventMachine::HttpResponse.new
63
+ a.status = 200
64
+ a.send_response
65
+ assert_equal([
66
+ "HTTP/1.1 200 ...\r\n",
67
+ "Content-length: 0\r\n",
68
+ "\r\n"
69
+ ].join, a.output_data)
70
+ assert_equal( true, a.closed_after_writing )
71
+ end
72
+
73
+ def test_send_response_1
74
+ a = EventMachine::HttpResponse.new
75
+ a.status = 200
76
+ a.content_type "text/plain"
77
+ a.content = "ABC"
78
+ a.send_response
79
+ assert_equal([
80
+ "HTTP/1.1 200 ...\r\n",
81
+ "Content-length: 3\r\n",
82
+ "Content-type: text/plain\r\n",
83
+ "\r\n",
84
+ "ABC"
85
+ ].join, a.output_data)
86
+ assert( a.closed_after_writing )
87
+ end
88
+
89
+ def test_send_response_no_close
90
+ a = EventMachine::HttpResponse.new
91
+ a.status = 200
92
+ a.content_type "text/plain"
93
+ a.content = "ABC"
94
+ a.keep_connection_open
95
+ a.send_response
96
+ assert_equal([
97
+ "HTTP/1.1 200 ...\r\n",
98
+ "Content-length: 3\r\n",
99
+ "Content-type: text/plain\r\n",
100
+ "\r\n",
101
+ "ABC"
102
+ ].join, a.output_data)
103
+ assert( ! a.closed_after_writing )
104
+ end
105
+
106
+ def test_send_response_multiple_times
107
+ a = EventMachine::HttpResponse.new
108
+ a.status = 200
109
+ a.send_response
110
+ assert_raise( RuntimeError ) {
111
+ a.send_response
112
+ }
113
+ end
114
+
115
+ def test_send_headers
116
+ a = EventMachine::HttpResponse.new
117
+ a.status = 200
118
+ a.send_headers
119
+ assert_equal([
120
+ "HTTP/1.1 200 ...\r\n",
121
+ "Content-length: 0\r\n",
122
+ "\r\n"
123
+ ].join, a.output_data)
124
+ assert( ! a.closed_after_writing )
125
+ assert_raise( RuntimeError ) {
126
+ a.send_headers
127
+ }
128
+ end
129
+
130
+ def test_send_chunks
131
+ a = EventMachine::HttpResponse.new
132
+ a.chunk "ABC"
133
+ a.chunk "DEF"
134
+ a.chunk "GHI"
135
+ a.keep_connection_open
136
+ a.send_response
137
+ assert_equal([
138
+ "HTTP/1.1 200 ...\r\n",
139
+ "Transfer-encoding: chunked\r\n",
140
+ "\r\n",
141
+ "3\r\n",
142
+ "ABC\r\n",
143
+ "3\r\n",
144
+ "DEF\r\n",
145
+ "3\r\n",
146
+ "GHI\r\n",
147
+ "0\r\n",
148
+ "\r\n"
149
+ ].join, a.output_data)
150
+ assert( !a.closed_after_writing )
151
+ end
152
+
153
+ def test_send_chunks_with_close
154
+ a = EventMachine::HttpResponse.new
155
+ a.chunk "ABC"
156
+ a.chunk "DEF"
157
+ a.chunk "GHI"
158
+ a.send_response
159
+ assert_equal([
160
+ "HTTP/1.1 200 ...\r\n",
161
+ "Transfer-encoding: chunked\r\n",
162
+ "\r\n",
163
+ "3\r\n",
164
+ "ABC\r\n",
165
+ "3\r\n",
166
+ "DEF\r\n",
167
+ "3\r\n",
168
+ "GHI\r\n",
169
+ "0\r\n",
170
+ "\r\n"
171
+ ].join, a.output_data)
172
+ assert( a.closed_after_writing )
173
+ end
174
+
175
+ end
176
+
177
+
178
+