Imported File#ftype spec from rubyspecs.
[rbx.git] / lib / net / protocol.rb
blobb898030093853a4f4cf33ebaedf5ea0a7d2bd9d4
2 # = net/protocol.rb
4 #--
5 # Copyright (c) 1999-2005 Yukihiro Matsumoto
6 # Copyright (c) 1999-2005 Minero Aoki
8 # written and maintained by Minero Aoki <aamine@loveruby.net>
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.
14 # $Id: protocol.rb 11708 2007-02-12 23:01:19Z shyouhei $
15 #++
17 # WARNING: This file is going to remove.
18 # Do not rely on the implementation written in this file.
21 require 'socket'
22 require 'timeout'
24 module Net # :nodoc:
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
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
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
57     attr_reader :io
58     attr_accessor :read_timeout
59     attr_accessor :debug_output
61     def inspect
62       "#<#{self.class} io=#{@io}>"
63     end
65     def closed?
66       @io.closed?
67     end
69     def close
70       @io.close
71     end
73     #
74     # Read
75     #
77     public
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
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
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
129     private
131     def rbuf_fill
132       timeout(@read_timeout) {
133         @rbuf << @io.sysread(1024)
134       }
135     end
137     def rbuf_consume(len)
138       s = @rbuf.slice!(0, len)
139       @debug_output << %Q[-> #{s.dump}\n] if @debug_output
140       s
141     end
143     #
144     # Write
145     #
147     public
149     def write(str)
150       writing {
151         write0 str
152       }
153     end
155     def writeline(str)
156       writing {
157         write0 str + "\r\n"
158       }
159     end
161     private
163     def writing
164       @written_bytes = 0
165       @debug_output << '<- ' if @debug_output
166       yield
167       @debug_output << "\n" if @debug_output
168       bytes = @written_bytes
169       @written_bytes = nil
170       bytes
171     end
173     def write0(str)
174       @debug_output << str.dump if @debug_output
175       len = @io.write(str)
176       @written_bytes += len
177       len
178     end
180     #
181     # Logging
182     #
184     private
186     def LOG_off
187       @save_debug_out = @debug_output
188       @debug_output = nil
189     end
191     def LOG_on
192       @debug_output = @save_debug_out
193     end
195     def LOG(msg)
196       return unless @debug_output
197       @debug_output << msg + "\n"
198     end
199   end
202   class InternetMessageIO < BufferedIO   #:nodoc: internal use only
203     def InternetMessageIO.old_open(addr, port,
204         open_timeout = nil, read_timeout = nil, debug_output = nil)
205       debug_output << "opening connection to #{addr}...\n" if debug_output
206       s = timeout(open_timeout) { TCPsocket.new(addr, port) }
207       io = new(s)
208       io.read_timeout = read_timeout
209       io.debug_output = debug_output
210       io
211     end
213     def initialize(io)
214       super
215       @wbuf = nil
216     end
218     #
219     # Read
220     #
222     def each_message_chunk
223       LOG 'reading message...'
224       LOG_off()
225       read_bytes = 0
226       while (line = readuntil("\r\n")) != ".\r\n"
227         read_bytes += line.size
228         yield line.sub(/\A\./, '')
229       end
230       LOG_on()
231       LOG "read message (#{read_bytes} bytes)"
232     end
233   
234     # *library private* (cannot handle 'break')
235     def each_list_item
236       while (str = readuntil("\r\n")) != ".\r\n"
237         yield str.chop
238       end
239     end
241     def write_message_0(src)
242       prev = @written_bytes
243       each_crlf_line(src) do |line|
244         write0 line.sub(/\A\./, '..')
245       end
246       @written_bytes - prev
247     end
249     #
250     # Write
251     #
253     def write_message(src)
254       LOG "writing message from #{src.class}"
255       LOG_off()
256       len = writing {
257         using_each_crlf_line {
258           write_message_0 src
259         }
260       }
261       LOG_on()
262       LOG "wrote #{len} bytes"
263       len
264     end
266     def write_message_by_block(&block)
267       LOG 'writing message from block'
268       LOG_off()
269       len = writing {
270         using_each_crlf_line {
271           begin
272             block.call(WriteAdapter.new(self, :write_message_0))
273           rescue LocalJumpError
274             # allow `break' from writer block
275           end
276         }
277       }
278       LOG_on()
279       LOG "wrote #{len} bytes"
280       len
281     end
283     private
285     def using_each_crlf_line
286       @wbuf = ''
287       yield
288       if not @wbuf.empty?   # unterminated last line
289         write0 @wbuf.chomp + "\r\n"
290       elsif @written_bytes == 0   # empty src
291         write0 "\r\n"
292       end
293       write0 ".\r\n"
294       @wbuf = nil
295     end
297     def each_crlf_line(src)
298       buffer_filling(@wbuf, src) do
299         while line = @wbuf.slice!(/\A.*(?:\n|\r\n|\r(?!\z))/n)
300           yield line.chomp("\n") + "\r\n"
301         end
302       end
303     end
305     def buffer_filling(buf, src)
306       case src
307       when String    # for speeding up.
308         0.step(src.size - 1, 1024) do |i|
309           buf << src[i, 1024]
310           yield
311         end
312       when File    # for speeding up.
313         while s = src.read(1024)
314           buf << s
315           yield
316         end
317       else    # generic reader
318         src.each do |s|
319           buf << s
320           yield if buf.size > 1024
321         end
322         yield unless buf.empty?
323       end
324     end
325   end
328   #
329   # The writer adapter class
330   #
331   class WriteAdapter
332     def initialize(socket, method)
333       @socket = socket
334       @method_id = method
335     end
337     def inspect
338       "#<#{self.class} socket=#{@socket.inspect}>"
339     end
341     def write(str)
342       @socket.__send__(@method_id, str)
343     end
345     alias print write
347     def <<(str)
348       write str
349       self
350     end
352     def puts(str = '')
353       write str.chomp("\n") + "\n"
354     end
356     def printf(*args)
357       write sprintf(*args)
358     end
359   end
362   class ReadAdapter   #:nodoc: internal use only
363     def initialize(block)
364       @block = block
365     end
367     def inspect
368       "#<#{self.class}>"
369     end
371     def <<(str)
372       call_block(str, &@block) if @block
373     end
375     private
377     # This method is needed because @block must be called by yield,
378     # not Proc#call.  You can see difference when using `break' in
379     # the block.
380     def call_block(str)
381       yield str
382     end
383   end
386   module NetPrivate   #:nodoc: obsolete
387     Socket = ::Net::InternetMessageIO
388   end
390 end   # module Net