Connection to a websocket client

Can be used to communicate with a client

Introduced properties

fun can_read(timeout: Int): Bool

websocket :: WebsocketConnection :: can_read

Is there some data available to be read ?
fun closed: Bool

websocket :: WebsocketConnection :: closed

Is self closed?
protected fun closed=(closed: Bool)

websocket :: WebsocketConnection :: closed=

Is self closed?
fun connected: Bool

websocket :: WebsocketConnection :: connected

Checks if a connection to a client is available
fun frame_type: Int

websocket :: WebsocketConnection :: frame_type

Type of the current frame
protected fun frame_type=(frame_type: Int)

websocket :: WebsocketConnection :: frame_type=

Type of the current frame
protected fun read_http_frame(buf: Buffer): String

websocket :: WebsocketConnection :: read_http_frame

Read an HTTP frame

Redefined properties

redef type SELF: WebsocketConnection

websocket $ WebsocketConnection :: SELF

Type of this instance, automatically specialized in every class
redef fun close

websocket $ WebsocketConnection :: close

Disconnect from a client
redef fun is_writable: Bool

websocket $ WebsocketConnection :: is_writable

Can the stream be used to write
redef fun poll_in: Bool

websocket $ WebsocketConnection :: poll_in

Is there something to read? (without blocking)
redef fun raw_read_byte: Int

websocket $ WebsocketConnection :: raw_read_byte

Read a byte directly from the underlying stream, without
redef fun raw_read_bytes(ns: CString, len: Int): Int

websocket $ WebsocketConnection :: raw_read_bytes

Read at most max bytes from the underlying stream into buf,
redef fun write(msg: Text)

websocket $ WebsocketConnection :: write

Write a string
redef fun write_bytes_from_cstring(ns: CString, len: Int)

websocket $ WebsocketConnection :: write_bytes_from_cstring

Write len bytes from ns

All properties

fun !=(other: nullable Object): Bool

core :: Object :: !=

Have self and other different values?
fun ==(other: nullable Object): Bool

core :: Object :: ==

Have self and other the same value?
type CLASS: Class[SELF]

core :: Object :: CLASS

The type of the class of self.
type SELF: Object

core :: Object :: SELF

Type of this instance, automatically specialized in every class
fun append_line_to(s: Buffer)

core :: Reader :: append_line_to

Read a string until the end of the line and append it to s.
fun big_endian: Bool

binary :: BinaryStream :: big_endian

Use the big-endian convention? otherwise use little-endian.
fun big_endian=(big_endian: Bool)

binary :: BinaryStream :: big_endian=

Use the big-endian convention? otherwise use little-endian.
fun can_read(timeout: Int): Bool

websocket :: WebsocketConnection :: can_read

Is there some data available to be read ?
protected fun class_factory(name: String): CLASS

core :: Object :: class_factory

Implementation used by get_class to create the specific class.
fun class_name: String

core :: Object :: class_name

The class name of the object.
abstract fun close

core :: Stream :: close

close the stream
fun closed: Bool

websocket :: WebsocketConnection :: closed

Is self closed?
protected fun closed=(closed: Bool)

websocket :: WebsocketConnection :: closed=

Is self closed?
fun codec: Codec

core :: Stream :: codec

Codec used to transform raw data to text
fun codec=(c: Codec)

core :: Stream :: codec=

Change the codec for this stream.
fun connected: Bool

websocket :: WebsocketConnection :: connected

Checks if a connection to a client is available
init defaultinit(origin: STREAM)

core :: Protocol :: defaultinit

fun deserialize_msgpack(static_type: nullable String): nullable Object

core :: Reader :: deserialize_msgpack

Deserialize full Nit nullable Object from MessagePack formated data
fun each_line: LineIterator

core :: Reader :: each_line

Return an iterator that read each line.
fun eof: Bool

core :: Reader :: eof

Is there something to read.
fun finish

core :: Stream :: finish

Post-work hook.
fun frame_type: Int

websocket :: WebsocketConnection :: frame_type

Type of the current frame
protected fun frame_type=(frame_type: Int)

websocket :: WebsocketConnection :: frame_type=

Type of the current frame
fun get_class: CLASS

core :: Object :: get_class

The meta-object representing the dynamic type of self.
fun hash: Int

