eventmachine 0.12.6-x86-mswin32-60
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/.gitignore +13 -0
- data/Rakefile +254 -0
- data/docs/COPYING +60 -0
- data/docs/ChangeLog +211 -0
- data/docs/DEFERRABLES +138 -0
- data/docs/EPOLL +141 -0
- data/docs/GNU +281 -0
- data/docs/INSTALL +15 -0
- data/docs/KEYBOARD +38 -0
- data/docs/LEGAL +25 -0
- data/docs/LIGHTWEIGHT_CONCURRENCY +72 -0
- data/docs/PURE_RUBY +77 -0
- data/docs/README +74 -0
- data/docs/RELEASE_NOTES +96 -0
- data/docs/SMTP +9 -0
- data/docs/SPAWNED_PROCESSES +93 -0
- data/docs/TODO +10 -0
- data/eventmachine.gemspec +32 -0
- data/ext/binder.cpp +126 -0
- data/ext/binder.h +48 -0
- data/ext/cmain.cpp +586 -0
- data/ext/cplusplus.cpp +193 -0
- data/ext/ed.cpp +1522 -0
- data/ext/ed.h +380 -0
- data/ext/em.cpp +1937 -0
- data/ext/em.h +186 -0
- data/ext/emwin.cpp +300 -0
- data/ext/emwin.h +94 -0
- data/ext/epoll.cpp +26 -0
- data/ext/epoll.h +25 -0
- data/ext/eventmachine.h +98 -0
- data/ext/eventmachine_cpp.h +95 -0
- data/ext/extconf.rb +129 -0
- data/ext/fastfilereader/extconf.rb +77 -0
- data/ext/fastfilereader/mapper.cpp +214 -0
- data/ext/fastfilereader/mapper.h +59 -0
- data/ext/fastfilereader/rubymain.cpp +127 -0
- data/ext/files.cpp +94 -0
- data/ext/files.h +65 -0
- data/ext/kb.cpp +82 -0
- data/ext/page.cpp +107 -0
- data/ext/page.h +51 -0
- data/ext/pipe.cpp +351 -0
- data/ext/project.h +119 -0
- data/ext/rubymain.cpp +847 -0
- data/ext/sigs.cpp +89 -0
- data/ext/sigs.h +32 -0
- data/ext/ssl.cpp +423 -0
- data/ext/ssl.h +90 -0
- data/java/.classpath +8 -0
- data/java/.project +17 -0
- data/java/src/com/rubyeventmachine/Application.java +196 -0
- data/java/src/com/rubyeventmachine/Connection.java +74 -0
- data/java/src/com/rubyeventmachine/ConnectionFactory.java +37 -0
- data/java/src/com/rubyeventmachine/DefaultConnectionFactory.java +46 -0
- data/java/src/com/rubyeventmachine/EmReactor.java +408 -0
- data/java/src/com/rubyeventmachine/EmReactorException.java +40 -0
- data/java/src/com/rubyeventmachine/EventableChannel.java +57 -0
- data/java/src/com/rubyeventmachine/EventableDatagramChannel.java +171 -0
- data/java/src/com/rubyeventmachine/EventableSocketChannel.java +244 -0
- data/java/src/com/rubyeventmachine/PeriodicTimer.java +38 -0
- data/java/src/com/rubyeventmachine/Timer.java +54 -0
- data/java/src/com/rubyeventmachine/tests/ApplicationTest.java +108 -0
- data/java/src/com/rubyeventmachine/tests/ConnectTest.java +124 -0
- data/java/src/com/rubyeventmachine/tests/EMTest.java +80 -0
- data/java/src/com/rubyeventmachine/tests/TestDatagrams.java +53 -0
- data/java/src/com/rubyeventmachine/tests/TestServers.java +74 -0
- data/java/src/com/rubyeventmachine/tests/TestTimers.java +89 -0
- data/lib/em/deferrable.rb +208 -0
- data/lib/em/eventable.rb +39 -0
- data/lib/em/future.rb +62 -0
- data/lib/em/messages.rb +66 -0
- data/lib/em/processes.rb +113 -0
- data/lib/em/spawnable.rb +88 -0
- data/lib/em/streamer.rb +112 -0
- data/lib/eventmachine.rb +1926 -0
- data/lib/eventmachine_version.rb +31 -0
- data/lib/evma.rb +32 -0
- data/lib/evma/callback.rb +32 -0
- data/lib/evma/container.rb +75 -0
- data/lib/evma/factory.rb +77 -0
- data/lib/evma/protocol.rb +87 -0
- data/lib/evma/reactor.rb +48 -0
- data/lib/jeventmachine.rb +137 -0
- data/lib/pr_eventmachine.rb +1011 -0
- data/lib/protocols/buftok.rb +127 -0
- data/lib/protocols/header_and_content.rb +129 -0
- data/lib/protocols/httpcli2.rb +803 -0
- data/lib/protocols/httpclient.rb +270 -0
- data/lib/protocols/line_and_text.rb +126 -0
- data/lib/protocols/linetext2.rb +161 -0
- data/lib/protocols/memcache.rb +293 -0
- data/lib/protocols/postgres.rb +261 -0
- data/lib/protocols/saslauth.rb +179 -0
- data/lib/protocols/smtpclient.rb +308 -0
- data/lib/protocols/smtpserver.rb +556 -0
- data/lib/protocols/stomp.rb +153 -0
- data/lib/protocols/tcptest.rb +57 -0
- data/setup.rb +1585 -0
- data/tasks/cpp.rake +77 -0
- data/tasks/project.rake +78 -0
- data/tasks/tests.rake +193 -0
- data/tests/test_attach.rb +83 -0
- data/tests/test_basic.rb +231 -0
- data/tests/test_connection_count.rb +45 -0
- data/tests/test_defer.rb +47 -0
- data/tests/test_epoll.rb +163 -0
- data/tests/test_error_handler.rb +35 -0
- data/tests/test_errors.rb +82 -0
- data/tests/test_eventables.rb +77 -0
- data/tests/test_exc.rb +58 -0
- data/tests/test_futures.rb +214 -0
- data/tests/test_handler_check.rb +37 -0
- data/tests/test_hc.rb +218 -0
- data/tests/test_httpclient.rb +215 -0
- data/tests/test_httpclient2.rb +155 -0
- data/tests/test_kb.rb +61 -0
- data/tests/test_ltp.rb +188 -0
- data/tests/test_ltp2.rb +320 -0
- data/tests/test_next_tick.rb +109 -0
- data/tests/test_processes.rb +95 -0
- data/tests/test_pure.rb +129 -0
- data/tests/test_running.rb +47 -0
- data/tests/test_sasl.rb +74 -0
- data/tests/test_send_file.rb +243 -0
- data/tests/test_servers.rb +80 -0
- data/tests/test_smtpclient.rb +83 -0
- data/tests/test_smtpserver.rb +93 -0
- data/tests/test_spawn.rb +329 -0
- data/tests/test_ssl_args.rb +68 -0
- data/tests/test_ssl_methods.rb +50 -0
- data/tests/test_timers.rb +148 -0
- data/tests/test_ud.rb +43 -0
- data/tests/testem.rb +31 -0
- data/web/whatis +7 -0
- metadata +207 -0
@@ -0,0 +1,270 @@
|
|
1
|
+
# $Id$
|
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
|
+
|
27
|
+
|
28
|
+
module EventMachine
|
29
|
+
module Protocols
|
30
|
+
|
31
|
+
class HttpClient < Connection
|
32
|
+
include EventMachine::Deferrable
|
33
|
+
|
34
|
+
|
35
|
+
MaxPostContentLength = 20 * 1024 * 1024
|
36
|
+
|
37
|
+
# USAGE SAMPLE:
|
38
|
+
#
|
39
|
+
# EventMachine.run {
|
40
|
+
# http = EventMachine::Protocols::HttpClient.request(
|
41
|
+
# :host => server,
|
42
|
+
# :port => 80,
|
43
|
+
# :request => "/index.html",
|
44
|
+
# :query_string => "parm1=value1&parm2=value2"
|
45
|
+
# )
|
46
|
+
# http.callback {|response|
|
47
|
+
# puts response[:status]
|
48
|
+
# puts response[:headers]
|
49
|
+
# puts response[:content]
|
50
|
+
# }
|
51
|
+
# }
|
52
|
+
#
|
53
|
+
|
54
|
+
# TODO:
|
55
|
+
# Add streaming so we can support enormous POSTs. Current max is 20meg.
|
56
|
+
# Timeout for connections that run too long or hang somewhere in the middle.
|
57
|
+
# Persistent connections (HTTP/1.1), may need a associated delegate object.
|
58
|
+
# DNS: Some way to cache DNS lookups for hostnames we connect to. Ruby's
|
59
|
+
# DNS lookups are unbelievably slow.
|
60
|
+
# HEAD requests.
|
61
|
+
# Chunked transfer encoding.
|
62
|
+
# Convenience methods for requests. get, post, url, etc.
|
63
|
+
# SSL.
|
64
|
+
# Handle status codes like 304, 100, etc.
|
65
|
+
# Refactor this code so that protocol errors all get handled one way (an exception?),
|
66
|
+
# instead of sprinkling set_deferred_status :failed calls everywhere.
|
67
|
+
|
68
|
+
# === Arg list
|
69
|
+
# :host => 'ip/dns', :port => fixnum, :verb => 'GET', :request => 'path',
|
70
|
+
# :basic_auth => {:username => '', :password => ''}, :content => 'content',
|
71
|
+
# :contenttype => 'text/plain', :query_string => '', :host_header => '',
|
72
|
+
# :cookie => ''
|
73
|
+
|
74
|
+
def self.request( args = {} )
|
75
|
+
args[:port] ||= 80
|
76
|
+
EventMachine.connect( args[:host], args[:port], self ) {|c|
|
77
|
+
# According to the docs, we will get here AFTER post_init is called.
|
78
|
+
c.instance_eval {@args = args}
|
79
|
+
}
|
80
|
+
end
|
81
|
+
|
82
|
+
def post_init
|
83
|
+
@start_time = Time.now
|
84
|
+
@data = ""
|
85
|
+
@read_state = :base
|
86
|
+
end
|
87
|
+
|
88
|
+
# We send the request when we get a connection.
|
89
|
+
# AND, we set an instance variable to indicate we passed through here.
|
90
|
+
# That allows #unbind to know whether there was a successful connection.
|
91
|
+
# NB: This naive technique won't work when we have to support multiple
|
92
|
+
# requests on a single connection.
|
93
|
+
def connection_completed
|
94
|
+
@connected = true
|
95
|
+
send_request @args
|
96
|
+
end
|
97
|
+
|
98
|
+
def send_request args
|
99
|
+
args[:verb] ||= args[:method] # Support :method as an alternative to :verb.
|
100
|
+
args[:verb] ||= :get # IS THIS A GOOD IDEA, to default to GET if nothing was specified?
|
101
|
+
|
102
|
+
verb = args[:verb].to_s.upcase
|
103
|
+
unless ["GET", "POST", "PUT", "DELETE", "HEAD"].include?(verb)
|
104
|
+
set_deferred_status :failed, {:status => 0} # TODO, not signalling the error type
|
105
|
+
return # NOTE THE EARLY RETURN, we're not sending any data.
|
106
|
+
end
|
107
|
+
|
108
|
+
request = args[:request] || "/"
|
109
|
+
unless request[0,1] == "/"
|
110
|
+
request = "/" + request
|
111
|
+
end
|
112
|
+
|
113
|
+
qs = args[:query_string] || ""
|
114
|
+
if qs.length > 0 and qs[0,1] != '?'
|
115
|
+
qs = "?" + qs
|
116
|
+
end
|
117
|
+
|
118
|
+
version = args[:version] || "1.1"
|
119
|
+
|
120
|
+
# Allow an override for the host header if it's not the connect-string.
|
121
|
+
host = args[:host_header] || args[:host] || "_"
|
122
|
+
# For now, ALWAYS tuck in the port string, although we may want to omit it if it's the default.
|
123
|
+
port = args[:port]
|
124
|
+
|
125
|
+
# POST items.
|
126
|
+
postcontenttype = args[:contenttype] || "application/octet-stream"
|
127
|
+
postcontent = args[:content] || ""
|
128
|
+
raise "oversized content in HTTP POST" if postcontent.length > MaxPostContentLength
|
129
|
+
|
130
|
+
# ESSENTIAL for the request's line-endings to be CRLF, not LF. Some servers misbehave otherwise.
|
131
|
+
# TODO: We ASSUME the caller wants to send a 1.1 request. May not be a good assumption.
|
132
|
+
req = [
|
133
|
+
"#{verb} #{request}#{qs} HTTP/#{version}",
|
134
|
+
"Host: #{host}:#{port}",
|
135
|
+
"User-agent: Ruby EventMachine",
|
136
|
+
]
|
137
|
+
|
138
|
+
if verb == "POST" || verb == "PUT"
|
139
|
+
req << "Content-type: #{postcontenttype}"
|
140
|
+
req << "Content-length: #{postcontent.length}"
|
141
|
+
end
|
142
|
+
|
143
|
+
# TODO, this cookie handler assumes it's getting a single, semicolon-delimited string.
|
144
|
+
# Eventually we will want to deal intelligently with arrays and hashes.
|
145
|
+
if args[:cookie]
|
146
|
+
req << "Cookie: #{args[:cookie]}"
|
147
|
+
end
|
148
|
+
|
149
|
+
# Basic-auth stanza contributed by Matt Murphy.
|
150
|
+
if args[:basic_auth]
|
151
|
+
basic_auth_string = ["#{args[:basic_auth][:username]}:#{args[:basic_auth][:password]}"].pack('m').strip.gsub(/\n/,'')
|
152
|
+
req << "Authorization: Basic #{basic_auth_string}"
|
153
|
+
end
|
154
|
+
|
155
|
+
req << ""
|
156
|
+
reqstring = req.map {|l| "#{l}\r\n"}.join
|
157
|
+
send_data reqstring
|
158
|
+
|
159
|
+
if verb == "POST" || verb == "PUT"
|
160
|
+
send_data postcontent
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
|
165
|
+
def receive_data data
|
166
|
+
while data and data.length > 0
|
167
|
+
case @read_state
|
168
|
+
when :base
|
169
|
+
# Perform any per-request initialization here and don't consume any data.
|
170
|
+
@data = ""
|
171
|
+
@headers = []
|
172
|
+
@content_length = nil # not zero
|
173
|
+
@content = ""
|
174
|
+
@status = nil
|
175
|
+
@read_state = :header
|
176
|
+
@connection_close = nil
|
177
|
+
when :header
|
178
|
+
ary = data.split( /\r?\n/m, 2 )
|
179
|
+
if ary.length == 2
|
180
|
+
data = ary.last
|
181
|
+
if ary.first == ""
|
182
|
+
if (@content_length and @content_length > 0) || @connection_close
|
183
|
+
@read_state = :content
|
184
|
+
else
|
185
|
+
dispatch_response
|
186
|
+
@read_state = :base
|
187
|
+
end
|
188
|
+
else
|
189
|
+
@headers << ary.first
|
190
|
+
if @headers.length == 1
|
191
|
+
parse_response_line
|
192
|
+
elsif ary.first =~ /\Acontent-length:\s*/i
|
193
|
+
# Only take the FIRST content-length header that appears,
|
194
|
+
# which we can distinguish because @content_length is nil.
|
195
|
+
# TODO, it's actually a fatal error if there is more than one
|
196
|
+
# content-length header, because the caller is presumptively
|
197
|
+
# a bad guy. (There is an exploit that depends on multiple
|
198
|
+
# content-length headers.)
|
199
|
+
@content_length ||= $'.to_i
|
200
|
+
elsif ary.first =~ /\Aconnection:\s*close/i
|
201
|
+
@connection_close = true
|
202
|
+
end
|
203
|
+
end
|
204
|
+
else
|
205
|
+
@data << data
|
206
|
+
data = ""
|
207
|
+
end
|
208
|
+
when :content
|
209
|
+
# If there was no content-length header, we have to wait until the connection
|
210
|
+
# closes. Everything we get until that point is content.
|
211
|
+
# TODO: Must impose a content-size limit, and also must implement chunking.
|
212
|
+
# Also, must support either temporary files for large content, or calling
|
213
|
+
# a content-consumer block supplied by the user.
|
214
|
+
if @content_length
|
215
|
+
bytes_needed = @content_length - @content.length
|
216
|
+
@content += data[0, bytes_needed]
|
217
|
+
data = data[bytes_needed..-1] || ""
|
218
|
+
if @content_length == @content.length
|
219
|
+
dispatch_response
|
220
|
+
@read_state = :base
|
221
|
+
end
|
222
|
+
else
|
223
|
+
@content << data
|
224
|
+
data = ""
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
|
231
|
+
# We get called here when we have received an HTTP response line.
|
232
|
+
# It's an opportunity to throw an exception or trigger other exceptional
|
233
|
+
# handling.
|
234
|
+
def parse_response_line
|
235
|
+
if @headers.first =~ /\AHTTP\/1\.[01] ([\d]{3})/
|
236
|
+
@status = $1.to_i
|
237
|
+
else
|
238
|
+
set_deferred_status :failed, {
|
239
|
+
:status => 0 # crappy way of signifying an unrecognized response. TODO, find a better way to do this.
|
240
|
+
}
|
241
|
+
close_connection
|
242
|
+
end
|
243
|
+
end
|
244
|
+
private :parse_response_line
|
245
|
+
|
246
|
+
def dispatch_response
|
247
|
+
@read_state = :base
|
248
|
+
set_deferred_status :succeeded, {
|
249
|
+
:content => @content,
|
250
|
+
:headers => @headers,
|
251
|
+
:status => @status
|
252
|
+
}
|
253
|
+
# TODO, we close the connection for now, but this is wrong for persistent clients.
|
254
|
+
close_connection
|
255
|
+
end
|
256
|
+
|
257
|
+
def unbind
|
258
|
+
if !@connected
|
259
|
+
set_deferred_status :failed, {:status => 0} # YECCCCH. Find a better way to signal no-connect/network error.
|
260
|
+
elsif (@read_state == :content and @content_length == nil)
|
261
|
+
dispatch_response
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
|
@@ -0,0 +1,126 @@
|
|
1
|
+
# $Id$
|
2
|
+
#
|
3
|
+
# Author:: Francis Cianfrocca (gmail: blackhedd)
|
4
|
+
# Homepage:: http://rubyeventmachine.com
|
5
|
+
# Date:: 15 November 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
|
+
require File.dirname(__FILE__) + '/buftok'
|
27
|
+
|
28
|
+
module EventMachine
|
29
|
+
module Protocols
|
30
|
+
# A protocol that handles line-oriented data with interspersed binary text.
|
31
|
+
#
|
32
|
+
# This version is optimized for performance. See EventMachine::Protocols::LineText2
|
33
|
+
# for a version which is optimized for correctness with regard to binary text blocks
|
34
|
+
# that can switch back to line mode.
|
35
|
+
class LineAndTextProtocol < Connection
|
36
|
+
MaxLineLength = 16*1024
|
37
|
+
MaxBinaryLength = 32*1024*1024
|
38
|
+
|
39
|
+
def initialize *args
|
40
|
+
super
|
41
|
+
lbp_init_line_state
|
42
|
+
end
|
43
|
+
def receive_data data
|
44
|
+
if @lbp_mode == :lines
|
45
|
+
begin
|
46
|
+
@lpb_buffer.extract(data).each do |line|
|
47
|
+
receive_line(line.chomp) if respond_to?(:receive_line)
|
48
|
+
end
|
49
|
+
rescue Exception
|
50
|
+
receive_error('overlength line') if respond_to?(:receive_error)
|
51
|
+
close_connection
|
52
|
+
return
|
53
|
+
end
|
54
|
+
else
|
55
|
+
if @lbp_binary_limit > 0
|
56
|
+
wanted = @lbp_binary_limit - @lbp_binary_bytes_received
|
57
|
+
chunk = nil
|
58
|
+
if data.length > wanted
|
59
|
+
chunk = data.slice!(0...wanted)
|
60
|
+
else
|
61
|
+
chunk = data
|
62
|
+
data = ""
|
63
|
+
end
|
64
|
+
@lbp_binary_buffer[@lbp_binary_bytes_received...(@lbp_binary_bytes_received+chunk.length)] = chunk
|
65
|
+
@lbp_binary_bytes_received += chunk.length
|
66
|
+
if @lbp_binary_bytes_received == @lbp_binary_limit
|
67
|
+
receive_binary_data(@lbp_binary_buffer) if respond_to?(:receive_binary_data)
|
68
|
+
lbp_init_line_state
|
69
|
+
end
|
70
|
+
receive_data(data) if data.length > 0
|
71
|
+
else
|
72
|
+
receive_binary_data(data) if respond_to?(:receive_binary_data)
|
73
|
+
data = ""
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def unbind
|
79
|
+
if @lbp_mode == :binary and @lbp_binary_limit > 0
|
80
|
+
if respond_to?(:receive_binary_data)
|
81
|
+
receive_binary_data( @lbp_binary_buffer[0...@lbp_binary_bytes_received] )
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Set up to read the supplied number of binary bytes.
|
87
|
+
# This recycles all the data currently waiting in the line buffer, if any.
|
88
|
+
# If the limit is nil, then ALL subsequent data will be treated as binary
|
89
|
+
# data and passed to the upstream protocol handler as we receive it.
|
90
|
+
# If a limit is given, we'll hold the incoming binary data and not
|
91
|
+
# pass it upstream until we've seen it all, or until there is an unbind
|
92
|
+
# (in which case we'll pass up a partial).
|
93
|
+
# Specifying nil for the limit (the default) means there is no limit.
|
94
|
+
# Specifiyng zero for the limit will cause an immediate transition back to line mode.
|
95
|
+
#
|
96
|
+
def set_binary_mode size = nil
|
97
|
+
if @lbp_mode == :lines
|
98
|
+
if size == 0
|
99
|
+
receive_binary_data("") if respond_to?(:receive_binary_data)
|
100
|
+
# Do no more work here. Stay in line mode and keep consuming data.
|
101
|
+
else
|
102
|
+
@lbp_binary_limit = size.to_i # (nil will be stored as zero)
|
103
|
+
if @lbp_binary_limit > 0
|
104
|
+
raise "Overlength" if @lbp_binary_limit > MaxBinaryLength # arbitrary sanity check
|
105
|
+
@lbp_binary_buffer = "\0" * @lbp_binary_limit
|
106
|
+
@lbp_binary_bytes_received = 0
|
107
|
+
end
|
108
|
+
|
109
|
+
@lbp_mode = :binary
|
110
|
+
receive_data @lpb_buffer.flush
|
111
|
+
end
|
112
|
+
else
|
113
|
+
raise "invalid operation"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
#--
|
118
|
+
# For internal use, establish protocol baseline for handling lines.
|
119
|
+
def lbp_init_line_state
|
120
|
+
@lpb_buffer = BufferedTokenizer.new("\n", MaxLineLength)
|
121
|
+
@lbp_mode = :lines
|
122
|
+
end
|
123
|
+
private :lbp_init_line_state
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
# $Id$
|
2
|
+
#
|
3
|
+
# Author:: Francis Cianfrocca (gmail: blackhedd)
|
4
|
+
# Homepage:: http://rubyeventmachine.com
|
5
|
+
# Date:: 15 November 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
|
+
|
27
|
+
module EventMachine
|
28
|
+
module Protocols
|
29
|
+
# In the grand, time-honored tradition of re-inventing the wheel, we offer
|
30
|
+
# here YET ANOTHER protocol that handles line-oriented data with interspersed
|
31
|
+
# binary text. This one trades away some of the performance optimizations of
|
32
|
+
# EventMachine::Protocols::LineAndTextProtocol in order to get better correctness
|
33
|
+
# with regard to binary text blocks that can switch back to line mode. It also
|
34
|
+
# permits the line-delimiter to change in midstream.
|
35
|
+
# This was originally written to support Stomp.
|
36
|
+
module LineText2
|
37
|
+
# TODO! We're not enforcing the limits on header lengths and text-lengths.
|
38
|
+
# When we get around to that, call #receive_error if the user defined it, otherwise
|
39
|
+
# throw exceptions.
|
40
|
+
|
41
|
+
MaxLineLength = 16*1024
|
42
|
+
MaxBinaryLength = 32*1024*1024
|
43
|
+
|
44
|
+
#--
|
45
|
+
# Will be called recursively until there's no data to read.
|
46
|
+
# That way the user-defined handlers we call can modify the
|
47
|
+
# handling characteristics on a per-token basis.
|
48
|
+
#
|
49
|
+
def receive_data data
|
50
|
+
return unless (data and data.length > 0)
|
51
|
+
|
52
|
+
# Do this stuff in lieu of a constructor.
|
53
|
+
@lt2_mode ||= :lines
|
54
|
+
@lt2_delimiter ||= "\n"
|
55
|
+
@lt2_linebuffer ||= []
|
56
|
+
|
57
|
+
if @lt2_mode == :lines
|
58
|
+
if ix = data.index( @lt2_delimiter )
|
59
|
+
@lt2_linebuffer << data[0...ix]
|
60
|
+
ln = @lt2_linebuffer.join
|
61
|
+
@lt2_linebuffer.clear
|
62
|
+
if @lt2_delimiter == "\n"
|
63
|
+
ln.chomp!
|
64
|
+
end
|
65
|
+
receive_line ln
|
66
|
+
receive_data data[(ix+@lt2_delimiter.length)..-1]
|
67
|
+
else
|
68
|
+
@lt2_linebuffer << data
|
69
|
+
end
|
70
|
+
elsif @lt2_mode == :text
|
71
|
+
if @lt2_textsize
|
72
|
+
needed = @lt2_textsize - @lt2_textpos
|
73
|
+
will_take = if data.length > needed
|
74
|
+
needed
|
75
|
+
else
|
76
|
+
data.length
|
77
|
+
end
|
78
|
+
|
79
|
+
@lt2_textbuffer << data[0...will_take]
|
80
|
+
tail = data[will_take..-1]
|
81
|
+
|
82
|
+
@lt2_textpos += will_take
|
83
|
+
if @lt2_textpos >= @lt2_textsize
|
84
|
+
# Reset line mode (the default behavior) BEFORE calling the
|
85
|
+
# receive_binary_data. This makes it possible for user code
|
86
|
+
# to call set_text_mode, enabling chains of text blocks
|
87
|
+
# (which can possibly be of different sizes).
|
88
|
+
set_line_mode
|
89
|
+
receive_binary_data @lt2_textbuffer.join
|
90
|
+
receive_end_of_binary_data
|
91
|
+
end
|
92
|
+
|
93
|
+
receive_data tail
|
94
|
+
else
|
95
|
+
receive_binary_data data
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
def set_delimiter delim
|
102
|
+
@lt2_delimiter = delim.to_s
|
103
|
+
end
|
104
|
+
|
105
|
+
# Called internally but also exposed to user code, for the case in which
|
106
|
+
# processing of binary data creates a need to transition back to line mode.
|
107
|
+
# We support an optional parameter to "throw back" some data, which might
|
108
|
+
# be an umprocessed chunk of the transmitted binary data, or something else
|
109
|
+
# entirely.
|
110
|
+
def set_line_mode data=""
|
111
|
+
@lt2_mode = :lines
|
112
|
+
(@lt2_linebuffer ||= []).clear
|
113
|
+
receive_data data.to_s
|
114
|
+
end
|
115
|
+
|
116
|
+
def set_text_mode size=nil
|
117
|
+
if size == 0
|
118
|
+
set_line_mode
|
119
|
+
else
|
120
|
+
@lt2_mode = :text
|
121
|
+
(@lt2_textbuffer ||= []).clear
|
122
|
+
@lt2_textsize = size # which can be nil, signifying no limit
|
123
|
+
@lt2_textpos = 0
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# Alias for #set_text_mode, added for back-compatibility with LineAndTextProtocol.
|
128
|
+
def set_binary_mode size=nil
|
129
|
+
set_text_mode size
|
130
|
+
end
|
131
|
+
|
132
|
+
# In case of a dropped connection, we'll send a partial buffer to user code
|
133
|
+
# when in sized text mode. User overrides of #receive_binary_data need to
|
134
|
+
# be aware that they may get a short buffer.
|
135
|
+
def unbind
|
136
|
+
if @lt2_mode == :text and @lt2_textpos > 0
|
137
|
+
receive_binary_data @lt2_textbuffer.join
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# Stub. Should be subclassed by user code.
|
142
|
+
def receive_line ln
|
143
|
+
# no-op
|
144
|
+
end
|
145
|
+
|
146
|
+
# Stub. Should be subclassed by user code.
|
147
|
+
def receive_binary_data data
|
148
|
+
# no-op
|
149
|
+
end
|
150
|
+
|
151
|
+
# Stub. Should be subclassed by user code.
|
152
|
+
# This is called when transitioning internally from text mode
|
153
|
+
# back to line mode. Useful when client code doesn't want
|
154
|
+
# to keep track of how much data it's received.
|
155
|
+
def receive_end_of_binary_data
|
156
|
+
# no-op
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|