Still as part of the Stream refactor, this commit removes the BufferedReader dependency for FileReader.
Note: last commit only to review, the previous one is from #2648
Pull-Request: #2649
Reviewed-by: Jean Privat <jean@pryen.org>
# =============== Bitmap header ================
for x in [0..13] do
var b = fileReader.read_byte
- if b == null then
- return
- end
- bitmap_header[x] = b.to_i
+ if b < 0 then return
+ bitmap_header[x] = b
end
self.file_size = get_value(bitmap_header.subarray(2, 4))
self.data_offset = get_value(bitmap_header.subarray(10, 4))
# =============== DIB header ================
for x in [0..39] do
var b = fileReader.read_byte
- if b == null then return
- dib_header[x] = b.to_i
+ if b < 0 then return
+ dib_header[x] = b
end
var dib_size = get_value(dib_header.subarray(0, 4))
# only support BITMAPINFOHEADER
end
redef fun decode_string(ns, len) do
+ assert len >= 0
var ret = ns.to_s_unsafe(len, copy=false)
var rit = ret.as(FlatString).items
if rit == ns then
import re
import bytes
import fixed_ints
+import protocol
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# This file is free software, which comes along with NIT. This software is
+# distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE. You can modify it is you want, provided this header
+# is kept unaltered, and a notification of the changes is added.
+# You are allowed to redistribute it and sell it, alone or is a part of
+# another product.
+
+import stream
+
+# Stream class used as a Decorator over a stream
+class Protocol
+ super Stream
+
+ type STREAM: Stream
+
+ var origin: STREAM
+
+ redef fun close do origin.close
+end
+
+# Reader decorator over a read-capable stream
+class ReaderProtocol
+ super Reader
+ super Protocol
+
+ redef type STREAM: Reader
+
+ redef fun raw_read_byte do
+ return origin.read_byte
+ end
+
+ redef fun raw_read_bytes(ns, len) do
+ return origin.read_bytes_to_cstring(ns, len)
+ end
+end
+
+# Writer decorator over a write-capable stream
+class WriterProtocol
+ super Writer
+ super Protocol
+
+ redef type STREAM: Writer
+
+ redef fun write_byte(b) do
+ origin.write_byte(b)
+ end
+
+ redef fun write_bytes_from_cstring(ns, len) do
+ origin.write_bytes_from_cstring(ns, len)
+ end
+end
+
+# Reader/Writer decorator over a duplex-capable stream
+class DuplexProtocol
+ super Duplex
+ super WriterProtocol
+ super ReaderProtocol
+
+ redef type STREAM: Duplex
+end
# Reads a String of at most `i` length
fun read(i: Int): String do
+ assert i >= 0
var cs = new CString(i)
var rd = read_bytes_to_cstring(cs, i)
+ if rd < 0 then return ""
return codec.decode_string(cs, rd)
end
# Reads up to `max` bytes from source
fun read_bytes(max: Int): Bytes do
+ assert max >= 0
var cs = new CString(max)
var rd = read_bytes_to_cstring(cs, max)
return new Bytes(cs, rd, max)
# instead of a TCP connection and DNS hostname resolution.
var unix_socket_path: nullable String is writable
+ # The HTTP method, GET by default
+ #
+ # Must be a capitalized string with request name complying with RFC7231
+ var method: String = "GET" is optional, writable
+
# Execute HTTP request
#
# By default, the response body is returned in an instance of `CurlResponse`.
# This behavior can be customized by setting a custom `delegate`.
fun execute: CurlResponse
do
+ # Reset libcurl parameters as the lib is shared and options
+ # might affect requests from one another.
+ self.curl.native = new NativeCurl.easy_init
if not self.curl.is_ok then return answer_failure(0, "Curl instance is not correctly initialized")
var success_response = new CurlResponseSuccess
var callback_receiver: CurlCallbacks = success_response
- if self.delegate != null then callback_receiver = self.delegate.as(not null)
+ var err : CURLCode
+
+ # Prepare request
+ err = prepare_request(callback_receiver)
+ if not err.is_ok then return answer_failure(err.to_i, err.to_s)
+
+ # Perform request
+ var err_resp = perform
+ if err_resp != null then return err_resp
+
+ var st_code = self.curl.native.easy_getinfo_long(new CURLInfoLong.response_code)
+ if not st_code == null then success_response.status_code = st_code
+ self.curl.native.easy_clean
+
+ return success_response
+ end
+
+ # Internal function that sets cURL options and request' parameters
+ private fun prepare_request(callback_receiver: CurlCallbacks) : CURLCode
+ do
+ var err
+
+ # cURL options and delegates
+ err = set_curl_options
+ if not err.is_ok then return err
+
+ # Callbacks
+ err = set_curl_callback(callback_receiver)
+ if not err.is_ok then return err
+
+ # HTTP Header
+ err = set_curl_http_header
+ if not err.is_ok then return err
+
+ # Set HTTP method and body
+ err = set_method
+ if not err.is_ok then return err
+ err = set_body
+
+ return err
+ end
+
+ # Set cURL parameters according to assigned HTTP method set in method
+ # attribute and body if the method allows it according to RFC7231
+ private fun set_method : CURLCode
+ do
+ var err : CURLCode
+
+ if self.method=="GET" then
+ err=self.curl.native.easy_setopt(new CURLOption.get, 1)
+
+ else if self.method=="POST" then
+ err=self.curl.native.easy_setopt(new CURLOption.post, 1)
+
+ else if self.method=="HEAD" then
+ err=self.curl.native.easy_setopt(new CURLOption.no_body,1)
+
+ else
+ err=self.curl.native.easy_setopt(new CURLOption.custom_request,self.method)
+ end
+ return err
+ end
+
+ # Set request's body
+ private fun set_body : CURLCode
+ do
+ var err
+ var data = self.data
+ var body = self.body
+
+ if data != null then
+ var postdatas = data.to_url_encoded(self.curl)
+ err = self.curl.native.easy_setopt(new CURLOption.postfields, postdatas)
+ if not err.is_ok then return err
+ else if body != null then
+ err = self.curl.native.easy_setopt(new CURLOption.postfields, body)
+ if not err.is_ok then return err
+ end
+ return new CURLCode.ok
+ end
+
+ # Set cURL options
+ # such as delegate, follow location, URL, user agent and address family
+ private fun set_curl_options : CURLCode
+ do
var err
err = self.curl.native.easy_setopt(new CURLOption.follow_location, 1)
- if not err.is_ok then return answer_failure(err.to_i, err.to_s)
+ if not err.is_ok then return err
err = self.curl.native.easy_setopt(new CURLOption.url, url)
- if not err.is_ok then return answer_failure(err.to_i, err.to_s)
+ if not err.is_ok then return err
var user_agent = user_agent
if user_agent != null then
err = curl.native.easy_setopt(new CURLOption.user_agent, user_agent)
- if not err.is_ok then return answer_failure(err.to_i, err.to_s)
+ if not err.is_ok then return err
end
var unix_socket_path = unix_socket_path
if unix_socket_path != null then
err = self.curl.native.easy_setopt(new CURLOption.unix_socket_path, unix_socket_path)
- if not err.is_ok then return answer_failure(err.to_i, err.to_s)
+ if not err.is_ok then return err
end
+ return err
+ end
+
+ # Set cURL callback
+ private fun set_curl_callback(callback_receiver : CurlCallbacks) : CURLCode
+ do
+ var err
+
+ if self.delegate != null then callback_receiver = self.delegate.as(not null)
- # Callbacks
err = self.curl.native.register_callback_header(callback_receiver)
- if not err.is_ok then return answer_failure(err.to_i, err.to_s)
+ if not err.is_ok then return err
err = self.curl.native.register_callback_body(callback_receiver)
- if not err.is_ok then return answer_failure(err.to_i, err.to_s)
+ if not err.is_ok then return err
- # HTTP Header
+ return err
+ end
+
+ # Set cURL request header according to attribute headers
+ private fun set_curl_http_header : CURLCode
+ do
var headers = self.headers
if headers != null then
var headers_joined = headers.join_pairs(": ")
- err = self.curl.native.easy_setopt(new CURLOption.httpheader, headers_joined.to_curlslist)
- if not err.is_ok then return answer_failure(err.to_i, err.to_s)
- end
-
- # Datas
- var data = self.data
- if data != null then
- var postdatas = data.to_url_encoded(self.curl)
- err = self.curl.native.easy_setopt(new CURLOption.postfields, postdatas)
- if not err.is_ok then return answer_failure(err.to_i, err.to_s)
- else if body != null then
- err = self.curl.native.easy_setopt(new CURLOption.postfields, body.as(not null))
- if not err.is_ok then return answer_failure(err.to_i, err.to_s)
+ var err = self.curl.native.easy_setopt(new CURLOption.httpheader, headers_joined.to_curlslist)
+ if not err.is_ok then return err
end
-
- var err_resp = perform
- if err_resp != null then return err_resp
-
- var st_code = self.curl.native.easy_getinfo_long(new CURLInfoLong.response_code)
- if not st_code == null then success_response.status_code = st_code
-
- return success_response
+ return new CURLCode.ok
end
# Download to file given resource
end
end
+
# CURL Mail Request
#
# ~~~
# Execute Mail request with settings configured through attribute
fun execute: nullable CurlResponseFailed
do
+ self.curl.native = new NativeCurl.easy_init
if not self.curl.is_ok then return answer_failure(0, "Curl instance is not correctly initialized")
var lines = new Array[String]
var err_resp = perform
if err_resp != null then return err_resp
+ self.curl.native.easy_clean
+
return null
end
end
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2018 Matthieu Samuel Le Guellaut <leguellaut.matthieu@gmail.com>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+# This example will send a Person object to the specified address
+#
+import curl
+import json
+
+class Person
+ serialize
+
+ var name : String
+ var age : Int
+
+end
+
+var url = "http://example.com"
+
+# POST REQUEST
+var my_request = new CurlHTTPRequest(url, method="POST")
+var person = new Person("Jean",12)
+my_request.body = person.serialize_to_json
+my_request.execute
+
+# # USE WITH SOCKET ADDRESS FAMILY
+# var my_unix_request = new CurlHTTPRequest("http:///tmp/", method="POST")
+# my_unix_request.body = person.serialize_to_json
+# my_unix_request.unix_socket_path = "/tmp/test.sock"
+# my_unix_request.execute
# Simple communication stream with a remote socket
class TCPStream
super TCPSocket
- super BufferedReader
- super Writer
super PollableReader
+ super Duplex
# Real canonical name of the host to which `self` is connected
var host: String
private var addrin: NativeSocketAddrIn is noinit
- redef var end_reached = false
+ redef var closed = false
# TODO make init private
# Creates a socket connection to host `host` on port `port`
init connect(host: String, port: Int)
do
- _buffer = new CString(1024)
- _buffer_pos = 0
native = new NativeSocket.socket(new NativeSocketAddressFamilies.af_inet,
new NativeSocketTypes.sock_stream, new NativeSocketProtocolFamilies.pf_unspec)
if native.address_is_null then
- end_reached = true
closed = true
return
end
if not native.setsockopt(new NativeSocketOptLevels.socket, new NativeSocketOptNames.reuseaddr, 1) then
- end_reached = true
closed = true
return
end
last_error = new IOError.from_h_errno
closed = true
- end_reached = true
return
end
init(addrin.port, hostent.h_name.to_s)
closed = not internal_connect
- end_reached = closed
if closed then
# Connection failed
last_error = new IOError.from_errno
end
-
- prepare_buffer(1024)
end
# Creates a client socket, this is meant to be used by accept only
address = addrin.address.to_s
init(addrin.port, address)
-
- prepare_buffer(1024)
end
redef fun poll_in do return ready_to_read(0)
#
# timeout : Time in milliseconds before stopping listening for events on this socket
private fun pollin(event_types: Array[NativeSocketPollValues], timeout: Int): Array[NativeSocketPollValues] do
- if end_reached then return new Array[NativeSocketPollValues]
+ if closed then return new Array[NativeSocketPollValues]
return native.socket_poll(new PollFD.from_poll_values(native.descriptor, event_types), timeout)
end
# timeout : Time in milliseconds before stopping to wait for events
fun ready_to_read(timeout: Int): Bool
do
- if _buffer_pos < _buffer_length then return true
- if end_reached then return false
var events = [new NativeSocketPollValues.pollin]
return pollin(events, timeout).length != 0
end
end
end
- redef fun is_writable do return not end_reached
+ redef fun is_writable do return not closed
# Establishes a connection to socket addrin
#
return native.connect(addrin) >= 0
end
+ redef fun raw_read_byte do
+ var rd = native.read(write_buffer, 1)
+ if rd < 1 then return -1
+ return write_buffer[0].to_i
+ end
+
+ redef fun raw_read_bytes(ns, max) do
+ var rd = native.read(ns, max)
+ print "Read {rd} bytes"
+ if rd < 0 then return -1
+ return rd
+ end
+
# If socket.end_reached, nothing will happen
redef fun write(msg)
do
write "\n"
end
- redef fun fill_buffer
- do
- if not connected then return
-
- var read = native.read(_buffer, _buffer_capacity)
- if read == -1 then
- close
- end_reached = true
- end
-
- _buffer_length = read
- _buffer_pos = 0
- end
-
- # Enlarge `_buffer` to at least `len` bytes
- fun enlarge(len: Int) do
- if _buffer_capacity >= len then return
- _buffer_capacity = len
-
- var ns = new CString(_buffer_capacity)
- _buffer.copy_to(ns, _buffer_length - _buffer_pos, _buffer_pos, 0)
- _buffer = ns
- end
-
redef fun close
do
if closed then return
if native.close >= 0 then
closed = true
- end_reached = true
end
end
import websocket
-var sock = new WebSocketListener(8088, 1)
+var sock = new WebsocketServer.with_infos(8088, 1)
var msg: String
print sys.errno.strerror
end
-var cli: TCPStream
+var cli: WebsocketConnection
while not sock.closed do
cli = sock.accept
while cli.connected do
if sys.stdin.poll_in then
msg = gets
- printn "Received message : {msg}"
+ printn "Sending message : {msg}"
if msg == "disconnect" then cli.close
cli.write(msg)
end
# This file is part of NIT ( http://www.nitlanguage.org ).
#
-# Copyright 2014 Lucas Bajolet <r4pass@hotmail.com>
-#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
import socket
import sha1
import base64
+import crypto
-intrude import core::stream
-intrude import core::bytes
-
-# Websocket compatible listener
+# Websocket compatible server
#
# Produces Websocket client-server connections
-class WebSocketListener
- super Socket
+class WebsocketServer
- # Socket listening to connections on a defined port
+ # Socket listening for incoming Websocket connections
var listener: TCPServer
- # Creates a new Websocket server listening on given port with `max_clients` slots available
- init(port: Int, max_clients: Int)
+ # Is `self` closed?
+ var closed = false
+
+ # Creates a new Websocket server listening on given port
+ # with `max_clients` slots available
+ init with_infos(port: Int, max_clients: Int)
do
- listener = new TCPServer(port)
+ var listener = new TCPServer(port)
listener.listen max_clients
+ init(listener)
end
# Accepts an incoming connection
var client = listener.accept
assert client != null
- return new WebsocketConnection(listener.port, "", client)
+ return new WebsocketConnection(client)
end
- # Stop listening for incoming connections
+ # Close the server and the socket below it
fun close
do
listener.close
+ closed = true
end
end
#
# Can be used to communicate with a client
class WebsocketConnection
- super TCPStream
+ 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
- _buffer = new CString(1024)
- _buffer_pos = 0
- _buffer_capacity = 1024
- _buffer_length = 0
var headers = parse_handshake
var resp = handshake_response(headers)
- client.write(resp)
+ origin.write(resp)
end
- # Client connection to the server
- var client: TCPStream
-
# Disconnect from a client
- redef fun close
- do
- client.close
+ redef fun close do
+ origin.close
+ closed = true
end
- # Parses the input handshake sent by the client
+ # 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
return headmap
end
- # Generates the handshake
+ # Generate a handshake response
private fun handshake_response(heads: Map[String,String]): String
do
var resp_map = new HashMap[String,String]
return resp
end
- # Frames a text message to be sent to a client
- private fun frame_message(msg: String): Bytes
+ # 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.length)
+ 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(129u8)
ans_buffer.add((msg.length >> 8).to_b)
ans_buffer.add(msg.length.to_b)
end
- if msg isa FlatString then
- ans_buffer.append_ns_from(msg.items, msg.length, msg.first_byte)
- else
- for i in msg.substrings do
- ans_buffer.append_ns_from(i.as(FlatString).items, i.length, i.as(FlatString).first_byte)
- end
- end
+ msg.append_to_bytes(ans_buffer)
return ans_buffer
end
- # Reads an HTTP frame
+ # Read an HTTP frame
protected fun read_http_frame(buf: Buffer): String
do
- var ln = client.read_line
+ 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
- # Gets the message from the client, unpads it and reconstitutes the message
- private fun unpad_message do
- var fin = false
- var bf = new Bytes.empty
- while not fin do
- var fst_byte = client.read_byte
- var snd_byte = client.read_byte
- if fst_byte < 0 or snd_byte < 0 then
- last_error = new IOError("Error: bad frame")
- client.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
- # 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 fin_flag = fst_byte & 0b1000_0000
- if fin_flag != 0 then fin = true
- var opcode = fst_byte & 0b0000_1111
- if opcode == 9 then
- bf.add(138u8)
- bf.add(0u8)
- client.write(bf.to_s)
- _buffer_pos = _buffer_length
+ # 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
- if opcode == 8 then
- self.client.close
+ 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
- # 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 = client.read_bytes(2)
- if tmp.length != 2 then
- last_error = new IOError("Error: received interrupted frame")
- client.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 = client.read_bytes(8)
- if tmp.length != 8 then
- last_error = new IOError("Error: received interrupted frame")
- client.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
- var mask = client.read_bytes(4).items
- if payload_ext_len != 0 then
- len = payload_ext_len
- end
- var msg = client.read_bytes(len).items
- bf.append_ns(unmask_message(mask, msg, len), len)
+ for i in [0 .. 8[ do
+ payload_ext_len += tmp[i].to_i << (8 * (7 - i))
end
end
- _buffer = bf.items
- _buffer_length = bf.length
+ 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
- # Unmasks a message sent by a client
- private fun unmask_message(key: CString, message: CString, len: Int): CString
- do
- var return_message = new CString(len)
-
- for i in [0 .. len[ do
- return_message[i] = message[i] ^ key[i % 4]
+ 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
- return return_message
+ 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
- redef fun connected do return client.connected
+ fun connected: Bool do return not closed and origin.connected
redef fun write_bytes_from_cstring(ns, len) do
- client.write_bytes(frame_message(ns.to_s_unsafe(len)))
+ origin.write_bytes(frame_message(ns.to_s_unsafe(len)))
end
- redef fun write(msg) do client.write(frame_message(msg.to_s).to_s)
-
- redef fun is_writable do return client.connected
-
- redef fun fill_buffer
- do
- buffer_reset
- unpad_message
- end
+ redef fun write(msg) do origin.write_bytes(frame_message(msg))
- redef fun end_reached do return client._buffer_pos >= client._buffer_length and client.end_reached
+ redef fun is_writable do return origin.connected
# Is there some data available to be read ?
- fun can_read(timeout: Int): Bool do return client.ready_to_read(timeout)
+ fun can_read(timeout: Int): Bool do return not closed and origin.ready_to_read(timeout)
- redef fun poll_in do return client.poll_in
+ redef fun poll_in do return origin.poll_in
end