Skip to content

Commit

Permalink
Added WebSocketServer and WebSocketServerExample. Also fixed behavior…
Browse files Browse the repository at this point in the history
… when underlying socket is closed.
  • Loading branch information
romamik committed Aug 31, 2016
1 parent 36b0749 commit 08d36ed
Show file tree
Hide file tree
Showing 8 changed files with 215 additions and 16 deletions.
3 changes: 3 additions & 0 deletions exampleCpp.hxml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-cpp output/outCpp
-cp src
-main haxe.net.example.WebSocketExample
9 changes: 9 additions & 0 deletions serverExample.hxml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-neko output/serverExample.n
-cp src
-main haxe.net.example.WebSocketServerExample

--next

-cpp output/serverExampleCpp
-cp src
-main haxe.net.example.WebSocketServerExample
2 changes: 1 addition & 1 deletion src/haxe/net/Socket2.hx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class Socket2 {
#if flash
return new haxe.net.impl.SocketFlash(host, port, secure, debug);
#elseif sys
return new haxe.net.impl.SocketSys(host, port, secure, debug);
return haxe.net.impl.SocketSys.create(host, port, secure, debug);
#else
#error "Unsupported platform"
#end
Expand Down
2 changes: 1 addition & 1 deletion src/haxe/net/WebSocket.hx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class WebSocket {
return new haxe.net.impl.WebSocketFlashExternalInterface(url, protocols);
}
#end
return new haxe.net.impl.WebSocketGeneric(url, protocols, origin, "wskey", debug);
return haxe.net.impl.WebSocketGeneric.create(url, protocols, origin, "wskey", debug);
#end
}

Expand Down
93 changes: 93 additions & 0 deletions src/haxe/net/WebSocketServer.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package haxe.net;
import haxe.crypto.Base64;
import haxe.crypto.Sha1;
import haxe.io.Bytes;
import haxe.net.impl.SocketSys;
import haxe.net.impl.WebSocketGeneric;
import sys.net.Host;
import sys.net.Socket;

