From: Jean Privat Date: Wed, 14 Jan 2015 01:16:51 +0000 (-0500) Subject: Merge: Websockets X-Git-Tag: v0.7.1~30 X-Git-Url: http://nitlanguage.org?hp=c15b6f7b27691aebfd663b7890f36a3312f15814 Merge: Websockets Fixed websockets with the new and improved sockets. While I'm at it, changed a bit the way it works, the old one was a bit (a lot ?) quick and dirty, so here's that. Pull-Request: #1096 Reviewed-by: Jean Privat Reviewed-by: Alexandre Terrasa --- diff --git a/lib/socket/socket.nit b/lib/socket/socket.nit index 77acfbe..d9bc5a3 100644 --- a/lib/socket/socket.nit +++ b/lib/socket/socket.nit @@ -111,7 +111,7 @@ class TCPStream fun ready_to_read(timeout: Int): Bool do if _buffer_pos < _buffer.length then return true - if eof then return false + if end_reached then return false var events = [new NativeSocketPollValues.pollin] return pollin(events, timeout).length != 0 end @@ -172,6 +172,7 @@ class TCPStream if closed then return if socket.close >= 0 then closed = true + end_reached = true end end diff --git a/lib/standard/string.nit b/lib/standard/string.nit index f1d3288..2c46179 100644 --- a/lib/standard/string.nit +++ b/lib/standard/string.nit @@ -1092,15 +1092,19 @@ class FlatString from = 0 end - var realFrom = index_from + from + var new_from = index_from + from - if (realFrom + count) > index_to then return new FlatString.with_infos(items, index_to - realFrom + 1, realFrom, index_to) + if (new_from + count) > index_to then + var new_len = index_to - new_from + 1 + if new_len <= 0 then return empty + return new FlatString.with_infos(items, new_len, new_from, index_to) + end - if count == 0 then return empty + if count <= 0 then return empty - var to = realFrom + count - 1 + var to = new_from + count - 1 - return new FlatString.with_infos(items, to - realFrom + 1, realFrom, to) + return new FlatString.with_infos(items, to - new_from + 1, new_from, to) end redef fun empty do return "".as(FlatString) diff --git a/lib/websocket/examples/websocket_server.nit b/lib/websocket/examples/websocket_server.nit index 089df1b..8b4584c 100644 --- a/lib/websocket/examples/websocket_server.nit +++ b/lib/websocket/examples/websocket_server.nit @@ -19,7 +19,7 @@ module websocket_server import websocket -var sock = new WebSocket(8088, 1) +var sock = new WebSocketListener(8088, 1) var msg: String @@ -27,19 +27,21 @@ if sock.listener.closed then print sys.errno.strerror end -sock.accept +var cli: TCPStream -while not sock.listener.closed do - if not sock.connected then sock.accept - if sys.stdin.poll_in then - msg = gets - printn "Received message : {msg}" - if msg == "exit" then sock.close - if msg == "disconnect" then sock.disconnect_client - sock.write(msg) - end - if sock.can_read(10) then - msg = sock.read_line - if msg != "" then print msg +while not sock.closed do + cli = sock.accept + while cli.connected do + if sys.stdin.poll_in then + msg = gets + printn "Received message : {msg}" + if msg == "disconnect" then cli.close + cli.write(msg) + end + if cli.can_read(10) then + msg = "" + while cli.can_read(0) do msg += cli.read(100) + if msg != "" then print msg + end end end diff --git a/lib/websocket/websocket.nit b/lib/websocket/websocket.nit index ebf3f49..7e4d329 100644 --- a/lib/websocket/websocket.nit +++ b/lib/websocket/websocket.nit @@ -24,14 +24,11 @@ import base64 intrude import standard::stream -# Websocket compatible server, works as an extra layer to the original Sockets -class WebSocket - super BufferedIStream - super OStream - super PollableIStream - - # Client connection to the server - var client: TCPStream +# Websocket compatible listener +# +# Produces Websocket client-server connections +class WebSocketListener + super Socket # Socket listening to connections on a defined port var listener: TCPServer @@ -39,39 +36,50 @@ class WebSocket # Creates a new Websocket server listening on given port with `max_clients` slots available init(port: Int, max_clients: Int) do - _buffer = new FlatBuffer - _buffer_pos = 0 listener = new TCPServer(port) listener.listen max_clients end - # Accept an incoming connection and initializes the handshake - fun accept + # Accepts an incoming connection + fun accept: WebsocketConnection do assert not listener.closed var client = listener.accept assert client != null - self.client = client + return new WebsocketConnection(listener.port, "", client) + end + + # Stop listening for incoming connections + fun close + do + listener.close + end +end + +# Connection to a websocket client +# +# Can be used to communicate with a client +class WebsocketConnection + super TCPStream + + init do + _buffer = new FlatBuffer + _buffer_pos = 0 var headers = parse_handshake var resp = handshake_response(headers) client.write(resp) end - # Disconnect from a client - fun disconnect_client - do - client.close - end + # Client connection to the server + var client: TCPStream - # Disconnects the client if one is connected - # And stops the server + # Disconnect from a client redef fun close do client.close - listener.close end # Parses the input handshake sent by the client @@ -132,9 +140,9 @@ class WebSocket # Reads an HTTP frame protected fun read_http_frame(buf: Buffer): String do - client.append_line_to(buf) - buf.chars.add('\n') - if buf.has_substring("\r\n\r\n", buf.length - 4) then return buf.to_s + buf.append client.read_line + buf.append("\r\n") + if buf.has_suffix("\r\n\r\n") then return buf.to_s return read_http_frame(buf) end @@ -222,9 +230,9 @@ class WebSocket end # Checks if a connection to a client is available - fun connected: Bool do return client.connected + redef fun connected do return client.connected - redef fun write(msg: Text) + redef fun write(msg) do client.write(frame_message(msg.to_s)) end @@ -238,7 +246,7 @@ class WebSocket unpad_message end - redef fun end_reached do return _buffer_pos >= _buffer.length and client.eof + redef fun end_reached do return client._buffer_pos >= client._buffer.length and client.end_reached # Is there some data available to be read ? fun can_read(timeout: Int): Bool do return client.ready_to_read(timeout) diff --git a/src/interpreter/debugger_socket.nit b/src/interpreter/debugger_socket.nit index 5675a7d..50f98c7 100644 --- a/src/interpreter/debugger_socket.nit +++ b/src/interpreter/debugger_socket.nit @@ -97,15 +97,16 @@ redef class ModelBuilder sock.close sys.set_io(ns,ns,ns) else if self.toolcontext.opt_websocket_mode.value then - var websock = new WebSocket(toolcontext.opt_debug_port.value, 1) - websock.accept - sys.set_io(websock,websock,websock) + var websock = new WebSocketListener(toolcontext.opt_debug_port.value, 1) + var cli = websock.accept + websock.close + sys.set_io(cli,cli,cli) end end fun close_stdstreams do - if sys.stdin isa WebSocket or sys.stdin isa TCPStream then + if sys.stdin isa TCPStream then sys.stdin.close sys.stdout.close sys.stderr.close @@ -118,6 +119,6 @@ redef class Sys do self.stdin = istream self.stdout = ostream - self.stderr = ostream + self.stderr = errstream end end diff --git a/src/interpreter/primitive_types.nit b/src/interpreter/primitive_types.nit index 127815b..0de93d3 100644 --- a/src/interpreter/primitive_types.nit +++ b/src/interpreter/primitive_types.nit @@ -15,22 +15,23 @@ module primitive_types intrude import standard::file +intrude import standard::string # Wrapper for `NativeFile` class PrimitiveNativeFile - var file: FStream + var file: IOS init native_stdin do - file = new IFStream.from_fd(0) + file = sys.stdin end init native_stdout do - file = new OFStream.from_fd(1) + file = sys.stdout end init native_stderr do - file = new OFStream.from_fd(2) + file = sys.stderr end init io_open_read(path: String) do @@ -41,19 +42,42 @@ class PrimitiveNativeFile file = new OFStream.open(path.to_s) end - fun address_is_null: Bool do return file._file.address_is_null + fun address_is_null: Bool do + if file isa FStream then return file.as(FStream)._file.address_is_null + return false + end - fun io_read(buf: NativeString, len: Int): Int do return file._file.io_read(buf, len) + fun io_read(buf: NativeString, len: Int): Int do + if file isa FStream then return file.as(FStream)._file.io_read(buf, len) + var str = file.as(IStream).read(len) + str.to_cstring.copy_to(buf, str.length, 0, 0) + return str.length + end - fun io_write(buf: NativeString, len: Int): Int do return file._file.io_write(buf, len) + fun io_write(buf: NativeString, len: Int): Int do + if file isa FStream then return file.as(FStream)._file.io_write(buf, len) + file.as(OStream).write(buf.to_s_with_length(len)) + return len + end - fun io_close: Int do return file._file.io_close + fun io_close: Int do + if file isa FStream then return file.as(FStream)._file.io_close + file.close + return 0 + end - fun fileno: Int do return file._file.fileno + fun fileno: Int do + if file isa FStream then return file.as(FStream)._file.fileno + return 0 + end - fun flush: Int do return file._file.flush + fun flush: Int do + if file isa FStream then return file.as(FStream)._file.flush + return 0 + end fun set_buffering_type(size, mode: Int): Int do - return file._file.set_buffering_type(size, mode) + if file isa FStream then return file.as(FStream)._file.set_buffering_type(size, mode) + return 0 end end