eventmachine-win32 0.5.3 → 0.7.0

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.
@@ -0,0 +1,134 @@
1
+ # $Id: header_and_content.rb 281 2006-11-20 03:17:22Z blackhedd $
2
+ #
3
+ # Author:: blackhedd (gmail address: garbagecat10).
4
+ # Date:: 15 November 2006
5
+ #
6
+ # Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved.
7
+ #
8
+ # This program is made available under the terms of the GPL version 2.
9
+ #
10
+ # See EventMachine and EventMachine::Connection for documentation and
11
+ # usage examples.
12
+ #
13
+ #----------------------------------------------------------------------------
14
+ #
15
+ # Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved.
16
+ #
17
+ # Gmail: garbagecat10
18
+ #
19
+ # This program is free software; you can redistribute it and/or modify
20
+ # it under the terms of the GNU General Public License as published by
21
+ # the Free Software Foundation; either version 2 of the License, or
22
+ # (at your option) any later version.
23
+ #
24
+ # This program is distributed in the hope that it will be useful,
25
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
26
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27
+ # GNU General Public License for more details.
28
+ #
29
+ # You should have received a copy of the GNU General Public License
30
+ # along with this program; if not, write to the Free Software
31
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
32
+ #
33
+ #---------------------------------------------------------------------------
34
+ #
35
+ #
36
+
37
+
38
+
39
+ module EventMachine
40
+ module Protocols
41
+
42
+ class HeaderAndContentProtocol < LineAndTextProtocol
43
+
44
+ ContentLengthPattern = /Content-length:\s*(\d+)/i
45
+
46
+ def initialize *args
47
+ super
48
+ init_for_request
49
+ end
50
+
51
+ def receive_line line
52
+ case @hc_mode
53
+ when :discard_blanks
54
+ unless line == ""
55
+ @hc_mode = :headers
56
+ receive_line line
57
+ end
58
+ when :headers
59
+ if line == ""
60
+ raise "unrecognized state" unless @hc_headers.length > 0
61
+ if respond_to?(:receive_headers)
62
+ receive_headers @hc_headers
63
+ end
64
+ # @hc_content_length will be nil, not 0, if there was no content-length header.
65
+ if @hc_content_length.to_i > 0
66
+ set_binary_mode @hc_content_length
67
+ else
68
+ dispatch_request
69
+ end
70
+ else
71
+ @hc_headers << line
72
+ if ContentLengthPattern =~ line
73
+ # There are some attacks that rely on sending multiple content-length
74
+ # headers. This is a crude protection, but needs to become tunable.
75
+ raise "extraneous content-length header" if @hc_content_length
76
+ @hc_content_length = $1.to_i
77
+ end
78
+ if @hc_headers.length == 1 and respond_to?(:receive_first_header_line)
79
+ receive_first_header_line line
80
+ end
81
+ end
82
+ else
83
+ raise "internal error, unsupported mode"
84
+ end
85
+ end
86
+
87
+ def receive_binary_data text
88
+ @hc_content = text
89
+ dispatch_request
90
+ end
91
+
92
+ def dispatch_request
93
+ if respond_to?(:receive_request)
94
+ receive_request @hc_headers, @hc_content
95
+ end
96
+ init_for_request
97
+ end
98
+ private :dispatch_request
99
+
100
+ def init_for_request
101
+ @hc_mode = :discard_blanks
102
+ @hc_headers = []
103
+ # originally was @hc_headers ||= []; @hc_headers.clear to get a performance
104
+ # boost, but it's counterproductive because a subclassed handler will have to
105
+ # call dup to use the header array we pass in receive_headers.
106
+
107
+ @hc_content_length = nil
108
+ @hc_content = ""
109
+ end
110
+ private :init_for_request
111
+
112
+ # Basically a convenience method. We might create a subclass that does this
113
+ # automatically. But it's such a performance killer.
114
+ def headers_2_hash hdrs
115
+ self.class.headers_2_hash hdrs
116
+ end
117
+
118
+ class << self
119
+ def headers_2_hash hdrs
120
+ hash = {}
121
+ hdrs.each {|h|
122
+ if /\A([^\s:]+)\s*:\s*/ =~ h
123
+ tail = $'.dup
124
+ hash[ $1.downcase.gsub(/-/,"_").intern ] = tail
125
+ end
126
+ }
127
+ hash
128
+ end
129
+ end
130
+
131
+ end
132
+ end
133
+ end
134
+
@@ -0,0 +1,233 @@
1
+ # $Id: httpclient.rb 226 2006-08-10 08:55:49Z blackhedd $
2
+ #
3
+ # Author:: blackhedd (gmail address: garbagecat10).
4
+ # Date:: 16 July 2006
5
+ #
6
+ # Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved.
7
+ #
8
+ # This program is made available under the terms of the GPL version 2.
9
+ #
10
+ # See EventMachine and EventMachine::Connection for documentation and
11
+ # usage examples.
12
+ #
13
+ #----------------------------------------------------------------------------
14
+ #
15
+ # Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved.
16
+ #
17
+ # Gmail: garbagecat10
18
+ #
19
+ # This program is free software; you can redistribute it and/or modify
20
+ # it under the terms of the GNU General Public License as published by
21
+ # the Free Software Foundation; either version 2 of the License, or
22
+ # (at your option) any later version.
23
+ #
24
+ # This program is distributed in the hope that it will be useful,
25
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
26
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27
+ # GNU General Public License for more details.
28
+ #
29
+ # You should have received a copy of the GNU General Public License
30
+ # along with this program; if not, write to the Free Software
31
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
32
+ #
33
+ #---------------------------------------------------------------------------
34
+ #
35
+ #
36
+
37
+
38
+
39
+ module EventMachine
40
+ module Protocols
41
+
42
+ class HttpClient < Connection
43
+ include EventMachine::Deferrable
44
+
45
+
46
+ # USAGE SAMPLE:
47
+ # WARNING, POST is not yet supported!!!!!
48
+ #
49
+ # EventMachine.run {
50
+ # http = EventMachine::Protocols::HttpClient.request(
51
+ # :host => server,
52
+ # :port => 80,
53
+ # :request => "/index.html",
54
+ # :query_string => "parm1=value1&parm2=value2"
55
+ # )
56
+ # http.callback {|response|
57
+ # puts response[:status]
58
+ # puts response[:headers]
59
+ # puts response[:content]
60
+ # }
61
+ # }
62
+ #
63
+
64
+ # TODO:
65
+ # POST REQUESTS!!!!!!!!!!!
66
+ # Timeout for connections that run too long or hang somewhere in the middle.
67
+ # Persistent connections (HTTP/1.1), may need a associated delegate object.
68
+ # DNS: Some way to cache DNS lookups for hostnames we connect to. Ruby's
69
+ # DNS lookups are unbelievably slow.
70
+ # HEAD requests.
71
+ # Chunked transfer encoding.
72
+ # Convenience methods for requests. get, post, url, etc.
73
+ # SSL.
74
+ # Handle status codes like 304, 100, etc.
75
+ # Refactor this code so that protocol errors all get handled one way (an exception?),
76
+ # instead of sprinkling set_deferred_status :failed calls everywhere.
77
+
78
+ def self.request( args = {} )
79
+ args[:port] ||= 80
80
+ EventMachine.connect( args[:host], args[:port], self ) {|c|
81
+ # According to the docs, we will get here AFTER post_init is called.
82
+ c.instance_eval {@args = args}
83
+ }
84
+ end
85
+
86
+ def post_init
87
+ @start_time = Time.now
88
+ @data = ""
89
+ @read_state = :base
90
+ end
91
+
92
+ # We send the request when we get a connection.
93
+ # AND, we set an instance variable to indicate we passed through here.
94
+ # That allows #unbind to know whether there was a successful connection.
95
+ # NB: This naive technique won't work when we have to support multiple
96
+ # requests on a single connection.
97
+ def connection_completed
98
+ @connected = true
99
+ send_request @args
100
+ end
101
+
102
+ def send_request args
103
+ args[:verb] ||= :get # IS THIS A GOOD IDEA, to default to GET if nothing was specified?
104
+
105
+ verb = args[:verb].to_s.upcase
106
+ unless ["GET", "POST", "HEAD"].include?(verb)
107
+ set_deferred_status :failed, {:status => 0} # TODO, not signalling the error type
108
+ return # NOTE THE EARLY RETURN, we're not sending any data.
109
+ end
110
+
111
+ request = args[:request] || "/"
112
+ unless request[0,1] == "/"
113
+ request = "/" + request
114
+ end
115
+
116
+ qs = args[:query_string] || ""
117
+ if qs.length > 0 and qs[0,1] != '?'
118
+ qs = "?" + qs
119
+ end
120
+
121
+ # Allow an override for the host header if it's not the connect-string.
122
+ host = args[:host_header] || args[:host] || "_"
123
+
124
+ # ESSENTIAL for the request's line-endings to be CRLF, not LF. Some servers misbehave otherwise.
125
+ req = [
126
+ "#{verb} #{request}#{qs} HTTP/1.1",
127
+ "Host: #{host}",
128
+ "User-agent: Ruby EventMachine",
129
+ ""
130
+ ].map {|l| "#{l}\r\n"}.join
131
+
132
+ send_data req
133
+ end
134
+
135
+
136
+ def receive_data data
137
+ while data and data.length > 0
138
+ case @read_state
139
+ when :base
140
+ # Perform any per-request initialization here and don't consume any data.
141
+ @data = ""
142
+ @headers = []
143
+ @content_length = nil # not zero
144
+ @content = ""
145
+ @status = nil
146
+ @read_state = :header
147
+ when :header
148
+ ary = data.split( /\r?\n/m, 2 )
149
+ if ary.length == 2
150
+ data = ary.last
151
+ if ary.first == ""
152
+ @read_state = :content
153
+ else
154
+ @headers << ary.first
155
+ if @headers.length == 1
156
+ parse_response_line
157
+ elsif ary.first =~ /\Acontent-length:\s*/i
158
+ # Only take the FIRST content-length header that appears,
159
+ # which we can distinguish because @content_length is nil.
160
+ # TODO, it's actually a fatal error if there is more than one
161
+ # content-length header, because the caller is presumptively
162
+ # a bad guy. (There is an exploit that depends on multiple
163
+ # content-length headers.)
164
+ @content_length ||= $'.to_i
165
+ end
166
+ end
167
+ else
168
+ @data << data
169
+ data = ""
170
+ end
171
+ when :content
172
+ # If there was no content-length header, we have to wait until the connection
173
+ # closes. Everything we get until that point is content.
174
+ # TODO: Must impose a content-size limit, and also must implement chunking.
175
+ # Also, must support either temporary files for large content, or calling
176
+ # a content-consumer block supplied by the user.
177
+ if @content_length
178
+ bytes_needed = @content_length - @content.length
179
+ @content += data[0, bytes_needed]
180
+ data = data[bytes_needed..-1] || ""
181
+ if @content_length == @content.length
182
+ dispatch_response
183
+ @read_state = :base
184
+ end
185
+ else
186
+ @content << data
187
+ data = ""
188
+ end
189
+ end
190
+ end
191
+ end
192
+
193
+
194
+ # We get called here when we have received an HTTP response line.
195
+ # It's an opportunity to throw an exception or trigger other exceptional
196
+ # handling.
197
+ def parse_response_line
198
+ if @headers.first =~ /\AHTTP\/1\.[01] ([\d]{3})/
199
+ @status = $1.to_i
200
+ else
201
+ set_deferred_status :failed, {
202
+ :status => 0 # crappy way of signifying an unrecognized response. TODO, find a better way to do this.
203
+ }
204
+ close_connection
205
+ end
206
+ end
207
+ private :parse_response_line
208
+
209
+ def dispatch_response
210
+ @read_state = :base
211
+ set_deferred_status :succeeded, {
212
+ :content => @content,
213
+ :headers => @headers,
214
+ :status => @status
215
+ }
216
+ # TODO, we close the connection for now, but this is wrong for persistent clients.
217
+ close_connection
218
+ end
219
+
220
+ def unbind
221
+ if !@connected
222
+ set_deferred_status :failed, {:status => 0} # YECCCCH. Find a better way to signal no-connect/network error.
223
+ elsif (@read_state == :content and @content_length == nil)
224
+ dispatch_response
225
+ end
226
+ end
227
+ end
228
+
229
+
230
+ end
231
+ end
232
+
233
+
@@ -0,0 +1,141 @@
1
+ # $Id: line_and_text.rb 279 2006-11-18 15:40:25Z blackhedd $
2
+ #
3
+ # Author:: blackhedd (gmail address: garbagecat10).
4
+ # Date:: 15 November 2006
5
+ #
6
+ # Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved.
7
+ #
8
+ # This program is made available under the terms of the GPL version 2.
9
+ #
10
+ # See EventMachine and EventMachine::Connection for documentation and
11
+ # usage examples.
12
+ #
13
+ #----------------------------------------------------------------------------
14
+ #
15
+ # Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved.
16
+ #
17
+ # Gmail: garbagecat10
18
+ #
19
+ # This program is free software; you can redistribute it and/or modify
20
+ # it under the terms of the GNU General Public License as published by
21
+ # the Free Software Foundation; either version 2 of the License, or
22
+ # (at your option) any later version.
23
+ #
24
+ # This program is distributed in the hope that it will be useful,
25
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
26
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27
+ # GNU General Public License for more details.
28
+ #
29
+ # You should have received a copy of the GNU General Public License
30
+ # along with this program; if not, write to the Free Software
31
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
32
+ #
33
+ #---------------------------------------------------------------------------
34
+ #
35
+ #
36
+
37
+
38
+
39
+ module EventMachine
40
+ module Protocols
41
+
42
+ class LineAndTextProtocol < Connection
43
+ MaxLineLength = 16*1024
44
+ MaxBinaryLength = 32*1024*1024
45
+
46
+ def initialize *args
47
+ super
48
+ lbp_init_line_state
49
+ end
50
+ def receive_data data
51
+ if @lbp_mode == :lines
52
+ @lbp_data << data
53
+ while i = @lbp_data.index("\n")
54
+ # line-length test is provisional. Need to be tunable and do something
55
+ # more intelligent than throwing something.
56
+ if i > MaxLineLength
57
+ receive_error("overlength line") if respond_to?(:receive_error)
58
+ close_connection
59
+ break # exit the while loop
60
+ end
61
+ line = @lbp_data.slice!(0..i).chomp
62
+ receive_line line if respond_to?(:receive_line)
63
+ end
64
+ else
65
+ if @lbp_binary_limit > 0
66
+ wanted = @lbp_binary_limit - @lbp_binary_bytes_received
67
+ chunk = nil
68
+ if data.length > wanted
69
+ chunk = data.slice!(0...wanted)
70
+ else
71
+ chunk = data
72
+ data = ""
73
+ end
74
+ @lbp_binary_buffer[@lbp_binary_bytes_received...(@lbp_binary_bytes_received+chunk.length)] = chunk
75
+ @lbp_binary_bytes_received += chunk.length
76
+ if @lbp_binary_bytes_received == @lbp_binary_limit
77
+ receive_binary_data(@lbp_binary_buffer) if respond_to?(:receive_binary_data)
78
+ lbp_init_line_state
79
+ end
80
+ receive_data(data) if data.length > 0
81
+ else
82
+ receive_binary_data(data) if respond_to?(:receive_binary_data)
83
+ data = ""
84
+ end
85
+ end
86
+ end
87
+
88
+ def unbind
89
+ if @lbp_mode == :binary and @lbp_binary_limit > 0
90
+ if respond_to?(:receive_binary_data)
91
+ receive_binary_data( @lbp_binary_buffer[0...@lbp_binary_bytes_received] )
92
+ end
93
+ end
94
+ end
95
+
96
+ # Set up to read the supplied number of binary bytes.
97
+ # This recycles all the data currently waiting in the line buffer, if any.
98
+ # If the limit is nil, then ALL subsequent data will be treated as binary
99
+ # data and passed to the upstream protocol handler as we receive it.
100
+ # If a limit is given, we'll hold the incoming binary data and not
101
+ # pass it upstream until we've seen it all, or until there is an unbind
102
+ # (in which case we'll pass up a partial).
103
+ # Specifying nil for the limit (the default) means there is no limit.
104
+ # Specifiyng zero for the limit will cause an immediate transition back to line mode.
105
+ #
106
+ def set_binary_mode size = nil
107
+ if @lbp_mode == :lines
108
+ if size == 0
109
+ receive_binary_data("") if respond_to?(:receive_binary_data)
110
+ # Do no more work here. Stay in line mode and keep consuming data.
111
+ else
112
+ @lbp_binary_limit = size.to_i # (nil will be stored as zero)
113
+ if @lbp_binary_limit > 0
114
+ raise "Overlength" if @lbp_binary_limit > MaxBinaryLength # arbitrary sanity check
115
+ @lbp_binary_buffer = "\0" * @lbp_binary_limit
116
+ @lbp_binary_bytes_received = 0
117
+ end
118
+
119
+ @lbp_mode = :binary
120
+ if @lbp_data.length > 0
121
+ d,@lbp_data = @lbp_data,""
122
+ receive_data d
123
+ end
124
+ end
125
+ else
126
+ raise "invalid operation"
127
+ end
128
+ end
129
+
130
+ #--
131
+ # For internal use, establish protocol baseline for handling lines.
132
+ def lbp_init_line_state
133
+ @lbp_data = ""
134
+ @lbp_mode = :lines
135
+ end
136
+ private :lbp_init_line_state
137
+
138
+ end
139
+ end
140
+ end
141
+