core :: Object :: hash

The hash code of the object.
init init

core :: Object :: init

fun inspect: String

core :: Object :: inspect

Developer readable representation of self.
protected fun inspect_head: String

core :: Object :: inspect_head

Return "CLASSNAME:#OBJECTID".
intern fun is_same_instance(other: nullable Object): Bool

core :: Object :: is_same_instance

Return true if self and other are the same instance (i.e. same identity).
fun is_same_serialized(other: nullable Object): Bool

core :: Object :: is_same_serialized

Is self the same as other in a serialization context?
intern fun is_same_type(other: Object): Bool

core :: Object :: is_same_type

Return true if self and other have the same dynamic type.
abstract fun is_writable: Bool

core :: Writer :: is_writable

Can the stream be used to write
fun last_error: nullable IOError

core :: Stream :: last_error

Error produced by the file stream
protected fun last_error=(last_error: nullable IOError)

core :: Stream :: last_error=

Error produced by the file stream
protected fun lookahead: CString

core :: Stream :: lookahead

Lookahead buffer for codecs
protected fun lookahead=(lookahead: CString)

core :: Stream :: lookahead=

Lookahead buffer for codecs
protected fun lookahead_capacity: Int

core :: Stream :: lookahead_capacity

Capacity of the lookahead
protected fun lookahead_capacity=(lookahead_capacity: Int)

core :: Stream :: lookahead_capacity=

Capacity of the lookahead
protected fun lookahead_length: Int

core :: Stream :: lookahead_length

Current occupation of the lookahead
protected fun lookahead_length=(lookahead_length: Int)

core :: Stream :: lookahead_length=

Current occupation of the lookahead
intern fun object_id: Int

core :: Object :: object_id

An internal hash code for the object based on its identity.
protected fun origin=(origin: STREAM)

core :: Protocol :: origin=

fun output

core :: Object :: output

Display self on stdout (debug only).
intern fun output_class_name

core :: Object :: output_class_name

Display class name on stdout (debug only).
abstract fun poll_in: Bool

core :: PollableReader :: poll_in

Is there something to read? (without blocking)
protected abstract fun raw_read_byte: Int

core :: Reader :: raw_read_byte

Read a byte directly from the underlying stream, without
protected fun raw_read_bytes(buf: CString, max: Int): Int

core :: Reader :: raw_read_bytes

Read at most max bytes from the underlying stream into buf,
fun read(i: Int): String

core :: Reader :: read

Reads a String of at most i length
fun read_all: String

core :: Reader :: read_all

Read all the stream until the eof.
fun read_all_bytes: Bytes

core :: Reader :: read_all_bytes

Read all the stream until the eof.
fun read_bits: Array[Bool]

core :: Reader :: read_bits

Get an Array of 8 Bool by reading a single byte
fun read_block: String

core :: Reader :: read_block

Read the length as a 64 bits integer, then the content of the block
fun read_bool: Bool

core :: Reader :: read_bool

Read a single byte and return true if its value is different than 0
fun read_byte: Int

core :: Reader :: read_byte

Reads a byte. Returns a negative value on error
fun read_bytes(max: Int): Bytes

core :: Reader :: read_bytes

Reads up to max bytes from source
fun read_bytes_to_cstring(bytes: CString, max: Int): Int

core :: Reader :: read_bytes_to_cstring

Reads up to max bytes from source and stores them in bytes
fun read_char: nullable Char

core :: Reader :: read_char

Reads a character. Returns null on EOF or timeout
fun read_double: Float

core :: Reader :: read_double

Read a floating point on 64 bits and return it as a Float
fun read_float: Float

core :: Reader :: read_float

Read a floating point on 32 bits and return it as a Float
protected fun read_http_frame(buf: Buffer): String

websocket :: WebsocketConnection :: read_http_frame

Read an HTTP frame
fun read_int64: Int

core :: Reader :: read_int64

Read a signed integer on 64 bits and return is an Int
fun read_line: String

core :: Reader :: read_line

Read a string until the end of the line.
fun read_lines: Array[String]

core :: Reader :: read_lines

Read all the lines until the eof.
fun read_msgpack: nullable Serializable

core :: Reader :: read_msgpack

Read the next MessagePack object and return it as a simple Nit object
fun read_nonwhitespace: nullable Char

