lib/net/protocol.rb
DEFINITIONS
This source file includes following functions.
1 =begin
2
3 = net/protocol.rb
4
5 Copyright (c) 1999-2002 Yukihiro Matsumoto
6
7 written & maintained by Minero Aoki <aamine@loveruby.net>
8
9 This program is free software. You can re-distribute and/or
10 modify this program under the same terms as Ruby itself,
11 Ruby Distribute License or GNU General Public License.
12
13 NOTE: You can find Japanese version of this document in
14 the doc/net directory of the standard ruby interpreter package.
15
16 $Id: protocol.rb,v 1.63 2002/07/11 08:22:16 matz Exp $
17
18 =end
19
20 require 'socket'
21 require 'timeout'
22
23
24 module Net
25
26 class Protocol
27
28 Version = '1.2.3'
29 Revision = %q$Revision: 1.63 $.split(/\s+/)[1]
30
31
32 class << self
33
34 def port
35 default_port
36 end
37
38 private
39
40 def protocol_param( name, val )
41 module_eval <<-End, __FILE__, __LINE__ + 1
42 def self.#{name.id2name}
43 #{val}
44 end
45 End
46 end
47
48 end
49
50
51 #
52 # --- Configuration Staffs for Sub Classes ---
53 #
54 # class method default_port
55 # class method command_type
56 # class method socket_type
57 #
58 # private method do_start
59 # private method do_finish
60 #
61 # private method conn_address
62 # private method conn_port
63 #
64
65
66 def Protocol.start( address, port = nil, *args )
67 instance = new(address, port)
68
69 if block_given? then
70 instance.start(*args) { return yield(instance) }
71 else
72 instance.start(*args)
73 instance
74 end
75 end
76
77 def initialize( addr, port = nil )
78 @address = addr
79 @port = port || self.class.default_port
80
81 @command = nil
82 @socket = nil
83
84 @started = false
85
86 @open_timeout = 30
87 @read_timeout = 60
88
89 @debug_output = nil
90 end
91
92 attr_reader :address
93 attr_reader :port
94
95 attr_reader :command
96 attr_reader :socket
97
98 attr_accessor :open_timeout
99
100 attr_reader :read_timeout
101
102 def read_timeout=( sec )
103 @socket.read_timeout = sec if @socket
104 @read_timeout = sec
105 end
106
107 def started?
108 @started
109 end
110
111 alias active? started?
112
113 def set_debug_output( arg ) # un-documented
114 @debug_output = arg
115 end
116
117 def inspect
118 "#<#{self.class} #{@address}:#{@port} open=#{active?}>"
119 end
120
121 #
122 # open
123 #
124
125 def start( *args )
126 @started and raise IOError, 'protocol has been opened already'
127
128 if block_given? then
129 begin
130 do_start( *args )
131 @started = true
132 return yield(self)
133 ensure
134 finish if @started
135 end
136 end
137
138 do_start( *args )
139 @started = true
140 self
141 end
142
143 private
144
145 # abstract do_start()
146
147 def conn_socket
148 @socket = self.class.socket_type.open(
149 conn_address(), conn_port(),
150 @open_timeout, @read_timeout, @debug_output )
151 on_connect
152 end
153
154 alias conn_address address
155 alias conn_port port
156
157 def reconn_socket
158 @socket.reopen @open_timeout
159 on_connect
160 end
161
162 def conn_command
163 @command = self.class.command_type.new(@socket)
164 end
165
166 def on_connect
167 end
168
169 #
170 # close
171 #
172
173 public
174
175 def finish
176 @started or raise IOError, 'closing already closed protocol'
177 do_finish
178 @started = false
179 nil
180 end
181
182 private
183
184 # abstract do_finish()
185
186 def disconn_command
187 @command.quit if @command and not @command.critical?
188 @command = nil
189 end
190
191 def disconn_socket
192 if @socket and not @socket.closed? then
193 @socket.close
194 end
195 @socket = nil
196 end
197
198 end
199
200 Session = Protocol
201
202
203 class Response
204
205 def initialize( ctype, code, msg )
206 @code_type = ctype
207 @code = code
208 @message = msg
209 super()
210 end
211
212 attr_reader :code_type
213 attr_reader :code
214 attr_reader :message
215 alias msg message
216
217 def inspect
218 "#<#{self.class} #{@code}>"
219 end
220
221 def error!
222 raise error_type().new(code + ' ' + @message.dump, self)
223 end
224
225 def error_type
226 @code_type.error_type
227 end
228
229 end
230
231
232 class ProtocolError < StandardError; end
233 class ProtoSyntaxError < ProtocolError; end
234 class ProtoFatalError < ProtocolError; end
235 class ProtoUnknownError < ProtocolError; end
236 class ProtoServerError < ProtocolError; end
237 class ProtoAuthError < ProtocolError; end
238 class ProtoCommandError < ProtocolError; end
239 class ProtoRetriableError < ProtocolError; end
240 ProtocRetryError = ProtoRetriableError
241
242 class ProtocolError
243
244 def initialize( msg, resp )
245 super msg
246 @response = resp
247 end
248
249 attr_reader :response
250 alias data response
251
252 def inspect
253 "#<#{self.class} #{self.message}>"
254 end
255
256 end
257
258
259 class Code
260
261 def initialize( paren, err )
262 @parents = [self] + paren
263 @error_type = err
264 end
265
266 def parents
267 @parents.dup
268 end
269
270 attr_reader :error_type
271
272 def inspect
273 "#<#{self.class} #{sprintf '0x%x', __id__}>"
274 end
275
276 def ===( response )
277 response.code_type.parents.each {|c| c == self and return true }
278 false
279 end
280
281 def mkchild( err = nil )
282 self.class.new(@parents, err || @error_type)
283 end
284
285 end
286
287 ReplyCode = Code.new( [], ProtoUnknownError )
288 InformationCode = ReplyCode.mkchild( ProtoUnknownError )
289 SuccessCode = ReplyCode.mkchild( ProtoUnknownError )
290 ContinueCode = ReplyCode.mkchild( ProtoUnknownError )
291 ErrorCode = ReplyCode.mkchild( ProtocolError )
292 SyntaxErrorCode = ErrorCode.mkchild( ProtoSyntaxError )
293 FatalErrorCode = ErrorCode.mkchild( ProtoFatalError )
294 ServerErrorCode = ErrorCode.mkchild( ProtoServerError )
295 AuthErrorCode = ErrorCode.mkchild( ProtoAuthError )
296 RetriableCode = ReplyCode.mkchild( ProtoRetriableError )
297 UnknownCode = ReplyCode.mkchild( ProtoUnknownError )
298
299
300 class Command
301
302 def initialize( sock )
303 @socket = sock
304 @last_reply = nil
305 @atomic = false
306 end
307
308 attr_accessor :socket
309 attr_reader :last_reply
310
311 def inspect
312 "#<#{self.class} socket=#{@socket.inspect} critical=#{@atomic}>"
313 end
314
315 # abstract quit()
316
317 private
318
319 def check_reply( *oks )
320 @last_reply = get_reply()
321 reply_must @last_reply, *oks
322 end
323
324 # abstract get_reply()
325
326 def reply_must( rep, *oks )
327 oks.each do |i|
328 return rep if i === rep
329 end
330 rep.error!
331 end
332
333 def getok( line, expect = SuccessCode )
334 @socket.writeline line
335 check_reply expect
336 end
337
338 #
339 # critical session
340 #
341
342 public
343
344 def critical?
345 @atomic
346 end
347
348 def error_ok
349 @atomic = false
350 end
351
352 private
353
354 def atomic
355 @atomic = true
356 ret = yield
357 @atomic = false
358 ret
359 end
360
361 end
362
363
364 class InternetMessageIO
365
366 class << self
367 alias open new
368 end
369
370 def initialize( addr, port, otime = nil, rtime = nil, dout = nil )
371 @address = addr
372 @port = port
373 @read_timeout = rtime
374 @debug_output = dout
375
376 @socket = nil
377 @rbuf = nil
378
379 connect otime
380 D 'opened'
381 end
382
383 attr_reader :address
384 attr_reader :port
385
386 def ip_address
387 @socket or return ''
388 @socket.addr[3]
389 end
390
391 attr_accessor :read_timeout
392
393 attr_reader :socket
394
395 def connect( otime )
396 D "opening connection to #{@address}..."
397 timeout( otime ) {
398 @socket = TCPSocket.new( @address, @port )
399 }
400 @rbuf = ''
401 end
402 private :connect
403
404 def close
405 if @socket then
406 @socket.close
407 D 'closed'
408 else
409 D 'close call for already closed socket'
410 end
411 @socket = nil
412 @rbuf = ''
413 end
414
415 def reopen( otime = nil )
416 D 'reopening...'
417 close
418 connect otime
419 D 'reopened'
420 end
421
422 def closed?
423 not @socket
424 end
425
426 def inspect
427 "#<#{type} #{closed? ? 'closed' : 'opened'}>"
428 end
429
430 ###
431 ### READ
432 ###
433
434 public
435
436 def read( len, dest = '', ignore = false )
437 D_off "reading #{len} bytes..."
438
439 rsize = 0
440 begin
441 while rsize + @rbuf.size < len do
442 rsize += rbuf_moveto(dest, @rbuf.size)
443 rbuf_fill
444 end
445 rbuf_moveto dest, len - rsize
446 rescue EOFError
447 raise unless ignore
448 end
449
450 D_on "read #{len} bytes"
451 dest
452 end
453
454 def read_all( dest = '' )
455 D_off 'reading all...'
456
457 rsize = 0
458 begin
459 while true do
460 rsize += rbuf_moveto(dest, @rbuf.size)
461 rbuf_fill
462 end
463 rescue EOFError
464 ;
465 end
466
467 D_on "read #{rsize} bytes"
468 dest
469 end
470
471 def readuntil( target, ignore = false )
472 dest = ''
473 begin
474 while true do
475 idx = @rbuf.index(target)
476 break if idx
477 rbuf_fill
478 end
479 rbuf_moveto dest, idx + target.size
480 rescue EOFError
481 raise unless ignore
482 rbuf_moveto dest, @rbuf.size
483 end
484 dest
485 end
486
487 def readline
488 ret = readuntil("\n")
489 ret.chop!
490 ret
491 end
492
493 private
494
495 BLOCK_SIZE = 1024
496
497 def rbuf_fill
498 until IO.select [@socket], nil, nil, @read_timeout do
499 on_read_timeout
500 end
501 @rbuf << @socket.sysread(BLOCK_SIZE)
502 end
503
504 def on_read_timeout
505 raise TimeoutError, "socket read timeout (#{@read_timeout} sec)"
506 end
507
508 def rbuf_moveto( dest, len )
509 dest << (s = @rbuf.slice!(0, len))
510 @debug_output << %Q[-> #{s.dump}\n] if @debug_output
511 len
512 end
513
514 #
515 # message read
516 #
517
518 public
519
520 def read_message_to( dest )
521 D_off 'reading text...'
522
523 rsize = 0
524 while (str = readuntil("\r\n")) != ".\r\n" do
525 rsize += str.size
526 dest << str.sub(/\A\./, '')
527 end
528
529 D_on "read #{rsize} bytes"
530 dest
531 end
532
533 # private use only (cannot handle 'break')
534 def each_list_item
535 while (str = readuntil("\r\n")) != ".\r\n" do
536 yield str.chop
537 end
538 end
539
540
541 ###
542 ### WRITE
543 ###
544
545 #
546 # basic write
547 #
548
549 public
550
551 def write( str )
552 writing {
553 do_write str
554 }
555 end
556
557 def writeline( str )
558 writing {
559 do_write str + "\r\n"
560 }
561 end
562
563 private
564
565 def writing
566 @writtensize = 0
567 @debug_output << '<- ' if @debug_output
568 yield
569 @socket.flush
570 @debug_output << "\n" if @debug_output
571 @writtensize
572 end
573
574 def do_write( str )
575 @debug_output << str.dump if @debug_output
576 @writtensize += (n = @socket.write(str))
577 n
578 end
579
580 #
581 # message write
582 #
583
584 public
585
586 def write_message( src )
587 D_off "writing text from #{src.type}"
588
589 wsize = using_each_crlf_line {
590 wpend_in src
591 }
592
593 D_on "wrote #{wsize} bytes text"
594 wsize
595 end
596
597 def through_message
598 D_off 'writing text from block'
599
600 wsize = using_each_crlf_line {
601 yield WriteAdapter.new(self, :wpend_in)
602 }
603
604 D_on "wrote #{wsize} bytes text"
605 wsize
606 end
607
608 private
609
610 def wpend_in( src )
611 line = nil
612 pre = @writtensize
613 each_crlf_line( src ) do |line|
614 do_write '.' if line[0] == ?.
615 do_write line
616 end
617
618 @writtensize - pre
619 end
620
621 def using_each_crlf_line
622 writing {
623 @wbuf = ''
624
625 yield
626
627 if not @wbuf.empty? then # unterminated last line
628 if @wbuf[-1] == ?\r then
629 @wbuf.chop!
630 end
631 @wbuf.concat "\r\n"
632 do_write @wbuf
633 elsif @writtensize == 0 then # empty src
634 do_write "\r\n"
635 end
636 do_write ".\r\n"
637
638 @wbuf = nil
639 }
640 end
641
642 def each_crlf_line( src )
643 str = m = beg = nil
644
645 adding( src ) do
646 beg = 0
647 buf = @wbuf
648 while buf.index( /\n|\r\n|\r/, beg ) do
649 m = Regexp.last_match
650 if m.begin(0) == buf.size - 1 and buf[-1] == ?\r then
651 # "...\r" : can follow "\n..."
652 break
653 end
654 str = buf[ beg ... m.begin(0) ]
655 str.concat "\r\n"
656 yield str
657 beg = m.end(0)
658 end
659 @wbuf = buf[ beg ... buf.size ]
660 end
661 end
662
663 def adding( src )
664 i = nil
665
666 case src
667 when String
668 0.step( src.size - 1, 2048 ) do |i|
669 @wbuf << src[i,2048]
670 yield
671 end
672
673 when File
674 while true do
675 i = src.read(2048)
676 break unless i
677 i[0,0] = @wbuf
678 @wbuf = i
679 yield
680 end
681
682 else
683 src.each do |i|
684 @wbuf << i
685 if @wbuf.size > 2048 then
686 yield
687 end
688 end
689 yield unless @wbuf.empty?
690 end
691 end
692
693 ###
694 ### DEBUG
695 ###
696
697 private
698
699 def D_off( msg )
700 D msg
701 @savedo, @debug_output = @debug_output, nil
702 end
703
704 def D_on( msg )
705 @debug_output = @savedo
706 D msg
707 end
708
709 def D( msg )
710 @debug_output or return
711 @debug_output << msg
712 @debug_output << "\n"
713 end
714
715 end
716
717
718 class WriteAdapter
719
720 def initialize( sock, mid )
721 @socket = sock
722 @mid = mid
723 end
724
725 def inspect
726 "#<#{type} socket=#{@socket.inspect}>"
727 end
728
729 def write( str )
730 @socket.__send__ @mid, str
731 end
732
733 alias print write
734
735 def <<( str )
736 write str
737 self
738 end
739
740 def puts( str = '' )
741 write str.sub(/\n?/, "\n")
742 end
743
744 def printf( *args )
745 write sprintf(*args)
746 end
747
748 end
749
750
751 class ReadAdapter
752
753 def initialize( block )
754 @block = block
755 end
756
757 def inspect
758 "#<#{type}>"
759 end
760
761 def <<( str )
762 call_block str, &@block if @block
763 end
764
765 private
766
767 def call_block( str )
768 yield str
769 end
770
771 end
772
773
774 # for backward compatibility
775 module NetPrivate
776 Response = ::Net::Response
777 Command = ::Net::Command
778 Socket = ::Net::InternetMessageIO
779 BufferedSocket = ::Net::InternetMessageIO
780 WriteAdapter = ::Net::WriteAdapter
781 ReadAdapter = ::Net::ReadAdapter
782 end
783 BufferedSocket = ::Net::InternetMessageIO
784
785 end # module Net