1 module sockjs.server;
2 
3 import std.stdio;
4 
5 import vibe.d;
6 import sockjs.sockjs:SockJS;
7 import sockjs.connection;
8 import sockjs.sockjsSyntax;
9 
10 ///
11 class SockJsException : Exception
12 {
13 	///
14 	public this(string _s)
15 	{
16 		super(_s);
17 	}
18 }
19 
20 ///
21 public class Server
22 {
23 public:
24 
25 	///
26 	this(SockJS.Options _options)
27 	{
28 		m_options = _options;
29 	}
30 
31 	///
32 	void handleRequest(HTTPServerRequest req, HTTPServerResponse res)
33 	{
34 		scope(failure) logCritical("handle request failed");
35 
36 		auto url = req.requestURL;
37 
38 		if(url.length >= m_options.prefix.length)
39 		{
40 			if(url[0..m_options.prefix.length] == m_options.prefix)
41 			{	
42 				munch(url, m_options.prefix);
43 
44 				string[] elements = url.split("/");
45 
46 				string _body = cast(string)req.bodyReader.readAll();
47 
48 				try handleSockJs(elements,_body,res,req.peer);
49 				catch(SockJsException e)
50 				{
51 					logError("handleSockJs failed: %s",e);
52 
53 					res.statusCode = 404;
54 					res.writeBody("");
55 				}
56 			}
57 		}
58 	}
59 
60 	///
61 	alias void delegate(Connection) EventOnConnection;
62 
63 	///
64 	@property void onConnection(EventOnConnection _callback) { m_onConnection = _callback; }
65 
66 	///
67 	@property const(SockJS.Options)* options() const { return &m_options; }
68 
69 private:
70 	EventOnConnection	m_onConnection;
71 	SockJS.Options		m_options;
72 	Connection[string] 	m_connections;
73 
74 	///
75 	void handleSockJs(string[] _urlElements, string _body, HTTPServerResponse _res, string _remotePeer)
76 	{
77 		//writefln("handle: %s",_urlElements);
78 
79 		//TODO: cleanup
80 		_res.headers["Access-Control-Allow-Origin"] = "http://localhost:8080";
81 		_res.headers["Access-Control-Allow-Credentials"] = "true";
82 		_res.headers["Connection"] = "keep-alive";
83 		_res.headers["Cache-Control"] = "no-store, no-cache, must-revalidate, max-age=0";
84 
85 		if(_urlElements.length == 1 && _urlElements[0] == "info")
86 		{
87 			SockJsSyntax.writeInfo(_res);
88 		}
89 		else if(_urlElements.length == 3)
90 		{
91 			string serverId = _urlElements[0];
92 			string userId = _urlElements[1];
93 			string method = _urlElements[2];
94 
95 			if(userId in m_connections)
96 			{
97 				auto conn = m_connections[userId];
98 
99 				if(conn.isOpen)
100 					conn.handleRequest(method == "xhr_send",_body,_res);
101 				else
102 					SockJsSyntax.writeClose(_res, conn.closeMsg);
103 			}
104 			else
105 			{
106 				if(method != "xhr")
107 					throw new SockJsException("wrong connect method");
108 				else
109 				{
110 					auto newConn = new Connection(this, _remotePeer, userId);
111 
112 					m_onConnection(newConn);
113 
114 					m_connections[userId] = newConn;
115 
116 					//logInfo("[sockjs] new connection (count: %s)",m_connections.length);
117 
118 					SockJsSyntax.writeOpen(_res);
119 				}
120 			}
121 		}
122 		else
123 			throw new SockJsException("wrong param count");
124 	}
125 
126 	///
127 	package void connectionClosed(Connection _conn)
128 	{
129 
130 		if(_conn.userId in m_connections)
131 		{
132 			m_connections.remove(_conn.userId);
133 
134 			//logInfo("[sockjs] closed connection (count: %s)",m_connections.length);
135 		}
136 
137 		_conn.destroy();
138 	}
139 }