core :: Reader :: read_nonwhitespace

Skip whitespace characters (if any) then return the following non-whitespace character.
fun read_string: String

core :: Reader :: read_string

Read a null terminated string
fun read_word: String

core :: Reader :: read_word

Read the next sequence of non whitespace characters.
fun serialization_hash: Int

core :: Object :: serialization_hash

Hash value use for serialization
fun serialize_msgpack(value: nullable Serializable, plain: nullable Bool)

core :: Writer :: serialize_msgpack

Serialize value in MessagePack format
protected fun set_codec(codec: Codec)

core :: Stream :: set_codec

Codec used to transform raw data to text
fun start

core :: Stream :: start

Pre-work hook.
intern fun sys: Sys

core :: Object :: sys

Return the global sys object, the only instance of the Sys class.
abstract fun to_jvalue(env: JniEnv): JValue

core :: Object :: to_jvalue

fun to_s: String

core :: Object :: to_s

User readable representation of self.
abstract fun write(s: Text)

core :: Writer :: write

Write a string
fun write_bits(bits: Bool...)

core :: Writer :: write_bits

Write up to 8 Bool in a byte
fun write_block(text: Text)

core :: Writer :: write_block

Write the length as a 64 bits integer, then the content of text
fun write_bool(value: Bool)

core :: Writer :: write_bool

Write a boolean value on a byte, using 0 for false and 1 for true
protected fun write_buffer: CString

core :: Stream :: write_buffer

Buffer for writing data to a stream
protected fun write_buffer=(write_buffer: CString)

core :: Stream :: write_buffer=

Buffer for writing data to a stream
abstract fun write_byte(value: Int)

core :: Writer :: write_byte

Write a single byte
fun write_bytes(s: Bytes)

core :: Writer :: write_bytes

Write bytes from s
abstract fun write_bytes_from_cstring(ns: CString, len: Int)

core :: Writer :: write_bytes_from_cstring

Write len bytes from ns
fun write_char(c: Char)

core :: Writer :: write_char

Write a single char
fun write_double(value: Float)

core :: Writer :: write_double

Write a floating point value on 64 bits
fun write_float(value: Float)

core :: Writer :: write_float

Write a floating point value on 32 bits
fun write_int64(value: Int)

core :: Writer :: write_int64

Write value as a signed integer on 64 bits
fun write_msgpack_array(len: Int)

core :: Writer :: write_msgpack_array

Write an array header for len items in the shortest possible MessagePack array format
fun write_msgpack_array16(len: Int)

core :: Writer :: write_msgpack_array16

Write an array header for len items, max of 0xFFFF items
fun write_msgpack_array32(len: Int)

core :: Writer :: write_msgpack_array32

Write an array header for len items, max of 0xFFFF_FFFF items
fun write_msgpack_bin(data: Bytes)

core :: Writer :: write_msgpack_bin

Write data in the shortest possible MessagePack bin format
fun write_msgpack_bin16(data: Bytes)

core :: Writer :: write_msgpack_bin16

Write data in bin16 format, max of 0xFFFF bytes
fun write_msgpack_bin32(data: Bytes)

core :: Writer :: write_msgpack_bin32

Write data in bin32 format, max of 0xFFFF_FFFF bytes
fun write_msgpack_bin8(data: Bytes)

core :: Writer :: write_msgpack_bin8

Write data in bin8 format, max of 0xFF bytes
fun write_msgpack_bool(bool: Bool)

core :: Writer :: write_msgpack_bool

Write bool in MessagePack format
fun write_msgpack_double(value: Float)

core :: Writer :: write_msgpack_double

Write value as a MessagePack double
fun write_msgpack_ext(typ: Int, bytes: Bytes)

core :: Writer :: write_msgpack_ext

Write an application-specific extension for typ and bytes in the shortest possible MessagePack ext format
fun write_msgpack_ext16(typ: Int, len: Int)

core :: Writer :: write_msgpack_ext16

Write the header for an application-specific extension of len data bytes
fun write_msgpack_ext32(typ: Int, len: Int)

core :: Writer :: write_msgpack_ext32

Write the header for an application-specific extension of len data bytes
fun write_msgpack_ext8(typ: Int, len: Int)

