| Class | Irc::Socket |
| In: |
lib/rbot/ircsocket.rb
|
| Parent: | Object |
wrapped TCPSocket for communication with the server. emulates a subset of TCPSocket functionality
| MAX_IRC_SEND_PENALTY | = | 10 |
| bytes_received | [R] | total number of bytes received from the irc server |
| bytes_sent | [R] | total number of bytes sent to the irc server |
| filter | [R] | an optional filter object. we call @filter.in(data) for all incoming data and @filter.out(data) for all outgoing data |
| lines_received | [R] | total number of lines received from the irc server |
| lines_sent | [R] | total number of lines sent to the irc server |
| penalty_pct | [RW] | penalty multiplier (percent) |
| server_uri | [R] | normalized uri of the current server |
| throttle_bytes | [R] | accumulator for the throttle |
| server_list: | list of servers to connect to |
| host: | optional local host to bind to (ruby 1.7+ required) |
create a new Irc::Socket
# File lib/rbot/ircsocket.rb, line 277
277: def initialize(server_list, host, opts={})
278: @server_list = server_list.dup
279: @server_uri = nil
280: @conn_count = 0
281: @host = host
282: @sock = nil
283: @filter = IdentityFilter.new
284: @spooler = false
285: @lines_sent = 0
286: @lines_received = 0
287: @ssl = opts[:ssl]
288: @penalty_pct = opts[:penalty_pct] || 100
289: end
open a TCP connection to the server
# File lib/rbot/ircsocket.rb, line 296
296: def connect
297: if connected?
298: warning "reconnecting while connected"
299: return
300: end
301: srv_uri = @server_list[@conn_count % @server_list.size].dup
302: srv_uri = 'irc://' + srv_uri if !(srv_uri =~ /:\/\//)
303: @conn_count += 1
304: @server_uri = URI.parse(srv_uri)
305: @server_uri.port = 6667 if !@server_uri.port
306: debug "connection attempt \##{@conn_count} (#{@server_uri.host}:#{@server_uri.port})"
307:
308: if(@host)
309: begin
310: sock=TCPSocket.new(@server_uri.host, @server_uri.port, @host)
311: rescue ArgumentError => e
312: error "Your version of ruby does not support binding to a "
313: error "specific local address, please upgrade if you wish "
314: error "to use HOST = foo"
315: error "(this option has been disabled in order to continue)"
316: sock=TCPSocket.new(@server_uri.host, @server_uri.port)
317: end
318: else
319: sock=TCPSocket.new(@server_uri.host, @server_uri.port)
320: end
321: if(@ssl)
322: require 'openssl'
323: ssl_context = OpenSSL::SSL::SSLContext.new()
324: ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
325: sock = OpenSSL::SSL::SSLSocket.new(sock, ssl_context)
326: sock.sync_close = true
327: sock.connect
328: end
329: @sock = sock
330: @last_send = Time.new
331: @flood_send = Time.new
332: @burst = 0
333: @sock.extend(MonitorMixin)
334: @sendq = MessageQueue.new
335: @qthread = Thread.new { writer_loop }
336: end
used to send lines to the remote IRCd by skipping the queue message: IRC message to send it should only be used for stuff that *must not* be queued, i.e. the initial PASS, NICK and USER command or the final QUIT message
# File lib/rbot/ircsocket.rb, line 343
343: def emergency_puts(message, penalty = false)
344: @sock.synchronize do
345: # debug "In puts - got @sock"
346: puts_critical(message, penalty)
347: end
348: end
set filter to identity, not to nil
# File lib/rbot/ircsocket.rb, line 270
270: def filter=(f)
271: @filter = f || IdentityFilter.new
272: end
get the next line from the server (blocks)
# File lib/rbot/ircsocket.rb, line 359
359: def gets
360: if @sock.nil?
361: warning "socket get attempted while closed"
362: return nil
363: end
364: begin
365: reply = @filter.in(@sock.gets)
366: @lines_received += 1
367: reply.strip! if reply
368: debug "RECV: #{reply.inspect}"
369: return reply
370: rescue Exception => e
371: handle_socket_error(:RECV, e)
372: end
373: end
# File lib/rbot/ircsocket.rb, line 350
350: def handle_socket_error(string, e)
351: error "#{string} failed: #{e.pretty_inspect}"
352: # We assume that an error means that there are connection
353: # problems and that we should reconnect, so we
354: shutdown
355: raise SocketError.new(e.inspect)
356: end
# File lib/rbot/ircsocket.rb, line 375
375: def queue(msg, chan=nil, ring=0)
376: @sendq.push msg, chan, ring
377: end
Wraps Kernel.select on the socket
# File lib/rbot/ircsocket.rb, line 389
389: def select(timeout=nil)
390: Kernel.select([@sock], nil, nil, timeout)
391: end
shutdown the connection to the server
# File lib/rbot/ircsocket.rb, line 394
394: def shutdown(how=2)
395: return unless connected?
396: @qthread.kill
397: @qthread = nil
398: begin
399: @sock.close
400: rescue Exception => e
401: error "error while shutting down: #{e.pretty_inspect}"
402: end
403: @sock = nil
404: @sendq.clear
405: end
same as puts, but expects to be called with a lock held on @sock
# File lib/rbot/ircsocket.rb, line 431
431: def puts_critical(message, penalty=false)
432: # debug "in puts_critical"
433: begin
434: debug "SEND: #{message.inspect}"
435: if @sock.nil?
436: error "SEND attempted on closed socket"
437: else
438: # we use Socket#syswrite() instead of Socket#puts() because
439: # the latter is racy and can cause double message output in
440: # some circumstances
441: actual = @filter.out(message) + "\n"
442: now = Time.new
443: @sock.syswrite actual
444: @last_send = now
445: @flood_send = now if @flood_send < now
446: @flood_send += message.irc_send_penalty*@penalty_pct/100.0 if penalty
447: @lines_sent += 1
448: end
449: rescue Exception => e
450: handle_socket_error(:SEND, e)
451: end
452: end
# File lib/rbot/ircsocket.rb, line 409
409: def writer_loop
410: loop do
411: begin
412: now = Time.now
413: flood_delay = @flood_send - MAX_IRC_SEND_PENALTY - now
414: delay = [flood_delay, 0].max
415: if delay > 0
416: debug "sleep(#{delay}) # (f: #{flood_delay})"
417: sleep(delay)
418: end
419: msg = @sendq.shift
420: debug "got #{msg.inspect} from queue, sending"
421: emergency_puts(msg, true)
422: rescue Exception => e
423: error "Spooling failed: #{e.pretty_inspect}"
424: debug e.backtrace.join("\n")
425: raise e
426: end
427: end
428: end