Class: Iodine::Protocol

Inherits:
Object
  • Object
show all
Defined in:
lib/iodine/protocol.rb

Overview

This is the Basic Iodine server unit - a network protocol.

A new protocol instance will be created for every network connection.

The recommended use is to inherit this class and override any of the following:

on_open

called whenever the Protocol is initialized. Override this to initialize the Protocol object.

on_message(data)

called whenever data is received from the IO. Override this to implement the actual network protocol.

on_close

called AFTER the Protocol’s IO is closed.

on_shutdown

called when the server’s shutdown process had started and BEFORE the Protocol’s IO is closed. It allows graceful shutdown for network protocols.

ping

called when timeout was reached. see #set_timeout

Once the network protocol class was created, remember to tell Iodine about it:

class MyProtocol << Iodine::Protocol
    # your code here
end
# tell Iodine
Iodine.protocol = MyProtocol

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(io) ⇒ Protocol

This method is used by Iodine to initialized the Protocol.

A new Protocol instance set itself up as the IO’s protocol (replacing any previous protocol).

Normally you won’t need to override this method. Override #on_open instead.



133
134
135
136
137
138
139
140
141
142
143
# File 'lib/iodine/protocol.rb', line 133

def initialize io
	@timeout ||= nil
	@send_locker = Mutex.new
	@locker = Mutex.new
	@io = io
	touch
	@locker.synchronize do
		Iodine.switch_protocol @io.to_io, self
		on_open
	end
end

Instance Attribute Details

#ioObject (readonly)

returns the IO object. If the connection uses SSL/TLS, this will return the SSLSocket (not a native IO object).

Using one of the Protocol methods #write, #read, #close is prefered over direct access.



26
27
28
# File 'lib/iodine/protocol.rb', line 26

def io
  @io
end

Class Method Details

.eachEnumerable

if a block is passed, than this method exceutes the block.

Returns:

  • (Enumerable)

    returns an Enumerable with all the active connections (instances of THIS Protocol or it’s children).



115
116
117
118
119
120
121
# File 'lib/iodine/protocol.rb', line 115

def self.each
	if block_given?
		Iodine.to_a.each {|p| yield(p) if p.is_a?(self) }
	else
		( Iodine.to_a.select {|p| p.is_a?(self) } ).each
	end
end

Instance Method Details

#callObject

Called by Iodine whenever there is data in the IO’s read buffer.

Normally you won’t need to override this method. Override #on_message instead.



148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/iodine/protocol.rb', line 148

def call
	return unless @locker.try_lock
	begin
		data = read
		if data
			on_message(data)
			data.clear
		end
	ensure
		@locker.unlock
	end
end

#closenil Also known as: disconnect

Closes the IO object.

Returns:

  • (nil)


69
70
71
72
# File 'lib/iodine/protocol.rb', line 69

def close
	@io.close unless @io.closed?
	nil
end

#closed?Boolean

Returns:

  • (Boolean)


74
75
76
# File 'lib/iodine/protocol.rb', line 74

def closed?
	@io.closed?
end

#idObject

returns the connection’s unique local ID as a Hex string.

This can be used locally but not across processes.



108
109
110
# File 'lib/iodine/protocol.rb', line 108

def id
	@id ||= @io.to_io.object_id.to_s(16)
end

#on_closeObject

This method is called AFTER the Protocol’s IO is closed - it will only be called once.



44
45
# File 'lib/iodine/protocol.rb', line 44

def on_close
end

#on_message(data) ⇒ Object

This method is called whenever data is received from the IO.



40
41
# File 'lib/iodine/protocol.rb', line 40

def on_message data
end

#on_openObject

This method is called whenever the Protocol is initialized - i.e.: a new connection is established or an old connection switches to this protocol.



37
38
# File 'lib/iodine/protocol.rb', line 37

def on_open
end

#on_shutdownObject

This method is called when the server’s shutdown process had started and BEFORE the Protocol’s IO is closed. It allows graceful shutdown for network protocols.



48
49
# File 'lib/iodine/protocol.rb', line 48

def on_shutdown
end

#pingObject

This method is called whenever a timeout has occurred. Either implement a ping or close the connection. The default implementation closes the connection.



53
54
55
# File 'lib/iodine/protocol.rb', line 53

def ping
	close
end

#read(size = 2_097_152) ⇒ Object

reads from the IO up to the specified number of bytes (defaults to ~2Mb).



79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/iodine/protocol.rb', line 79

def read size = 2_097_152
	touch
	ssl? ? read_ssl(size) : @io.recv_nonblock( size  )
	# @io.read_nonblock( size  ) # this one is a bit slower...
rescue OpenSSL::SSL::SSLErrorWaitReadable, IO::WaitReadable, IO::WaitWritable
	nil
rescue IOError, Errno::ECONNRESET
	close
rescue => e
	Iodine.warn "Protocol read error: #{e.class.name} #{e.message} (closing connection)"
	close
end

#set_timeout(seconds) ⇒ Object

Sets the timeout in seconds for IO activity (set timeout within #on_open).

After timeout is reached, #ping will be called. The connection will be closed if #ping returns ‘false` or `nil`.



31
32
33
# File 'lib/iodine/protocol.rb', line 31

def set_timeout seconds
	@timeout = seconds
end

#ssl?Boolean

returns true if the protocol is using an encrypted connection (the IO is an OpenSSL::SSL::SSLSocket).

Returns:

  • (Boolean)


62
63
64
# File 'lib/iodine/protocol.rb', line 62

def ssl?
	@io.is_a?(OpenSSL::SSL::SSLSocket) # io.npn_protocol
end

#timeout?(time) ⇒ Boolean

This method is used by Iodine to ask whether a timeout has occured.

Normally you won’t need to override this method. See #ping

Returns:

  • (Boolean)


165
166
167
# File 'lib/iodine/protocol.rb', line 165

def timeout? time
	ping if @timeout && !@send_locker.locked? && ( (time - @last_active) > @timeout )
end

#write(data) ⇒ Object

this method, writes data to the socket / io object.



93
94
95
96
97
98
99
100
101
102
103
# File 'lib/iodine/protocol.rb', line 93

def write data
	begin
		@send_locker.synchronize do
			r = @io.write data
			touch
			r
		end
	rescue => e
		close
	end
end