core :: Writer :: write_msgpack_ext8

Write the header for an application-specific extension of len data bytes
fun write_msgpack_fixarray(len: Int)

core :: Writer :: write_msgpack_fixarray

Write an array header for len items, max of 0x0F items
fun write_msgpack_fixext1(typ: Int)

core :: Writer :: write_msgpack_fixext1

Write the header for an application-specific extension of one data byte
fun write_msgpack_fixext16(typ: Int)

core :: Writer :: write_msgpack_fixext16

Write the header for an application-specific extension of 16 data bytes
fun write_msgpack_fixext2(typ: Int)

core :: Writer :: write_msgpack_fixext2

Write the header for an application-specific extension of two data bytes
fun write_msgpack_fixext4(typ: Int)

core :: Writer :: write_msgpack_fixext4

Write the header for an application-specific extension of 4 data bytes
fun write_msgpack_fixext8(typ: Int)

core :: Writer :: write_msgpack_fixext8

Write the header for an application-specific extension of 8 data bytes
fun write_msgpack_fixint(value: Int)

core :: Writer :: write_msgpack_fixint

Write value as a single byte with metadata
fun write_msgpack_fixmap(len: Int)

core :: Writer :: write_msgpack_fixmap

Write a map header for len key/value pairs, max of 0x0F pairs
fun write_msgpack_fixstr(text: Text)

core :: Writer :: write_msgpack_fixstr

Write text in fixstr format, max of 0x1F bytes
fun write_msgpack_float(value: Float)

core :: Writer :: write_msgpack_float

Write value as a MessagePack float (losing precision)
fun write_msgpack_int(value: Int)

core :: Writer :: write_msgpack_int

Write the integer value either as the shortest possible MessagePack int
fun write_msgpack_int16(value: Int)

core :: Writer :: write_msgpack_int16

Write value over two signed bytes, following 1 metadata byte
fun write_msgpack_int32(value: Int)

core :: Writer :: write_msgpack_int32

Write value over 4 signed bytes, following 1 metadata byte
fun write_msgpack_int64(value: Int)

core :: Writer :: write_msgpack_int64

Write value over 8 signed bytes, following 1 metadata byte
fun write_msgpack_int8(value: Int)

core :: Writer :: write_msgpack_int8

Write value over one signed byte, following 1 metadata byte
fun write_msgpack_map(len: Int)

core :: Writer :: write_msgpack_map

Write a map header for len keys/value pairs in the shortest possible MessagePack map format
fun write_msgpack_map16(len: Int)

core :: Writer :: write_msgpack_map16

Write a map header for len key/value pairs, max of 0xFFFF pairs
fun write_msgpack_map32(len: Int)

core :: Writer :: write_msgpack_map32

Write a map header for len key/value pairs, max of 0xFFFF_FFFF pairs
fun write_msgpack_null

core :: Writer :: write_msgpack_null

Write null, or nil, in MessagePack format
fun write_msgpack_str(text: Text)

core :: Writer :: write_msgpack_str

Write text in the shortest possible MessagePack format
fun write_msgpack_str16(text: Text)

core :: Writer :: write_msgpack_str16

Write text in str16 format, max of 0xFFFF bytes
fun write_msgpack_str32(text: Text)

core :: Writer :: write_msgpack_str32

Write text in str32 format, max of 0xFFFF_FFFF bytes
fun write_msgpack_str8(text: Text)

core :: Writer :: write_msgpack_str8

Write text in str8 format, max of 0xFF bytes
fun write_msgpack_uint16(value: Int)

core :: Writer :: write_msgpack_uint16

Write value over two unsigned bytes, following 1 metadata byte
fun write_msgpack_uint32(value: Int)

core :: Writer :: write_msgpack_uint32

Write value over 4 unsigned bytes, following 1 metadata byte
fun write_msgpack_uint64(value: Int)

core :: Writer :: write_msgpack_uint64

Write value over 8 unsigned bytes, following 1 metadata byte
fun write_msgpack_uint8(value: Int)

core :: Writer :: write_msgpack_uint8

Write value over one unsigned byte, following 1 metadata byte
fun write_string(text: Text)

core :: Writer :: write_string

