1 #
2 # = net/protocol.rb
3 #
4 #--
5 # Copyright (c) 1999-2005 Yukihiro Matsumoto
6 # Copyright (c) 1999-2005 Minero Aoki
7 #
8 # written and maintained by Minero Aoki <aamine@loveruby.net>
9 #
10 # This program is free software. You can re-distribute and/or
11 # modify this program under the same terms as Ruby itself,
12 # Ruby Distribute License or GNU General Public License.
13 #
14 # $Id: protocol.rb 12092 2007-03-19 02:39:22Z aamine $
15 #++
16 #
17 # WARNING: This file is going to remove.
18 # Do not rely on the implementation written in this file.
19 #
20
21 require 'socket'
22 require 'timeout'
23
24 module Net # :nodoc:
25
26 class Protocol #:nodoc: internal use only
27 private
28 def Protocol.protocol_param(name, val)
29 module_eval(<<-End, __FILE__, __LINE__ + 1)
30 def #{name}
31 #{val}
32 end
33 End
34 end
35 end
36
37
38 class ProtocolError < StandardError; end
39 class ProtoSyntaxError < ProtocolError; end
40 class ProtoFatalError < ProtocolError; end
41 class ProtoUnknownError < ProtocolError; end
42 class ProtoServerError < ProtocolError; end
43 class ProtoAuthError < ProtocolError; end
44 class ProtoCommandError < ProtocolError; end
45 class ProtoRetriableError < ProtocolError; end
46 ProtocRetryError = ProtoRetriableError
47
48
49 class BufferedIO #:nodoc: internal use only
50 def initialize(io)
51 @io = io
52 @read_timeout = 60
53 @debug_output = nil
54 @rbuf = ''
55 end
56
57 attr_reader :io
58 attr_accessor :read_timeout
59 attr_accessor :debug_output
60
61 def inspect
62 "#<#{self.class} io=#{@io}>"
63 end
64
65 def closed?
66 @io.closed?
67 end
68
69 def close
70 @io.close
71 end
72
73 #
74 # Read
75 #
76
77 public
78
79 def read(len, dest = '', ignore_eof = false)
80 LOG "reading #{len} bytes..."
81 read_bytes = 0
82 begin
83 while read_bytes + @rbuf.size < len
84 dest << (s = rbuf_consume(@rbuf.size))
85 read_bytes += s.size
86 rbuf_fill
87 end
88 dest << (s = rbuf_consume(len - read_bytes))
89 read_bytes += s.size
90 rescue EOFError
91 raise unless ignore_eof
92 end
93 LOG "read #{read_bytes} bytes"
94 dest
95 end
96
97 def read_all(dest = '')
98 LOG 'reading all...'
99 read_bytes = 0
100 begin
101 while true
102 dest << (s = rbuf_consume(@rbuf.size))
103 read_bytes += s.size
104 rbuf_fill
105 end
106 rescue EOFError
107 ;
108 end
109 LOG "read #{read_bytes} bytes"
110 dest
111 end
112
113 def readuntil(terminator, ignore_eof = false)
114 begin
115 until idx = @rbuf.index(terminator)
116 rbuf_fill
117 end
118 return rbuf_consume(idx + terminator.size)
119 rescue EOFError
120 raise unless ignore_eof
121 return rbuf_consume(@rbuf.size)
122 end
123 end
124
125 def readline
126 readuntil("\n").chop
127 end
128
129 private
130
131 BUFSIZE = 1024 * 16
132
133 def rbuf_fill
134 timeout(@read_timeout) {
135 @rbuf << @io.sysread(BUFSIZE)
136 }
137 end
138
139 def rbuf_consume(len)
140 s = @rbuf.slice!(0, len)
141 @debug_output << %Q[-> #{s.dump}\n] if @debug_output
142 s
143 end
144
145 #
146 # Write
147 #
148
149 public
150
151 def write(str)
152 writing {
153 write0 str
154 }
155 end
156
157 def writeline(str)
158 writing {
159 write0 str + "\r\n"
160 }
161 end
162
163 private
164
165 def writing
166 @written_bytes = 0
167 @debug_output << '<- ' if @debug_output
168 yield
169 @debug_output << "\n" if @debug_output
170 bytes = @written_bytes
171 @written_bytes = nil
172 bytes
173 end
174
175 def write0(str)
176 @debug_output << str.dump if @debug_output
177 len = @io.write(str)
178 @written_bytes += len
179 len
180 end
181
182 #
183 # Logging
184 #
185
186 private
187
188 def LOG_off
189 @save_debug_out = @debug_output
190 @debug_output = nil
191 end
192
193 def LOG_on
194 @debug_output = @save_debug_out
195 end
196
197 def LOG(msg)
198 return unless @debug_output
199 @debug_output << msg + "\n"
200 end
201 end
202
203
204 class InternetMessageIO < BufferedIO #:nodoc: internal use only
205 def InternetMessageIO.old_open(addr, port,
206 open_timeout = nil, read_timeout = nil, debug_output = nil)
207 debug_output << "opening connection to #{addr}...\n" if debug_output
208 s = timeout(open_timeout) { TCPsocket.new(addr, port) }
209 io = new(s)
210 io.read_timeout = read_timeout
211 io.debug_output = debug_output
212 io
213 end
214
215 def initialize(io)
216 super
217 @wbuf = nil
218 end
219
220 #
221 # Read
222 #
223
224 def each_message_chunk
225 LOG 'reading message...'
226 LOG_off()
227 read_bytes = 0
228 while (line = readuntil("\r\n")) != ".\r\n"
229 read_bytes += line.size
230 yield line.sub(/\A\./, '')
231 end
232 LOG_on()
233 LOG "read message (#{read_bytes} bytes)"
234 end
235
236 # *library private* (cannot handle 'break')
237 def each_list_item
238 while (str = readuntil("\r\n")) != ".\r\n"
239 yield str.chop
240 end
241 end
242
243 def write_message_0(src)
244 prev = @written_bytes
245 each_crlf_line(src) do |line|
246 write0 line.sub(/\A\./, '..')
247 end
248 @written_bytes - prev
249 end
250
251 #
252 # Write
253 #
254
255 def write_message(src)
256 LOG "writing message from #{src.class}"
257 LOG_off()
258 len = writing {
259 using_each_crlf_line {
260 write_message_0 src
261 }
262 }
263 LOG_on()
264 LOG "wrote #{len} bytes"
265 len
266 end
267
268 def write_message_by_block(&block)
269 LOG 'writing message from block'
270 LOG_off()
271 len = writing {
272 using_each_crlf_line {
273 begin
274 block.call(WriteAdapter.new(self, :write_message_0))
275 rescue LocalJumpError
276 # allow `break' from writer block
277 end
278 }
279 }
280 LOG_on()
281 LOG "wrote #{len} bytes"
282 len
283 end
284
285 private
286
287 def using_each_crlf_line
288 @wbuf = ''
289 yield
290 if not @wbuf.empty? # unterminated last line
291 write0 @wbuf.chomp + "\r\n"
292 elsif @written_bytes == 0 # empty src
293 write0 "\r\n"
294 end
295 write0 ".\r\n"
296 @wbuf = nil
297 end
298
299 def each_crlf_line(src)
300 buffer_filling(@wbuf, src) do
301 while line = @wbuf.slice!(/\A.*(?:\n|\r\n|\r(?!\z))/n)
302 yield line.chomp("\n") + "\r\n"
303 end
304 end
305 end
306
307 def buffer_filling(buf, src)
308 case src
309 when String # for speeding up.
310 0.step(src.size - 1, 1024) do |i|
311 buf << src[i, 1024]
312 yield
313 end
314 when File # for speeding up.
315 while s = src.read(1024)
316 buf << s
317 yield
318 end
319 else # generic reader
320 src.each do |s|
321 buf << s
322 yield if buf.size > 1024
323 end
324 yield unless buf.empty?
325 end
326 end
327 end
328
329
330 #
331 # The writer adapter class
332 #
333 class WriteAdapter
334 def initialize(socket, method)
335 @socket = socket
336 @method_id = method
337 end
338
339 def inspect
340 "#<#{self.class} socket=#{@socket.inspect}>"
341 end
342
343 def write(str)
344 @socket.__send__(@method_id, str)
345 end
346
347 alias print write
348
349 def <<(str)
350 write str
351 self
352 end
353
354 def puts(str = '')
355 write str.chomp("\n") + "\n"
356 end
357
358 def printf(*args)
359 write sprintf(*args)
360 end
361 end
362
363
364 class ReadAdapter #:nodoc: internal use only
365 def initialize(block)
366 @block = block
367 end
368
369 def inspect
370 "#<#{self.class}>"
371 end
372
373 def <<(str)
374 call_block(str, &@block) if @block
375 end
376
377 private
378
379 # This method is needed because @block must be called by yield,
380 # not Proc#call. You can see difference when using `break' in
381 # the block.
382 def call_block(str)
383 yield str
384 end
385 end
386
387
388 module NetPrivate #:nodoc: obsolete
389 Socket = ::Net::InternetMessageIO
390 end
391
392 end # module Net