class WebSocketServer {

var _isDebug:Bool;
var _listenSocket:Socket;

function new(host:String, port:Int, maxConnections:Int, isDebug:Bool) {
_isDebug = isDebug;
_listenSocket = new Socket();
_listenSocket.bind(new Host(host), port);
_listenSocket.listen(maxConnections);
}

public static function create(host:String, port:Int, maxConnections:Int, isDebug:Bool) {
return new WebSocketServer(host, port, maxConnections, isDebug);
}

@:access(haxe.net.impl.WebSocketGeneric.createFromExistingSocket)
@:access(haxe.net.impl.SocketSys.createFromExistingSocket)
public function accept() {
var socket = _listenSocket.accept();

var requestLines:Array<String> = [];

while(true) {
var line = socket.input.readLine();
if (line == '') break;
requestLines.push(line);
}

if (_isDebug) trace('Recieved request: \n${requestLines.join("\n")}');

{
var request = requestLines.shift();
var regexp = ~/^GET (.*) HTTP\/1.1$/;
if (!regexp.match(request)) throw 'bad request';
var url = regexp.matched(1);
//TODO check url
}

var acceptKey:String = {
var key:String = null;
var version:String = null;
var upgrade:String = null;
var connection:String = null;
var regexp = ~/^(.*): (.*)$/;
for (header in requestLines) {
if (!regexp.match(header)) throw 'bad request';
var name = regexp.matched(1);
var value = regexp.matched(2);
switch(name) {
case 'Sec-WebSocket-Key': key = value;
case 'Sec-WebSocket-Version': version = value;
case 'Upgrade': upgrade = value;
case 'Connection': connection = value;
}
}

if (
version != '13'
|| upgrade != 'websocket'
|| connection.indexOf('Upgrade') < 0
|| key == null
) {
throw 'bad request';
}

Base64.encode(Sha1.make(Bytes.ofString(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
}

var responce = [
'HTTP/1.1 101 Switching Protocols',
'Upgrade: websocket',
'Connection: Upgrade',
'Sec-WebSocket-Accept: $acceptKey',
'', ''
];
socket.output.writeString(responce.join('\r\n'));

if (_isDebug) trace('Websocket succefully connected');

return WebSocketGeneric.createFromExistingSocket(SocketSys.createFromExistingSocket(socket, _isDebug), _isDebug);
}

}
49 changes: 49 additions & 0 deletions src/haxe/net/example/WebSocketServerExample.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package haxe.net.example;

import haxe.CallStack;
import haxe.Json;
import haxe.io.Bytes;
import haxe.net.WebSocketServer;

class WebSocketServerExample {

static function main() {
var port = 8000;
var server = WebSocketServer.create('0.0.0.0', port, 1, true);
while (true) {
try{
trace('listening on port $port');

var websocket = server.accept();

websocket.onmessageString = function(message:String) {
trace('Recieved message: $message');
websocket.sendString('Your message was: $message');
}

websocket.onmessageBytes = function(message:Bytes) {
trace('Recieved bytes message: $message');
websocket.sendBytes(message);
}

websocket.onclose = function() {
trace('websocket closed');
websocket = null;
}

websocket.sendString('hello from server');

var n = 0;
while (websocket != null) {
websocket.process();
Sys.sleep(0.5);
}
}
catch (e:Dynamic) {
trace('Error', e);
trace(CallStack.exceptionStack());
}
}
}

}
39 changes: 33 additions & 6 deletions src/haxe/net/impl/SocketSys.hx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package haxe.net.impl;

import haxe.Constraints.Function;
import haxe.io.Bytes;
import haxe.io.Error;
import sys.net.Host;
import sys.net.Socket;

Expand All @@ -9,9 +11,11 @@ class SocketSys extends Socket2 {
private var sendConnect:Bool = false;
private var sendError:Bool = false;
private var secure:Bool;
private var isClosed:Bool = false;

public function new(host:String, port:Int, secure:Bool, debug:Bool = false) {
super(host, port, debug);
private function new(host:String, port:Int, debug:Bool = false) super(host, port, debug);

function initialize(secure:Bool) {
this.secure = secure;
var impl:Dynamic = null;
if (secure) {
Expand All @@ -34,9 +38,28 @@ class SocketSys extends Socket2 {
this.sendError = true;
if (debug) trace('socket.error! $e');
}

return this;
}

public static function create(host:String, port:Int, secure:Bool, debug:Bool = false) {
return new SocketSys(host, port, debug).initialize(secure);
}

static function createFromExistingSocket(socket:sys.net.Socket, debug:Bool) {
var socketSys = new SocketSys(socket.host().host.host, socket.host().port, debug);
socket.setBlocking(false);
socketSys.impl = socket;
socketSys.secure = false;
return socketSys;
}

override public function close() {
if (!isClosed) {
isClosed = true;
impl.close();
onclose();
}
}

override public function process() {
Expand All @@ -53,9 +76,10 @@ class SocketSys extends Socket2 {
}

var result = sys.net.Socket.select([this.impl], [this.impl], [this.impl], 0.4);

if (result.read.length > 0) {
var out = new BytesRW();
var needClose:Bool = false;
try {
var input = this.impl.input;
while (true) {
Expand All @@ -65,9 +89,12 @@ class SocketSys extends Socket2 {
out.writeBytes(data.sub(0, readed));
}
} catch (e:Dynamic) {

}
ondata(out.readAllAvailableBytes());
needClose = !(Std.is(e, Error) && (e:Error).match(Error.Blocked));
}
ondata(out.readAllAvailableBytes());
if (needClose) {
close();
}
}
}

Expand Down
34 changes: 26 additions & 8 deletions src/haxe/net/impl/WebSocketGeneric.hx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package haxe.net.impl;

import haxe.crypto.Base64;
import haxe.io.Bytes;
import haxe.net.Socket2;
class WebSocketGeneric extends WebSocket {
private var socket:Socket2;
private var origin = "http://127.0.0.1/";
Expand All @@ -15,8 +16,7 @@ class WebSocketGeneric extends WebSocket {
private var state = State.Handshake;
public var debug:Bool = true;

public function new(uri:String, protocols:Array<String> = null, origin:String = null, key:String = "wskey", debug:Bool = true) {
super();
function initialize(uri:String, protocols:Array<String> = null, origin:String = null, key:String = "wskey", debug:Bool = true) {
if (origin == null) origin = "http://127.0.0.1/";
this.protocols = protocols;
this.origin = origin;
Expand All @@ -39,12 +39,18 @@ class WebSocketGeneric extends WebSocket {

socket = Socket2.create(host, port, secure, debug);
state = State.Handshake;
socketData = new BytesRW();
socket.onconnect = function() {
_debug('socket connected');
writeBytes(prepareClientHandshake(path, host, port, key, origin));
//this.onopen();
};
commonInitialize();

return this;
}

function commonInitialize() {
socketData = new BytesRW();
socket.onclose = function() {
_debug('socket closed');
this.onclose();
Expand All @@ -57,9 +63,20 @@ class WebSocketGeneric extends WebSocket {
socketData.writeBytes(data);
handleData();
};


}
}

public static function create(uri:String, protocols:Array<String> = null, origin:String = null, key:String = "wskey", debug:Bool = true) {
return new WebSocketGeneric().initialize(uri, protocols, origin, key, debug);
}

static function createFromExistingSocket(socket:Socket2, debug:Bool) {
var websocket = new WebSocketGeneric();
websocket.socket = socket;
websocket.state = State.Head;
websocket.debug = debug;
websocket.commonInitialize();
return websocket;
}

override public function process() {
socket.process();
Expand Down Expand Up @@ -113,6 +130,7 @@ class WebSocketGeneric extends WebSocket {
state = State.Head;
case State.Head:
if (socketData.available < 2) return;

var b0 = socketData.readByte();
var b1 = socketData.readByte();

Expand Down Expand Up @@ -166,9 +184,9 @@ class WebSocketGeneric extends WebSocket {
//onPong.dispatch(null);
lastPong = Date.now();
case Opcode.Close:
_debug("Socket Closed");
_debug("Received Close");
//onClose.dispatch(null);
//socket.close();
socket.close();
}
state = State.Head;
default:
Expand Down

0 comments on commit 08d36ed

Please sign in to comment.