Write text as a null terminated string
package_diagram websocket::WebsocketConnection WebsocketConnection core::DuplexProtocol DuplexProtocol websocket::WebsocketConnection->core::DuplexProtocol core::PollableReader PollableReader websocket::WebsocketConnection->core::PollableReader core::Duplex Duplex core::DuplexProtocol->core::Duplex core::WriterProtocol WriterProtocol core::DuplexProtocol->core::WriterProtocol core::ReaderProtocol ReaderProtocol core::DuplexProtocol->core::ReaderProtocol core::Reader Reader core::PollableReader->core::Reader ...core::Duplex ... ...core::Duplex->core::Duplex ...core::WriterProtocol ... ...core::WriterProtocol->core::WriterProtocol ...core::ReaderProtocol ... ...core::ReaderProtocol->core::ReaderProtocol ...core::Reader ... ...core::Reader->core::Reader

Ancestors

abstract class BinaryStream

binary :: BinaryStream

A stream of binary data
abstract class Duplex

core :: Duplex

A Stream that can be written to and read from
interface Object

core :: Object

The root of the class hierarchy.
class Protocol

core :: Protocol

Stream class used as a Decorator over a stream
abstract class Reader

core :: Reader

A Stream that can be read from
class ReaderProtocol

core :: ReaderProtocol

Reader decorator over a read-capable stream
abstract class Stream

core :: Stream

Any kind of stream to read/write/both to or from a source
abstract class Writer

core :: Writer

A Stream that can be written to
class WriterProtocol

core :: WriterProtocol

Writer decorator over a write-capable stream

Parents

class DuplexProtocol

core :: DuplexProtocol

Reader/Writer decorator over a duplex-capable stream
abstract class PollableReader

core :: PollableReader

Reader capable of declaring if readable without blocking

Class definitions

