eventmachine-mkroman 1.3.0.dev.1

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.
Files changed (182) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +179 -0
  3. data/GNU +281 -0
  4. data/LICENSE +60 -0
  5. data/README.md +110 -0
  6. data/docs/DocumentationGuidesIndex.md +27 -0
  7. data/docs/GettingStarted.md +520 -0
  8. data/docs/old/ChangeLog +211 -0
  9. data/docs/old/DEFERRABLES +246 -0
  10. data/docs/old/EPOLL +141 -0
  11. data/docs/old/INSTALL +13 -0
  12. data/docs/old/KEYBOARD +42 -0
  13. data/docs/old/LEGAL +25 -0
  14. data/docs/old/LIGHTWEIGHT_CONCURRENCY +130 -0
  15. data/docs/old/PURE_RUBY +75 -0
  16. data/docs/old/RELEASE_NOTES +94 -0
  17. data/docs/old/SMTP +4 -0
  18. data/docs/old/SPAWNED_PROCESSES +148 -0
  19. data/docs/old/TODO +8 -0
  20. data/examples/guides/getting_started/01_eventmachine_echo_server.rb +18 -0
  21. data/examples/guides/getting_started/02_eventmachine_echo_server_that_recognizes_exit_command.rb +22 -0
  22. data/examples/guides/getting_started/03_simple_chat_server.rb +149 -0
  23. data/examples/guides/getting_started/04_simple_chat_server_step_one.rb +27 -0
  24. data/examples/guides/getting_started/05_simple_chat_server_step_two.rb +43 -0
  25. data/examples/guides/getting_started/06_simple_chat_server_step_three.rb +98 -0
  26. data/examples/guides/getting_started/07_simple_chat_server_step_four.rb +121 -0
  27. data/examples/guides/getting_started/08_simple_chat_server_step_five.rb +141 -0
  28. data/examples/old/ex_channel.rb +43 -0
  29. data/examples/old/ex_queue.rb +2 -0
  30. data/examples/old/ex_tick_loop_array.rb +15 -0
  31. data/examples/old/ex_tick_loop_counter.rb +32 -0
  32. data/examples/old/helper.rb +2 -0
  33. data/ext/binder.cpp +124 -0
  34. data/ext/binder.h +52 -0
  35. data/ext/cmain.cpp +1046 -0
  36. data/ext/ed.cpp +2243 -0
  37. data/ext/ed.h +463 -0
  38. data/ext/em.cpp +2378 -0
  39. data/ext/em.h +266 -0
  40. data/ext/eventmachine.h +152 -0
  41. data/ext/extconf.rb +291 -0
  42. data/ext/fastfilereader/extconf.rb +120 -0
  43. data/ext/fastfilereader/mapper.cpp +214 -0
  44. data/ext/fastfilereader/mapper.h +59 -0
  45. data/ext/fastfilereader/rubymain.cpp +126 -0
  46. data/ext/kb.cpp +79 -0
  47. data/ext/page.cpp +107 -0
  48. data/ext/page.h +51 -0
  49. data/ext/pipe.cpp +354 -0
  50. data/ext/project.h +174 -0
  51. data/ext/rubymain.cpp +1643 -0
  52. data/ext/ssl.cpp +701 -0
  53. data/ext/ssl.h +103 -0
  54. data/ext/wait_for_single_fd.h +36 -0
  55. data/java/.classpath +8 -0
  56. data/java/.project +17 -0
  57. data/java/src/com/rubyeventmachine/EmReactor.java +625 -0
  58. data/java/src/com/rubyeventmachine/EmReactorException.java +40 -0
  59. data/java/src/com/rubyeventmachine/EmReactorInterface.java +70 -0
  60. data/java/src/com/rubyeventmachine/EventableChannel.java +72 -0
  61. data/java/src/com/rubyeventmachine/EventableDatagramChannel.java +201 -0
  62. data/java/src/com/rubyeventmachine/EventableSocketChannel.java +415 -0
  63. data/java/src/com/rubyeventmachine/NullEmReactor.java +157 -0
  64. data/java/src/com/rubyeventmachine/NullEventableChannel.java +81 -0
  65. data/lib/em/buftok.rb +59 -0
  66. data/lib/em/callback.rb +58 -0
  67. data/lib/em/channel.rb +69 -0
  68. data/lib/em/completion.rb +307 -0
  69. data/lib/em/connection.rb +802 -0
  70. data/lib/em/deferrable/pool.rb +2 -0
  71. data/lib/em/deferrable.rb +210 -0
  72. data/lib/em/file_watch.rb +73 -0
  73. data/lib/em/future.rb +61 -0
  74. data/lib/em/io_streamer.rb +68 -0
  75. data/lib/em/iterator.rb +252 -0
  76. data/lib/em/messages.rb +66 -0
  77. data/lib/em/pool.rb +151 -0
  78. data/lib/em/process_watch.rb +45 -0
  79. data/lib/em/processes.rb +123 -0
  80. data/lib/em/protocols/header_and_content.rb +138 -0
  81. data/lib/em/protocols/httpclient.rb +303 -0
  82. data/lib/em/protocols/httpclient2.rb +602 -0
  83. data/lib/em/protocols/line_and_text.rb +125 -0
  84. data/lib/em/protocols/line_protocol.rb +33 -0
  85. data/lib/em/protocols/linetext2.rb +179 -0
  86. data/lib/em/protocols/memcache.rb +331 -0
  87. data/lib/em/protocols/object_protocol.rb +46 -0
  88. data/lib/em/protocols/postgres3.rb +246 -0
  89. data/lib/em/protocols/saslauth.rb +175 -0
  90. data/lib/em/protocols/smtpclient.rb +394 -0
  91. data/lib/em/protocols/smtpserver.rb +666 -0
  92. data/lib/em/protocols/socks4.rb +66 -0
  93. data/lib/em/protocols/stomp.rb +205 -0
  94. data/lib/em/protocols/tcptest.rb +54 -0
  95. data/lib/em/protocols.rb +37 -0
  96. data/lib/em/pure_ruby.rb +1300 -0
  97. data/lib/em/queue.rb +80 -0
  98. data/lib/em/resolver.rb +232 -0
  99. data/lib/em/spawnable.rb +84 -0
  100. data/lib/em/streamer.rb +118 -0
  101. data/lib/em/threaded_resource.rb +90 -0
  102. data/lib/em/tick_loop.rb +85 -0
  103. data/lib/em/timers.rb +61 -0
  104. data/lib/em/version.rb +3 -0
  105. data/lib/eventmachine.rb +1602 -0
  106. data/lib/jeventmachine.rb +319 -0
  107. data/rakelib/package.rake +120 -0
  108. data/rakelib/test.rake +6 -0
  109. data/rakelib/test_pure.rake +11 -0
  110. data/tests/client.crt +31 -0
  111. data/tests/client.key +51 -0
  112. data/tests/dhparam.pem +13 -0
  113. data/tests/em_ssl_handlers.rb +165 -0
  114. data/tests/em_test_helper.rb +198 -0
  115. data/tests/encoded_client.key +54 -0
  116. data/tests/jruby/test_jeventmachine.rb +38 -0
  117. data/tests/test_attach.rb +199 -0
  118. data/tests/test_basic.rb +321 -0
  119. data/tests/test_channel.rb +75 -0
  120. data/tests/test_completion.rb +178 -0
  121. data/tests/test_connection_count.rb +83 -0
  122. data/tests/test_connection_write.rb +35 -0
  123. data/tests/test_defer.rb +35 -0
  124. data/tests/test_deferrable.rb +35 -0
  125. data/tests/test_epoll.rb +141 -0
  126. data/tests/test_error_handler.rb +38 -0
  127. data/tests/test_exc.rb +37 -0
  128. data/tests/test_file_watch.rb +86 -0
  129. data/tests/test_fork.rb +75 -0
  130. data/tests/test_futures.rb +170 -0
  131. data/tests/test_handler_check.rb +35 -0
  132. data/tests/test_hc.rb +155 -0
  133. data/tests/test_httpclient.rb +238 -0
  134. data/tests/test_httpclient2.rb +132 -0
  135. data/tests/test_idle_connection.rb +31 -0
  136. data/tests/test_inactivity_timeout.rb +102 -0
  137. data/tests/test_io_streamer.rb +48 -0
  138. data/tests/test_ipv4.rb +96 -0
  139. data/tests/test_ipv6.rb +107 -0
  140. data/tests/test_iterator.rb +122 -0
  141. data/tests/test_kb.rb +28 -0
  142. data/tests/test_keepalive.rb +113 -0
  143. data/tests/test_line_protocol.rb +33 -0
  144. data/tests/test_ltp.rb +155 -0
  145. data/tests/test_ltp2.rb +332 -0
  146. data/tests/test_many_fds.rb +21 -0
  147. data/tests/test_next_tick.rb +104 -0
  148. data/tests/test_object_protocol.rb +36 -0
  149. data/tests/test_pause.rb +109 -0
  150. data/tests/test_pending_connect_timeout.rb +52 -0
  151. data/tests/test_pool.rb +196 -0
  152. data/tests/test_process_watch.rb +50 -0
  153. data/tests/test_processes.rb +147 -0
  154. data/tests/test_proxy_connection.rb +180 -0
  155. data/tests/test_pure.rb +156 -0
  156. data/tests/test_queue.rb +64 -0
  157. data/tests/test_resolver.rb +129 -0
  158. data/tests/test_running.rb +14 -0
  159. data/tests/test_sasl.rb +46 -0
  160. data/tests/test_send_file.rb +217 -0
  161. data/tests/test_servers.rb +32 -0
  162. data/tests/test_shutdown_hooks.rb +23 -0
  163. data/tests/test_smtpclient.rb +75 -0
  164. data/tests/test_smtpserver.rb +90 -0
  165. data/tests/test_sock_opt.rb +53 -0
  166. data/tests/test_spawn.rb +290 -0
  167. data/tests/test_ssl_args.rb +70 -0
  168. data/tests/test_ssl_dhparam.rb +57 -0
  169. data/tests/test_ssl_ecdh_curve.rb +57 -0
  170. data/tests/test_ssl_extensions.rb +24 -0
  171. data/tests/test_ssl_inline_cert.rb +222 -0
  172. data/tests/test_ssl_methods.rb +31 -0
  173. data/tests/test_ssl_protocols.rb +190 -0
  174. data/tests/test_ssl_verify.rb +108 -0
  175. data/tests/test_stomp.rb +38 -0
  176. data/tests/test_system.rb +46 -0
  177. data/tests/test_threaded_resource.rb +68 -0
  178. data/tests/test_tick_loop.rb +58 -0
  179. data/tests/test_timers.rb +150 -0
  180. data/tests/test_ud.rb +8 -0
  181. data/tests/test_unbind_reason.rb +40 -0
  182. metadata +389 -0