websocket $ WebsocketConnection
# Connection to a websocket client
#
# Can be used to communicate with a client
class WebsocketConnection
	super DuplexProtocol
	super PollableReader

	redef type STREAM: TCPStream

	# Does the current frame have a mask?
	private var has_mask = false

	# Mask with which to XOR input data
	private var mask = new CString(4)

	# Offset of the mask to use when decoding input data
	private var mask_offset = -1

	# Length of the current frame
	private var frame_length = -1

	# Position in current frame
	private var frame_cursor = -1

	# Type of the current frame
	var frame_type = -1

	# Is `self` closed?
	var closed = false

	init do
		var headers = parse_handshake
		var resp = handshake_response(headers)

		origin.write(resp)
	end

	# Disconnect from a client
	redef fun close do
		origin.close
		closed = true
	end

	# Ping response message
	private fun pong_msg: Bytes do return once b"\x8a\x00"

	# Parse the input handshake sent by the client
	# See RFC 6455 for information
	private fun parse_handshake: Map[String,String]
	do
		var recved = read_http_frame(new FlatBuffer)
		var headers = recved.split("\r\n")
		var headmap = new HashMap[String,String]
		for i in headers do
			var temp_head = i.split(" ")
			var head = temp_head.shift
			if head.is_empty or head.length == 1 then continue
			if head.chars.last == ':' then
				head = head.substring(0, head.length - 1)
			end
			var body = temp_head.join(" ")
			headmap[head] = body
		end
		return headmap
	end

	# Generate a handshake response
	private fun handshake_response(heads: Map[String,String]): String
	do
		var resp_map = new HashMap[String,String]
		resp_map["HTTP/1.1"] = "101 Switching Protocols"
		resp_map["Upgrade:"] = "websocket"
		resp_map["Connection:"] = "Upgrade"
		var key = heads["Sec-WebSocket-Key"]
		key += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
		key = key.sha1.encode_base64.to_s
		resp_map["Sec-WebSocket-Accept:"] = key
		var resp = resp_map.join("\r\n", " ")
		resp += "\r\n\r\n"
		return resp
	end

	# Frame a text message to be sent to a client
	private fun frame_message(msg: Text): Bytes
	do
		var ans_buffer = new Bytes.with_capacity(msg.byte_length + 2)
		# Flag for final frame set to 1
		# opcode set to 1 (for text)
		ans_buffer.add(129)
		if msg.length < 126 then
			ans_buffer.add(msg.length)
		end
		if msg.length >= 126 and msg.length <= 65535 then
			ans_buffer.add(126)
			ans_buffer.add(msg.length >> 8)
			ans_buffer.add(msg.length)
		end
		msg.append_to_bytes(ans_buffer)
		return ans_buffer
	end

	# Read an HTTP frame
	protected fun read_http_frame(buf: Buffer): String
	do
		var ln = origin.read_line
		buf.append ln
		buf.append "\r\n"
		if buf.has_suffix("\r\n\r\n") then return buf.to_s
		return read_http_frame(buf)
	end

	# Get a frame's information
	private fun read_frame_info do
		var fst_byte = origin.read_byte
		var snd_byte = origin.read_byte
		if fst_byte < 0 or snd_byte < 0 then
			last_error = new IOError("Error: bad frame")
			close
			return
		end
		# First byte in msg is formatted this way :
		# |(fin - 1bit)|(RSV1 - 1bit)|(RSV2 - 1bit)|(RSV3 - 1bit)|(opcode - 4bits)
		# fin = Flag indicating if current frame is the last one for the current message
		# RSV1/2/3 = Extension flags, unsupported
		# Opcode values :
		#	%x0 denotes a continuation frame
		#	%x1 denotes a text frame
		#	%x2 denotes a binary frame
		#	%x3-7 are reserved for further non-control frames
		#	%x8 denotes a connection close
		#	%x9 denotes a ping
		#	%xA denotes a pong
		#	%xB-F are reserved for further control frames
		var opcode = fst_byte & 0b0000_1111
		if opcode == 9 then
			origin.write_bytes(pong_msg)
			return
		end
		if opcode == 8 then
			close
			return
		end
		frame_type = opcode
		# Second byte is formatted this way :
		# |(mask - 1bit)|(payload length - 7 bits)
		# As specified, if the payload length is 126 or 127
		# The next 16 or 64 bits contain an extended payload length
		var mask_flag = snd_byte & 0b1000_0000
		var len = snd_byte & 0b0111_1111
		var payload_ext_len = 0
		if len == 126 then
			var tmp = origin.read_bytes(2)
			if tmp.length != 2 then
				last_error = new IOError("Error: received interrupted frame")
				origin.close
				return
			end
			payload_ext_len += tmp[0].to_i << 8
			payload_ext_len += tmp[1].to_i
		else if len == 127 then
			var tmp = origin.read_bytes(8)
			if tmp.length != 8 then
				last_error = new IOError("Error: received interrupted frame")
				origin.close
				return
			end
			for i in [0 .. 8[ do
				payload_ext_len += tmp[i].to_i << (8 * (7 - i))
			end
		end
		if mask_flag != 0 then
			origin.read_bytes_to_cstring(mask, 4)
			has_mask = true
		else
			mask.memset(0, 4)
			has_mask = false
		end
		if payload_ext_len != 0 then
			len = payload_ext_len
		end
		frame_length = len
		frame_cursor = 0
	end

	redef fun raw_read_byte do
		while not closed and frame_cursor >= frame_length do
			read_frame_info
		end
		if closed then return -1
		var b = origin.read_byte
		if b >= 0 then
			frame_cursor += 1
		end
		return b
	end

	redef fun raw_read_bytes(ns, len) do
		while not closed and frame_cursor >= frame_length do
			read_frame_info
		end
		if closed then return -1
		var available = frame_length - frame_cursor
		var to_rd = len.min(available)
		var rd = origin.read_bytes_to_cstring(ns, to_rd)
		if rd < 0 then
			close
			return 0
		end
		if has_mask then
			ns.xor(mask, rd, 4, mask_offset)
			mask_offset = rd % 4
		end
		frame_cursor += rd
		return rd
	end

	# Checks if a connection to a client is available
	fun connected: Bool do return not closed and origin.connected

	redef fun write_bytes_from_cstring(ns, len) do
		origin.write_bytes(frame_message(ns.to_s_unsafe(len)))
	end

	redef fun write(msg) do origin.write_bytes(frame_message(msg))

	redef fun is_writable do return origin.connected

	# Is there some data available to be read ?
	fun can_read(timeout: Int): Bool do return  not closed and origin.ready_to_read(timeout)

	redef fun poll_in do return origin.poll_in
end
lib/websocket/websocket.nit:63,1--294,3