@@ -0,0 +1,602 @@
1
+ #--
2
+ #
3
+ # Author:: Francis Cianfrocca (gmail: blackhedd)
4
+ # Homepage:: http://rubyeventmachine.com
5
+ # Date:: 16 July 2006
6
+ #
7
+ # See EventMachine and EventMachine::Connection for documentation and
8
+ # usage examples.
9
+ #
10
+ #----------------------------------------------------------------------------
11
+ #
12
+ # Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
13
+ # Gmail: blackhedd
14
+ #
15
+ # This program is free software; you can redistribute it and/or modify
16
+ # it under the terms of either: 1) the GNU General Public License
17
+ # as published by the Free Software Foundation; either version 2 of the
18
+ # License, or (at your option) any later version; or 2) Ruby's License.
19
+ #
20
+ # See the file COPYING for complete licensing information.
21
+ #
22
+ #---------------------------------------------------------------------------
23
+ #
24
+ #
25
+
26
+ module EventMachine
27
+ module Protocols
28
+
29
+ # ### Usage
30
+ #
31
+ # ```ruby
32
+ # EM.run{
33
+ # conn = EM::Protocols::HttpClient2.connect 'google.com', 80
34
+ #
35
+ # req = conn.get('/')
36
+ # req.callback{ |response|
37
+ # p(response.status)
38
+ # p(response.headers)
39
+ # p(response.content)
40
+ # }
41
+ # }
42
+ # ```
43
+ #
44
+ # @deprecated Please use [EM-HTTP-Request](https://github.com/igrigorik/em-http-request) instead.
45
+ #
46
+ class HttpClient2 < Connection
47
+ include LineText2
48
+
49
+ def initialize
50
+ warn "HttpClient2 is deprecated and will be removed. EM-Http-Request should be used instead."
51
+
52
+ @authorization = nil
53
+ @closed = nil
54
+ @requests = nil
55
+ end
56
+
57
+ # @private
58
+ class Request
59
+ include Deferrable
60
+
61
+ attr_reader :version
62
+ attr_reader :status
63
+ attr_reader :header_lines
64
+ attr_reader :headers
65
+ attr_reader :content
66
+ attr_reader :internal_error
67
+
68
+ def initialize conn, args
69
+ @conn = conn
70
+ @args = args
71
+ @header_lines = []
72
+ @headers = {}
73
+ @blanks = 0
74
+ @chunk_trailer = nil
75
+ @chunking = nil
76
+ end
77
+
78
+ def send_request
79
+ az = @args[:authorization] and az = "Authorization: #{az}\r\n"
80
+
81
+ r = [
82
+ "#{@args[:verb]} #{@args[:uri]} HTTP/#{@args[:version] || "1.1"}\r\n",
83
+ "Host: #{@args[:host_header] || "_"}\r\n",
84
+ az || "",
85
+ "\r\n"
86
+ ]
87
+ @conn.send_data r.join
88
+ end
89
+
90
+ #--
91
+ #
92
+ def receive_line ln
93
+ if @chunk_trailer
94
+ receive_chunk_trailer(ln)
95
+ elsif @chunking
96
+ receive_chunk_header(ln)
97
+ else
98
+ receive_header_line(ln)
99
+ end
100
+ end
101
+
102
+ #--
103
+ #
104
+ def receive_chunk_trailer ln
105
+ if ln.length == 0
106
+ @conn.pop_request
107
+ succeed(self)
108
+ else
109
+ p "Received chunk trailer line"
110
+ end
111
+ end
112
+
113
+ #--
114
+ # Allow up to ten blank lines before we get a real response line.
115
+ # Allow no more than 100 lines in the header.
116
+ #
117
+ def receive_header_line ln
118
+ if ln.length == 0
119
+ if @header_lines.length > 0
120
+ process_header
121
+ else
122
+ @blanks += 1
123
+ if @blanks > 10
124
+ @conn.close_connection
125
+ end
126
+ end
127
+ else
128
+ @header_lines << ln
129
+ if @header_lines.length > 100
130
+ @internal_error = :bad_header
131
+ @conn.close_connection
132
+ end
133
+ end
134
+ end
135
+
136
+ #--
137
+ # Cf RFC 2616 pgh 3.6.1 for the format of HTTP chunks.
138
+ #
139
+ def receive_chunk_header ln
140
+ if ln.length > 0
141
+ chunksize = ln.to_i(16)
142
+ if chunksize > 0
143
+ @conn.set_text_mode(ln.to_i(16))
144
+ else
145
+ @content = @content ? @content.join : ''
146
+ @chunk_trailer = true
147
+ end
148
+ else
149
+ # We correctly come here after each chunk gets read.
150
+ # p "Got A BLANK chunk line"
151
+ end
152
+
153
+ end
154
+
155
+
156
+ #--
157
+ # We get a single chunk. Append it to the incoming content and switch back to line mode.
158
+ #
159
+ def receive_chunked_text text
160
+ # p "RECEIVED #{text.length} CHUNK"
161
+ (@content ||= []) << text
162
+ end
163
+
164
+
165
+ #--
166
+ # TODO, inefficient how we're handling this. Part of it is done so as to
167
+ # make sure we don't have problems in detecting chunked-encoding, content-length,
168
+ # etc.
169
+ #
170
+ HttpResponseRE = /\AHTTP\/(1.[01]) ([\d]{3})/i
171
+ ClenRE = /\AContent-length:\s*(\d+)/i
172
+ ChunkedRE = /\ATransfer-encoding:\s*chunked/i
173
+ ColonRE = /\:\s*/
174
+
175
+ def process_header
176
+ unless @header_lines.first =~ HttpResponseRE
177
+ @conn.close_connection
178
+ @internal_error = :bad_request
179
+ end
180
+ @version = $1.dup
181
+ @status = $2.dup.to_i
182
+
183
+ clen = nil
184
+ chunks = nil
185
+ @header_lines.each_with_index do |e,ix|
186
+ if ix > 0
187
+ hdr,val = e.split(ColonRE,2)
188
+ (@headers[hdr.downcase] ||= []) << val
189
+ end
190
+
191
+ if clen == nil and e =~ ClenRE
192
+ clen = $1.dup.to_i
193
+ end
194
+ if e =~ ChunkedRE
195
+ chunks = true
196
+ end
197
+ end
198
+
199
+ if clen
200
+ # If the content length is zero we should not call set_text_mode,
201
+ # because a value of zero will make it wait forever, hanging the
202
+ # connection. Just return success instead, with empty content.
203
+ if clen == 0 then
204
+ @content = ""
205
+ @conn.pop_request
206
+ succeed(self)
207
+ else
208
+ @conn.set_text_mode clen
209
+ end
210
+ elsif chunks
211
+ @chunking = true
212
+ else
213
+ # Chunked transfer, multipart, or end-of-connection.
214
+ # For end-of-connection, we need to go the unbind
215
+ # method and suppress its desire to fail us.
216
+ p "NO CLEN"
217
+ p @args[:uri]
218
+ p @header_lines
219
+ @internal_error = :unsupported_clen
220
+ @conn.close_connection
221
+ end
222
+ end
223
+ private :process_header
224
+
225
+
226
+ def receive_text text
227
+ @chunking ? receive_chunked_text(text) : receive_sized_text(text)
228
+ end
229
+
230
+ #--
231
+ # At the present time, we only handle contents that have a length
232
+ # specified by the content-length header.
233
+ #
234
+ def receive_sized_text text
235
+ @content = text
236
+ @conn.pop_request
237
+ succeed(self)
238
+ end
239
+ end
240
+
241
+ # Make a connection to a remote HTTP server.
242
+ # Can take either a pair of arguments (which will be interpreted as
243
+ # a hostname/ip-address and a port), or a hash.
244
+ # If the arguments are a hash, then supported values include:
245
+ # :host => a hostname or ip-address
246
+ # :port => a port number
247
+ # :ssl => true to enable ssl
248
+ def self.connect *args
249
+ if args.length == 2
250
+ args = {:host=>args[0], :port=>args[1]}
251
+ else
252
+ args = args.first
253
+ end
254
+
255
+ h,prt,ssl = args[:host], Integer(args[:port]), (args[:tls] || args[:ssl])
256
+ conn = EM.connect( h, prt, self )
257
+ conn.start_tls if ssl
258
+ conn.set_default_host_header( h, prt, ssl )
259
+ conn
260
+ end
261
+
262
+ # Get a url
263
+ #
264
+ # req = conn.get(:uri => '/')
265
+ # req.callback{|response| puts response.content }
266
+ #
267
+ def get args
268
+ if args.is_a?(String)
269
+ args = {:uri=>args}
270
+ end
271
+ args[:verb] = "GET"
272
+ request args
273
+ end
274
+
275
+ # Post to a url
276
+ #
277
+ # req = conn.post('/data')
278
+ # req.callback{|response| puts response.content }
279
+ #--
280
+ # XXX there's no way to supply a POST body.. wtf?
281
+ def post args
282
+ if args.is_a?(String)
283
+ args = {:uri=>args}
284
+ end
285
+ args[:verb] = "POST"
286
+ request args
287
+ end
288
+
289
+
290
+ #--
291
+ # Compute and remember a string to be used as the host header in HTTP requests
292
+ # unless the user overrides it with an argument to #request.
293
+ #
294
+ # @private
295
+ def set_default_host_header host, port, ssl
296
+ if (ssl and port != 443) or (!ssl and port != 80)
297
+ @host_header = "#{host}:#{port}"
298
+ else
299
+ @host_header = host
300
+ end
301
+ end
302
+
303
+
304
+ # @private
305
+ def post_init
306
+ super
307
+ @connected = EM::DefaultDeferrable.new
308
+ end
309
+
310
+ # @private
311
+ def connection_completed
312
+ super
313
+ @connected.succeed
314
+ end
315
+
316
+ #--
317
+ # All pending requests, if any, must fail.
318
+ # We might come here without ever passing through connection_completed
319
+ # in case we can't connect to the server. We'll also get here when the
320
+ # connection closes (either because the server closes it, or we close it
321
+ # due to detecting an internal error or security violation).
322
+ # In either case, run down all pending requests, if any, and signal failure
323
+ # on them.
324
+ #
325
+ # Set and remember a flag (@closed) so we can immediately fail any
326
+ # subsequent requests.
327
+ #
328
+ # @private
329
+ def unbind
330
+ super
331
+ @closed = true
332
+ (@requests || []).each {|r| r.fail}
333
+ end
334
+
335
+ # @private
336
+ def request args
337
+ args[:host_header] = @host_header unless args.has_key?(:host_header)
338
+ args[:authorization] = @authorization unless args.has_key?(:authorization)
339
+ r = Request.new self, args
340
+ if @closed
341
+ r.fail
342
+ else
343
+ (@requests ||= []).unshift r
344
+ @connected.callback {r.send_request}
345
+ end
346
+ r
347
+ end
348
+
349
+ # @private
350
+ def receive_line ln
351
+ if req = @requests.last
352
+ req.receive_line ln
353
+ else
354
+ p "??????????"
355
+ p ln
356
+ end
357
+ end
358
+
359
+ # @private
360
+ def receive_binary_data text
361
+ @requests.last.receive_text text
362
+ end
363
+
364
+ #--
365
+ # Called by a Request object when it completes.
366
+ #
367
+ # @private
368
+ def pop_request
369
+ @requests.pop
370
+ end
371
+ end
372
+
373
+
374
+ =begin
375
+ class HttpClient2x < Connection
376
+ include LineText2
377
+
378
+ # TODO: Make this behave appropriate in case a #connect fails.
379
+ # Currently, this produces no errors.
380
+
381
+ # Make a connection to a remote HTTP server.
382
+ # Can take either a pair of arguments (which will be interpreted as
383
+ # a hostname/ip-address and a port), or a hash.
384
+ # If the arguments are a hash, then supported values include:
385
+ # :host => a hostname or ip-address;
386
+ # :port => a port number
387
+ #--
388
+ # TODO, support optional encryption arguments like :ssl
389
+ def self.connect *args
390
+ if args.length == 2
391
+ args = {:host=>args[0], :port=>args[1]}
392
+ else
393
+ args = args.first
394
+ end
395
+
396
+ h,prt = args[:host],Integer(args[:port])
397
+ EM.connect( h, prt, self, h, prt )
398
+ end
399
+
400
+
401
+ #--
402
+ # Sugars a connection that makes a single request and then
403
+ # closes the connection. Matches the behavior and the arguments
404
+ # of the original implementation of class HttpClient.
405
+ #
406
+ # Intended primarily for back compatibility, but the idiom
407
+ # is probably useful so it's not deprecated.
408
+ # We return a Deferrable, as did the original implementation.
409
+ #
410
+ # Because we're improving the way we deal with errors and exceptions
411
+ # (specifically, HTTP response codes other than 2xx will trigger the
412
+ # errback rather than the callback), this may break some existing code.
413
+ #
414
+ def self.request args
415
+ c = connect args
416
+ end
417
+
418
+ #--
419
+ # Requests can be pipelined. When we get a request, add it to the
420
+ # front of a queue as an array. The last element of the @requests
421
+ # array is always the oldest request received. Each element of the
422
+ # @requests array is a two-element array consisting of a hash with
423
+ # the original caller's arguments, and an initially-empty Ostruct
424
+ # containing the data we retrieve from the server's response.
425
+ # Maintain the instance variable @current_response, which is the response
426
+ # of the oldest pending request. That's just to make other code a little
427
+ # easier. If the variable doesn't exist when we come here, we're
428
+ # obviously the first request being made on the connection.
429
+ #
430
+ # The reason for keeping this method private (and requiring use of the
431
+ # convenience methods #get, #post, #head, etc) is to avoid the small
432
+ # performance penalty of canonicalizing the verb.
433
+ #
434
+ def request args
435
+ d = EventMachine::DefaultDeferrable.new
436
+
437
+ if @closed
438
+ d.fail
439
+ return d
440
+ end
441
+
442
+ o = OpenStruct.new
443
+ o.deferrable = d
444
+ (@requests ||= []).unshift [args, o]
445
+ @current_response ||= @requests.last.last
446
+ @connected.callback {
447
+ az = args[:authorization] and az = "Authorization: #{az}\r\n"
448
+
449
+ r = [
450
+ "#{args[:verb]} #{args[:uri]} HTTP/#{args[:version] || "1.1"}\r\n",
451
+ "Host: #{args[:host_header] || @host_header}\r\n",
452
+ az || "",
453
+ "\r\n"
454
+ ]
455
+ p r
456
+ send_data r.join
457
+ }
458
+ o.deferrable
459
+ end
460
+ private :request
461
+
462
+ def get args
463
+ if args.is_a?(String)
464
+ args = {:uri=>args}
465
+ end
466
+ args[:verb] = "GET"
467
+ request args
468
+ end
469
+
470
+ def initialize host, port
471
+ super
472
+ @host_header = "#{host}:#{port}"
473
+ end
474
+ def post_init
475
+ super
476
+ @connected = EM::DefaultDeferrable.new
477
+ end
478
+
479
+
480
+ def connection_completed
481
+ super
482
+ @connected.succeed
483
+ end
484
+
485
+ #--
486
+ # Make sure to throw away any leftover incoming data if we've
487
+ # been closed due to recognizing an error.
488
+ #
489
+ # Generate an internal error if we get an unreasonable number of
490
+ # header lines. It could be malicious.
491
+ #
492
+ def receive_line ln
493
+ p ln
494
+ return if @closed
495
+
496
+ if ln.length > 0
497
+ (@current_response.headers ||= []).push ln
498
+ abort_connection if @current_response.headers.length > 100
499
+ else
500
+ process_received_headers
501
+ end
502
+ end
503
+
504
+ #--
505
+ # We come here when we've seen all the headers for a particular request.
506
+ # What we do next depends on the response line (which should be the
507
+ # first line in the header set), and whether there is content to read.
508
+ # We may transition into a text-reading state to read content, or
509
+ # we may abort the connection, or we may go right back into parsing
510
+ # responses for the next response in the chain.
511
+ #
512
+ # We make an ASSUMPTION that the first line is an HTTP response.
513
+ # Anything else produces an error that aborts the connection.
514
+ # This may not be enough, because it may be that responses to pipelined
515
+ # requests will come with a blank-line delimiter.
516
+ #
517
+ # Any non-2xx response will be treated as a fatal error, and abort the
518
+ # connection. We will set up the status and other response parameters.
519
+ # TODO: we will want to properly support 1xx responses, which some versions
520
+ # of IIS copiously generate.
521
+ # TODO: We need to give the option of not aborting the connection with certain
522
+ # non-200 responses, in order to work with NTLM and other authentication
523
+ # schemes that work at the level of individual connections.
524
+ #
525
+ # Some error responses will get sugarings. For example, we'll return the
526
+ # Location header in the response in case of a 301/302 response.
527
+ #
528
+ # Possible dispositions here:
529
+ # 1) No content to read (either content-length is zero or it's a HEAD request);
530
+ # 2) Switch to text mode to read a specific number of bytes;
531
+ # 3) Read a chunked or multipart response;
532
+ # 4) Read till the server closes the connection.
533
+ #
534
+ # Our reponse to the client can be either to wait till all the content
535
+ # has been read and then to signal caller's deferrable, or else to signal
536
+ # it when we finish the processing the headers and then expect the caller
537
+ # to have given us a block to call as the content comes in. And of course
538
+ # the latter gets stickier with chunks and multiparts.
539
+ #
540
+ HttpResponseRE = /\AHTTP\/(1.[01]) ([\d]{3})/i
541
+ ClenRE = /\AContent-length:\s*(\d+)/i
542
+ def process_received_headers
543
+ abort_connection unless @current_response.headers.first =~ HttpResponseRE
544
+ @current_response.version = $1.dup
545
+ st = $2.dup
546
+ @current_response.status = st.to_i
547
+ abort_connection unless st[0,1] == "2"
548
+
549
+ clen = nil
550
+ @current_response.headers.each do |e|
551
+ if clen == nil and e =~ ClenRE
552
+ clen = $1.dup.to_i
553
+ end
554
+ end
555
+
556
+ if clen
557
+ set_text_mode clen
558
+ end
559
+ end
560
+ private :process_received_headers
561
+
562
+
563
+ def receive_binary_data text
564
+ @current_response.content = text
565
+ @current_response.deferrable.succeed @current_response
566
+ @requests.pop
567
+ @current_response = (@requests.last || []).last
568
+ set_line_mode
569
+ end
570
+
571
+
572
+
573
+ # We've received either a server error or an internal error.
574
+ # Close the connection and abort any pending requests.
575
+ #--
576
+ # When should we call close_connection? It will cause #unbind
577
+ # to be fired. Should the user expect to see #unbind before
578
+ # we call #receive_http_error, or the other way around?
579
+ #
580
+ # Set instance variable @closed. That's used to inhibit further
581
+ # processing of any inbound data after an error has been recognized.
582
+ #
583
+ # We shouldn't have to worry about any leftover outbound data,
584
+ # because we call close_connection (not close_connection_after_writing).
585
+ # That ensures that any pipelined requests received after an error
586
+ # DO NOT get streamed out to the server on this connection.
587
+ # Very important. TODO, write a unit-test to establish that behavior.
588
+ #
589
+ def abort_connection
590
+ close_connection
591
+ @closed = true
592
+ @current_response.deferrable.fail( @current_response )
593
+ end
594
+
595
+
596
+ #------------------------
597
+ # Below here are user-overridable methods.
598
+
599
+ end
600
+ =end
601
+ end
602
+ end