--- /dev/null
+[submodule "rules"]
+ path = rules
+ url = https://git.ebersold.fr/repos/rules.git
+[submodule "utils"]
+ path = utils
+ url = https://git.ebersold.fr/repos/aebutils.git
+[submodule "xml-transform"]
+ path = xml-transform
+ url = https://git.ebersold.fr/repos/xml-transform.git
--- /dev/null
+PROJECT(wsproxy-root)
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6.8)
+
+LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/rules")
+
+SUBDIRS(rules utils xml-transform wsproxy data-models)
+
+INCLUDE_DIRECTORIES(${aebutils_SOURCE_DIR})
+
+#
+# Try generation of configuration reader
+#
+FIND_LIBRARY(EXPAT_LIB expat "/usr/local/lib:/usr/lib")
+INCLUDE_DIRECTORIES(${EXPAT_INCLUDE_DIR})
+FIND_LIBRARY(DL_LIB dl "/lib:/usr/local/lib:/usr/lib")
+
+INCLUDE_DIRECTORIES("${xml-t_SOURCE_DIR}/libxsd/include")
+INCLUDE_DIRECTORIES("${CMAKE_CURRENT_BINARY_DIR}/include")
+INCLUDE_DIRECTORIES(".")
+# Does not work under ubuntu cmake 2.8.7 It says empty string !!!
+# INCLUDE_DIRECTORIES("${aebutils_SOURCE_DIR}")
+INCLUDE_DIRECTORIES("/usr/local/include")
+
+SET(SAXON CACHE STRING "~/Tools/saxon/saxon9.jar")
+SET(XSD2CPP "${xml-t_SOURCE_DIR}/xsd2cpp.xsl")
+
--- /dev/null
+PROJECT(wsproxy-dm)
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6.8)
+
+LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/rules")
+
+
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<xsd:schema
+ targetNamespace="http://www.ale.com/oxo/config/wsproxy"
+ xmlns="http://www.ale.com/oxo/config/wsproxy"
+ xmlns:tconfig="http://www.ale.com/oxo/config/wsproxy"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ elementFormDefault="unqualified" >
+
+ <xsd:simpleType name="UrlType">
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="http" />
+ <xsd:enumeration value="https" />
+ <xsd:enumeration value="ws" />
+ <xsd:enumeration value="wss" />
+ <xsd:enumeration value="ftp" />
+ <xsd:enumeration value="ftps" />
+ <xsd:enumeration value="xmpp" />
+ <xsd:enumeration value="xmpps" />
+ </xsd:restriction>
+ </xsd:simpleType>
+
+ <xsd:simpleType name="AuthType">
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="none" />
+ <xsd:enumeration value="basic" />
+ </xsd:restriction>
+ </xsd:simpleType>
+
+ <xsd:simpleType name='AuthorizationType'>
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="wan" />
+ <xsd:enumeration value="https" />
+ <xsd:enumeration value="http" />
+ </xsd:restriction>
+ </xsd:simpleType>
+
+ <xsd:simpleType name="ListAuthorization">
+ <xsd:list itemType="AuthorizationType" />
+ </xsd:simpleType>
+
+ <xsd:simpleType name="ListGateway">
+ <xsd:list itemType="xsd:string" />
+ </xsd:simpleType>
+
+ <xsd:simpleType name="LogLevel">
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="EMERG" />
+ <xsd:enumeration value="ALERT" />
+ <xsd:enumeration value="CRIT" />
+ <xsd:enumeration value="ERROR" />
+ <xsd:enumeration value="WARNING" />
+ <xsd:enumeration value="NOTICE" />
+ <xsd:enumeration value="DEBUG" />
+ </xsd:restriction>
+ </xsd:simpleType>
+
+
+ <xsd:complexType name="SessionTrackingType">
+ <xsd:simpleContent>
+ <xsd:extension base="xsd:string">
+ <xsd:attribute name="active" type="xsd:boolean"/>
+ <xsd:attribute name="max_inactive" type="xsd:integer"/>
+ <xsd:attribute name="max_simultaneous" type="xsd:integer"/>
+ </xsd:extension>
+ </xsd:simpleContent>
+ </xsd:complexType>
+
+ <xsd:complexType name="AccessFilterType">
+ <xsd:simpleContent>
+ <xsd:extension base="xsd:string">
+ <xsd:attribute name="authorize" type="ListAuthorization"/>
+ <!-- gateways should be a list -->
+ <xsd:attribute name="gateway" type="ListGateway"/>
+ <xsd:attribute name="dynamic" type="xsd:integer"/>
+ </xsd:extension>
+ </xsd:simpleContent>
+ </xsd:complexType>
+
+ <xsd:complexType name="SubdirType">
+ <xsd:sequence>
+ <xsd:element name="user" type="xsd:string" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ <xsd:attribute name='stealth' type="xsd:integer"/>
+ <xsd:attribute name='dir' type="xsd:string"/>
+ <xsd:attribute name='auth' type="AuthType"/>
+ </xsd:complexType>
+
+
+ <xsd:complexType name="AccessRightsType">
+ <xsd:simpleContent>
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="R" />
+ <xsd:enumeration value="W" />
+ </xsd:restriction>
+ </xsd:simpleContent>
+ </xsd:complexType>
+
+
+ <xsd:complexType name="FileServerType">
+ <xsd:simpleContent>
+ <xsd:extension base="xsd:string">
+ <xsd:attribute name='rights' type="AccessRightsType"/>
+ </xsd:extension>
+ </xsd:simpleContent>
+ </xsd:complexType>
+
+ <xsd:complexType name="PluginType">
+ <xsd:simpleContent>
+ <xsd:extension base="xsd:string">
+ <xsd:attribute name='name' type="xsd:string"/>
+ <xsd:attribute name='load' type="xsd:string"/>
+ </xsd:extension>
+ </xsd:simpleContent>
+ </xsd:complexType>
+
+ <xsd:complexType name="ParamType">
+ <xsd:simpleContent>
+ <xsd:extension base="xsd:string">
+ <xsd:attribute name='name' type="xsd:string" />
+ <xsd:attribute name='value' type="xsd:string"/>
+ </xsd:extension>
+ </xsd:simpleContent>
+ </xsd:complexType>
+
+
+
+ <xsd:complexType name="ApplicationType">
+ <xsd:sequence>
+ <xsd:choice>
+ <xsd:element name='filter' type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation>
+ Field that defines the path of the request. Ex: /app/toto
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ <xsd:element name='file_server' type="FileServerType"/>
+ </xsd:choice>
+ <xsd:element name='destination' type="xsd:string" minOccurs="1" maxOccurs="1">
+ <xsd:annotation>
+ <xsd:documentation>
+ Field that defines the backend port and address. This parameter is in conflict with attribute host and port !!!
+ It would be good to see how we could define an unix socket name.
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ <xsd:element name='access_filter' type="AccessFilterType"/>
+ <xsd:element name='session_tracking' type="SessionTrackingType"/>
+ <xsd:element name='sub_dir' type="xsd:string" maxOccurs="unbounded"/>
+ <xsd:element name='param' type='ParamType' maxOccurs="unbounded"/>
+ </xsd:sequence>
+ <xsd:attribute name="name" type='xsd:string' minOccurs="0"/>
+ <xsd:attribute name="host" type="xsd:string" minOccurs="0"/>
+ <xsd:attribute name="port" type="xsd:integer" minOccurs="0"/>
+ <!-- The flow is binary or text. Need to be removed when code will be inline again -->
+ <xsd:attribute name="flow" type="xsd:string" default="text"/>
+ <xsd:attribute name="max_frame_size" type="xsd:integer" use="required" default="65535"/>
+ </xsd:complexType>
+
+ <!-- This is my point of view -->
+ <xsd:complexType name="GatewayType">
+ <xsd:sequence>
+ <xsd:element name="host" type="xsd:string"/>
+ <xsd:element name="port" type="xsd:integer"/>
+ <xsd:element name="schema" type="UrlType"/>
+ <xsd:element name="certfile" type="xsd:string" minOccurs="0"/>
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string"/>
+ <xsd:attribute name="queue" type="xsd:integer"/>
+ </xsd:complexType>
+
+ <xsd:complexType name="ReverseProxyType">
+ <xsd:sequence>
+ <xsd:element name="application" type="ApplicationType" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:complexType>
+ <!-- Web Socket new -->
+
+ <xsd:complexType name="WsApplicationType">
+ <xsd:sequence>
+ <xsd:element name="schema" type="UrlType"/>
+ <xsd:element name="certfile" type="xsd:string" minOccurs="0"/>
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string"/>
+ <xsd:attribute name="host" type="xsd:string"/>
+ <xsd:attribute name="port" type="xsd:integer"/>
+ <!-- The flow is binary or text -->
+ <xsd:attribute name="flow" type="xsd:string" default="text"/>
+ <xsd:attribute name="max_frame_size" type="xsd:integer" use="required" default="65535"/>
+ </xsd:complexType>
+
+ <xsd:complexType name="WebSocketType">
+ <xsd:sequence>
+ <xsd:element name="application" type="WsApplicationType" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:complexType>
+
+ <xsd:complexType name="LogType">
+ <xsd:sequence>
+ <xsd:element name="file" type="xsd:string" minOccurs="0" maxOccurs="1"/>
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string"/>
+ <xsd:attribute name="level" type="LogLevel"/>
+ <xsd:attribute name="syslog" type="xsd:boolean"/>
+ </xsd:complexType>
+
+ <!-- Plugins stuff -->
+
+ <xsd:complexType name="PluginsType">
+ <xsd:sequence>
+ <xsd:element name="plugin" type="PluginType" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:complexType>
+
+ <!-- Global settings-->
+ <xsd:complexType name="GlobalType">
+ <xsd:sequence>
+ <xsd:element name="log" type="LogType" maxOccurs="unbounded"/>
+ <xsd:element name="workers" type="xsd:integer" minOccurs="0" maxOccurs="1" default="1"/>
+ <xsd:element name="user" type="xsd:string" maxOccurs="1" default="root">
+ <xsd:annotation>
+ <xsd:documentation>
+ Defines the user name the process should have once running.
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ <xsd:element name="group" type="xsd:string" maxOccurs="1" default="root">
+ <xsd:annotation>
+ <xsd:documentation>
+ Defines the group name the process should have once running.
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ </xsd:sequence>
+ </xsd:complexType>
+
+ <!-- Main entry .... -->
+ <xsd:complexType name="ConfigurationType">
+ <xsd:sequence>
+ <xsd:element name="global" type="GlobalType" maxOccurs="1"/>
+ <xsd:element name="plugins" type="PluginsType" minOccurs="0" maxOccurs="1"/>
+ <xsd:element name="listen" type="GatewayType" maxOccurs="unbounded"/>
+ <xsd:element name="rproxy" type="ReverseProxyType" maxOccur="1"/>
+ <xsd:element name="websocket" type="WebSocketType" maxOccur="1"/>
+ </xsd:sequence>
+ </xsd:complexType>
+ <xsd:element name="CONFIGURATION" type="ConfigurationType"/>
+</xsd:schema>
--- /dev/null
+Subproject commit aeca878a84169490996e427211493ac306f00c7c
--- /dev/null
+Subproject commit 5f6e9134ab67b61537fc0bf557842221001f76b1
--- /dev/null
+PROJECT(wsproxy)
+
+INCLUDE_DIRECTORIES(${aebutils_SOURCE_DIR})
+
+#
+# Try generation of configuration reader
+#
+FIND_LIBRARY(EXPAT_LIB expat "/usr/local/lib:/usr/lib")
+INCLUDE_DIRECTORIES(${EXPAT_INCLUDE_DIR})
+FIND_LIBRARY(DL_LIB dl "/lib:/usr/local/lib:/usr/lib")
+
+INCLUDE_DIRECTORIES("${xml-t_SOURCE_DIR}/libxsd/include")
+INCLUDE_DIRECTORIES("${CMAKE_CURRENT_BINARY_DIR}/include")
+INCLUDE_DIRECTORIES(".")
+INCLUDE_DIRECTORIES("${aebutils_SOURCE_DIR}")
+INCLUDE_DIRECTORIES(${aebutils_SOURCE_DIR}/aeb)
+INCLUDE_DIRECTORIES(${libltdl_SOURCE_DIR})
+INCLUDE_DIRECTORIES("/usr/local/include")
+
+SUBDIRS(plugins)
+
+SET(SAXON CACHE STRING "~/Tools/saxon/saxon9.jar")
+SET(XSD2CPP "${xml-t_SOURCE_DIR}/xsd2cpp.xsl")
+SET(PROFILE
+ "${wsproxy-root_SOURCE_DIR}/data-models/reverse_proxy.xsd")
+
+
+ADD_CUSTOM_COMMAND(
+ SOURCE ${PROFILE}
+ COMMAND java
+ ARGS -jar ${SAXON} -s ${PROFILE} -xsl:${XSD2CPP}
+ rootdir="./parser/"
+ target="release" root-element="CONFIGURATION"
+ shared-ptr="aeb" incdir="/../include"
+ srcdir="./"
+ OUTPUT
+ ${CMAKE_CURRENT_BINARY_DIR}/parser/p_reverse_proxy.cpp
+ ${CMAKE_CURRENT_BINARY_DIR}/parser/reverse_proxy.cpp
+ COMMENT "Transform reverse_proxy.xsd"
+)
+
+
+
+
+ADD_EXECUTABLE(wsproxy
+ main.cpp
+ ws_proxy_handler.cpp
+ ws_proxy_endpoint.cpp
+ ws_handler_factory.cpp
+ ws_http_header.cpp
+ ws_acceptor.cpp
+ ws_base64.cpp
+ ws_frame.cpp
+ ws_log.cpp
+ sha1.cpp
+ ws_plugin_manager.cpp
+ ${CMAKE_CURRENT_BINARY_DIR}/parser/reverse_proxy.cpp
+ ${CMAKE_CURRENT_BINARY_DIR}/parser/p_reverse_proxy.cpp
+)
+
+TARGET_LINK_LIBRARIES(wsproxy ltdl libxsd ${EXPAT_LIB})
+
+IF( UNIX )
+ELSE (UNIX )
+ TARGET_LINK_LIBRARIES(ip4_server ws2_32)
+ENDIF (UNIX )
+
+
+
+INSTALL(TARGETS wsproxy DESTINATION bin
+ COMPONENT wsproxy
+ )
--- /dev/null
+2015/09/22:
+-----------
+ - TODO create ws_handler abstract class
+ - TODO create ws_connection_handler class ( handles connection state,
+ CONNECTING,OPEN,CLOSING,CLOSED) decides if http or https according to
+ config
+ - TODO move ws logic into plugin_ws
+ - TODO plugin configuration management ...
+ - TODO backend class seems to be ws_proxy_endpoint ...
+ - TODO servlet support
+ - TODO fastcgi support
+ - TODO auth module support
+2015/07/28:
+-----------
+ - CMake installer
+2014/12/04:
+-----------
+ - DONE telnet seems to work pretty well.
+ - DONE log information in configuration file
+ - frame limit check must be finalized.
+ - Packaging ...
+ - Documentation on the web site would be of great help as well.
+ - add a factory for handlers ws_handler_factory. So that I can leave in
+ peace.
+ - improve aeb lib to support SSL / TLS
+ - improve http header analysis (Origin, Cookies, Host)
+ - Authentication mechanism SASL, Basic Auth ....
+ - Add statistics number of connections, inbound / outbound flow ....
+2014/11/24:
+-----------
+ - DONE include base64 functions
+ - DONE include sha-1 encoding functions
+ - DONE add configuration file
+ - I think a class ws_http_header would be good to handle WS HTTP initiation
+ - Improve error handling (exception or return code) when dealing with sockets
+ and streams
+ - integrate telnet example for proof of concept
+ - don't forget installer
+ - check if logging information are defined in configuration file log level,
+ log file, log syslog...
+ - check
+2014/11/19:
+-----------
+ - There is a need for a logger.
+ - remove the traces in aeb/net
+ - include base64 functions
+ - include sha-1 encoding functions
+ - add configuration file
+ - implement end point class
--- /dev/null
+<?xml version="1.0"?>
+<CONFIGURATION>
+ <global>
+ <log name="wsproxy" level="DEBUG" syslog="true"></log>
+ <log name="ws_proxy_handler" level="DEBUG" syslog="false"></log>
+ <log name="ws_proxy_endpoint" level="WARNING" syslog="false"></log>
+ <log name="ws_handler_factory" level="WARNING" syslog="false"></log>
+ <log name="ws_acceptor" level="WARNING" syslog="false"></log>
+ <log name="ws_frame" level="DEBUG" syslog="false"></log>
+ <user>aebersol</user>
+ <group>users</group>
+ </global>
+ <listen>
+ <host>localhost</host>
+ <port>10323</port>
+ <certfile>test.cert</certfile>
+ </listen>
+ <!-- Reverse proxy backends -->
+ <rproxy>
+ <application name="csta" host="172.25.16.146" port="2555" flow="binary"></application>
+ <application name="telnet" host="172.25.16.146" port="22" flow="binary"></application>
+ <application name="sip" host="172.25.16.146" port="5060" flow="text"></application>
+ </rproxy>
+ <websocket>
+ <application name="csta" host="172.25.16.146" port="2555" flow="binary"></application>
+ </websocket>
+</CONFIGURATION>
--- /dev/null
+<?xml version="1.0"?>
+<CONFIGURATION>
+ <global>
+ <log name="wsproxy" level="DEBUG" syslog="true"></log>
+ <log name="ws_proxy_handler" level="DEBUG" syslog="false"></log>
+ <log name="ws_proxy_endpoint" level="WARNING" syslog="false"></log>
+ <log name="ws_handler_factory" level="WARNING" syslog="false"></log>
+ <log name="ws_acceptor" level="DEBUG" syslog="false"></log>
+ <log name="ws_frame" level="DEBUG" syslog="false"></log>
+ <user>aebersol</user>
+ <group>users</group>
+ </global>
+ <plugins>
+ <plugin name="default" load="libplugin_default"/>
+ <plugin name="SSL" load="libplugin_log"/>
+ </plugins>
+ <listen name="lan" queue="5">
+ <host>localhost</host>
+ <port>8082</port>
+ <certfile>test.cert</certfile>
+ </listen>
+ <listen name="wan" queue="15">
+ <host>localhost</host>
+ <port>11323</port>
+ <certfile>test.cert</certfile>
+ </listen>
+ <!-- Reverse proxy backends -->
+ <rproxy>
+ <application name="csta" host="172.25.16.146" port="2555" >
+ <filter>/services/</filter>
+ <access_filter gateway="wan"></access_filter>
+ <param name="DefaultEnabled" value="on"/>
+ <param name="DefaultAction" value="on"/>
+ <param name="AuthType" value="basic"/>
+ </application>
+ <application name="telnet" host="172.25.16.146" port="22" >
+ <filter>/services/telnet</filter>
+ <access_filter gateway="lan"></access_filter>
+ </application>
+ <application name="xmlOxo" host="localhost" port="22" >
+ <filter>/services/XmlPhone</filter>
+ <access_filter gateway="lan wan"></access_filter>
+ </application>
+ <application name="xmlAdmin" host="172.25.16.146" port="22" >
+ <filter>/services/XmlAdmin</filter>
+ <access_filter gateway="lan"></access_filter>
+ </application>
+ <application name="IcsLocator" host="172.25.16.146" port="8894" ></application>
+ </rproxy>
+ <websocket>
+ <application name="csta" host="172.25.16.146" port="2556" flow="binary"></application>
+ <application name="sip" host="172.25.16.146" port="5060" flow="text"></application>
+ </websocket>
+</CONFIGURATION>
--- /dev/null
+Copyright (c) 2010 Peter Nitsch
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html>
+<head>
+ <title>jsTerm Example</title>
+ <script src='../src/Term.js'></script>
+ <script src='../src/parser/Keyboard.js'></script>
+ <script src='../src/parser/CharacterCodes.js'></script>
+ <script src='../src/parser/SixteenColors.js'></script>
+ <script src='../src/parser/ByteArray.js'></script>
+ <script src='../src/parser/EscapeSequencer.js'></script>
+ <script src='../src/parser/AnsiParser.js'></script>
+ <script src='../src/parser/NVTCodes.js'></script>
+ <script src='../src/parser/Telnet.js'></script>
+ <script src='../src/viewer/Point.js'></script>
+ <script src='../src/viewer/Font.js'></script>
+ <script src='../src/viewer/Cursor.js'></script>
+ <script src='../src/viewer/AnsiViewer.js'></script>
+ <script src='../src/telnet/Socket.js'></script>
+ <script src='../src/telnet/Session.js'></script>
+
+ <script src='http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js'></script>
+ <script>
+ $(document).ready(function(){
+
+ var session = new TERM.Session("../fonts/ansilove_font_pc_80x25.png");
+ var host = document.getElementById("host");
+ var port = document.getElementById("port");
+ var connect = document.getElementById("connect");
+ var bookmarks = document.getElementById("bookmarks");
+
+ connect.onclick = function() {
+ session.connect(host.value, port.value);
+ }
+
+ bookmarks.onchange = function() {
+ var val = bookmarks.value;
+ if(val!=""){
+ var i = val.indexOf(":");
+ host.value = val.slice(0, i);
+ port.value = val.slice(i+1, val.length);
+ }
+ }
+
+ });
+ </script>
+</head>
+<body>
+ <div id="terminal"><canvas id="canvas" width="650" height="440"></canvas> </div>
+
+ <div id="panel">
+ <select id="bookmarks" name="bookmarks">
+ <option value="">- Bookmarks -</option>
+ <option value="towel.blinkenlights.nl:23">towel.blinkenlights.nl</option>
+ <option value="vert.synchro.net:23">vert.synchro.net</option>
+ <option value="bbs.pharcyde.org:23">bbs.pharcyde.org</option>
+ <option value="d1st.org:23">d1st.org</option>
+ <option value="bbs.electronicchicken.com:23">bbs.electronicchicken.com</option>
+ <option value="entropybbs.co.nz:23">entropybbs.co.nz</option>
+ <option value="lunatic.zapto.org:23">lunatic.zapto.org</option>
+ <option value="bbs.roughneckbbs.com:23">bbs.roughneckbbs.com</option>
+ <option value="masqueradebbs.com:23">masqueradebbs.com</option>
+ <option value="alecoexp.dyndns.org:23">alecoexp.dyndns.org</option>
+ <option value="bluewavebbs.dyndns.org:23">bluewavebbs.dyndns.org</option>
+ <option value="bbs.akroncdnr.com:23">bbs.akroncdnr.com</option>
+ <option value="cbliss.synchro.net:23">cbliss.synchro.net</option>
+ <option value="ds69bbs.com:23">ds69bbs.com</option>
+ <option value="bbs.defcon.no:23">bbs.defcon.no</option>
+ <option value="bbs.dmine.net:23">bbs.dmine.net</option>
+ <option value="bbs.diskshop.ca:23">bbs.diskshop.ca</option>
+ <option value="eotd.com:23">eotd.com</option>
+ <option value="flashpointbbs.no-ip.org:23">flashpointbbs.no-ip.org</option>
+ <option value="fjbbs.gotdns.com:23">fjbbs.gotdns.com</option>
+ <option value="isisunveiled.no-ip.org:23">isisunveiled.no-ip.org</option>
+ <option value="bbs.zeusdev.co.uk:23">bbs.zeusdev.co.uk</option>
+ <option value="musicstation.spb.ru:5223">musicstation.spb.ru</option>
+ <option value="phybbs.dyndns.org:23">phybbs.dyndns.org</option>
+ <option value="rdfig.net:23">rdfig.net</option>
+ <option value="redhill.net.nz:4500">redhill.net.nz</option>
+ <option value="bbs.dxrw.org:23">bbs.dxrw.org</option>
+ <option value="bbs.scansoutheasternmass.com:23">bbs.scansoutheasternmass.com</option>
+ <option value="thechangeosp.nl:23">thechangeosp.nl</option>
+ <option value="darkside.dtdns.net:23">darkside.dtdns.net</option>
+ <option value="white-squirrel.dnsalias.net:23">white-squirrel.dnsalias.net</option>
+ <option value="wildcatcastle.gotdns.com:23">wildcatcastle.gotdns.com</option>
+ <option value="bbs.zzap.org:23">bbs.zzap.org</option>
+ </select>
+ <input id="host" type="text" value="towel.blinkenlights.nl">
+ <input id="port" type="text" value="23">
+ <input id="connect" type="button" value="Connect">
+ </div>
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html>
+<head>
+ <title>jsTerm Example</title>
+ <script src='../src/Term.js'></script>
+ <script src='../src/parser/Keyboard.js'></script>
+ <script src='../src/parser/CharacterCodes.js'></script>
+ <script src='../src/parser/SixteenColors.js'></script>
+ <script src='../src/parser/ByteArray.js'></script>
+ <script src='../src/parser/EscapeSequencer.js'></script>
+ <script src='../src/parser/AnsiParser.js'></script>
+ <script src='../src/parser/NVTCodes.js'></script>
+ <script src='../src/parser/Telnet.js'></script>
+ <script src='../src/viewer/Point.js'></script>
+ <script src='../src/viewer/Font.js'></script>
+ <script src='../src/viewer/Cursor.js'></script>
+ <script src='../src/viewer/AnsiViewer.js'></script>
+ <script src='../src/telnet/Socket.js'></script>
+ <script src='../src/telnet/Session.js'></script>
+
+ <script src='http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js'></script>
+ <script>
+ $(document).ready(function(){
+
+ var session = new TERM.Session("../fonts/ansilove_font_pc_80x25.png");
+ var host = document.getElementById("host");
+ var port = document.getElementById("port");
+ var connect = document.getElementById("connect");
+ var bookmarks = document.getElementById("bookmarks");
+
+ connect.onclick = function() {
+ session.connect(host.value, port.value);
+ }
+
+ bookmarks.onchange = function() {
+ var val = bookmarks.value;
+ if(val!=""){
+ var i = val.indexOf(":");
+ host.value = val.slice(0, i);
+ port.value = val.slice(i+1, val.length);
+ }
+ }
+
+ });
+ </script>
+</head>
+<body>
+ <div id="terminal"><canvas id="canvas" width="650" height="440"></canvas> </div>
+
+ <div id="panel">
+ <select id="bookmarks" name="bookmarks">
+ <option value="">- Bookmarks -</option>
+ <option value="towel.blinkenlights.nl:23">towel.blinkenlights.nl</option>
+ <option value="vert.synchro.net:23">vert.synchro.net</option>
+ <option value="bbs.pharcyde.org:23">bbs.pharcyde.org</option>
+ <option value="d1st.org:23">d1st.org</option>
+ <option value="bbs.electronicchicken.com:23">bbs.electronicchicken.com</option>
+ <option value="entropybbs.co.nz:23">entropybbs.co.nz</option>
+ <option value="lunatic.zapto.org:23">lunatic.zapto.org</option>
+ <option value="bbs.roughneckbbs.com:23">bbs.roughneckbbs.com</option>
+ <option value="masqueradebbs.com:23">masqueradebbs.com</option>
+ <option value="alecoexp.dyndns.org:23">alecoexp.dyndns.org</option>
+ <option value="bluewavebbs.dyndns.org:23">bluewavebbs.dyndns.org</option>
+ <option value="bbs.akroncdnr.com:23">bbs.akroncdnr.com</option>
+ <option value="cbliss.synchro.net:23">cbliss.synchro.net</option>
+ <option value="ds69bbs.com:23">ds69bbs.com</option>
+ <option value="bbs.defcon.no:23">bbs.defcon.no</option>
+ <option value="bbs.dmine.net:23">bbs.dmine.net</option>
+ <option value="bbs.diskshop.ca:23">bbs.diskshop.ca</option>
+ <option value="eotd.com:23">eotd.com</option>
+ <option value="flashpointbbs.no-ip.org:23">flashpointbbs.no-ip.org</option>
+ <option value="fjbbs.gotdns.com:23">fjbbs.gotdns.com</option>
+ <option value="isisunveiled.no-ip.org:23">isisunveiled.no-ip.org</option>
+ <option value="bbs.zeusdev.co.uk:23">bbs.zeusdev.co.uk</option>
+ <option value="musicstation.spb.ru:5223">musicstation.spb.ru</option>
+ <option value="phybbs.dyndns.org:23">phybbs.dyndns.org</option>
+ <option value="rdfig.net:23">rdfig.net</option>
+ <option value="redhill.net.nz:4500">redhill.net.nz</option>
+ <option value="bbs.dxrw.org:23">bbs.dxrw.org</option>
+ <option value="bbs.scansoutheasternmass.com:23">bbs.scansoutheasternmass.com</option>
+ <option value="thechangeosp.nl:23">thechangeosp.nl</option>
+ <option value="darkside.dtdns.net:23">darkside.dtdns.net</option>
+ <option value="white-squirrel.dnsalias.net:23">white-squirrel.dnsalias.net</option>
+ <option value="wildcatcastle.gotdns.com:23">wildcatcastle.gotdns.com</option>
+ <option value="bbs.zzap.org:23">bbs.zzap.org</option>
+ </select>
+ <input id="host" type="text" value="towel.blinkenlights.nl">
+ <input id="port" type="text" value="23">
+ <input id="connect" type="button" value="Connect">
+ </div>
+</body>
+</html>
--- /dev/null
+/**
+ * Copyright (c) 2010 Peter Nitsch
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author Peter Nitsch
+ * http://jsterm.com
+ * http://www.peternitsch.net
+ */
+
+var TERM = TERM || {
+ socket : null,
+ SERVER_URL : "172.25.16.146", // Your node.js server IP
+ SERVER_PORT : "10222", // Your node.js server port
+ VERSION : "0.1 alpha"
+};
--- /dev/null
+/**
+ * @author Peter Nitsch
+ */
+
+TERM.AnsiParser = function (viewer){
+
+ var viewer = viewer;
+ var escapeCommand = [];
+ var bufferEscapeCommand = false;
+
+ this.escapeCommands = new TERM.EscapeSequencer(viewer);
+ this._bytes;
+
+ this.parse = function (bytes) {
+
+ if( bytes != null )
+ this._bytes = new TERM.ByteArray(bytes);
+ while( this._bytes.bytesAvailable > 0 ){
+ var result = this._bytes.readUnsignedByte();
+
+ if( result == SUBSTITUTE) {
+ break;
+ } else {
+ this.readByte( result );
+ }
+ }
+
+ this._bytes.position = 0;
+ this._bytes = null;
+ };
+
+ this._exceptionsLib = [];
+ this._exceptions = [];
+
+ this.hasException = function( code ) {
+ if( this._exceptions.indexOf(code) != -1 )
+ return true;
+ return false;
+ };
+
+ this.writeException = function( code, callback ) {
+ if( !this.hasException(code) ){
+ this._exceptionsLib[code] = callback;
+ this._exceptions.push(code);
+ }
+ };
+
+ this.readByte = function (b) {
+ if(b == ESCAPE) {
+ escapeCommand = [];
+ escapeCommand.push(b);
+ bufferEscapeCommand = true;
+ this.escapeCommands.onRead(0,b);
+ } else {
+ if(bufferEscapeCommand){
+ escapeCommand.push(b);
+ this.escapeCommands.onRead(escapeCommand.length,b);
+ if( this.escapeCommands.complete && this.escapeCommands.checkCommandAction(escapeCommand.length, b) ) {
+ this.escapeCommands.executeCommand(escapeCommand);
+ bufferEscapeCommand = false;
+ this.escapeCommands.complete = false;
+ }
+ } else if( this.hasException(b) ) {
+ console.log("TERM.AnsiParser.readByte "+b+" Has exception");
+ this._exceptionsLib[b]( b, this._bytes );
+ } else if(b >= SPACE) {
+ viewer.drawCharacter(b);
+ } else {
+ console.log("TERM.AnsiParser.readByte switch: "+b+ " type="+typeof(b));
+ switch(parseInt(b)) {
+ case BACKSPACE:
+ viewer.moveBackward(1, true);
+ break;
+
+ case LINE_FEED:
+ viewer.carriageReturn();
+ viewer.moveDown(1);
+ break;
+
+ case CARRIAGE_RETURN:
+ viewer.carriageReturn();
+ break;
+
+ case FORM_FEED:
+ viewer.eraseScreen();
+ viewer.reposition(0, 0);
+ break;
+ default:
+ console.log("TERM.AnsiParser.readByte in switch default:");
+ //viewer.carriageReturn();
+ ;
+ }
+ }
+ }
+ };
+
+};
--- /dev/null
+/**
+ * @author Peter Nitsch
+ */
+
+TERM.ByteArray = function (bytes){
+
+ this.position = 0;
+ this.stringdata = bytes.toString();
+ this.readview = new Int8Array(bytes);
+ this.buffer = bytes;
+ this.readUnsignedByte = function() {
+ // var b = this.stringdata.charCodeAt(this.position);
+ // alert(" readUnsignedByte enter at position :"+this.position +" / "+ this.bytesAvailable);
+ var b = this.readview[this.position].toString(10);
+ //console.log(" readUnsignedByte return ="+b+" at position :"+this.position );
+ if(this.position!=this.buffer.byteLength) this.position++;
+ return b;
+ };
+
+ this.writeByte = function(){
+ // TO DO
+ };
+
+};
+
+TERM.ByteArray.prototype = {
+
+ get bytesAvailable (){
+ return this.buffer.byteLength - this.position;
+ },
+ get length (){
+ return this.buffer.byteLength;
+ }
+
+};
+
--- /dev/null
+/**
+ * @author Peter Nitsch
+ */
+
+const NULL = 0
+const START_OF_HEADING = 1;
+const START_OF_TEXT = 2;
+const END_OF_TEXT = 3;
+const END_OF_TRANSMISSION = 4;
+const ENQUIRY = 5;
+const ACKNOWLEDGE = 6;
+const BELL = 7;
+const BACKSPACE = 8;
+const HORIZONTAL_TABULATION = 9;
+const LINE_FEED = 10;
+const VERTICAL_TABULATION = 11;
+const FORM_FEED = 12;
+const CARRIAGE_RETURN = 13;
+const SHIFT_OUT = 14;
+const SHIFT_IN = 15;
+const DATA_LINK_ESCAPE = 16;
+const DEVICE_CONTROL_ONE = 17;
+const DEVICE_CONTROL_TWO = 18;
+const DEVICE_CONTROL_THREE = 19;
+const DEVICE_CONTROL_FOUR = 20;
+const NEGATIVE_ACKNOWLEDGE = 21;
+const SYNCHRONOUS_IDLE = 22;
+const END_OF_TRANSMISSION_BLOCK = 23;
+const CANCEL = 24;
+const END_OF_MEDIUM = 25;
+const SUBSTITUTE = 26;
+const ESCAPE = 27;
+const FILE_SEPARATOR = 28;
+const GROUP_SEPARATOR = 29;
+const RECORD_SEPARATOR = 30;
+const UNIT_SEPARATOR = 31;
+const SPACE = 32;
+const EXCLAMATION_MARK = 33;
+const QUOTATION_MARK = 34;
+const uint_SIGN = 35;
+const DOLLAR_SIGN = 36;
+const PERCENT_SIGN = 37;
+const AMPERSAND = 38;
+const APOSTROPHE = 39;
+const LEFT_PARENTHESIS = 40;
+const RIGHT_PARENTHESIS = 41;
+const ASTERISK = 42;
+const PLUS_SIGN = 43;
+const COMMA = 44;
+const HYPHEN_MINUS = 45;
+const FULL_STOP = 46;
+const SOLIDUS = 47;
+const DIGIT_ZERO = 48;
+const DIGIT_ONE = 49;
+const DIGIT_TWO = 50;
+const DIGIT_THREE = 51;
+const DIGIT_FOUR = 52;
+const DIGIT_FIVE = 53;
+const DIGIT_SIX = 54;
+const DIGIT_SEVEN = 55;
+const DIGIT_EIGHT = 56;
+const DIGIT_NINE = 57;
+const COLON = 58;
+const SEMICOLON = 59;
+const LESS_THAN_SIGN = 60;
+const EQUALS_SIGN = 61;
+const GREATER_THAN_SIGN = 62;
+const QUESTION_MARK = 63;
+const COMMERCIAL_AT = 64;
+const LATIN_CAPITAL_LETTER_A = 65;
+const LATIN_CAPITAL_LETTER_B = 66;
+const LATIN_CAPITAL_LETTER_C = 67;
+const LATIN_CAPITAL_LETTER_D = 68;
+const LATIN_CAPITAL_LETTER_E = 69;
+const LATIN_CAPITAL_LETTER_F = 70;
+const LATIN_CAPITAL_LETTER_G = 71;
+const LATIN_CAPITAL_LETTER_H = 72;
+const LATIN_CAPITAL_LETTER_I = 73;
+const LATIN_CAPITAL_LETTER_J = 74;
+const LATIN_CAPITAL_LETTER_K = 75;
+const LATIN_CAPITAL_LETTER_L = 76;
+const LATIN_CAPITAL_LETTER_M = 77;
+const LATIN_CAPITAL_LETTER_N = 78;
+const LATIN_CAPITAL_LETTER_O = 79;
+const LATIN_CAPITAL_LETTER_P = 80;
+const LATIN_CAPITAL_LETTER_Q = 81;
+const LATIN_CAPITAL_LETTER_R = 82;
+const LATIN_CAPITAL_LETTER_S = 83;
+const LATIN_CAPITAL_LETTER_T = 84;
+const LATIN_CAPITAL_LETTER_U = 85;
+const LATIN_CAPITAL_LETTER_V = 86;
+const LATIN_CAPITAL_LETTER_W = 87;
+const LATIN_CAPITAL_LETTER_X = 88;
+const LATIN_CAPITAL_LETTER_Y = 89;
+const LATIN_CAPITAL_LETTER_Z = 90;
+const LEFT_SQUARE_BRACKET = 91;
+const REVERSE_SOLIDUS = 92;
+const RIGHT_SQUARE_BRACKET = 93;
+const CIRCUMFLEX_ACCENT = 94;
+const LOW_LINE = 95;
+const GRAVE_ACCENT = 96;
+const LATIN_SMALL_LETTER_A = 97;
+const LATIN_SMALL_LETTER_B = 98;
+const LATIN_SMALL_LETTER_C = 99;
+const LATIN_SMALL_LETTER_D = 100;
+const LATIN_SMALL_LETTER_E = 101;
+const LATIN_SMALL_LETTER_F = 102;
+const LATIN_SMALL_LETTER_G = 103;
+const LATIN_SMALL_LETTER_H = 104;
+const LATIN_SMALL_LETTER_I = 105;
+const LATIN_SMALL_LETTER_J = 106;
+const LATIN_SMALL_LETTER_K = 107;
+const LATIN_SMALL_LETTER_L = 108;
+const LATIN_SMALL_LETTER_M = 109;
+const LATIN_SMALL_LETTER_N = 110;
+const LATIN_SMALL_LETTER_O = 111;
+const LATIN_SMALL_LETTER_P = 112;
+const LATIN_SMALL_LETTER_Q = 113;
+const LATIN_SMALL_LETTER_R = 114;
+const LATIN_SMALL_LETTER_S = 115;
+const LATIN_SMALL_LETTER_T = 116;
+const LATIN_SMALL_LETTER_U = 117;
+const LATIN_SMALL_LETTER_V = 118;
+const LATIN_SMALL_LETTER_W = 119;
+const LATIN_SMALL_LETTER_X = 120;
+const LATIN_SMALL_LETTER_Y = 121;
+const LATIN_SMALL_LETTER_Z = 122;
+const LEFT_CURLY_BRACKET = 123;
+const VERTICAL_LINE = 124;
+const RIGHT_CURLY_BRACKET = 125;
+const TILDE = 126;
+const DELETE = 127;
+const LATIN_CAPITAL_LETTER_C_WITH_CEDILLA = 128;
+const LATIN_SMALL_LETTER_U_WITH_DIAERESIS = 129;
+const LATIN_SMALL_LETTER_E_WITH_ACUTE = 130;
+const LATIN_SMALL_LETTER_A_WITH_CIRCUMFLEX = 131;
+const LATIN_SMALL_LETTER_A_WITH_DIAERESIS = 132;
+const LATIN_SMALL_LETTER_A_WITH_GRAVE = 133;
+const LATIN_SMALL_LETTER_A_WITH_RING_ABOVE = 134;
+const LATIN_SMALL_LETTER_C_WITH_CEDILLA = 135;
+const LATIN_SMALL_LETTER_E_WITH_CIRCUMFLEX = 136;
+const LATIN_SMALL_LETTER_E_WITH_DIAERESIS = 137;
+const LATIN_SMALL_LETTER_E_WITH_GRAVE = 138;
+const LATIN_SMALL_LETTER_I_WITH_DIAERESIS = 139;
+const LATIN_SMALL_LETTER_I_WITH_CIRCUMFLEX = 140;
+const LATIN_SMALL_LETTER_I_WITH_GRAVE = 141;
+const LATIN_CAPITAL_LETTER_A_WITH_DIAERESIS = 142;
+const LATIN_CAPITAL_LETTER_A_WITH_RING_ABOVE = 143;
+const LATIN_CAPITAL_LETTER_E_WITH_ACUTE = 144;
+const LATIN_SMALL_LETTER_AE = 145;
+const LATIN_CAPITAL_LETTER_AE = 146;
+const LATIN_SMALL_LETTER_O_WITH_CIRCUMFLEX = 147;
+const LATIN_SMALL_LETTER_O_WITH_DIAERESIS = 148;
+const LATIN_SMALL_LETTER_O_WITH_GRAVE = 149;
+const LATIN_SMALL_LETTER_U_WITH_CIRCUMFLEX = 150;
+const LATIN_SMALL_LETTER_U_WITH_GRAVE = 151;
+const LATIN_SMALL_LETTER_Y_WITH_DIAERESIS = 152;
+const LATIN_CAPITAL_LETTER_O_WITH_DIAERESIS = 153;
+const LATIN_CAPITAL_LETTER_U_WITH_DIAERESIS = 154;
+const CENT_SIGN = 155;
+const POUND_SIGN = 156;
+const YEN_SIGN = 157;
+const PESETA_SIGN = 158;
+const LATIN_SMALL_LETTER_F_WITH_HOOK = 159;
+const LATIN_SMALL_LETTER_A_WITH_ACUTE = 160;
+const LATIN_SMALL_LETTER_I_WITH_ACUTE = 161;
+const LATIN_SMALL_LETTER_O_WITH_ACUTE = 162;
+const LATIN_SMALL_LETTER_U_WITH_ACUTE = 163;
+const LATIN_SMALL_LETTER_N_WITH_TILDE = 164;
+const LATIN_CAPITAL_LETTER_N_WITH_TILDE = 165;
+const FEMININE_ORDINAL_INDICATOR = 166;
+const MASCULINE_ORDINAL_INDICATOR = 167;
+const INVERTED_QUESTION_MARK = 168;
+const REVERSED_NOT_SIGN = 169;
+const NOT_SIGN = 170;
+const VULGAR_FRACTION_ONE_HALF = 171;
+const VULGAR_FRACTION_ONE_QUARTER = 172;
+const INVERTED_EXCLAMATION_MARK = 173;
+const LEFT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK = 174;
+const RIGHT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK = 175;
+const LIGHT_SHADE = 176;
+const MEDIUM_SHADE = 177;
+const DARK_SHADE = 178;
+const BOX_DRAWINGS_LIGHT_VERTICAL = 179;
+const BOX_DRAWINGS_LIGHT_VERTICAL_AND_LEFT = 180;
+const BOX_DRAWINGS_VERTICAL_SINGLE_AND_LEFT_DOUBLE = 181;
+const BOX_DRAWINGS_VERTICAL_DOUBLE_AND_LEFT_SINGLE = 182;
+const BOX_DRAWINGS_DOWN_DOUBLE_AND_LEFT_SINGLE = 183;
+const BOX_DRAWINGS_DOWN_SINGLE_AND_LEFT_DOUBLE = 184;
+const BOX_DRAWINGS_DOUBLE_VERTICAL_AND_LEFT = 185;
+const BOX_DRAWINGS_DOUBLE_VERTICAL = 186;
+const BOX_DRAWINGS_DOUBLE_DOWN_AND_LEFT = 187;
+const BOX_DRAWINGS_DOUBLE_UP_AND_LEFT = 188;
+const BOX_DRAWINGS_UP_DOUBLE_AND_LEFT_SINGLE = 189;
+const BOX_DRAWINGS_UP_SINGLE_AND_LEFT_DOUBLE = 190;
+const BOX_DRAWINGS_LIGHT_DOWN_AND_LEFT = 191;
+const BOX_DRAWINGS_LIGHT_UP_AND_RIGHT = 192;
+const BOX_DRAWINGS_LIGHT_UP_AND_HORIZONTAL = 193;
+const BOX_DRAWINGS_LIGHT_DOWN_AND_HORIZONTAL = 194;
+const BOX_DRAWINGS_LIGHT_VERTICAL_AND_RIGHT = 195;
+const BOX_DRAWINGS_LIGHT_HORIZONTAL = 196;
+const BOX_DRAWINGS_LIGHT_VERTICAL_AND_HORIZONTAL = 197;
+const BOX_DRAWINGS_VERTICAL_SINGLE_AND_RIGHT_DOUBLE = 198;
+const BOX_DRAWINGS_VERTICAL_DOUBLE_AND_RIGHT_SINGLE = 199;
+const BOX_DRAWINGS_DOUBLE_UP_AND_RIGHT = 200;
+const BOX_DRAWINGS_DOUBLE_DOWN_AND_RIGHT = 201;
+const BOX_DRAWINGS_DOUBLE_UP_AND_HORIZONTAL = 202;
+const BOX_DRAWINGS_DOUBLE_DOWN_AND_HORIZONTAL = 203;
+const BOX_DRAWINGS_DOUBLE_VERTICAL_AND_RIGHT = 204;
+const BOX_DRAWINGS_DOUBLE_HORIZONTAL = 205;
+const BOX_DRAWINGS_DOUBLE_VERTICAL_AND_HORIZONTAL = 206;
+const BOX_DRAWINGS_UP_SINGLE_AND_HORIZONTAL_DOUBLE = 207;
+const BOX_DRAWINGS_UP_DOUBLE_AND_HORIZONTAL_SINGLE = 208;
+const BOX_DRAWINGS_DOWN_SINGLE_AND_HORIZONTAL_DOUBLE = 209;
+const BOX_DRAWINGS_DOWN_DOUBLE_AND_HORIZONTAL_SINGLE = 210;
+const BOX_DRAWINGS_UP_DOUBLE_AND_RIGHT_SINGLE = 211;
+const BOX_DRAWINGS_UP_SINGLE_AND_RIGHT_DOUBLE = 212;
+const BOX_DRAWINGS_DOWN_SINGLE_AND_RIGHT_DOUBLE = 213;
+const BOX_DRAWINGS_DOWN_DOUBLE_AND_RIGHT_SINGLE = 214;
+const BOX_DRAWINGS_VERTICAL_DOUBLE_AND_HORIZONTAL_SINGLE = 215;
+const BOX_DRAWINGS_VERTICAL_SINGLE_AND_HORIZONTAL_DOUBLE = 216;
+const BOX_DRAWINGS_LIGHT_UP_AND_LEFT = 217;
+const BOX_DRAWINGS_LIGHT_DOWN_AND_RIGHT = 218;
+const FULL_BLOCK = 219;
+const LOWER_HALF_BLOCK = 220;
+const LEFT_HALF_BLOCK = 221;
+const RIGHT_HALF_BLOCK = 222;
+const UPPER_HALF_BLOCK = 223;
+const GREEK_SMALL_LETTER_ALPHA = 224;
+const LATIN_SMALL_LETTER_SHARP_S = 225;
+const GREEK_CAPITAL_LETTER_GAMMA = 226;
+const GREEK_SMALL_LETTER_PI = 227;
+const GREEK_CAPITAL_LETTER_SIGMA = 228;
+const GREEK_SMALL_LETTER_SIGMA = 229;
+const MICRO_SIGN = 230;
+const GREEK_SMALL_LETTER_TAU = 231;
+const GREEK_CAPITAL_LETTER_PHI = 232;
+const GREEK_CAPITAL_LETTER_THETA = 233;
+const GREEK_CAPITAL_LETTER_OMEGA = 234;
+const GREEK_SMALL_LETTER_DELTA = 235;
+const INFINITY = 236;
+const GREEK_SMALL_LETTER_PHI = 237;
+const GREEK_SMALL_LETTER_EPSILON = 238;
+const INTERSECTION = 239;
+const IDENTICAL_TO = 240;
+const PLUS_MINUS_SIGN = 241;
+const GREATER_THAN_OR_EQUAL_TO = 242;
+const LESS_THAN_OR_EQUAL_TO = 243;
+const TOP_HALF_INTEGRAL = 244;
+const BOTTOM_HALF_INTEGRAL = 245;
+const DIVISION_SIGN = 246;
+const ALMOST_EQUAL_TO = 247;
+const DEGREE_SIGN = 248;
+const BULLET_OPERATOR = 249;
+const MIDDLE_DOT = 250;
+const SQUARE_ROOT = 251;
+const SUPERSCRIPT_LATIN_SMALL_LETTER_N = 252;
+const SUPERSCRIPT_TWO = 253;
+const BLACK_SQUARE = 254;
+const NO_BREAK_SPACE = 255;
\ No newline at end of file
--- /dev/null
+/**
+ * @author Peter Nitsch
+ */
+/**
+const LEFT_SQUARE_BRACKET = 91;
+const REVERSE_SOLIDUS = 92;
+const RIGHT_SQUARE_BRACKET = 93;
+const CIRCUMFLEX_ACCENT = 94;
+const LOW_LINE = 95;
+const GRAVE_ACCENT = 96;
+*/
+TERM.InitState = function () {
+ this.onRead = function (ctx,position,character) {
+ console.log("TERM.InitState.onRead "+character);
+ if (character == ESCAPE)
+ ctx.setState(new TERM.EscapeState());
+ };
+ console.log("TERM.InitState new called");
+};
+
+
+TERM.EscapeState = function () {
+
+ this.onRead = function (ctx,position,character) {
+ console.log("TERM.EscapeState.onRead "+character);
+ if (character == LEFT_SQUARE_BRACKET)
+ ctx.setState(new TERM.CsiState());
+ if (character == RIGHT_SQUARE_BRACKET)
+ ctx.setState(new TERM.OscState());
+ };
+};
+
+TERM.OscState = function () {
+
+ this.onRead = function (ctx,position,character) {
+ console.log("TERM.OscState.onRead "+character);
+ if (character == LATIN_CAPITAL_LETTER_G || character == BELL) {
+ ctx.complete = true;
+ ctx.setState(new TERM.InitState());
+ }
+ };
+};
+
+TERM.CsiState = function () {
+
+ this.onRead = function (ctx,position,character) {
+ console.log("TERM.CsiState.onRead "+character);
+ if (ctx.checkCommandAction(position,character)) {
+ ctx.complete = true;
+ ctx.setState(new TERM.InitState());
+ }
+ };
+};
+
+
+
+TERM.EscapeSequencer = function (viewer){
+
+ var viewer = viewer;
+ var telnet;
+ var state;
+ var complete = false;
+ var _customCommand;
+ var _currentCustomCommand = {};
+
+ this.actionCharacterLib = [];
+
+ this.init = function() {
+ this.actionCharacterLib[ LATIN_CAPITAL_LETTER_H ] = this.cursorPosition;
+ this.actionCharacterLib[ LATIN_SMALL_LETTER_F ] = this.cursorPosition;
+ this.actionCharacterLib[ LATIN_CAPITAL_LETTER_A ] = this.cursorUp;
+ this.actionCharacterLib[ LATIN_CAPITAL_LETTER_B ] = this.cursorDown;
+ this.actionCharacterLib[ LATIN_CAPITAL_LETTER_C] = this.cursorForward;
+ this.actionCharacterLib[ LATIN_CAPITAL_LETTER_D ] = this.cursorBackward;
+ this.actionCharacterLib[ LATIN_SMALL_LETTER_S ] = this.saveCursorPosition;
+ this.actionCharacterLib[ LATIN_SMALL_LETTER_U ] = this.restoreCursorPosition;
+ this.actionCharacterLib[ LATIN_CAPITAL_LETTER_K ] = this.eraseLine;
+ this.actionCharacterLib[ LATIN_CAPITAL_LETTER_J ] = this.eraseDisplay;
+ this.actionCharacterLib[ LATIN_SMALL_LETTER_N ] = this.deviceRequest;
+ this.actionCharacterLib[ LATIN_SMALL_LETTER_M ] = this.setGraphicsMode;
+ this.actionCharacterLib[ LATIN_SMALL_LETTER_H ] = this.setMode;
+ this.actionCharacterLib[ LATIN_SMALL_LETTER_L ] = this.resetMode;
+ this.actionCharacterLib[ LATIN_SMALL_LETTER_P ] = this.setKeyboardStrings;
+ this.actionCharacterLib[ LATIN_CAPITAL_LETTER_M ] = this.scrollUp;
+ this.actionCharacterLib[ LATIN_SMALL_LETTER_R ] = this.scrollScreen;
+
+ // TO DO
+ this.actionCharacterLib[ LATIN_SMALL_LETTER_A ] = this.unused;
+ this.actionCharacterLib[ LATIN_SMALL_LETTER_D ] = this.unused;
+ this.actionCharacterLib[ LATIN_SMALL_LETTER_E ] = this.unused;
+ this.actionCharacterLib[ LATIN_CAPITAL_LETTER_L ] = this.unused;
+ this.actionCharacterLib[ LATIN_CAPITAL_LETTER_P ] = this.unused;
+ this.actionCharacterLib[ LATIN_CAPITAL_LETTER_E ] = this.unused;
+ this.actionCharacterLib[ LATIN_CAPITAL_LETTER_F ] = this.unused;
+ this.actionCharacterLib[ LATIN_CAPITAL_LETTER_X ] = this.unused;
+ this.actionCharacterLib[ BELL ] = this.title;
+
+ this.state = new TERM.InitState();
+ };
+
+ this.setState = function (_state) {
+ this.state = _state;
+ };
+
+ this.onRead = function (position,character) {
+ this.state.onRead(this,position,character);
+ };
+
+ this.executeCommand = function(command) {
+ try {
+ this.actionCharacterLib[ command[command.length-1] ]( command );
+ } catch(error) {
+ console.log(error);
+ }
+ };
+
+ this.checkCommandAction = function(position, character) {
+ if( this.actionCharacterLib[character] != undefined )
+ return true;
+ console.log("EscapeSequencer.checkCommandAction undefined "+character);
+ return false;
+ };
+
+ this.unused = function(params) {
+ // EMPTY
+ };
+
+ this.title = function(params) {
+ console.log("Title commande ");
+ };
+
+ this.deviceRequest = function(params) {
+ if( params[2]==DIGIT_FIVE ){
+ TERM.socket.writeByte(ESCAPE);
+ TERM.socket.writeByte(LEFT_SQUARE_BRACKET);
+ TERM.socket.writeByte(DIGIT_ZERO);
+ TERM.socket.writeByte(LATIN_SMALL_LETTER_N);
+ } else if( params[2]==DIGIT_SIX ) {
+ var i;
+ var rows = "" + viewer.cursor.y;
+ var cols = "" + viewer.cursor.x;
+
+ TERM.socket.writeByte(ESCAPE);
+ TERM.socket.writeByte(LEFT_SQUARE_BRACKET);
+ for(i=0;i<rows.length;i++) TERM.socket.writeByte(rows.charCodeAt(i));
+ TERM.socket.writeByte(SEMICOLON);
+ for(i=0;i<cols.length;i++) TERM.socket.writeByte(cols.charCodeAt(i));
+ TERM.socket.writeByte(LATIN_CAPITAL_LETTER_R);
+ } else {
+ // 0 - Report Device OK
+ // 3 - Report Device Failure
+ }
+ };
+
+ this.cursorPosition = function(params) {
+ var lastCharacter = params[params.length-1];
+
+ if( params.length==3 && lastCharacter==LATIN_CAPITAL_LETTER_H){
+ viewer.home();
+ } else {
+ var lineArray = [];
+ var lineStr = "";
+ var line = 0;
+
+ var columnArray = [];
+ var columnStr = "";
+ var column = 0;
+
+ if(params.indexOf(SEMICOLON) != -1){
+ var semicolonIndex = params.indexOf(SEMICOLON);
+
+ if( params[semicolonIndex-1] != LEFT_SQUARE_BRACKET ) {
+ lineArray = params.slice(2, semicolonIndex);
+ for( i=0; i<lineArray.length; i++ ){
+ lineStr += (lineArray[i] - 48).toString();
+ }
+ line = parseInt(lineStr);
+ }
+
+ columnArray = params.slice(semicolonIndex+1, params.length-1);
+ for( i=0; i<columnArray.length; i++ ){
+ columnStr += (columnArray[i] - 48).toString();
+ }
+ column = parseInt(columnStr);
+
+ } else if(params.slice(2, params.indexOf(lastCharacter)).length > 0){
+ lineArray = params.slice(2, params.length-1);
+ for( i=0; i<lineArray.length; i++ ){
+ lineStr += (lineArray[i] - 48).toString();
+ }
+ line = parseInt(lineStr);
+ }
+
+ column = (column>0) ? column-1 : 0;
+ line = (line>0) ? line-1 : 0;
+
+ viewer.reposition(column, line);
+ }
+ };
+
+ this.cursorUp = function(params) {
+ var valueArray = params.slice(2, params.length-1);
+ var valueStr = "";
+ for( i=0; i<valueArray.length; i++ ){
+ valueStr += (valueArray[i] - 48).toString();
+ }
+ var value = (valueStr.length > 0) ? parseInt(valueStr) : 1;
+
+ viewer.moveUp(value);
+ };
+
+ this.cursorDown = function(params) {
+ var valueArray = params.slice(2, params.length-1);
+ var valueStr = "";
+ for( i=0; i<valueArray.length; i++ ){
+ valueStr += (valueArray[i] - 48).toString();
+ }
+ var value = (valueStr.length > 0) ? parseInt(valueStr) : 1;
+
+ viewer.moveDown(value);
+ };
+
+ this.cursorForward = function(params) {
+ var valueArray = params.slice(2, params.length-1);
+ var valueStr = "";
+ for( i=0; i<valueArray.length; i++ ){
+ valueStr += (valueArray[i] - 48).toString();
+ }
+ var value = (valueStr.length > 0) ? parseInt(valueStr) : 1;
+
+ viewer.moveForward(value);
+ };
+
+ this.cursorBackward = function(params) {
+ var valueArray = params.slice(2, params.length-1);
+ var valueStr = "";
+ for( i=0; i<valueArray.length; i++ ){
+ valueStr += (valueArray[i] - 48).toString();
+ }
+ var value = (valueStr.length > 0) ? parseInt(valueStr) : 1;
+
+ viewer.moveBackward(value);
+ };
+
+ this.saveCursorPosition = function(params) {
+ viewer.savePosition();
+ };
+
+ this.restoreCursorPosition = function(params) {
+ viewer.restorePosition();
+ };
+
+ // Set Graphic Mode functions
+ var _bold = false;
+ var _reverse = false;
+
+ var _boldColors = [BLACK_BOLD, RED_BOLD, GREEN_BOLD, YELLOW_BOLD, BLUE_BOLD, MAGENTA_BOLD, CYAN_BOLD, WHITE_BOLD];
+ var _normalColors = [BLACK_NORMAL, RED_NORMAL, GREEN_NORMAL, YELLOW_NORMAL, BLUE_NORMAL, MAGENTA_NORMAL, CYAN_NORMAL, WHITE_NORMAL];
+
+ var _currentForegroundColor = WHITE_NORMAL;
+ var _currentBackgroundColor = BLACK_NORMAL;
+
+ this.setGraphicsMode = function(params) {
+ console.log("setGraphicsMode");
+ for( i=2; i<params.length; i++ ){
+ switch( params[i] ){
+
+ /* Reset */
+ case LATIN_SMALL_LETTER_M:
+ case DIGIT_ZERO:
+ if(params[i-1] == SEMICOLON || params[i-1] == LEFT_SQUARE_BRACKET){
+ _bold = false;
+ _reverse = false;
+
+ _currentForegroundColor = WHITE_NORMAL;
+ _currentBackgroundColor = BLACK_NORMAL;
+
+ viewer.foregroundColorChanged(_currentForegroundColor);
+ viewer.backgroundColorChanged(_currentBackgroundColor);
+ }
+ break;
+
+ /* Bold ON */
+ case DIGIT_ONE:
+ if(params[i-1] == SEMICOLON || params[i-1] == LEFT_SQUARE_BRACKET) {
+ _bold = true;
+
+ for( j=0; j<_normalColors.length; j++ ){
+ if( _currentForegroundColor == _normalColors[j] )
+ _currentForegroundColor = _boldColors[j];
+ }
+
+ viewer.foregroundColorChanged(_currentForegroundColor);
+ }
+ break;
+
+ /* Dim */
+ case DIGIT_TWO:
+ if(params[i-1] == SEMICOLON || params[i-1] == LEFT_SQUARE_BRACKET) {
+ _bold = false;
+
+ for( j=0; j<_normalColors.length; j++ ){
+ if( _currentForegroundColor == _boldColors[j] )
+ _currentForegroundColor = _normalColors[j];
+ }
+
+ viewer.foregroundColorChanged(_currentForegroundColor);
+ }
+ break;
+
+ /* Set foreground color */
+ case DIGIT_THREE:
+ if(params[i-1] == SEMICOLON || params[i-1] == LEFT_SQUARE_BRACKET){
+ if(params[i+1] != SEMICOLON && params[i+1] != LATIN_SMALL_LETTER_M){
+
+ var position = params[i+1] - 48;
+ if(_reverse) {
+ _currentBackgroundColor = _normalColors[position];
+ viewer.backgroundColorChanged(_currentBackgroundColor);
+ }else {
+ _currentForegroundColor = (_bold) ? _boldColors[position] : _normalColors[position];
+ viewer.foregroundColorChanged(_currentForegroundColor);
+ }
+
+ }
+ }
+ break;
+
+ /* Set background color */
+ case DIGIT_FOUR:
+ if(params[i-1] == SEMICOLON || params[i-1] == LEFT_SQUARE_BRACKET){
+ if(params[i+1] != SEMICOLON && params[i+1] != LATIN_SMALL_LETTER_M){
+ position = params[i+1] - 48;
+ if(_reverse) {
+ _currentForegroundColor = (_bold) ? _boldColors[position] : _normalColors[position];
+ viewer.foregroundColorChanged(_currentForegroundColor);
+ } else {
+ _currentBackgroundColor = _normalColors[position];
+ viewer.backgroundColorChanged(_currentBackgroundColor);
+ }
+
+ /* Underline ON */
+ } else {
+ // TO DO
+ }
+ }
+ break;
+
+ /* Blink ON */
+ case DIGIT_FIVE:
+ // TO DO
+ break;
+
+ /* Reverse ON */
+ case DIGIT_SEVEN:
+ if(params[i-1] == SEMICOLON || params[i-1] == LEFT_SQUARE_BRACKET)
+ _reverse = true;
+ break;
+
+ /* Concealed ON */
+ case DIGIT_EIGHT:
+ // TO DO
+ break;
+
+ /* Reset to normal? */
+ case DIGIT_NINE:
+ // TO DO
+ break;
+ }
+ }
+ };
+
+ this.scrollScreen = function(params) {
+ if(params.length==3){
+ console.log("Escape::scrollScreen 3 params");
+ viewer.scrollScreen();
+ } else {
+ console.log("Escape::scrollScreen params length="+params.length);
+ var lastCharacter = params[params.length-1];
+
+ var sArray = [];
+ var sStr = "";
+ var s = 0;
+
+ var eArray = [];
+ var eStr = "";
+ var e = 0;
+
+ var semicolonIndex = params.indexOf(SEMICOLON);
+
+ sArray = params.slice(2, semicolonIndex);
+ for( i=0; i<sArray.length; i++ ){
+ console.log("Escape::scrollScreen S getting "+sArray[i]);
+ sStr += (sArray[i] - 48).toString();
+ }
+ s = parseInt(sStr);
+
+
+ eArray = params.slice(semicolonIndex+1, params.length-1);
+ for( i=0; i<eArray.length; i++ ){
+ console.log("Escape::scrollScreen E getting "+eArray[i]);
+ if ( eArray[i] > 48)
+ eStr += (eArray[i] - 48).toString();
+ }
+ e = parseInt(eStr);
+
+ viewer.scrollScreen(s, e);
+ }
+ };
+
+ this.scrollUp = function(params) {
+ viewer.scrollUp(1);
+ };
+
+ this.eraseLine = function(params) {
+ if( params[2]==DIGIT_ONE ){
+ viewer.eraseStartOfLine();
+ } else if( params[2]==DIGIT_TWO ) {
+ viewer.eraseLine();
+ } else {
+ viewer.eraseEndOfLine();
+ }
+ };
+
+ this.eraseDisplay = function(params) {
+ console.log("Escape::eraseDisaply");
+ if( params[2]==DIGIT_ONE ){
+ viewer.eraseUp();
+ viewer.reposition(0, 0);
+ } else if( params[2]==DIGIT_TWO ) {
+ viewer.eraseScreen();
+ viewer.reposition(0, 0);
+ } else {
+ viewer.eraseDown();
+ }
+ };
+
+ // Terminal functions
+ this.setMode = function(){
+ // TO DO
+ };
+
+ this.resetMode = function(){
+ // TO DO
+ };
+
+ this.setKeyboardStrings = function(){
+ // TO DO
+ };
+
+ this.init();
+
+};
--- /dev/null
+/**
+ * @author Peter Nitsch
+ */
+
+const KEYBOARD_LEFT = 37;
+const KEYBOARD_RIGHT = 39;
+const KEYBOARD_UP = 38;
+const KEYBOARD_DOWN = 40;
+const KEYBOARD_PAGE_UP = 33;
+const KEYBOARD_PAGE_DOWN = 34;
+const KEYBOARD_HOME = 36;
--- /dev/null
+/**
+ * @author Peter Nitsch
+ */
+
+const IAC = 255;
+const GA = 249;
+const WILL = 251;
+const WONT = 252;
+const DO = 253;
+const DONT = 254;
+const SB = 250;
+const SE = 240;
+const NOP = 241;
+const DM = 242;
+const BRK = 243;
+
+const IP = 244;
+const AO = 245;
+const AYT = 246;
+const EC = 247;
+const EL = 248;
+
+const ECHO = 1;
+const SUPGA = 3;
+
+const NAWS = 31;
+const TTYPE = 24;
+const IS = 0;
+const SEND = 1;
+const LOGOUT = 18;
+
+const LINEMODE = 34;
+const LM_MODE = 1;
+const LM_EDIT = 1;
+const LM_TRAPSIG = 2;
+const LM_MODEACK = 4;
+const LM_FORWARDMASK = 2;
+
+const LM_SLC = 3;
+const LM_SLC_NOSUPPORT = 0;
+const LM_SLC_DEFAULT = 3;
+const LM_SLC_VALUE = 2;
+const LM_SLC_CANTCHANGE = 1;
+const LM_SLC_LEVELBITS = 3;
+const LM_SLC_ACK = 128;
+const LM_SLC_FLUSHIN = 64;
+const LM_SLC_FLUSHOUT = 32;
+
+const LM_SLC_SYNCH = 1;
+const LM_SLC_BRK = 2;
+const LM_SLC_IP = 3;
+const LM_SLC_AO = 4;
+const LM_SLC_AYT = 5;
+const LM_SLC_EOR = 6;
+const LM_SLC_ABORT = 7;
+const LM_SLC_EOF = 8;
+const LM_SLC_SUSP = 9;
+
+const NEWENV = 39;
+const NE_INFO = 2;
+const NE_VAR = 0;
+const NE_VALUE = 1;
+const NE_ESC = 2;
+const NE_USERVAR = 3;
+
+const NE_VAR_OK = 2;
+const NE_VAR_DEFINED = 1;
+const NE_VAR_DEFINED_EMPTY = 0;
+const NE_VAR_UNDEFINED = -1;
+const NE_IN_ERROR = -2;
+const NE_IN_END = -3;
+const NE_VAR_NAME_MAXLENGTH = 50;
+const NE_VAR_VALUE_MAXLENGTH = 1000;
+
+// Unused
+const EXT_ASCII = 17;
+const SEND_LOC = 23;
+const AUTHENTICATION = 37;
+const ENCRYPT = 38;
\ No newline at end of file
--- /dev/null
+/**
+ * @author Peter Nitsch
+ */
+
+const BLACK_NORMAL = "#000000";
+const BLACK_BOLD = "#545454";
+const RED_NORMAL = "#a80000";
+const RED_BOLD = "#fc5454";
+const GREEN_NORMAL = "#00a800";
+const GREEN_BOLD = "#54fc54";
+const YELLOW_NORMAL = "#a85400";
+const YELLOW_BOLD = "#fcfc54";
+const BLUE_NORMAL = "#0000a8";
+const BLUE_BOLD = "#5454fc";
+const MAGENTA_NORMAL = "#a800a8";
+const MAGENTA_BOLD = "#fc54fc";
+const CYAN_NORMAL = "#00a8a8";
+const CYAN_BOLD = "#54fcfc";
+const WHITE_NORMAL = "#a8a8a8";
+const WHITE_BOLD = "#fcfcfc";
\ No newline at end of file
--- /dev/null
+/**
+ * @author Peter Nitsch
+ */
+
+TERM.Telnet = function (viewer){
+
+ var parser = viewer.parser;
+ var _actionCharacterLib = [];
+
+ var bytes;
+
+ this.init = function() {
+ _actionCharacterLib[ IAC ] = interpretCommand;
+ parser.writeException(IAC, interpretCommand);
+ };
+
+ function interpretCommand(code, b) {
+ bytes = b;
+ var command = bytes.readUnsignedByte();
+ handleC(command);
+ };
+
+ function read16int () {
+ var c = 0;
+ try {
+ c = bytes.readUnsignedByte();
+ return c;
+ } catch (e) {
+ console.log(e);
+ }
+
+ return c;
+ };
+
+ function IamHere() {
+ try {
+ TERM.socket.writeByte(IAC);
+ TERM.socket.writeByte(DO);
+ TERM.socket.writeByte(AYT);
+ } catch (error) {
+ console.log("IamHere() Error "+ error);
+ }
+ };
+
+ function nvtBreak() {
+ // TO DO
+ };
+
+ function setTerminalGeometry (width, height) {
+ // TO DO
+ };
+
+ function setEcho(b) {
+ // TO DO
+ };
+
+ var buffer = [0,0];
+
+ var DO_ECHO = false;
+ var DO_SUPGA = false;
+ var DO_NAWS = false;
+ var DO_TTYPE = false;
+ var DO_LINEMODE = false;
+ var DO_NEWENV = false;
+
+ var WAIT_DO_REPLY_SUPGA = false;
+ var WAIT_DO_REPLY_ECHO = false;
+ var WAIT_DO_REPLY_NAWS = false;
+ var WAIT_DO_REPLY_TTYPE = false;
+ var WAIT_DO_REPLY_LINEMODE = false;
+ var WAIT_LM_MODE_ACK = false;
+ var WAIT_LM_DO_REPLY_FORWARDMASK = false;
+ var WAIT_DO_REPLY_NEWENV = false;
+ var WAIT_NE_SEND_REPLY = false;
+
+ var WAIT_WILL_REPLY_SUPGA = false;
+ var WAIT_WILL_REPLY_ECHO = false;
+ var WAIT_WILL_REPLY_NAWS = false;
+ var WAIT_WILL_REPLY_TTYPE = false;
+
+ function doCharacterModeInit() {
+ sendCommand(WILL, ECHO, true);
+ sendCommand(DONT, ECHO, true);
+ sendCommand(DO, NAWS, true);
+ sendCommand(WILL, SUPGA, true);
+ sendCommand(DO, SUPGA, true);
+ sendCommand(DO, TTYPE, true);
+ sendCommand(DO, NEWENV, true);
+ };
+
+ function doLineModeInit() {
+ sendCommand(DO, NAWS, true);
+ sendCommand(WILL, SUPGA, true);
+ sendCommand(DO, SUPGA, true);
+ sendCommand(DO, TTYPE, true);
+ sendCommand(DO, LINEMODE, true);
+ sendCommand(DO, NEWENV, true);
+ };
+
+ function handleC(i) {
+ buffer[0] = i;
+ if (!parseTWO(buffer)) {
+ try {
+ buffer[1] = bytes.readUnsignedByte();
+ parse(buffer);
+ } catch(e) {
+ console.log(e);
+ }
+ }
+
+ buffer[0] = 0;
+ buffer[1] = 0;
+ };
+
+ function parseTWO(buf) {
+ switch (buf[0]) {
+ case IAC:
+ break;
+ case AYT:
+ IamHere();
+ break;
+ case AO:
+ case IP:
+ case EL:
+ case EC:
+ case NOP:
+ break;
+ case BRK:
+ nvtBreak();
+ break;
+ default:
+ return false;
+ }
+ return true;
+ };
+
+ function parse(buf) {
+ switch (buf[0]) {
+ case WILL:
+ if (supported(buf[1]) && isEnabled(buf[1])) {
+ ;
+ } else {
+ if (waitDOreply(buf[1]) && supported(buf[1])) {
+ enable(buf[1]);
+ setWait(DO, buf[1], false);
+ } else {
+ if (supported(buf[1])) {
+ sendCommand(DO, buf[1], false);
+ enable(buf[1]);
+ } else {
+ sendCommand(DONT, buf[1], false);
+ }
+ }
+ }
+ break;
+ case WONT:
+ if (waitDOreply(buf[1]) && supported(buf[1])) {
+ setWait(DO, buf[1], false);
+ } else {
+ if (supported(buf[1]) && isEnabled(buf[1])) {
+ enable(buf[1]);
+ }
+ }
+ break;
+ case DO:
+ if (supported(buf[1]) && isEnabled(buf[1])) {
+ } else {
+ if (waitWILLreply(buf[1]) && supported(buf[1])) {
+ enable(buf[1]);
+ setWait(WILL, buf[1], false);
+ } else {
+ if (supported(buf[1])) {
+ sendCommand(WILL, buf[1], false);
+ enable(buf[1]);
+ } else {
+ sendCommand(WONT, buf[1], false);
+ }
+ }
+ }
+ break;
+ case DONT:
+ if (waitWILLreply(buf[1]) && supported(buf[1])) {
+ setWait(WILL, buf[1], false);
+ } else {
+ if (supported(buf[1]) && isEnabled(buf[1])) {
+ enable(buf[1]);
+ }
+ }
+ break;
+
+ case DM:
+ break;
+ case SB:
+ if ((supported(buf[1])) && (isEnabled(buf[1]))) {
+ switch (buf[1]) {
+ case NAWS:
+ handleNAWS();
+ break;
+ case TTYPE:
+ handleTTYPE();
+ break;
+ case LINEMODE:
+ handleLINEMODE();
+ break;
+ case NEWENV:
+ handleNEWENV();
+ break;
+ default:
+ ;
+ }
+ } else {
+
+ }
+ break;
+ default:
+ ;
+ }
+ };
+
+
+ function handleNAWS() {
+ var width = read16int();
+ if (width == 255) {
+ width = read16int();
+ }
+ var height = read16int();
+ if (height == 255) {
+ height = read16int();
+ }
+ skipToSE();
+ setTerminalGeometry(width, height);
+ };
+
+ function handleTTYPE() {
+ var tmpstr = "";
+ var b = bytes.readUnsignedByte();
+
+ switch(b) {
+ case SEND:
+ var cont = true;
+ do {
+ var i;
+ i = bytes.readUnsignedByte();
+ if (i == SE) {
+ cont = false;
+ }
+
+ } while (cont);
+
+ _session.flush();
+
+ TERM.socket.writeByte(IAC);
+ TERM.socket.writeByte(SB);
+ TERM.socket.writeByte(TTYPE);
+ TERM.socket.writeByte(IS);
+ writeMultiByte("ansi", "us-ascii");
+ TERM.socket.writeByte(IAC);
+ TERM.socket.writeByte(SE);
+ break;
+
+ case IS:
+ tmpstr = readIACSETerminatedString(40);
+ break;
+ }
+ };
+
+ function handleLINEMODE() {
+ var c = bytes.readUnsignedByte();
+ switch (c) {
+ case LM_MODE:
+ handleLMMode();
+ break;
+ case LM_SLC:
+ handleLMSLC();
+ break;
+ case WONT:
+ case WILL:
+ handleLMForwardMask(c);
+ break;
+ default:
+ skipToSE();
+ }
+ };
+
+ function handleLMMode() {
+ if (WAIT_LM_MODE_ACK) {
+ var mask = bytes.readUnsignedByte();
+ if (mask != (LM_EDIT | LM_TRAPSIG | LM_MODEACK)) {
+ }
+ WAIT_LM_MODE_ACK = false;
+ }
+ skipToSE();
+ };
+
+ function handleLMSLC() {
+ var triple = [0,0,0];
+ if (!readTriple(triple)) return;
+
+ if ((triple[0] == 0) && (triple[1] == LM_SLC_DEFAULT) && (triple[2] == 0)) {
+ skipToSE();
+ TERM.socket.writeByte(IAC);
+ TERM.socket.writeByte(SB);
+ TERM.socket.writeByte(LINEMODE);
+ TERM.socket.writeByte(LM_SLC);
+
+ for (var i = 1; i < 12; i++) {
+ TERM.socket.writeByte(i);
+ TERM.socket.writeByte(LM_SLC_DEFAULT);
+ TERM.socket.writeByte(0);
+ }
+
+ TERM.socket.writeByte(IAC);
+ TERM.socket.writeByte(SE);
+ } else {
+ TERM.socket.writeByte(IAC);
+ TERM.socket.writeByte(SB);
+ TERM.socket.writeByte(LINEMODE);
+ TERM.socket.writeByte(LM_SLC);
+ TERM.socket.writeByte(triple[0]);
+ TERM.socket.writeByte(triple[1] | LM_SLC_ACK);
+ TERM.socket.writeByte(triple[2]);
+ while (readTriple(triple)) {
+ TERM.socket.writeByte(triple[0]);
+ TERM.socket.writeByte(triple[1] | LM_SLC_ACK);
+ TERM.socket.writeByte(triple[2]);
+ }
+ TERM.socket.writeByte(IAC);
+ TERM.socket.writeByte(SE);
+ }
+ };
+
+ function handleLMForwardMask(WHAT) {
+ switch (WHAT) {
+ case WONT:
+ if (WAIT_LM_DO_REPLY_FORWARDMASK) {
+ WAIT_LM_DO_REPLY_FORWARDMASK = false;
+ }
+ break;
+ }
+ skipToSE();
+ };
+
+ function handleNEWENV() {
+ var c = bytes.readUnsignedByte();
+ switch (c) {
+ case IS:
+ handleNEIs();
+ break;
+ case NE_INFO:
+ handleNEInfo();
+ break;
+ default:
+ skipToSE();
+ }
+ };
+
+ function readNEVariableName(sbuf) {
+ var i = -1;
+ do {
+ i = bytes.readUnsignedByte();
+ if (i == -1) {
+ return NE_IN_ERROR;
+ } else if (i == IAC) {
+ i = bytes.readUnsignedByte();
+ if (i == IAC) {
+ sbuf.concat(i);
+ } else if (i == SE) {
+ return NE_IN_END;
+ } else {
+ return NE_IN_ERROR;
+ }
+ } else if (i == NE_ESC) {
+ i = bytes.readUnsignedByte();
+ if (i == NE_ESC || i == NE_VAR || i == NE_USERVAR || i == NE_VALUE) {
+ sbuf.concat(i);
+ } else {
+ return NE_IN_ERROR;
+ }
+ } else if (i == NE_VAR || i == NE_USERVAR) {
+ return NE_VAR_UNDEFINED;
+ } else if (i == NE_VALUE) {
+ return NE_VAR_DEFINED;
+ } else {
+ if (sbuf.length >= NE_VAR_NAME_MAXLENGTH) {
+ return NE_IN_ERROR;
+ } else {
+ sbuf.concat(i);
+ }
+ }
+ } while (true);
+
+ return i;
+ };
+
+ function readNEVariableValue(sbuf) {
+ var i = bytes.readUnsignedByte();
+ if (i == -1) {
+ return NE_IN_ERROR;
+ } else if (i == IAC) {
+ i = bytes.readUnsignedByte();
+ if (i == IAC) {
+ return NE_VAR_DEFINED_EMPTY;
+ } else if (i == SE) {
+ return NE_IN_END;
+ } else {
+ return NE_IN_ERROR;
+ }
+ } else if (i == NE_VAR || i == NE_USERVAR) {
+ return NE_VAR_DEFINED_EMPTY;
+ } else if (i == NE_ESC) {
+ i = bytes.readUnsignedByte();
+ if (i == NE_ESC || i == NE_VAR || i == NE_USERVAR || i == NE_VALUE) {
+ sbuf.concat(i);
+ } else {
+ return NE_IN_ERROR;
+ }
+ } else {
+ sbuf.concat(i);
+ }
+
+ do {
+ i = bytes.readUnsignedByte();
+ if (i == -1) {
+ return NE_IN_ERROR;
+ } else if (i == IAC) {
+ i = bytes.readUnsignedByte();
+ if (i == IAC) {
+ sbuf.concat(i);
+ } else if (i == SE) {
+ return NE_IN_END;
+ } else {
+ return NE_IN_ERROR;
+ }
+ } else if (i == NE_ESC) {
+ i = bytes.readUnsignedByte();
+ if (i == NE_ESC || i == NE_VAR || i == NE_USERVAR || i == NE_VALUE) {
+ sbuf.concat(i);
+ } else {
+ return NE_IN_ERROR;
+ }
+ } else if (i == NE_VAR || i == NE_USERVAR) {
+ return NE_VAR_OK;
+ } else {
+ if (sbuf.length > NE_VAR_VALUE_MAXLENGTH) {
+ return NE_IN_ERROR;
+ } else {
+ sbuf.concat(i);
+ }
+ }
+ } while (true);
+
+ return i;
+ };
+
+
+ function readNEVariables() {
+ var sbuf = "";
+ var i = bytes.readUnsignedByte();
+ if (i == IAC) {
+ skipToSE();
+ console.log("readNEVariables()::INVALID VARIABLE");
+ return;
+ }
+ var cont = true;
+ if (i == NE_VAR || i == NE_USERVAR) {
+ do {
+ switch (readNEVariableName(sbuf)) {
+ case NE_IN_ERROR:
+ console.log("readNEVariables()::NE_IN_ERROR");
+ return;
+ case NE_IN_END:
+ console.log("readNEVariables()::NE_IN_END");
+ return;
+ case NE_VAR_DEFINED:
+ console.log("readNEVariables()::NE_VAR_DEFINED");
+ var str = sbuf;
+ sbuf = "";
+ switch (readNEVariableValue(sbuf)) {
+ case NE_IN_ERROR:
+ console.log("readNEVariables()::NE_IN_ERROR");
+ return;
+ case NE_IN_END:
+ console.log("readNEVariables()::NE_IN_END");
+ return;
+ case NE_VAR_DEFINED_EMPTY:
+ console.log("readNEVariables()::NE_VAR_DEFINED_EMPTY");
+ break;
+ case NE_VAR_OK:
+ console.log("readNEVariables()::NE_VAR_OK:VAR=" + str + " VAL=" + sbuf.toString());
+ sbuf = "";
+ break;
+ }
+ break;
+ case NE_VAR_UNDEFINED:
+ console.log("readNEVariables()::NE_VAR_UNDEFINED");
+ break;
+ }
+ } while (cont);
+ }
+ };
+
+ function handleNEIs() {
+ if (isEnabled(NEWENV)) {
+ readNEVariables();
+ }
+ };
+
+ function handleNEInfo() {
+ if (isEnabled(NEWENV)) {
+ readNEVariables();
+ }
+ };
+
+ function getTTYPE() {
+ if (isEnabled(TTYPE)) {
+ TERM.socket.writeByte(IAC);
+ TERM.socket.writeByte(SB);
+ TERM.socket.writeByte(TTYPE);
+ TERM.socket.writeByte(SEND);
+ TERM.socket.writeByte(IAC);
+ TERM.socket.writeByte(SE);
+ }
+ };
+
+ function negotiateLineMode() {
+ if (isEnabled(LINEMODE)) {
+ TERM.socket.writeByte(IAC);
+ TERM.socket.writeByte(SB);
+ TERM.socket.writeByte(LINEMODE);
+ TERM.socket.writeByte(LM_MODE);
+ TERM.socket.writeByte(LM_EDIT | LM_TRAPSIG);
+ TERM.socket.writeByte(IAC);
+ TERM.socket.writeByte(SE);
+ WAIT_LM_MODE_ACK = true;
+
+ TERM.socket.writeByte(IAC);
+ TERM.socket.writeByte(SB);
+ TERM.socket.writeByte(LINEMODE);
+ TERM.socket.writeByte(DONT);
+ TERM.socket.writeByte(LM_FORWARDMASK);
+ TERM.socket.writeByte(IAC);
+ TERM.socket.writeByte(SE);
+ WAIT_LM_DO_REPLY_FORWARDMASK = true;
+ }
+ };
+
+ function negotiateEnvironment() {
+ if (isEnabled(NEWENV)) {
+ ba.TERM.socket.writeByte(IAC);
+ ba.TERM.socket.writeByte(SB);
+ ba.TERM.socket.writeByte(NEWENV);
+ ba.TERM.socket.writeByte(SEND);
+ ba.TERM.socket.writeByte(NE_VAR);
+ ba.TERM.socket.writeByte(NE_USERVAR);
+ ba.TERM.socket.writeByte(IAC);
+ ba.TERM.socket.writeByte(SE);
+ WAIT_NE_SEND_REPLY = true;
+ }
+ };
+
+ function skipToSE() {
+ while (bytes.readUnsignedByte() != SE) ;
+ };
+
+ function readTriple(triple) {
+ triple[0] = bytes.readUnsignedByte();
+ triple[1] = bytes.readUnsignedByte();
+ if ((triple[0] == IAC) && (triple[1] == SE)) {
+ return false;
+ } else {
+ triple[2] = bytes.readUnsignedByte();
+ return true;
+ }
+
+ return false;
+ };
+
+
+ function readIACSETerminatedString(maxlength) {
+ var where = 0;
+ var cbuf = [];
+ var b = ' ';
+ var cont = true;
+
+ do {
+ var i;
+ i = bytes.readUnsignedByte();
+ switch (i) {
+ case IAC:
+ i = bytes.readUnsignedByte();
+ if (i == SE) {
+ cont = false;
+ }
+ break;
+ case -1:
+ return ("default");
+ default:
+ }
+ if (cont) {
+ b = i.toString();
+ if (b == '\n' || b == '\r' || where == maxlength) {
+ cont = false;
+ } else {
+ cbuf[where++] = b;
+ }
+ }
+ } while (cont);
+
+ var str = "";
+ for(var j=0; j<where; j++) {
+ str.concat(cbuf[i]);
+ }
+
+ return (str);
+ };
+
+ function supported(i) {
+ switch (i) {
+ case SUPGA:
+ case ECHO:
+ case NAWS:
+ case TTYPE:
+ return true;
+ case LINEMODE:
+ return false;
+ default:
+ return false;
+ }
+ };
+
+ function sendCommand(i, j, westarted) {
+ TERM.socket.writeByte(IAC);
+ TERM.socket.writeByte(i);
+ TERM.socket.writeByte(j);
+
+ if ((i == DO) && westarted) setWait(DO, j, true);
+ if ((i == WILL) && westarted) setWait(WILL, j, true);
+ };
+
+ function enable(i) {
+ switch (i) {
+ case SUPGA:
+ if (DO_SUPGA) {
+ DO_SUPGA = false;
+ } else {
+ DO_SUPGA = true;
+ }
+ break;
+ case ECHO:
+ if (DO_ECHO) {
+ DO_ECHO = false;
+ } else {
+ DO_ECHO = true;
+ }
+ break;
+ case NAWS:
+ if (DO_NAWS) {
+ DO_NAWS = false;
+ } else {
+ DO_NAWS = true;
+ }
+ break;
+ case TTYPE:
+ if (DO_TTYPE) {
+ DO_TTYPE = false;
+ } else {
+ DO_TTYPE = true;
+ getTTYPE();
+ }
+ break;
+ case LINEMODE:
+ if (DO_LINEMODE) {
+ DO_LINEMODE = false;
+ } else {
+ DO_LINEMODE = true;
+ negotiateLineMode();
+ }
+ break;
+ case NEWENV:
+ if (DO_NEWENV) {
+ DO_NEWENV = false;
+ } else {
+ DO_NEWENV = true;
+ negotiateEnvironment();
+ }
+ break;
+ }
+ };
+
+ function isEnabled(i) {
+ switch (i) {
+ case SUPGA:
+ return DO_SUPGA;
+ case ECHO:
+ return DO_ECHO;
+ case NAWS:
+ return DO_NAWS;
+ case TTYPE:
+ return DO_TTYPE;
+ case LINEMODE:
+ return DO_LINEMODE;
+ case NEWENV:
+ return DO_NEWENV;
+ default:
+ return false;
+ }
+
+ return false;
+ };
+
+ function waitWILLreply(i) {
+ switch (i) {
+ case SUPGA:
+ return WAIT_WILL_REPLY_SUPGA;
+ case ECHO:
+ return WAIT_WILL_REPLY_ECHO;
+ case NAWS:
+ return WAIT_WILL_REPLY_NAWS;
+ case TTYPE:
+ return WAIT_WILL_REPLY_TTYPE;
+ default:
+ return false;
+ }
+
+ return false;
+ };
+
+ function waitDOreply(i) {
+ switch (i) {
+ case SUPGA:
+ return WAIT_DO_REPLY_SUPGA;
+ case ECHO:
+ return WAIT_DO_REPLY_ECHO;
+ case NAWS:
+ return WAIT_DO_REPLY_NAWS;
+ case TTYPE:
+ return WAIT_DO_REPLY_TTYPE;
+ case LINEMODE:
+ return WAIT_DO_REPLY_LINEMODE;
+ case NEWENV:
+ return WAIT_DO_REPLY_NEWENV;
+ default:
+ return false;
+ }
+
+ return false;
+ };
+
+ function setWait(WHAT, OPTION, WAIT) {
+ switch (WHAT) {
+ case DO:
+ switch (OPTION) {
+ case SUPGA:
+ WAIT_DO_REPLY_SUPGA = WAIT;
+ break;
+ case ECHO:
+ WAIT_DO_REPLY_ECHO = WAIT;
+ break;
+ case NAWS:
+ WAIT_DO_REPLY_NAWS = WAIT;
+ break;
+ case TTYPE:
+ WAIT_DO_REPLY_TTYPE = WAIT;
+ break;
+ case LINEMODE:
+ WAIT_DO_REPLY_LINEMODE = WAIT;
+ break;
+ case NEWENV:
+ WAIT_DO_REPLY_NEWENV = WAIT;
+ break;
+ }
+ break;
+ case WILL:
+ switch (OPTION) {
+ case SUPGA:
+ WAIT_WILL_REPLY_SUPGA = WAIT;
+ break;
+ case ECHO:
+ WAIT_WILL_REPLY_ECHO = WAIT;
+ break;
+ case NAWS:
+ WAIT_WILL_REPLY_NAWS = WAIT;
+ break;
+ case TTYPE:
+ WAIT_WILL_REPLY_TTYPE = WAIT;
+ break;
+ }
+ break;
+ }
+
+ return false;
+ };
+
+ this.init();
+
+};
\ No newline at end of file
--- /dev/null
+/**
+ * @author Peter Nitsch
+ */
+
+TERM.Session = function (fontMapURL){
+
+ var viewer;
+ var commands;
+
+ function initKeyboard() {
+ //document.addEventListener("keydown", function(e) {
+ document.onkeypress = function(e) {
+ var key = e.keyCode || e.charCode;
+ console.log("TERM.Session fot getkey:"+e.keyCode);
+ switch (key) {
+ case KEYBOARD_LEFT :
+ TERM.socket.writeByte(ESCAPE);
+ TERM.socket.writeByte(LEFT_SQUARE_BRACKET);
+ TERM.socket.writeByte(LATIN_CAPITAL_LETTER_D);
+ break;
+
+ case KEYBOARD_RIGHT :
+ TERM.socket.writeByte(ESCAPE);
+ TERM.socket.writeByte(LEFT_SQUARE_BRACKET);
+ TERM.socket.writeByte(LATIN_CAPITAL_LETTER_C);
+ break;
+
+ case KEYBOARD_UP :
+ TERM.socket.writeByte(ESCAPE);
+ TERM.socket.writeByte(LEFT_SQUARE_BRACKET);
+ TERM.socket.writeByte(LATIN_CAPITAL_LETTER_A);
+ break;
+
+ case KEYBOARD_DOWN :
+ TERM.socket.writeByte(ESCAPE);
+ TERM.socket.writeByte(LEFT_SQUARE_BRACKET);
+ TERM.socket.writeByte(LATIN_CAPITAL_LETTER_B);
+ break;
+
+ case KEYBOARD_PAGE_UP :
+ TERM.socket.writeByte(ESCAPE);
+ TERM.socket.writeByte(LEFT_SQUARE_BRACKET);
+ TERM.socket.writeByte(LATIN_CAPITAL_LETTER_M);
+ break;
+
+ case KEYBOARD_PAGE_DOWN :
+ TERM.socket.writeByte(ESCAPE);
+ TERM.socket.writeByte(LEFT_SQUARE_BRACKET);
+ TERM.socket.writeByte(LATIN_CAPITAL_LETTER_H);
+ TERM.socket.writeByte(SEMICOLON);
+ TERM.socket.writeByte(ESCAPE);
+ TERM.socket.writeByte(LEFT_SQUARE_BRACKET);
+ TERM.socket.writeByte(DIGIT_TWO);
+ TERM.socket.writeByte(LATIN_CAPITAL_LETTER_J);
+ break;
+
+ case KEYBOARD_HOME :
+ TERM.socket.writeByte(ESCAPE);
+ TERM.socket.writeByte(LEFT_SQUARE_BRACKET);
+ TERM.socket.writeByte(LATIN_CAPITAL_LETTER_H);
+ break;
+
+ default:
+ TERM.socket.writeByte( key );
+ break;
+
+ }
+ return true;
+
+ //}, false);
+ };
+ };
+
+ var fontmap = new Image();
+ fontmap.onload = function (){
+ viewer = new TERM.AnsiViewer(this);
+ commands = new TERM.Telnet(viewer);
+
+ if(!("WebSocket" in window)) {
+ alert("Sorry, the build of your browser does not support HTML5 WebSockets.");
+ return;
+ }
+
+ initKeyboard();
+ };
+ fontmap.src = fontMapURL;
+
+ this.connect = function(host, port) {
+ if(TERM.socket != undefined && TERM.socket.readyState == 1) {
+ viewer.displayCleared();
+ viewer.reposition(0, 0);
+ //TERM.socket.send("telnet|"+host+"|"+port);
+ TERM.socket.send("\n");
+ } else if(TERM.socket == undefined ){
+ TERM.socket = new TERM.Socket();
+ TERM.socket.init(host, port, function(e){
+ try {
+ //alert("Got : "+e.data.toString());
+ //viewer.readBytes(e.data.toString());
+ viewer.readBytes(e.data);
+ } catch (exp) {
+ alert("Exception "+exp);
+ }
+ });
+ }
+ };
+
+}
--- /dev/null
+/**
+ * @author Peter Nitsch
+ */
+
+TERM.Socket = function (){
+
+ var ws;
+
+ this.init = function(host, port, onmessage){
+ ws = new WebSocket("ws://"+TERM.SERVER_URL+":"+TERM.SERVER_PORT+"/","telnet");
+ ws.binaryType = "arraybuffer";
+ ws.onmessage = onmessage;
+ ws.onclose = function(evt) {
+ //alert("Close ");
+ ws.close();
+ ws = null;
+ };
+ ws.onopen = function(evt) {
+ // alert("Open");
+ //ws.send("telnet|"+host+"|"+port);
+ ws.send("\n");
+
+ };
+ ws.onerror = function(evt) { alert("Got an error "+evt.data.toString());};
+ };
+
+ this.writeByte = function(code) {
+ ws.send(String.fromCharCode(code));
+ };
+
+ this.writeMultiByte = function(string, code) {
+ ws.send(string);
+ };
+
+ this.send = function(packet){
+ ws.send(packet);
+ };
+
+};
+
+TERM.Socket.prototype = {
+ get readyState(){
+ return ws.readyState;
+ }
+};
--- /dev/null
+/**
+ * @author Peter Nitsch
+ */
+
+TERM.AnsiViewer = function (fontmap){
+
+ this.cursor = new TERM.Cursor();
+ this.parser = new TERM.AnsiParser(this);
+
+ var fontmap = fontmap;
+ var font = new TERM.Font();
+ var canvas = document.getElementById("canvas");
+ var width = canvas.width;
+ var height = canvas.height;
+ var topMargin = 1;
+ var botMargin = 25;
+ var ctx = canvas.getContext("2d");
+ var scroll = true;
+ var _savedPosition = new TERM.Point();
+
+ this.readBytes = function (bytes) {
+ this.parser.parse(bytes);
+ };
+
+ this.clearCanvas = function(){
+ ctx.fillStyle = BLACK_NORMAL;
+ ctx.fillRect(0, 0, width, height);
+ };
+
+ this.colorTable = function(val) {
+ switch(val) {
+ case BLACK_NORMAL: return 0; break;
+ case BLUE_NORMAL: return 1; break;
+ case GREEN_NORMAL: return 2; break;
+ case CYAN_NORMAL: return 3; break;
+ case RED_NORMAL: return 4; break;
+ case MAGENTA_NORMAL: return 5; break;
+ case YELLOW_NORMAL: return 6; break;
+ case WHITE_NORMAL: return 7; break;
+ case BLACK_BOLD: return 8; break;
+ case BLUE_BOLD: return 9; break;
+ case GREEN_BOLD: return 10; break;
+ case CYAN_BOLD: return 11; break;
+ case RED_BOLD: return 12; break;
+ case MAGENTA_BOLD: return 13; break;
+ case YELLOW_BOLD: return 14; break;
+ case WHITE_BOLD: return 15; break;
+ }
+ return 0;
+ };
+
+ this.drawCharacter = function(character) {
+ this.draw(character);
+ this.cursor.moveForward(1);
+
+ if(!this.cursor.infinitewidth && this.cursor.x + this.cursor.columnWidth > this.cursor.maxColumnWidth * this.cursor.columnWidth){
+ this.moveDown(1);
+ this.cursor.carriageReturn();
+ }
+ };
+
+ this.draw = function(charCode) {
+ //console.log(charCode +" "+ this.cursor.x +" "+ this.cursor.y)
+
+ ctx.fillStyle = this.cursor.backgroundColor;
+ ctx.fillRect(this.cursor.x, this.cursor.y, font.width, font.height);
+
+ ctx.drawImage(fontmap,
+ charCode*(font.width+1), this.colorTable(this.cursor.foregroundColor)*font.height, font.width, font.height,
+ this.cursor.x, this.cursor.y, font.width, font.height);
+ };
+
+ this.carriageReturn = function() {
+ this.cursor.carriageReturn();
+ };
+
+ this.formFeed = function() {
+ this.cursor.x = 0;
+ this.cursor.y = 0;
+ };
+
+ this.moveBackward = function(val, erase) {
+ var movements = val;
+
+ while( movements > 0 ) {
+ this.cursor.moveBackward(1);
+ if(erase) this.draw(SPACE);
+ movements--;
+ }
+ };
+
+ this.moveDown = function(val) {
+ console.log("Viewer.moveDown "+this.cursor.y+" max "+(this.cursor.lineHeight*(botMargin-1)) );
+ if(this.cursor.y >= this.cursor.lineHeight*(botMargin-1) && scroll){
+ this.scrollUp(1);
+ } else {
+ this.cursor.moveDown(val);
+ }
+ };
+
+ this.moveForward = function(val) {
+ this.cursor.moveForward(val);
+ };
+
+ this.moveUp = function(val) {
+ this.cursor.moveUp(val);
+ };
+
+ this.reposition = function(x, y) {
+ this.cursor.x = x * this.cursor.columnWidth;
+ this.cursor.y = y * this.cursor.lineHeight;
+ };
+
+ this.restorePosition = function() {
+ this.cursor.x = _savedPosition.x;
+ this.cursor.y = _savedPosition.y;
+ };
+
+ this.savePosition = function() {
+ _savedPosition.x = this.cursor.x;
+ _savedPosition.y = this.cursor.y;
+ };
+
+ this.displayCleared = function() {
+ ctx.fillStyle = BLACK_NORMAL;
+ ctx.fillRect(0, 0, this.cursor.maxColumnWidth * this.cursor.columnWidth, this.cursor.maxLineHeight * this.cursor.lineHeight);
+ };
+
+ this.eraseUp = function() {
+ ctx.fillStyle = BLACK_NORMAL;
+ ctx.fillRect(0, 0, this.cursor.maxColumnWidth * this.cursor.columnWidth, this.cursor.y);
+ };
+
+ this.eraseScreen = function() {
+ ctx.fillStyle = this.cursor.backgroundColor;
+ ctx.fillRect(0, 0, this.cursor.maxColumnwidth * this.cursor.columnWidth, this.cursor.maxLineHeight * this.cursor.lineHeight);
+ };
+
+ this.eraseDown = function() {
+ console.log("Viewer::eraseDown 0,"+this.cursor.y);
+ ctx.fillStyle = BLACK_NORMAL;
+ ctx.fillRect(0, this.cursor.y, this.cursor.maxColumnWidth * this.cursor.columnWidth, (this.cursor.maxLineHeight * this.cursor.lineHeight) - this.cursor.y);
+ };
+
+ this.eraseEndOfLine = function() {
+ ctx.fillStyle = BLACK_NORMAL;
+ var w = (this.cursor.maxColumnWidth * this.cursor.columnWidth) - (this.cursor.x - this.cursor.columnWidth);
+ ctx.fillRect(this.cursor.x, this.cursor.y, w, this.cursor.lineHeight);
+ };
+
+ this.eraseStartOfLine = function() {
+ ctx.fillStyle = BLACK_NORMAL;
+ ctx.fillRect(0, this.cursor.y, this.cursor.x, this.cursor.lineHeight);
+ };
+
+ this.eraseLine = function() {
+ ctx.fillStyle = BLACK_NORMAL;
+ ctx.fillRect(0, this.cursor.y, this.cursor.maxColumnWidth * this.cursor.columnWidth, this.cursor.lineHeight);
+ };
+
+ this.backgroundColorChanged = function(color) {
+ this.cursor.backgroundColor = color;
+ };
+
+ this.foregroundColorChanged = function(color) {
+ this.cursor.foregroundColor = color;
+ };
+
+ this.home = function() {
+ this.cursor.x = 0;
+ this.cursor.y = (topMargin-1) * this.cursor.maxLineHeight;
+ };
+
+ this.scrollScreen = function(start, end) {
+ console.log("Viewer::scrollScreen handle Home KO start="+start+" end="+end+" topM="+topMargin+" "+botMargin);
+ topMargin = start;
+ if (end >0 && end < 25)
+ botMargin = end;
+ this.scrollUp(start);
+ handleHome();
+ };
+
+ this.scrollUp = function(val) {
+ var canvasData = ctx.getImageData(0, topMargin * this.cursor.lineHeight, this.cursor.maxColumnWidth*this.cursor.columnWidth, this.cursor.lineHeight * (botMargin-topMargin));
+ this.displayCleared();
+ ctx.putImageData(canvasData, 0, this.cursor.lineHeight*(topMargin-1));
+ };
+
+ this.clearCanvas();
+
+};
--- /dev/null
+/**
+ * @author Peter Nitsch
+ */
+
+TERM.Cursor = function (){
+
+ this.foregroundColor = WHITE_NORMAL;
+ this.backgroundColor = BLACK_NORMAL;
+ this.position = new TERM.Point();
+ this.maxColumnWidth = 80;
+ this.maxLineHeight = 25;
+ this.columnWidth = 8;
+ this.lineHeight = 16;
+ this.maxColumns = 80;
+ this.infiniteWidth = false;
+ this.infiniteHeight = false;
+
+ this.moveForward = function(columns) {
+ if( this.position.x + (columns*this.columnWidth) <= this.maxColumns * this.columnWidth )
+ this.position.x = this.position.x + (columns*this.columnWidth);
+ else {
+ //this.position.x = (this.maxColumns * this.columnWidth) - this.columnWidth;
+ this.position.x = 0;
+ this.moveDown(1);
+ }
+ };
+
+ this.moveBackward = function(columns) {
+ if( this.position.x - (columns*this.columnWidth) >= 0 )
+ this.position.x = this.position.x - (columns*this.columnWidth);
+ else
+ this.position.x = 0;
+ };
+
+ this.moveDown = function(lines) {
+ this.position.y = this.position.y + (lines*this.lineHeight);
+ };
+
+ this.moveUp = function(lines) {
+ if( this.position.y - (lines*this.lineHeight) >= 0 )
+ this.position.y = this.position.y - (lines*this.lineHeight);
+ else
+ this.position.y = 0;
+ };
+
+ this.carriageReturn = function() {
+ this.position.x = 0;
+ // this.moveDown(1);
+ };
+
+};
+
+TERM.Cursor.prototype = {
+
+ get x (){
+ return this.position.x;
+ },
+ set x (val){
+ this.position.x = val;
+ },
+
+ get y (){
+ return this.position.y;
+ },
+ set y (val){
+ this.position.y = val;
+ }
+
+};
--- /dev/null
+/**
+ * @author Peter Nitsch
+ */
+
+TERM.Font = function() {
+
+ this.width = 8;
+ this.height = 16;
+ this.lineHeight = 23;
+
+};
\ No newline at end of file
--- /dev/null
+/**
+ * @author Peter Nitsch
+ */
+
+TERM.Point = function() {
+
+ this.x = 0;
+ this.y = 0;
+
+};
\ No newline at end of file
--- /dev/null
+#include <iostream>
+#include <list>
+#include <limits>
+#include <cstdlib>
+
+#include <aeb/net/ip/tcp.h>
+#include <aeb/net/ip/basic_endpoint.h>
+#include <aeb/net/socket_base.h>
+#include <aeb/net/io_dispatcher.h>
+#include <aeb/net/basic_socket_acceptor.h>
+#include <aeb/net/basic_socket_stream.h>
+#include <aeb/pointer/intrusive_ptr.h>
+#if defined(__MINGW32__)
+#include <aeb/net/detail/winsock_init.h>
+#endif
+
+// Signal stuff
+#include <signal.h>
+#include <stdlib.h>
+#include <fstream>
+using namespace aeb::net;
+typedef aeb::net::io_dispatcher<aeb::net::detail::socket_type,ip::tcp> io_service;
+typedef aeb::net::basic_socket<ip::tcp> tcp_socket;
+
+#include <reverse_proxy.h>
+
+#include "ws_allocator.h"
+#include "ws_log.h"
+#include "ws_constant.h"
+#include "ws_frame.h"
+
+#include "ws_proxy_endpoint.h"
+#include "ws_proxy_handler.h"
+#include "ws_acceptor.h"
+
+#include "ws_handler_factory.h"
+#include <xsd/xsd.h>
+#include <xsd/parser/expat/elements.h>
+
+#include <p_reverse_proxy.h>
+
+#include "optionparser.h"
+/**
+ * plugin stuff
+ */
+#include "ws_plugin_request.h"
+#include "ws_plugin.h"
+#include "ws_plugin_manager.h"
+
+
+// Local debug macro
+#define LOG_MAIN_DEBUG(x) do { m_logger->log(eLOG_DEBUG,"%s\n",x); } while (0);
+#define LOG_MAIN_DEBUG_FMT(format,args...) do {m_logger->log(eLOG_DEBUG,format "\n" ,args);} while (0)
+
+#define LOG_MAIN_INFO(x) do {m_logger->log(eLOG_INFO,"%s\n",x);} while (0);
+#define LOG_MAIN_INFO_FMT(format,args...) do { m_logger->log(eLOG_INFO,format "\n",args);} while (0) ;
+
+#define LOG_MAIN_NOTICE(x) do {m_logger->log(eLOG_NOTICE,"%s\n",x);} while (0);
+#define LOG_MAIN_NOTICE_FMT(format,args...) do { m_logger->log(eLOG_NOTICE,format "\n",args);} while (0) ;
+
+#define LOG_MAIN_WARN(x) do {m_logger->log(eLOG_WARN,"%s\n",x);} while (0);
+#define LOG_MAIN_WARN_FMT(format,args...) do { m_logger->log(eLOG_WARN,format "\n",args);} while (0) ;
+
+#define LOG_MAIN_ERROR(x) do {m_logger->log(eLOG_ERROR,"%s\n",x);} while (0);
+#define LOG_MAIN_ERROR_FMT(format,args...) do {m_logger->log(eLOG_ERROR,format "\n",args);} while (0) ;
+
+static ws_log *m_logger = ws_log::getInstance("wsproxy");
+
+using namespace wsproxy;
+
+typedef std::list<ws_acceptor::ptr>::iterator acceptor_iterator;
+
+std::list<ws_acceptor::ptr> g_acceptors;
+
+
+//
+// Load configuration
+//
+int load_configuration(const char *filename,ConfigurationType_skel &config)
+{
+ xsd::parser::SimpleElement se;
+ p_ConfigurationType_skel p;
+ p_GatewayType_skel p_gateway;
+ p_ReverseProxyType_skel p_rproxy;
+ p_ApplicationType_skel p_app;
+ p_WsApplicationType_skel p_wsapp;
+ p_WebSocketType_skel p_ws;
+ p_AccessFilterType_skel p_ar;
+ p_ListAuthorization_skel p_lauth;
+ p_ListGateway_skel p_lg;
+ p_LogType_skel p_log;
+ p_LogLevel_skel p_logl;
+ p_GlobalType_skel p_global;
+ p_ParamType_skel p_param;
+ p_PluginsType_skel p_plugins;
+ p_PluginType_skel p_plugin;
+ // set parser
+ p.listen_parser(p_gateway);
+ p.plugins_parser(p_plugins);
+ p.rproxy_parser(p_rproxy);
+ p.websocket_parser(p_ws);
+ p.global_parser(p_global);
+ //
+ p_global.log_parser(p_log);
+ //
+ //p_log.name_parser(se);
+ p_log.level_parser(p_logl);
+ //p_log.syslog_parser(se);
+ p_plugins.plugin_parser(p_plugin);
+ //
+ p_global.workers_parser(se);
+ p_global.user_parser(se);
+ p_global.group_parser(se);
+ //
+ p_gateway.host_parser(se);
+ p_gateway.port_parser(se);
+ p_gateway.certfile_parser(se);
+
+ p_rproxy.application_parser(p_app);
+ p_ar.authorize_parser(p_lauth);
+ p_ar.gateway_parser(p_lg);
+ p_app.access_filter_parser(p_ar);
+ p_app.filter_parser(se);
+ p_app.param_parser(p_param);
+
+ //
+ p_ws.application_parser(p_wsapp);
+ // p_app.name_parser(se);
+ // p_app.host_parser(se);
+ // p_app_port_parser(se);
+ // start parsing
+ std::ifstream iss1;
+ iss1.open(filename,std::ifstream::in);
+ xsd::parser::expat::Document doc(p,"CONFIGURATION");
+ doc.parse(iss1);
+ p.post(config);
+
+}
+
+//
+// Setup log levels
+//
+void setup_log( wsproxy::GlobalType_skel &g)
+{
+ if (g.log().size() == 0)
+ {
+ std::cerr<<"setup_log no log entries in configuration file"<<std::endl;
+ return;
+ }
+
+ wsproxy::GlobalType_skel::log_sequence::iterator it = g.log().begin();
+
+ for ( ; it != g.log().end() ; ++it)
+ {
+ if (! std::string("DEBUG").compare((*it)->attr_level().content()))
+ {
+ ws_log::getInstance((*it)->attr_name())->set_level(eLOG_DEBUG);
+ }
+ else if (! std::string("NOTICE").compare((*it)->attr_level().content()))
+ {
+ ws_log::getInstance((*it)->attr_name())->set_level(eLOG_NOTICE);
+ }
+ else if (! std::string("WARNING").compare((*it)->attr_level().content()))
+ {
+ ws_log::getInstance((*it)->attr_name())->set_level(eLOG_WARN);
+ }
+ else if (! std::string("ERROR").compare((*it)->attr_level().content()))
+ {
+ ws_log::getInstance((*it)->attr_name())->set_level(eLOG_ERROR);
+ }
+ else if (! std::string("INFO").compare((*it)->attr_level().content()))
+ {
+ ws_log::getInstance((*it)->attr_name())->set_level(eLOG_INFO);
+ }
+ // Set syslog
+ if ((*it)->attr_syslog())
+ {
+ ws_log::getInstance((*it)->attr_name())->set_syslog() ;
+ }
+ // Check if log to file
+ if ( (*it)->isset_file())
+ {
+ }
+ }
+}
+
+//
+// Signal Handlers
+//
+volatile sig_atomic_t main_running = true;
+
+void main_stop(int sig)
+{
+ LOG_MAIN_DEBUG_FMT("main_stop: signal %d",sig);
+ main_running = false;
+ return ;
+}
+
+/**
+ * Option management
+ */
+enum optionIndex {UNKNOWN, HELP, DBG,FICHIER,DAEMON,OPT_END};
+
+const option::Descriptor usage[] = {
+ {UNKNOWN, 0, "", "", option::Arg::None,"Usage wsproxy [options]\n\texample: wsproxy -D -c /users/toto/config.xml \n"},
+ {HELP, 0, "h", "help", option::Arg::None,"--help,-h \tPrint help and quit."},
+ {DBG, 0, "d", "debug", option::Arg::Optional,"--debug,-d \tDebug mode"},
+ {FICHIER, 0, "c", "config", option::Arg::Required,"--config,-c \txml configuration file"},
+ {DAEMON, 0, "D", "daemon", option::Arg::None,"--daemon,-D \tRun program as daemon"},
+ {0,0,0,0,0,0}
+};
+
+typedef ws_allocator<char,32> ws32_alloc;
+typedef std::basic_string<char,std::char_traits<char>,ws32_alloc> ws32_string;
+
+//
+// Main entry point
+//
+int main(int argc, char **argv)
+{
+
+ ConfigurationType_skel config;
+ ip::basic_endpoint<ip::tcp> ep(ip::tcp::v4(),10222);
+
+ std::vector<ip::tcp::endpoint_type> endpoints;
+
+ {
+ ws32_alloc alloc;
+ ws32_string str;
+ str = "DEDE";
+ std::cout<<str<<std::endl;
+ }
+ signal(SIGTERM,main_stop); // Kill -TERM
+ signal(SIGINT,main_stop); // ^C
+ signal(SIGQUIT,main_stop); // Looks like backgound
+ //signal(SIGPIPE,main_stop);
+ signal(SIGPIPE,SIG_IGN);
+
+ // Block SIGTERM.
+ sigset_t sigset, oldset,oldloopset;
+ sigemptyset(&sigset);
+ // sigaddset(&sigset, SIGTERM);
+ // sigaddset(&sigset, SIGINT);
+ // sigprocmask(SIG_BLOCK, &sigset, &oldset);
+
+ // Option stuff must go here
+ option::Stats stats(usage,argc-1,&argv[1]);
+ const int max(stats.options_max);
+ option::Option options[OPT_END*2],buffer[OPT_END*3];
+ option::Parser parser(usage,argc-1,&argv[1],options,buffer);
+ if (parser.error())
+ {
+ return 1;
+ }
+ if (options[HELP] )
+ {
+ option::printUsage(std::cout,usage);
+ return 1;
+ }
+
+
+ // Maybe add some stuff to make it a deamon, change the user and so one
+ // to secure the process
+ if (options[FICHIER] )
+ {
+ option::Option *opt = options[FICHIER];
+ if (opt->arg)
+ {
+ load_configuration(opt->arg,config);
+ } else
+ {
+ return 1;
+ }
+ } else {
+ // Lookup for config file in several places before trying to load the config
+ load_configuration("wsproxy.xml",config);
+ }
+ //
+ if (config.isset_global() )
+ {
+ setup_log(config.global());
+ } else
+ {
+ LOG_MAIN_ERROR("main config.global not available in wsproxy.xml file");
+ exit(1);
+ }
+ // Handle daemon feature ....
+ if ( options[DAEMON] )
+ {
+ // Allright, fork ....
+ if ( pid_t pid = fork() )
+ {
+ // Parent process continues here.
+ if ( pid > 0 ) {
+ // detach from main ...
+ exit(0);
+ } else {
+ // Failure
+ LOG_MAIN_ERROR("Failed fork");
+ return 1;
+ }
+ }
+ // child process here (pid == 0)
+ setsid();
+ }
+
+ /**
+ * trying to Load Plugins
+ */
+ LOG_MAIN_INFO("Loading plugins...");
+ ws_plugin_manager *mgr = ws_plugin_manager::getInstance("plugins");
+
+ try
+ {
+ if (mgr && config.isset_plugins() )
+ {
+ PluginsType_skel::plugin_sequence::iterator pit = config.plugins().plugin().begin();
+ for (; pit != config.plugins().plugin().end() ; ++pit)
+ {
+ std::string pn = (*pit)->attr_load() ;
+ ws_plugin *p_default = mgr->load_plugin(pn);
+ if (p_default)
+ {
+ LOG_MAIN_INFO_FMT("Loaded plugin %s",pn.c_str());
+ // p_default->register_hooks();
+ } else {
+ LOG_MAIN_ERROR_FMT("Load Failed for plugin %s ",pn.c_str());
+ }
+ }
+
+ }
+ }
+ catch (std::exception &e)
+ {
+ LOG_MAIN_ERROR_FMT("EXCEPTION: %s",e.what());
+ }
+
+ // Loop over ports to listen to
+ ConfigurationType_skel::listen_sequence::iterator it = config.listen().begin();
+ for ( ; it != config.listen().end() ; ++it)
+ {
+ ip::basic_endpoint<ip::tcp> eps(ip::tcp::v4(),(*it)->port());
+ endpoints.push_back(eps);
+ LOG_MAIN_DEBUG_FMT("Listen on port: %d",(*it)->port());
+ }
+
+ // Loop over rporxy applications
+ ReverseProxyType_skel::application_sequence::iterator it_app = config.rproxy().application().begin();
+ if (config.isset_rproxy() )
+ {
+ for ( ; it_app != config.rproxy().application().end() ; ++it_app)
+ {
+ LOG_MAIN_INFO_FMT("Reverse Proxy Backend <%s>\tconnect to : %s on port : %d"
+ , (*it_app)->attr_name().c_str()
+ , (*it_app)->attr_host().c_str()
+ , (*it_app)->attr_port()
+ );
+ if ( (*it_app)->isset_access_filter())
+ {
+ AccessFilterType_skel access_filter((*it_app)->access_filter());
+ ListGateway_skel gw(access_filter.attr_gateway());
+ ListAuthorization_skel autorisations(access_filter.attr_authorize());
+ LOG_MAIN_INFO_FMT("Access Filter autorize length <%d> gw<%d>",autorisations.size(),gw.size());
+ }
+ // Alright, do the real job and register the appropriate acceptor
+ }
+ }
+ // Loop over websocket applications
+ if (config.isset_websocket() )
+ {
+ WebSocketType_skel::application_sequence::iterator it_wsapp = config.websocket().application().begin();
+ for ( ; it_wsapp != config.websocket().application().end() ; ++it_wsapp)
+ {
+ LOG_MAIN_INFO_FMT("WebSock Proxy Backend <%s>\tconnect to : %s on port : %d"
+ , (*it_wsapp)->attr_name().c_str()
+ , (*it_wsapp)->attr_host().c_str()
+ , (*it_wsapp)->attr_port()
+ );
+ }
+ } else
+ {
+ LOG_MAIN_WARN("No Web Socket application backend defined");
+ }
+
+ io_service *io = aeb::singleton<io_service>::get_instance();
+ try
+ {
+ // Register all ws application with all acceptors
+ for ( std::vector<ip::tcp::endpoint_type>::const_iterator ite = endpoints.begin()
+ ; ite != endpoints.end()
+ ; ++ite)
+ {
+ int *i = new int;
+ LOG_MAIN_DEBUG_FMT("Web Socket application start acceptor %d",endpoints.size());
+ try
+ {
+ ws_acceptor::ptr ws_ac( new ws_acceptor( (*ite)
+ ,config.rproxy().application())
+ );
+
+ ws_ac->listen();
+ g_acceptors.push_back(ws_ac);
+ io->register_handler(2,(ws_acceptor *)ws_ac);
+ }
+ catch (std::exception &e)
+ {
+ LOG_MAIN_ERROR_FMT("EXCEPTION when creating acceptor %s",e.what());
+ }
+ LOG_MAIN_DEBUG_FMT("Web Socket application end acceptor %d",endpoints.size());
+ }
+
+ do
+ {
+ sigprocmask(SIG_SETMASK,&sigset,&oldloopset);
+ io->run(true);
+ sigprocmask(SIG_SETMASK,&sigset,NULL);
+ }
+ while (main_running);
+
+ }
+ catch (std::exception &e)
+ {
+ std::cout<<e.what()<<std::endl;
+ main_running = false;
+ }
+
+ LOG_MAIN_NOTICE("Normal Existing");
+ ws_handler_factory::get_instance()->close_handlers();
+ LOG_MAIN_NOTICE("Shutdown listening sockets");
+ acceptor_iterator ait = g_acceptors.begin();
+ for( ; ait != g_acceptors.end() ; ++ait)
+ {
+ (*ait)->shutdown(SHUT_RDWR);
+ }
+
+ LOG_MAIN_DEBUG("Shutdown Wait one more round to normal end");
+ io->run(true);
+ // Unregister
+ ait = g_acceptors.begin();
+ for( ; ait != g_acceptors.end() ; ++ait)
+ {
+ io->unregister_handler(2,(ws_acceptor *)(*ait));
+ }
+ // destroy listener
+ g_acceptors.erase(g_acceptors.begin(),g_acceptors.end());
+ LOG_MAIN_NOTICE_FMT("Exist %s",argv[0]);
+}
+/**
+ :vim:et:sw=2:ts=2:
+ */
--- /dev/null
+PROJECT(ws-plugins)
+
+INCLUDE_DIRECTORIES(".")
+
+ADD_LIBRARY(plugin_default SHARED plugin_default.cpp)
+SET_TARGET_PROPERTIES(plugin_default PROPERTIES COMPILE_FLAGS -fPIC)
+SET_TARGET_PROPERTIES(plugin_default PROPERTIES LINK_FLAGS -Wl)
+ADD_LIBRARY(plugin_log SHARED plugin_log.cpp)
+SET_TARGET_PROPERTIES(plugin_log PROPERTIES COMPILE_FLAGS -fPIC)
--- /dev/null
+#ifndef PLUGIN_AUTH_H
+#define PLUGIN_AUTH_H
+#endif /*PLUGIN_AUTH_H*/
--- /dev/null
+#include <iostream>
+#include <map>
+#include "ws_plugin_request.h"
+#include "ws_plugin.h"
+#include "plugin_default.h"
+
+/**
+ * Default handler
+ */
+class default_handler : public ws_plugin_handler {
+ public:
+ default_handler();
+ default_handler(const default_handler &r);
+ ~default_handler();
+
+ int handle_request(ws_plugin_request &r);
+};
+
+// default Contructor
+default_handler::default_handler()
+{
+}
+
+//
+default_handler::default_handler(const default_handler &o)
+{
+}
+
+// default Desctructor
+default_handler::~default_handler()
+{
+}
+
+int
+default_handler::handle_request(ws_plugin_request &r)
+{
+ std::cout<<"default_handler::handle_request"<<std::endl;
+ return PLUGIN_OK;
+}
+
+/**
+ * plugin_default implementation
+ */
+
+plugin_default::plugin_default(const std::string &name) : ws_plugin(name)
+{
+
+ std::cout<<"plugin_default::plugin_default contructor with name("<<name<<")"<<std::endl;
+}
+
+void plugin_default::register_hooks()
+{
+ std::cout<<"plugin_default::register_hooks"<<std::endl;
+}
+
+
+
+
+/*
+ * Function that will create the plugin.
+ * will be called after dlopen
+ */
+extern "C" void *create_plugin() {
+ return new plugin_default("plugin_default");
+
+}
--- /dev/null
+#ifndef PLUGIN_DEFAULT_H
+#define PLUGIN_DEFAULT_H
+
+
+/**
+ * class plugin_default
+ *
+ * \brief
+ * \author
+ */
+class plugin_default : ws_plugin {
+ public:
+ //
+ plugin_default(const std::string &_n) ;
+ //
+ plugin_default(const plugin_default &o) ;
+ //
+ ~plugin_default() ;
+
+ void register_hooks() ;
+ protected:
+};
+
+
+#endif /*PLUGIN_DEFAULT_H*/
--- /dev/null
+#include <iostream>
+#include <map>
+#include "ws_plugin_request.h"
+#include "ws_plugin.h"
+#include "plugin_log.h"
+
+/**
+ * Default handler
+ */
+class log_handler : public ws_plugin_handler {
+ public:
+ log_handler();
+ log_handler(const log_handler &r);
+ ~log_handler();
+
+ int handle_request(ws_plugin_request &r);
+};
+
+// default Contructor
+log_handler::log_handler()
+{
+}
+
+//
+log_handler::log_handler(const log_handler &o)
+{
+}
+
+// default Desctructor
+log_handler::~log_handler()
+{
+}
+
+int
+log_handler::handle_request(ws_plugin_request &r)
+{
+ std::cout<<"log_handler::handle_request"<<std::endl;
+ return PLUGIN_OK;
+}
+
+/**
+ * plugin_log implementation
+ */
+
+plugin_log::plugin_log(const std::string &name) : ws_plugin(name)
+{
+
+ std::cout<<"plugin_log::plugin_log contructor with name("<<name<<")"<<std::endl;
+}
+
+void plugin_log::register_hooks()
+{
+ std::cout<<"plugin_log::register_hooks"<<std::endl;
+}
+
+
+
+
+/*
+ * Function that will create the plugin.
+ * will be called after dlopen
+ */
+extern "C" {
+
+void *create_plugin() {
+ return new plugin_log("plugin_log");
+}
+
+}
--- /dev/null
+#ifndef PLUGIN_LOG_H
+#define PLUGIN_LOG_H
+
+
+/**
+ * class plugin_log
+ *
+ * \brief
+ * \author
+ */
+class plugin_log : ws_plugin {
+ public:
+ //
+ plugin_log(const std::string &_n) ;
+ //
+ plugin_log(const plugin_log &o) ;
+ //
+ ~plugin_log() ;
+
+ void register_hooks() ;
+ protected:
+};
+
+
+#endif /*PLUGIN_DEFAULT_H*/
--- /dev/null
+#include <iostream>
+#include <map>
+#include "ws_plugin_request.h"
+#include "ws_plugin.h"
+#include "plugin_ssl.h"
+
+/**
+ * Default handler
+ */
+class ssl_handler : public ws_plugin_handler {
+ public:
+ ssl_handler();
+ ssl_handler(const ssl_handler &r);
+ ~ssl_handler();
+
+ int handle_request(ws_plugin_request &r);
+};
+
+// default Contructor
+ssl_handler::ssl_handler()
+{
+}
+
+//
+ssl_handler::ssl_handler(const ssl_handler &o)
+{
+}
+
+// default Desctructor
+ssl_handler::~ssl_handler()
+{
+}
+
+int
+ssl_handler::handle_request(ws_plugin_request &r)
+{
+ std::cout<<"ssl_handler::handle_request"<<std::endl;
+ return PLUGIN_OK;
+}
+
+/**
+ * plugin_ssl implementation
+ */
+
+plugin_ssl::plugin_ssl(const std::string &name) : ws_plugin(name)
+{
+
+ std::cout<<"plugin_ssl::plugin_ssl contructor with name("<<name<<")"<<std::endl;
+}
+
+void plugin_ssl::register_hooks()
+{
+ std::cout<<"plugin_ssl::register_hooks"<<std::endl;
+ // core.register_conn_hook();
+ //
+ // core.register_log_hook(ssl_handler);
+}
+
+
+
+
+/*
+ * Function that will create the plugin.
+ * will be called after dlopen
+ */
+extern "C" void *create_plugin() {
+ return new plugin_ssl("plugin_ssl");
+
+}
--- /dev/null
+#ifndef PLUGIN_SSL_H
+#define PLUGIN_SSL_H
+/**
+ * class plugin_ssl
+ *
+ * \brief
+ * \author
+ */
+class plugin_ssl : ws_plugin {
+ public:
+ //
+ plugin_ssl(const std::string &_n) ;
+ //
+ plugin_ssl(const plugin_ssl &o) ;
+ //
+ ~plugin_ssl() ;
+
+ void register_hooks() ;
+ protected:
+};
+
+#endif /*PLUGIN_SSL_H*/
--- /dev/null
+#include <iostream>
+#include <map>
+#include "ws_plugin_request.h"
+#include "ws_plugin.h"
+#include "plugin_ws.h"
+
+/**
+ * Default handler
+ */
+class ws_handler : public ws_plugin_handler {
+ public:
+ ws_handler();
+ ws_handler(const ws_handler &r);
+ ~ws_handler();
+
+ int handle_request(ws_plugin_request &r);
+};
+
+// default Contructor
+ws_handler::ws_handler()
+{
+}
+
+//
+ws_handler::ws_handler(const ws_handler &o)
+{
+}
+
+// default Desctructor
+ws_handler::~ws_handler()
+{
+}
+
+int
+ws_handler::handle_request(ws_plugin_request &r)
+{
+ std::cout<<"ws_handler::handle_request"<<std::endl;
+ return PLUGIN_OK;
+}
+
+/**
+ * plugin_ws implementation
+ */
+
+plugin_ws::plugin_ws(const std::string &name) : ws_plugin(name)
+{
+
+ std::cout<<"plugin_ws::plugin_ws contructor with name("<<name<<")"<<std::endl;
+}
+
+void plugin_ws::register_hooks()
+{
+ std::cout<<"plugin_ws::register_hooks"<<std::endl;
+}
+
+
+
+
+/*
+ * Function that will create the plugin.
+ * will be called after dlopen
+ */
+extern "C" void *create_plugin() {
+ return new plugin_ws("plugin_ws");
+
+}
--- /dev/null
+#ifndef PLUGIN_WS_H
+#define PLUGIN_WS_H
+
+
+/**
+ * class plugin_ws
+ *
+ * \brief
+ * \author
+ */
+class plugin_ws : ws_plugin {
+ public:
+ //
+ plugin_ws(const std::string &_n) ;
+ //
+ plugin_ws(const plugin_ws &o) ;
+ //
+ ~plugin_ws() ;
+
+ void register_hooks() ;
+ protected:
+};
+
+
+#endif /*PLUGIN_WS_H*/
--- /dev/null
+<!DOCTYPE html>
+
+<meta charset="utf-8" />
+
+<title>WebSocket Test</title>
+
+<script language="javascript" type="text/javascript">
+
+ //var wsUri = "ws://echo.websocket.org/";
+ var wsUri = "ws://172.25.16.146:10222/";
+ var output;
+
+ function init()
+ {
+ output = document.getElementById("output");
+ testWebSocket();
+ }
+
+ function testWebSocket()
+ {
+ websocket = new WebSocket(wsUri,"echo");
+ //websocket = new WebSocket(wsUri);
+ websocket.onopen = function(evt) { onOpen(evt) };
+ websocket.onclose = function(evt) { onClose(evt) };
+ websocket.onmessage = function(evt) { onMessage(evt) };
+ websocket.onerror = function(evt) { onError(evt) };
+ }
+
+ function onOpen(evt)
+ {
+ writeToScreen("CONNECTED");
+ doSend("WebSocket rocks");
+ }
+
+ function onClose(evt)
+ {
+ writeToScreen("DISCONNECTED");
+ }
+
+ function onMessage(evt)
+ {
+ writeToScreen('<span style="color: blue;">RESPONSE: ' + evt.data+'</span>');
+ websocket.close();
+ }
+
+ function onError(evt)
+ {
+ writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data);
+ }
+
+ function doSend(message)
+ {
+ writeToScreen("SENT: " + message);
+ websocket.send(message);
+ }
+
+ function writeToScreen(message)
+ {
+ var pre = document.createElement("p");
+ pre.style.wordWrap = "break-word";
+ pre.innerHTML = message;
+ output.appendChild(pre);
+ }
+
+ window.addEventListener("load", init, false);
+
+</script>
+
+<h2>WebSocket Test</h2>
+
+<div id="output"></div>
--- /dev/null
+<!DOCTYPE html>
+
+<meta charset="utf-8" />
+
+<title>WebSocket Test</title>
+
+<script language="javascript" type="text/javascript">
+
+ //var wsUri = "ws://echo.websocket.org/";
+ var wsUri = "ws://172.25.16.146:10222/";
+ var output;
+
+ function init()
+ {
+ output = document.getElementById("output");
+ testWebSocket();
+ }
+
+ function testWebSocket()
+ {
+ //websocket = new WebSocket(wsUri,"echo");
+ websocket = new WebSocket(wsUri);
+ websocket.onopen = function(evt) { onOpen(evt) };
+ websocket.onclose = function(evt) { onClose(evt) };
+ websocket.onmessage = function(evt) { onMessage(evt) };
+ websocket.onerror = function(evt) { onError(evt) };
+ }
+
+ function onOpen(evt)
+ {
+ writeToScreen("CONNECTED");
+ doSend("WebSocket rocks");
+ }
+
+ function onClose(evt)
+ {
+ writeToScreen("DISCONNECTED");
+ }
+
+ function onMessage(evt)
+ {
+ writeToScreen('<span style="color: blue;">RESPONSE: ' + evt.data+'</span>');
+ websocket.close();
+ }
+
+ function onError(evt)
+ {
+ writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data);
+ }
+
+ function doSend(message)
+ {
+ writeToScreen("SENT: " + message);
+ websocket.send(message);
+ }
+
+ function writeToScreen(message)
+ {
+ var pre = document.createElement("p");
+ pre.style.wordWrap = "break-word";
+ pre.innerHTML = message;
+ output.appendChild(pre);
+ }
+
+ window.addEventListener("load", init, false);
+
+</script>
+
+<h2>WebSocket Test</h2>
+
+<div id="output"></div>
--- /dev/null
+/*\r
+ Copyright (c) 2011, Micael Hildenborg\r
+ All rights reserved.\r
+\r
+ Redistribution and use in source and binary forms, with or without\r
+ modification, are permitted provided that the following conditions are met:\r
+ * Redistributions of source code must retain the above copyright\r
+ notice, this list of conditions and the following disclaimer.\r
+ * Redistributions in binary form must reproduce the above copyright\r
+ notice, this list of conditions and the following disclaimer in the\r
+ documentation and/or other materials provided with the distribution.\r
+ * Neither the name of Micael Hildenborg nor the\r
+ names of its contributors may be used to endorse or promote products\r
+ derived from this software without specific prior written permission.\r
+\r
+ THIS SOFTWARE IS PROVIDED BY Micael Hildenborg ''AS IS'' AND ANY\r
+ EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\r
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\r
+ DISCLAIMED. IN NO EVENT SHALL Micael Hildenborg BE LIABLE FOR ANY\r
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\r
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\r
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\r
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\r
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+ */\r
+\r
+/*\r
+ Contributors:\r
+ Gustav\r
+ Several members in the gamedev.se forum.\r
+ Gregory Petrosyan\r
+ */\r
+\r
+#include "sha1.h"\r
+\r
+namespace sha1\r
+{\r
+ namespace // local\r
+ {\r
+ // Rotate an integer value to left.\r
+ inline const unsigned int rol(const unsigned int value,\r
+ const unsigned int steps)\r
+ {\r
+ return ((value << steps) | (value >> (32 - steps)));\r
+ }\r
+\r
+ // Sets the first 16 integers in the buffert to zero.\r
+ // Used for clearing the W buffert.\r
+ inline void clearWBuffert(unsigned int* buffert)\r
+ {\r
+ for (int pos = 16; --pos >= 0;)\r
+ {\r
+ buffert[pos] = 0;\r
+ }\r
+ }\r
+\r
+ void innerHash(unsigned int* result, unsigned int* w)\r
+ {\r
+ unsigned int a = result[0];\r
+ unsigned int b = result[1];\r
+ unsigned int c = result[2];\r
+ unsigned int d = result[3];\r
+ unsigned int e = result[4];\r
+\r
+ int round = 0;\r
+\r
+ #define sha1macro(func,val) \\r
+ { \\r
+ const unsigned int t = rol(a, 5) + (func) + e + val + w[round]; \\r
+ e = d; \\r
+ d = c; \\r
+ c = rol(b, 30); \\r
+ b = a; \\r
+ a = t; \\r
+ }\r
+\r
+ while (round < 16)\r
+ {\r
+ sha1macro((b & c) | (~b & d), 0x5a827999)\r
+ ++round;\r
+ }\r
+ while (round < 20)\r
+ {\r
+ w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1);\r
+ sha1macro((b & c) | (~b & d), 0x5a827999)\r
+ ++round;\r
+ }\r
+ while (round < 40)\r
+ {\r
+ w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1);\r
+ sha1macro(b ^ c ^ d, 0x6ed9eba1)\r
+ ++round;\r
+ }\r
+ while (round < 60)\r
+ {\r
+ w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1);\r
+ sha1macro((b & c) | (b & d) | (c & d), 0x8f1bbcdc)\r
+ ++round;\r
+ }\r
+ while (round < 80)\r
+ {\r
+ w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1);\r
+ sha1macro(b ^ c ^ d, 0xca62c1d6)\r
+ ++round;\r
+ }\r
+\r
+ #undef sha1macro\r
+\r
+ result[0] += a;\r
+ result[1] += b;\r
+ result[2] += c;\r
+ result[3] += d;\r
+ result[4] += e;\r
+ }\r
+ } // namespace\r
+\r
+ void calc(const void* src, const int bytelength, unsigned char* hash)\r
+ {\r
+ // Init the result array.\r
+ unsigned int result[5] = { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0 };\r
+\r
+ // Cast the void src pointer to be the byte array we can work with.\r
+ const unsigned char* sarray = (const unsigned char*) src;\r
+\r
+ // The reusable round buffer\r
+ unsigned int w[80];\r
+\r
+ // Loop through all complete 64byte blocks.\r
+ const int endOfFullBlocks = bytelength - 64;\r
+ int endCurrentBlock;\r
+ int currentBlock = 0;\r
+\r
+ while (currentBlock <= endOfFullBlocks)\r
+ {\r
+ endCurrentBlock = currentBlock + 64;\r
+\r
+ // Init the round buffer with the 64 byte block data.\r
+ for (int roundPos = 0; currentBlock < endCurrentBlock; currentBlock += 4)\r
+ {\r
+ // This line will swap endian on big endian and keep endian on little endian.\r
+ w[roundPos++] = (unsigned int) sarray[currentBlock + 3]\r
+ | (((unsigned int) sarray[currentBlock + 2]) << 8)\r
+ | (((unsigned int) sarray[currentBlock + 1]) << 16)\r
+ | (((unsigned int) sarray[currentBlock]) << 24);\r
+ }\r
+ innerHash(result, w);\r
+ }\r
+\r
+ // Handle the last and not full 64 byte block if existing.\r
+ endCurrentBlock = bytelength - currentBlock;\r
+ clearWBuffert(w);\r
+ int lastBlockBytes = 0;\r
+ for (;lastBlockBytes < endCurrentBlock; ++lastBlockBytes)\r
+ {\r
+ w[lastBlockBytes >> 2] |= (unsigned int) sarray[lastBlockBytes + currentBlock] << ((3 - (lastBlockBytes & 3)) << 3);\r
+ }\r
+ w[lastBlockBytes >> 2] |= 0x80 << ((3 - (lastBlockBytes & 3)) << 3);\r
+ if (endCurrentBlock >= 56)\r
+ {\r
+ innerHash(result, w);\r
+ clearWBuffert(w);\r
+ }\r
+ w[15] = bytelength << 3;\r
+ innerHash(result, w);\r
+\r
+ // Store hash in result pointer, and make sure we get in in the correct order on both endian models.\r
+ for (int hashByte = 20; --hashByte >= 0;)\r
+ {\r
+ hash[hashByte] = (result[hashByte >> 2] >> (((3 - hashByte) & 0x3) << 3)) & 0xff;\r
+ }\r
+ }\r
+\r
+ void toHexString(const unsigned char* hash, char* hexstring)\r
+ {\r
+ const char hexDigits[] = { "0123456789abcdef" };\r
+\r
+ for (int hashByte = 20; --hashByte >= 0;)\r
+ {\r
+ hexstring[hashByte << 1] = hexDigits[(hash[hashByte] >> 4) & 0xf];\r
+ hexstring[(hashByte << 1) + 1] = hexDigits[hash[hashByte] & 0xf];\r
+ }\r
+ hexstring[40] = 0;\r
+ }\r
+} // namespace sha1\r
--- /dev/null
+/*\r
+ Copyright (c) 2011, Micael Hildenborg\r
+ All rights reserved.\r
+\r
+ Redistribution and use in source and binary forms, with or without\r
+ modification, are permitted provided that the following conditions are met:\r
+ * Redistributions of source code must retain the above copyright\r
+ notice, this list of conditions and the following disclaimer.\r
+ * Redistributions in binary form must reproduce the above copyright\r
+ notice, this list of conditions and the following disclaimer in the\r
+ documentation and/or other materials provided with the distribution.\r
+ * Neither the name of Micael Hildenborg nor the\r
+ names of its contributors may be used to endorse or promote products\r
+ derived from this software without specific prior written permission.\r
+\r
+ THIS SOFTWARE IS PROVIDED BY Micael Hildenborg ''AS IS'' AND ANY\r
+ EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\r
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\r
+ DISCLAIMED. IN NO EVENT SHALL Micael Hildenborg BE LIABLE FOR ANY\r
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\r
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\r
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\r
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\r
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+ */\r
+\r
+#ifndef SHA1_DEFINED\r
+#define SHA1_DEFINED\r
+\r
+namespace sha1\r
+{\r
+\r
+ /**\r
+ @param src points to any kind of data to be hashed.\r
+ @param bytelength the number of bytes to hash from the src pointer.\r
+ @param hash should point to a buffer of at least 20 bytes of size for storing the sha1 result in.\r
+ */\r
+ void calc(const void* src, const int bytelength, unsigned char* hash);\r
+\r
+ /**\r
+ @param hash is 20 bytes of sha1 hash. This is the same data that is the result from the calc function.\r
+ @param hexstring should point to a buffer of at least 41 bytes of size for storing the hexadecimal representation of the hash. A zero will be written at position 40, so the buffer will be a valid zero ended string.\r
+ */\r
+ void toHexString(const unsigned char* hash, char* hexstring);\r
+\r
+} // namespace sha1\r
+\r
+#endif // SHA1_DEFINED\r
--- /dev/null
+<!DOCTYPE html>
+
+<meta charset="utf-8" />
+
+<title>WebSocket Test</title>
+
+<script language="javascript" type="text/javascript">
+
+ //var wsUri = "ws://echo.websocket.org/";
+ var wsUri = "ws://127.0.0.1:10222/";
+ var output;
+
+ function init()
+ {
+ output = document.getElementById("output");
+ testWebSocket();
+ }
+
+ function testWebSocket()
+ {
+ websocket = new WebSocket(wsUri);
+ websocket.onopen = function(evt) { onOpen(evt) };
+ websocket.onclose = function(evt) { onClose(evt) };
+ websocket.onmessage = function(evt) { onMessage(evt) };
+ websocket.onerror = function(evt) { onError(evt) };
+ }
+
+ function onOpen(evt)
+ {
+ writeToScreen("CONNECTED");
+ doSend("WebSocket rocks");
+ }
+
+ function onClose(evt)
+ {
+ writeToScreen("DISCONNECTED");
+ }
+
+ function onMessage(evt)
+ {
+ writeToScreen('<span style="color: blue;">RESPONSE: ' + evt.data+'</span>');
+ websocket.close();
+ }
+
+ function onError(evt)
+ {
+ writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data);
+ }
+
+ function doSend(message)
+ {
+ writeToScreen("SENT: " + message);
+ websocket.send(message);
+ }
+
+ function writeToScreen(message)
+ {
+ var pre = document.createElement("p");
+ pre.style.wordWrap = "break-word";
+ pre.innerHTML = message;
+ output.appendChild(pre);
+ }
+
+ window.addEventListener("load", init, false);
+
+</script>
+
+<h2>WebSocket Test</h2>
+
+<div id="output"></div>
--- /dev/null
+#include <iostream>
+#include <list>
+
+#include <aeb/net/ip/tcp.h>
+#include <aeb/net/ip/basic_endpoint.h>
+#include <aeb/net/socket_base.h>
+#include <aeb/net/io_dispatcher.h>
+#include <aeb/net/basic_socket_acceptor.h>
+#include <aeb/net/basic_socket_stream.h>
+#include <aeb/pointer/intrusive_ptr.h>
+#if defined(__MINGW32__)
+#include <aeb/net/detail/winsock_init.h>
+#endif
+
+using namespace aeb::net;
+typedef aeb::net::io_dispatcher<aeb::net::detail::socket_type,ip::tcp> io_service;
+typedef aeb::net::basic_socket<ip::tcp> tcp_socket;
+
+#include "reverse_proxy.h"
+#include "ws_constant.h"
+#include "ws_log.h"
+#include "ws_frame.h"
+#include "ws_proxy_endpoint.h"
+#include "ws_proxy_handler.h"
+
+#include "ws_acceptor.h"
+#include "ws_handler_factory.h"
+
+// Local debug macro
+#define LOG_WSAC_DEBUG(x) do { m_logger->log(eLOG_DEBUG,"%s\n",x); } while (0)
+#define LOG_WSAC_DEBUG_FMT(format,args...) do {m_logger->log(eLOG_DEBUG,format "\n",args);} while (0)
+
+#define LOG_WSAC_NOTICE(x) do { m_logger->log(eLOG_NOTICE,"%s\n",x); } while (0)
+#define LOG_WSAC_NOTICE_FMT(format,args...) do { m_logger->log(eLOG_NOTICE,format "\n",args); } while (0)
+
+#define LOG_WSAC_WARN(x) do { m_logger->log(eLOG_WARN,"%s\n",x); } while (0)
+#define LOG_WSAC_WARN_FMT(format,args...) do { m_logger->log(eLOG_WARN,format "\n",args); } while (0)
+
+#define LOG_WSAC_ERROR(x) do { m_logger->log(eLOG_ERROR,"%s\n",x); } while (0)
+#define LOG_WSAC_ERROR_FMT(format,args...) do { m_logger->log(eLOG_ERROR,format "\n",args); } while (0)
+
+static ws_log *m_logger = ws_log::getInstance("ws_acceptor");
+
+
+
+ws_acceptor::ws_acceptor(const ip::basic_endpoint<ip::tcp> &e, const wsproxy::ReverseProxyType_skel::application_sequence &apps)
+ : basic_socket_acceptor<detail::socket_type,ip::tcp>(e)
+ , m_applications(apps)
+ , m_ref(1)
+{
+ LOG_WSAC_DEBUG_FMT("ws_acceptor::ws_acceptor %d apps.size=%d",m_ref,apps.size());
+ int val = 1;
+ m_socket.set_option(SO_REUSEADDR,val);
+}
+
+ws_acceptor::~ws_acceptor()
+{
+ LOG_WSAC_DEBUG_FMT("ws_acceptor::~ws_acceptor %d",m_ref);
+}
+
+void
+ws_acceptor::on_accept()
+{
+ endpoint_type tmp;
+ int len = tmp.size();
+ detail::socket_type s;
+ aeb::sys::error ec;
+ LOG_WSAC_DEBUG("ws_acceptor::on_accept");
+ s = aeb::net::accept<socklen_t>(this->m_descriptor,tmp.data(),&len,ec);
+
+ LOG_WSAC_NOTICE_FMT("ws_acceptor::on_accept on(%d) new socket %d error code is %d str(%s)",this->m_descriptor,s,ec.code(), ec.c_str());
+
+ if (s != aeb::net::detail::invalid_socket)
+ {
+ ws_handler_factory *f = ws_handler_factory::get_instance();
+ ws_proxy_handler::ptr handler = f->get_handler(s,m_applications);
+ }
+}
--- /dev/null
+#ifndef WS_ACCEPTOR_H
+#define WS_ACCEPTOR_H
+
+
+class ws_acceptor : public basic_socket_acceptor<aeb::net::detail::socket_type,ip::tcp>
+{
+ private:
+ int m_ref;
+
+ ws_acceptor(ws_acceptor &a) {
+ std::cout<<"ws_acceptor:: copy"<<std::endl;
+ }
+ virtual ~ws_acceptor();
+ public:
+ typedef aeb::intrusive_ptr<ws_acceptor> ptr;
+
+ inline void add_ref() {m_ref++;};
+
+ inline void release() {if (--m_ref == 0) delete this;};
+
+ public:
+ typedef aeb::net::detail::socket_type socket_type;
+
+ ws_acceptor(const ip::basic_endpoint<ip::tcp> &e, const wsproxy::ReverseProxyType_skel::application_sequence &);
+
+ void on_accept();
+ protected:
+ ws_proxy_handler::ptr m_handler;
+ wsproxy::ReverseProxyType_skel::application_sequence m_applications;
+};
+
+#endif
--- /dev/null
+#ifndef WS_ALLOCATOR_H
+#define WS_ALLOCATOR_H
+
+/**
+ * The purpose of the allocator is to
+ * align memory allocation to 8 bytes borders. This should help
+ * optimize the memory management.
+ * An maybe pool management as well to collect statistics.
+ *
+ * To be used for strings, vectors, maps, list, queues
+ */
+template <typename T ,std::size_t maxSize,int Align = 4>
+struct ws_allocator
+{
+ public:
+ // traits
+ typedef T * pointer;
+ typedef const T * const_pointer;
+ typedef T & reference;
+ typedef const T & const_reference;
+ typedef T value_type;
+ typedef std::size_t size_type;
+ typedef std::ptrdiff_t difference_type;
+ public:
+
+ ws_allocator() {}
+
+ ws_allocator(const ws_allocator &a) {}
+
+ ~ws_allocator() {}
+
+ pointer allocate(std::size_t n)
+ {
+ //check max size ...
+ if ( (n < maxSize) && (n <= std::numeric_limits<std::size_t>::max() / sizeof(T)) )
+ {
+ int l = (n * sizeof(T) / Align + 1) * Align;
+ if (void * ptr = std::malloc( ( n * sizeof(T)/Align + 1) * Align ) ) {
+ return static_cast<pointer>(ptr);
+ }
+ }
+ throw std::bad_alloc();
+ }
+
+ void deallocate(pointer ptr, std::size_t n) {
+ std::free(ptr);
+ }
+
+ void construct(pointer p, const_reference val) { new(p) T(val); }
+
+ void destroy(pointer p)
+ {
+ p->~T();
+ }
+
+ size_t max_size() const { return maxSize;}
+
+ template <typename U>
+ struct rebind { typedef ws_allocator<U,maxSize> other; } ;
+ protected:
+};
+
+template <typename T1,std::size_t S1,int A1,typename T2,std::size_t S2,int A2>
+inline bool operator ==(ws_allocator<T1,S1,A1> const &,ws_allocator<T2,S2,A2> const &) {
+ return true;
+}
+
+#endif /*WS_ALLOCATOR_H*/
--- /dev/null
+#include <iostream>
+#include "ws_base64.h"
+
+/*
+ * Translation Table as described in RFC1113
+ **/
+static const unsigned char cb64[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/*
+ * Translation Table to decode (created by author)
+ **/
+static const unsigned char cd64[]="|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq";
+
+
+
+void
+encode_base64_block(unsigned char *in,unsigned char *out,int len)
+{
+ out[0] = (unsigned char) cb64[ (int)(in[0] >> 2) & 0x3F ];
+ out[1] = (unsigned char) cb64[ (int)(((in[0] & 0x03) << 4) | ((in[1] & 0xf0) >> 4)) ];
+ out[2] = (unsigned char) (len > 1 ? cb64[ (int)(((in[1] & 0x0f) << 2) | ((in[2] & 0xc0) >> 6)) ] : '=');
+ out[3] = (unsigned char) (len > 2 ? cb64[ (int)(in[2] & 0x3f) ] : '=');
+ ;
+}
+
+
+void
+decode_base64_block(unsigned char *in,unsigned char *out,int len)
+{
+ out[ 0 ] = (unsigned char ) (in[0] << 2 | in[1] >> 4);
+ out[ 1 ] = (unsigned char ) (in[1] << 4 | in[2] >> 2);
+ out[ 2 ] = (unsigned char ) (((in[2] << 6) & 0xc0) | in[3]);
+}
+
+
+void encode_base64(const unsigned char *str,int len,std::string &output )
+{
+ int count = 0;
+ unsigned char *p = ( unsigned char *)(str);
+ const unsigned char *end =(unsigned char *)( str + len);
+
+ long size = ( (len - 1) /3) * 4 + 4;
+
+ output.resize(size);
+ unsigned char *out = (unsigned char *)(const_cast<char *>(output.c_str()));
+ while (p < end)
+ {
+ if (p+3 < end) {
+ encode_base64_block(p,out,end-p);
+ p+=3; out+=4;
+ } else
+ {
+ encode_base64_block(p,out,end-p);
+ p+= (end-p);
+ }
+ }
+
+
+}
+
--- /dev/null
+#ifndef WS_BASE64_H__
+#define WS_BASE64_H__
+void encode_base64(const unsigned char *,int len,std::string &output );
+
+#endif
--- /dev/null
+#ifndef WS_CONN_HANDLER_H
+#define WS_CONN_HANDLER_H
+
+class ws_conn_handler;
+
+/**
+ * \brief
+ * Possible state. IDLE, CONNECTING, SSL HANDSHACK, OPEN, CLOSING, CLOSED
+ * when ssl, conn goes through SSL HANDSHACK, otherwise direcly to OPEN
+ * Should I handle chunk and mime types here or at a higher level?
+ *
+ */
+class ws_conn_state
+{
+ ws_conn_state();
+ ws_conn_state(const ws_conn_state &con);
+ public:
+ virtual ~ws_conn_state();
+
+ virtual void onRead(ws_conn_handler &p,const ws_frame &buffer);
+
+ virtual void onClose(unsigned short _code) ;
+
+ virtual void doActivity(ws_conn_handler &p);
+
+};
+
+
+
+//
+// ws_conn_handler
+// \bried Actually, this is probably the lowest layer I should use.
+// Once connected, the flow should travel through upper layers
+// that know better what to do. Plugins should come in action
+// specifically conn hooks
+// - decides if the connection is accepted
+// - according to config, decides if its ssl or not
+// - conn_handler has a stated. IDLE CONNECTING OPEN CLOSING CLOSED
+// - several https/http/ws or what ever can be processed.
+// - if ssl do ssl auth stuff and so on
+// - if plain do plain read and so on
+//
+class ws_conn_handler : public ws_handler
+{
+ public:
+
+ bool available () const;
+
+ public:
+ ws_conn_handler(detail::socket_type s);
+
+ virtual ~ws_conn_handler() ;
+
+ virtual void onRead(ws_conn_handler &p,const ws_frame &buffer);
+
+ virtual void onClose(unsigned short _code) ;
+
+ void on_write() ;
+
+ void on_read() ;
+
+ void on_accept();
+
+ void io_register();
+
+ void io_unregister();
+ // reuse handler with a new socket
+ void reuse(aeb::net::detail::socket_type s);
+
+ protected:
+ ws_conn_handler(const ws_conn_handler &p);
+ ws_conn_state *m_state;
+ // Do I own stream objects or not
+};
+
+
+#endif /*WS_CONN_HANDLER_H*/
--- /dev/null
+#ifndef WS_CONSTANT_H__
+#define WS_CONSTANT_H__
+
+#define SEC_WEBSOCKET_VERSION "Sec-WebSocket-Version"
+#define SEC_WEBSOCKET_KEY "Sec-WebSocket-Key"
+#define SEC_WEBSOCKET_ACCEPT "Sec-WebSocket-Accept"
+#define SEC_WEBSOCKET_PROTOCOL "Sec-WebSocket-Protocol"
+#define WS_UPGRADE "Upgrade"
+#define WS_CONNECTION "Connection"
+#define WS_COOKIES "Cookies"
+#define WS_HOST "Host"
+#define WS_GET "GET"
+#define WS_ORIGIN "Origin"
+
+#define WS_HANDSHACK "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
+
+// Structures as well
+
+enum ws_opcodes {
+ ews_continue = 0x0
+ ,ews_text = 0x1
+ ,ews_binary = 0x2
+ ,ews_close = 0x8
+ ,ews_ping = 0x9
+ ,ews_pong = 0xA
+};
+
+enum ws_exit_code {
+ ews_exit_normal = 1000
+ ,ews_exit_server = 1001
+ ,ews_exit_protocol = 1001
+ ,ews_exit_bad_frame = 1003
+};
+
+typedef struct ws_header_ {
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+ // For Big endian
+ unsigned char fin :1;
+ unsigned char rsv1:1;
+ unsigned char rsv2:1;
+ unsigned char rsv3:1;
+ unsigned char opcode:4;
+#else
+ unsigned char opcode:4;
+ unsigned char rsv3:1;
+ unsigned char rsv1:1;
+ unsigned char rsv2:1;
+ unsigned char fin :1;
+#endif
+} ws_header;
+
+
+#endif
--- /dev/null
+#include <iostream>
+#include <iomanip>
+#include <sstream>
+#include <map>
+
+#include <stdlib.h> // for calloc
+#include <string.h>
+
+#include "ws_log.h"
+#include "ws_constant.h"
+#include "ws_frame.h"
+
+// Local debug macro
+#define LOG_WSFR_DEBUG(x) do { m_logger->log(eLOG_DEBUG,"%s\n",x); } while (0)
+#define LOG_WSFR_DEBUG_FMT(format,args...) do {m_logger->log(eLOG_DEBUG,format "\n",args);} while (0)
+
+#define LOG_WSFR_NOTICE(x) do { m_logger->log(eLOG_NOTICE,"%s\n",x); } while (0)
+#define LOG_WSFR_NOTICE_FMT(format,args...) do { m_logger->log(eLOG_NOTICE,format "\n",args); printf(format "\n",args);} while (0)
+
+#define LOG_WSFR_WARN(x) do { m_logger->log(eLOG_WARN,"%s\n",x); } while (0)
+#define LOG_WSFR_WARN_FMT(format,args...) do { m_logger->log(eLOG_WARN,format "\n",args); printf(format "\n",args);} while (0)
+
+#define LOG_WSFR_ERROR(x) do { m_logger->log(eLOG_ERROR,"%s\n",x); } while (0)
+#define LOG_WSFR_ERROR_FMT(format,args...) do { m_logger->log(eLOG_ERROR,format "\n",args); } while (0)
+
+static ws_log *m_logger = ws_log::getInstance("ws_frame");
+
+
+
+ws_frame::ws_frame(unsigned char *_d,long l)
+ : m_length(l) , m_buffer(_d) , m_data(_d) , m_allocated(false)
+{
+}
+
+ws_frame::~ws_frame()
+{
+ if (m_allocated)
+ free(m_buffer);
+}
+
+int
+ws_frame::decode( unsigned char *_d,size_t len)
+{
+ bool have_key = false;
+ m_buffer = _d;
+ m_data = _d;
+ m_header = *(ws_header *)_d;
+ m_data++;
+ LOG_WSFR_DEBUG_FMT("ws_frame::decode start len=%d",len);
+ if ( *m_data & 0x80)
+ have_key = true;
+ if ( (*m_data & 0x7F) == 0x7E) {
+ // 16 bits length header
+ m_data++;
+ unsigned short l = *(unsigned short *) m_data ;
+ m_data+=2;
+ m_length = l;
+ } else {
+ m_length = *m_data & 0x7F;
+ m_data++;
+ }
+ if (have_key)
+ {
+ int i = 0;
+ unsigned char *d = NULL;
+ for ( ; i < 4 ; m_key[i++] = *m_data, m_data++) ;
+ // Now if mask decode the buffer;
+ d = m_data;
+ len -= (d - _d);
+ i = 0;
+ LOG_WSFR_DEBUG_FMT("ws_frame::decode loop pos=%d len=%d",(int)(m_data-_d),len);
+ while (len--)
+ {
+ *d = *d ^ m_key[i % 4];
+ d++;
+ i++;
+ }
+ } else {
+ LOG_WSFR_ERROR("ws_frame::decode un encoded frame should quit");
+ }
+ LOG_WSFR_DEBUG("End Decode Frame");
+ LOG_WSFR_DEBUG_FMT("ws_frame::decode end m_length=%d data=%s",m_length,m_data);
+}
+
+int
+ws_frame::encode(const unsigned char *_b,size_t len,int mode)
+{
+ m_length = len;
+ m_allocated = true;
+
+ std::ostringstream ss;
+ std::ostringstream str;
+
+ int al = ( len / 8 + 2) * 8;
+ m_buffer = (unsigned char *)calloc( al , sizeof(char) );
+ LOG_WSFR_DEBUG_FMT("ws_frame::encode( len=%d) allocated %d",len,al);
+#if 0
+ // For debug purpose.
+ for (int i = 0 ; i < len ; i++ )
+ {
+ ss<<std::hex<<std::setw(2)<<std::setfill('0')<<(int)_b[i]<<" ";
+ if (_b[i] > 31 && _b[i] < 127) {
+ str<<_b[i];
+ } else {
+ str<<".";
+ }
+ if ( ! (i % 16 ) ) {
+ LOG_WSPH_DEBUG_FMT("ws_frame::encode(buffer=<%s %s> size=%lu)",ss.str().c_str(),str.str().c_str(),len);
+ ss.str(""); str.str("");
+ }
+ }
+#endif
+ if (len < 126)
+ {
+ ws_header *header = (ws_header *)m_buffer;
+ //header->opcode = ews_binary;
+ header->opcode = mode;
+ header->fin = 1;
+ m_buffer[1] = (char) len;
+ memcpy(&m_buffer[2],_b,len);
+ //m_stream.send(buffer,len+2);
+ m_buffer_length = len + 2;
+ } else if (len < 65535)
+ {
+ unsigned short *l = (unsigned short *)&m_buffer[2];
+ ws_header *header = (ws_header *)m_buffer;
+
+ //header->opcode = ews_binary;
+ header->opcode = mode;
+ header->fin = 1;
+ m_buffer[1] = (char) 126;
+ *l = (unsigned short)len;
+ memcpy(&m_buffer[4],_b,len);
+ //m_stream.send(buffer,len+4);
+ m_buffer_length = len + 4;
+ } else {
+ LOG_WSFR_ERROR("ws_frame::encode( frame_size Too big > 65535 not supported");
+ return 1;
+ }
+ return 0;
+
+}
--- /dev/null
+#ifndef WS_BUFFER_H__
+#define WS_BUFFER_H__
+
+class ws_frame
+{
+ public:
+ ws_frame(unsigned char *_d=NULL,long len=0);
+
+ ~ws_frame();
+
+ inline long length() const { return m_length; };
+
+ inline unsigned char * data() const { return m_data; };
+
+ inline const unsigned char * buffer() const { return m_buffer; };
+
+ inline long buffer_length() const { return m_buffer_length;};
+
+ inline int opcode() const { return m_header.opcode; };
+
+ int decode( unsigned char *b,size_t length);
+
+ int encode(const unsigned char *_b,size_t length , int mode = ews_binary);
+
+ private:
+ ws_frame(const ws_frame &) {} ;
+ protected:
+ unsigned char m_key[4];
+ unsigned char *m_buffer;
+ unsigned char *m_data;
+ long m_length;
+ long m_buffer_length;
+ ws_header m_header;
+ bool m_allocated;
+};
+#endif
--- /dev/null
+#ifndef WS_HANDLER_H
+#define WS_HANDLER_H
+
+
+//
+// ws_proxy_handler
+//
+class ws_handler : public event_handler<detail::socket_type,ip::tcp>
+{
+ private:
+ int m_ref;
+ public:
+
+ typedef basic_socket_stream<ip::tcp> Stream;
+ /// aeb::ssl::stream<ip::tcp> ? or detail::socket_type
+ typedef basic_socket_stream<ip::tcp> SSLStream;
+
+ typedef aeb::intrusive_ptr<ws_handler> ptr;
+
+ inline void add_ref() {m_ref++;};
+
+ inline void release() {
+ if (--m_ref == 0) delete this;
+ };
+ bool available () const;
+
+ public:
+ ws_handler(detail::socket_type s);
+
+ virtual ~ws_handler() ;
+
+
+ virtual void onRead(ws_proxy_handler &p,const ws_frame &buffer);
+
+ virtual void onClose(unsigned short _code) ;
+
+ void on_write() ;
+
+ void on_read() ;
+
+ void on_accept();
+
+ void io_register();
+
+ void io_unregister();
+ // reuse handler with a new socket
+ void reuse(aeb::net::detail::socket_type s);
+
+ protected:
+ ws_handler(const ws_handler &p) ;
+ // Should own an endpoint
+ long m_max_frame_size;
+};
+
+/**
+vim:et:sw=2:ts=2
+ */
+
+
+#endif /*WS_HANDLER_H*/
--- /dev/null
+#include <iostream>
+#include <map>
+#include <list>
+#include <algorithm> // find_if
+
+#include <aeb/net/ip/tcp.h>
+#include <aeb/net/ip/basic_endpoint.h>
+#include <aeb/net/socket_base.h>
+#include <aeb/net/io_dispatcher.h>
+#include <aeb/net/basic_socket_acceptor.h>
+#include <aeb/net/basic_socket_stream.h>
+#include <aeb/pointer/intrusive_ptr.h>
+#if defined(__MINGW32__)
+#include <aeb/net/detail/winsock_init.h>
+#endif
+
+
+#include "time.h"
+#include "reverse_proxy.h"
+#include "ws_constant.h"
+#include "ws_log.h"
+#include "ws_frame.h"
+#include "sha1.h"
+#include "ws_base64.h"
+
+using namespace aeb::net;
+typedef aeb::net::io_dispatcher<aeb::net::detail::socket_type,ip::tcp> io_service;
+#include "ws_proxy_endpoint.h"
+#include "ws_proxy_handler.h"
+#include "ws_handler_factory.h"
+
+// Log feature
+#define LOG_WSHF_DEBUG(x) do { m_logger->log(eLOG_DEBUG,"%s\n",x); } while (0);
+#define LOG_WSHF_DEBUG_FMT(format,args...) do {m_logger->log(eLOG_DEBUG,format "\n" ,args);} while (0)
+
+#define LOG_WSHF_NOTICE(x) do {m_logger->log(eLOG_NOTICE,"%s\n",x);} while (0);
+#define LOG_WSHF_NOTICE_FMT(format,args...) do { m_logger->log(eLOG_NOTICE,format "\n",args);} while (0)
+
+#define LOG_WSHF_WARN(x) do {m_logger->log(eLOG_WARN,"%s\n",x);} while (0)
+#define LOG_WSHF_WARN_FMT(format,args...) do { m_logger->log(eLOG_WARN,format "\n",args);} while (0)
+
+#define LOG_WSHF_ERROR(x) do {m_logger->log(eLOG_ERROR,"%s\n",x);} while (0)
+#define LOG_WSHF_ERROR_FMT(format,args...) do {m_logger->log(eLOG_ERROR,format "\n",args);} while (0)
+
+static ws_log *m_logger = ws_log::getInstance("ws_handler_factory");
+
+// Begin Of Code
+
+//
+ws_handler_factory::ws_handler_factory()
+{
+ LOG_WSHF_DEBUG("ws_handler_factory::ws_handler_factory");
+}
+
+//
+ws_handler_factory::ws_handler_factory(const ws_handler_factory &_f)
+{
+ LOG_WSHF_DEBUG("ws_handler_factory::ws_handler_factory copy");
+}
+
+// I need a finder object to find
+struct find_free {
+ find_free() {};
+ bool operator ()(ws_handler_factory::handler &h) {
+ return h->available();
+ };
+};
+
+// I need a finder object to find
+struct find_pointer {
+ find_pointer(ws_proxy_handler *_p) : m_p(_p) {};
+ bool operator ()(ws_handler_factory::handler &h) {
+ return m_p == (ws_proxy_handler *)h;
+ };
+ ws_proxy_handler *m_p;
+};
+// handler get_handler(aeb::net::detail::socket_type fd,const wsproxy::ReverseProxyType_skel::application_sequence &apps);
+//
+ws_handler_factory::handler
+ws_handler_factory::get_handler(aeb::net::detail::socket_type fd,const wsproxy::ReverseProxyType_skel::application_sequence &apps)
+{
+ LOG_WSHF_DEBUG_FMT("ws_handler_factory::get_handler active handlers %d", m_handlers.size());
+
+ handler_iterator it = std::find_if(m_handlers.begin(),m_handlers.end(),find_free());
+
+ if ( it == m_handlers.end() )
+ {
+ LOG_WSHF_DEBUG("ws_handler_factory::get_handler create new");
+ handler h( new ws_proxy_handler(fd,apps));
+ m_handlers.push_back(h);
+ return h;
+ }
+ else
+ {
+ handler h(*it);
+ LOG_WSHF_DEBUG("ws_handler_factory::get_handler recycle unused handler");
+ (*it)->reuse(fd);
+ return h;
+ }
+}
+
+bool ws_handler_factory::find(ws_proxy_handler *ptr,handler _rp)
+{
+ handler_iterator it = std::find_if(m_handlers.begin(),m_handlers.end(),find_pointer(ptr));
+ if (it != m_handlers.end())
+ {
+ _rp = *it;
+ return true;
+ } else {
+ return false;
+ }
+}
+//
+ws_handler_factory * ws_handler_factory::m_factory = NULL;
+
+ws_handler_factory *
+ws_handler_factory::get_instance() {
+ if (m_factory == NULL)
+ m_factory = new ws_handler_factory();
+ return m_factory;
+
+}
+
+void
+ws_handler_factory::close_handlers()
+{
+ LOG_WSHF_DEBUG_FMT("ws_handler_factory::close_handlers handlers size: %d",m_handlers.size());
+ handler_iterator it = m_handlers.begin();
+ for ( ; it != m_handlers.end() ; ++it)
+ {
+ if (!(*it)->available())
+ (*it)->onClose(ews_exit_server);
+ // (*it)->release();
+ }
+ //m_handlers.erase(m_handlers.begin(),m_handlers.end());
+
+ LOG_WSHF_DEBUG_FMT("ws_handler_factory::close_handlers handlers end size : %d",m_handlers.size());
+}
--- /dev/null
+#ifndef WS_HANDLER_FACTORY_H__
+#define WS_HANDLER_FACTORY_H__
+
+
+class ws_handler_factory
+{
+ public:
+ typedef ws_proxy_handler::ptr handler;
+ //typedef std::map<int,ws_proxy_handler::ptr> handler_map;
+ typedef std::list<ws_proxy_handler::ptr> handler_map;
+ typedef handler_map::iterator handler_iterator;
+
+ static ws_handler_factory *get_instance();
+ //
+ handler get_handler(aeb::net::detail::socket_type fd,const wsproxy::ReverseProxyType_skel::application_sequence &apps);
+ // In case of end, close all sockets and exit properly
+ void close_handlers();
+
+ bool find(ws_proxy_handler *ptr,handler _rp);
+ protected:
+ handler_map m_handlers;
+
+ private:
+ ws_handler_factory();
+
+ ws_handler_factory(const ws_handler_factory &_f);
+
+ static ws_handler_factory *m_factory;
+};
+
+/*
+ vim:et:sw=2:ts=2
+ */
+#endif
--- /dev/null
+#ifndef WS_HTTP_HANDLER_H
+#define WS_HTTP_HANDLER_H
+
+//
+// ws_http_handler
+//
+class ws_http_handler : public ws_handler
+{
+ public:
+
+ bool available () const;
+
+ public:
+ ws_http_handler(detail::socket_type s);
+
+ virtual ~ws_http_handler() ;
+
+
+ virtual void onRead(ws_handler &p,const ws_frame &buffer);
+
+ virtual void onClose(unsigned short _code) ;
+
+ void on_write() ;
+
+ void on_read() ;
+
+ void on_accept();
+
+ void io_register();
+
+ void io_unregister();
+ // reuse handler with a new socket
+ void reuse(aeb::net::detail::socket_type s);
+
+ protected:
+ ws_http_handler(const ws_http_handler &p) ;
+};
+
+/**
+vim:et:sw=2:ts=2
+ */
+
+#endif /*WS_HTTP_HANDLER_H*/
--- /dev/null
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <map>
+#include <limits>
+#include <cstdlib>
+
+#include "ws_constant.h"
+#include "ws_log.h"
+#include "ws_allocator.h"
+#include "ws_http_header.h"
+
+// Local debug macro
+#define LOG_WSHH_DEBUG(x) do { m_logger->log(eLOG_DEBUG,"%s\n",x); } while (0);
+#define LOG_WSHH_DEBUG_FMT(format,args...) do {m_logger->log(eLOG_DEBUG,format "\n" ,args);} while (0)
+
+#define LOG_WSHH_NOTICE(x) do {m_logger->log(eLOG_NOTICE,"%s\n",x);} while (0);
+#define LOG_WSHH_NOTICE_FMT(format,args...) do { m_logger->log(eLOG_NOTICE,format "\n",args);} while (0) ;
+
+#define LOG_WSHH_WARN(x) do {m_logger->log(eLOG_WARN,"%s\n",x);} while (0);
+#define LOG_WSHH_WARN_FMT(format,args...) do { m_logger->log(eLOG_WARN,format "\n",args);} while (0) ;
+
+#define LOG_WSHH_ERROR(x) do {m_logger->log(eLOG_ERROR,"%s\n",x);} while (0);
+#define LOG_WSHH_ERROR_FMT(format,args...) do {m_logger->log(eLOG_ERROR,format "\n",args);} while (0) ;
+
+static ws_log *m_logger = ws_log::getInstance("ws_proxy_handler");
+
+//using namespace aeb::net;
+//typedef aeb::net::io_dispatcher<aeb::net::detail::socket_type,ip::tcp> io_service;
+
+//#include "ws_proxy_endpoint.h"
+
+//
+ws_http_header::ws_http_header()
+{
+ LOG_WSHH_DEBUG("ws_http_header::ws_http_header");
+}
+
+
+ws_http_header::~ws_http_header()
+{
+ LOG_WSHH_DEBUG("ws_http_header::~ws_http_header");
+}
+
+int
+ws_http_header::parse(const std::string &_h)
+{
+ LOG_WSHH_DEBUG("ws_http_header::parse ");
+ std::istringstream stream(_h);
+ char result[256];
+
+ while( ! stream.getline(result,256).eof() ) {
+ int pos;
+ std::string s(result);
+ // std::cout<<"Process line:"<<s<<std::endl;
+ if ( (pos = s.find(SEC_WEBSOCKET_KEY)) != std::string::npos && (pos == 0)) {
+ set_entry(SEC_WEBSOCKET_KEY,s.substr(s.find(":")+1));
+ }
+ else if ( ((pos = s.find(WS_UPGRADE)) != std::string::npos ) && (pos == 0 )) {
+ set_entry(WS_UPGRADE,s.substr(s.find(":")+1));
+ }
+ else if ( ((pos = s.find(WS_ORIGIN)) != std::string::npos ) && (pos == 0 )) {
+ set_entry(WS_ORIGIN,s.substr(s.find(":")+1));
+ }
+ else if ( (pos = s.find(SEC_WEBSOCKET_VERSION)) != std::string::npos && (pos == 0)) {
+ set_entry(SEC_WEBSOCKET_VERSION,s.substr(s.find(":")+1));
+ }
+ else if ( (pos = s.find(WS_CONNECTION)) != std::string::npos) {
+ set_entry(WS_CONNECTION,s.substr(s.find(":")+1));
+ }
+ else if ( (pos = s.find(SEC_WEBSOCKET_PROTOCOL) != std::string::npos) && (pos == 0)) {
+ set_entry(SEC_WEBSOCKET_PROTOCOL,s.substr(s.find(":")+1));
+ }
+ else if ( (pos = s.find(WS_HOST) != std::string::npos) && (pos == 0)) {
+ set_entry(WS_HOST,s.substr(s.find(":")+1));
+ }
+ else if ( (pos = s.find(WS_COOKIES) != std::string::npos) && (pos == 0)) {
+ set_entry(WS_COOKIES,s.substr(s.find(":")+1));
+ } else {
+ }
+ }
+ return 0;
+}
+
+
+void
+ws_http_header::to_string(std::string &_h)
+{
+}
+
+void
+ws_http_header::set_entry(const string32 &key,const std::string &_val)
+{
+ string256 s(_val.substr(1,_val.size()-2).c_str());
+ LOG_WSHH_DEBUG_FMT("ws_proxy_handler::set_header key=%s Value(%s)",key.c_str(),s.c_str());
+ m_header[key] = s;
+}
--- /dev/null
+#ifndef WS_HTTP_HEADER_H__
+#define WS_HTTP_HEADER_H__
+
+class ws_http_header {
+ public:
+ typedef ws_allocator<char,256> ws256_alloc;
+ typedef ws_allocator<char,32> ws32_alloc;
+ typedef std::basic_string<char,std::char_traits<char>,ws256_alloc> string256;
+ typedef std::basic_string<char,std::char_traits<char>,ws32_alloc> string32;
+
+ ws_http_header();
+
+ virtual ~ws_http_header();
+
+ int parse(const std::string &_h);
+
+ void to_string(std::string &_h);
+
+ void to_string(int code,std::string &result);
+
+ void set_entry(const string32 &key,const std::string &_val);
+ protected:
+ // header entries
+ std::map<string32,string256> m_header;
+
+};
+
+#endif
--- /dev/null
+#ifndef WS_HTTPS_HANDLER_H
+#define WS_HTTPS_HANDLER_H
+
+//
+// ws_https_handler
+//
+class ws_https_handler : public ws_handler
+{
+ public:
+
+ bool available () const;
+
+ public:
+ ws_https_handler(detail::socket_type s);
+
+ virtual ~ws_https_handler() ;
+
+
+ virtual void onRead(ws_handler &p,const ws_frame &buffer);
+
+ virtual void onClose(unsigned short _code) ;
+
+ void on_write() ;
+
+ void on_read() ;
+
+ void on_accept();
+
+ void io_register();
+
+ void io_unregister();
+ // reuse handler with a new socket
+ void reuse(aeb::net::detail::socket_type s);
+
+ protected:
+ ws_https_handler(const ws_http_handler &p);
+
+};
+
+
+
+#endif /*WS_HTTPS_HANDLER_H*/
--- /dev/null
+#include <iostream>
+#include <string>
+#include <map>
+#include <stdarg.h>
+#include <string.h> // for memset
+#include <syslog.h>
+
+#include "ws_log.h"
+
+
+ws_log *ws_log::getInstance(const std::string &cls)
+{
+// std::cout<<"ws_log::getInstance "<<cls<<std::endl;
+ logger_const_iterator it = m_loggers.find(cls);
+// std::cout<<"ws_log::getInstance after fine "<<cls<<" m_loggers size = "<<m_loggers.size()<<std::endl;
+ if (m_loggers.size() == 0)
+ {
+ ws_log::m_loggers = ws_log::init_map();
+ }
+
+ if (it != m_loggers.end()) {
+ // std::cout<<"ws_log::getInstance found"<<cls<<std::endl;
+ return it->second;
+ }
+ ws_log *l = new ws_log(cls);
+ ws_log::m_loggers[cls] = l;
+ // std::cout<<"ws_log::getInstance before return "<<cls<<std::endl;
+ return l;
+}
+
+#define BUFFER_SIZE (1024*8)
+void ws_log::log(int level,const char *format,...)
+{
+ char buffer[BUFFER_SIZE];
+ va_list vl;
+ memset(buffer,0x00,BUFFER_SIZE);
+ va_start( vl,format);
+ vsprintf(buffer,format,vl);
+ if (level <= m_level)
+ std::cout<<buffer;
+ va_end(vl);
+ // Missing check if loger should write to syslog
+ switch (level)
+ {
+ case eLOG_ERROR:
+ syslog(LOG_ERR,buffer);
+ break;
+ case eLOG_WARN:
+ syslog(LOG_WARNING,buffer);
+ break;
+ default:
+ ;
+ }
+ //syslog(buffer);
+}
+
+ws_log::ws_log(const std::string &name)
+ : m_level(eLOG_DEBUG) , m_syslog(false)
+{
+}
+
+ws_log::ws_log(const ws_log &name)
+{
+ if ( !ws_log::m_syslog_open)
+ {
+ openlog("wsproxy",LOG_PID | LOG_NDELAY,LOG_DAEMON);
+ }
+}
+
+ws_log::logger_type
+ws_log::init_map()
+{
+ logger_type m;
+ std::cout<<"ws_log::init_map "<<std::endl;
+ m["null"] = NULL;
+ return m;
+}
+
+std::map<std::string,ws_log *> ws_log::m_loggers = init_map();
+bool ws_log::m_syslog_open = false;
+
--- /dev/null
+#ifndef WS_LOG_H__
+#define WS_LOG_H__
+
+
+enum e_ws_log_level {
+ eLOG_EMERG = 0
+ ,eLOG_ERROR = 1
+ ,eLOG_AUTH = 2
+ ,eLOG_WARN = 3
+ ,eLOG_NOTICE = 4
+ ,eLOG_INFO = 5
+ ,eLOG_DEBUG = 6
+
+};
+
+class ws_log
+{
+ public:
+ typedef std::map<std::string,ws_log *> logger_type;
+ typedef std::map<std::string,ws_log *>::const_iterator logger_const_iterator;
+ typedef std::map<std::string,ws_log *>::iterator logger_iterator;
+
+ inline void set_level(int _l) { m_level = _l;};
+
+ inline void set_syslog() {m_syslog = true; };
+
+ static ws_log *getInstance(const std::string &cls) ;
+
+ void log(int level,const char *format,...);
+
+ protected:
+ int m_level;
+ bool m_syslog; // Says if trace should be reported in syslog.
+ private:
+ ws_log(const std::string &name);
+
+ ws_log(const ws_log &name);
+ static logger_type init_map();
+ static logger_type m_loggers;
+ static bool m_syslog_open;
+};
+#endif
--- /dev/null
+#ifndef WS_PLUGING_H__
+#define WS_PLUGING_H__
+
+#define PLUGIN_OK 0
+#define PLUGIN_DECLINE -1
+
+/**
+ * Defines the kind of hool the plugin is going to register
+ */
+#define PLUGIN_HANDLER_FILTER_IN 0
+#define PLUGIN_HANDLER_CONTENT 1
+#define PLUGIN_HANDLER_FILTER_OUT 2
+
+
+/**
+ * class ws_plugin
+ *
+ * \brief Model class to implement a plugin
+ * \author
+ */
+class ws_plugin {
+ public:
+ //
+ ws_plugin(const std::string &_name) : m_name(_name) {} ;
+ //
+ ws_plugin(const ws_plugin &o) {} ;
+ //
+ ~ws_plugin() {} ;
+
+ // Functions called my the
+ virtual void register_hooks() = 0;
+ protected:
+ // Plugin name
+ std::string m_name;
+};
+
+
+/**
+ * class ws_plugin_handler
+ *
+ * \brief
+ * \author
+ */
+class ws_plugin_handler {
+ public:
+ //
+ ws_plugin_handler() {} ;
+ //
+ ws_plugin_handler(const ws_plugin_handler &o) {} ;
+ //
+ ~ws_plugin_handler() {};
+
+ virtual int handle_request(ws_plugin_request &r) = 0;
+ protected:
+};
+
+
+extern "C" void *create_plugin() ;
+
+#endif
--- /dev/null
+#include <iostream>
+#include <iostream>
+#include <map>
+
+#include "ltdl.h"
+
+#include "ws_log.h"
+
+#include "ws_plugin_request.h"
+#include "ws_plugin.h"
+#include "ws_plugin_manager.h"
+
+// Local debug macro
+#define LOG_PMGR_DEBUG(x) do { m_logger->log(eLOG_DEBUG,"%s\n",x); } while (0);
+#define LOG_PMGR_DEBUG_FMT(format,args...) do {m_logger->log(eLOG_DEBUG,format "\n" ,args);} while (0)
+
+#define LOG_PMGR_INFO(x) do {m_logger->log(eLOG_INFO,"%s\n",x);} while (0);
+#define LOG_PMGR_INFO_FMT(format,args...) do { m_logger->log(eLOG_INFO,format "\n",args);} while (0) ;
+
+#define LOG_PMGR_NOTICE(x) do {m_logger->log(eLOG_NOTICE,"%s\n",x);} while (0);
+#define LOG_PMGR_NOTICE_FMT(format,args...) do { m_logger->log(eLOG_NOTICE,format "\n",args);} while (0) ;
+
+#define LOG_PMGR_WARN(x) do {m_logger->log(eLOG_WARN,"%s\n",x);} while (0);
+#define LOG_PMGR_WARN_FMT(format,args...) do { m_logger->log(eLOG_WARN,format "\n",args);} while (0) ;
+
+#define LOG_PMGR_ERROR(x) do {m_logger->log(eLOG_ERROR,"%s\n",x);} while (0);
+#define LOG_PMGR_ERROR_FMT(format,args...) do {m_logger->log(eLOG_ERROR,format "\n",args);} while (0) ;
+
+static ws_log *m_logger = ws_log::getInstance("ws_plugin_manager");
+
+
+typedef void * (*fct)();
+
+class ltdl_manager : public ws_plugin_manager
+{
+ public:
+ ltdl_manager(const std::string &s ) : ws_plugin_manager(s)
+ {
+ int res = lt_dlinit();
+ LOG_PMGR_DEBUG_FMT("ltdl_manager::ltdl_manager init res=%d",res);
+ }
+
+ ~ltdl_manager() {
+ lt_dlclose(m_Handle);
+ }
+ ws_plugin *load_plugin(const std::string &s)
+ {
+ m_Handle = lt_dlopenext(s.c_str());
+ LOG_PMGR_DEBUG_FMT("ltdl_manager::load_plugin %s m_Handle=%x",s.c_str(),m_Handle);
+ if (m_Handle)
+ {
+ fct _create = reinterpret_cast<fct>(reinterpret_cast<long>(lt_dlsym(m_Handle,"create_plugin")));
+ if ( _create )
+ {
+ ws_plugin *p = static_cast<ws_plugin *>(_create());
+ if (p)
+ {
+ m_Plugins[s] = p;
+ }
+ return p;
+ } else
+ {
+ LOG_PMGR_ERROR_FMT("ltdl_manager::load_plugin Failed create_function lt_dlerror=%s",lt_dlerror());
+ std::cout<<"Failed load function create_plugin"<<std::endl;
+ }
+ } else {
+ LOG_PMGR_ERROR_FMT("ltdl_manager::load_plugin Failed load %s lt_dlerror=%s",s.c_str(),lt_dlerror());
+ }
+ return NULL;
+ }
+
+ protected:
+ lt_dlhandle m_Handle;
+};
+
+// default Contructor
+ws_plugin_manager::ws_plugin_manager(const std::string &n) : m_Dir(n)
+{
+}
+
+//
+ws_plugin_manager::ws_plugin_manager(const ws_plugin_manager &o)
+{
+}
+
+// default Desctructor
+ws_plugin_manager::~ws_plugin_manager()
+{
+}
+
+/**
+ * I should return a shared pointer
+ */
+ws_plugin *
+ws_plugin_manager::load_plugin(const std::string &s)
+{
+ return NULL;
+}
+
+/**
+ * Initialize static attribute
+ */
+ws_plugin_manager *
+ws_plugin_manager::m_PluginManager = NULL;
+
+ws_plugin_manager *
+ws_plugin_manager::getInstance(const std::string &_dir)
+{
+ if (m_PluginManager == NULL)
+ {
+ m_PluginManager = new ltdl_manager(_dir);
+ }
+ return m_PluginManager;
+}
+
--- /dev/null
+#ifndef WS_PLUGIN_MANAGER_H
+#define WS_PLUGIN_MANAGER_H
+
+/**
+ * class ws_plugin_manager
+ *
+ * \brief This class is responsible to provide all services related to plugin management.
+ * load plugins , get a plugins, unload_ plugins
+ * \author
+ */
+class ws_plugin_manager {
+ public:
+ typedef std::map<std::string,ws_plugin *> PluginsMap;
+ public:
+ static ws_plugin_manager *getInstance(const std::string &_dir) ;
+ protected:
+ //
+ ws_plugin_manager(const std::string &dir) ;
+ //
+ ws_plugin_manager(const ws_plugin_manager &o) ;
+ //
+ ~ws_plugin_manager() ;
+
+ public:
+ /**
+ * load plugin whose name is given in parameter
+ * \param _name: plugin name.
+ */
+ virtual ws_plugin* load_plugin(const std::string &_name) ;
+
+
+ protected:
+ std::string m_Dir;
+ // Local storage for loaded plugins
+ PluginsMap m_Plugins;
+ // Instance to store the Manager
+ static ws_plugin_manager *m_PluginManager;
+};
+
+
+#endif /*WS_PLUGIN_MANAGER_H*/
--- /dev/null
+#ifndef WS_PLUGIN_REQUEST_H
+#define WS_PLUGIN_REQUEST_H
+
+
+
+/**
+ * class ws_plugin_request
+ *
+ * \brief this class contains all information required to process an http
+ * request. Among the known information are :
+ * - the uri
+ * - the method (GET,INFO,PUT,DELETE,POST,...
+ * - header_input
+ * - header_output
+ * - content
+ * - connection on which the handler operated (needed for ssl management
+ * - The plugin handler requests by the server
+ * - add missing fields here.
+ * \author
+ */
+class ws_plugin_request {
+ public:
+ // could be improved.
+ typedef std::map<std::string,std::string> header;
+ public:
+ //
+ ws_plugin_request() ;
+ //
+ ws_plugin_request(const ws_plugin_request &o) ;
+ //
+ ~ws_plugin_request() ;
+ protected:
+ header m_HeaderIn;
+ header m_HeaderOut;
+ // Need the native socket so that I can decide what kind of handler to attach to the request.
+ // m_Conn; /* read write handler .... */
+};
+
+
+#endif /*WS_PLUGIN_REQUEST_H*/
--- /dev/null
+#include <iostream>
+
+#include <string.h> // for memset in linux
+#include <aeb/net/ip/basic_endpoint.h>
+#include <aeb/net/ip/tcp.h>
+#include <aeb/net/socket_base.h>
+#include <aeb/net/basic_socket_stream.h>
+
+#include <aeb/net/io_dispatcher.h>
+
+#include <aeb/pointer/intrusive_ptr.h>
+#if defined(__MINGW32__)
+#include <aeb/net/detail/winsock_init.h>
+#endif
+using namespace aeb::net;
+#include "ws_constant.h"
+#include "ws_log.h"
+#include "ws_frame.h"
+#include "reverse_proxy.h"
+#include "ws_proxy_endpoint.h"
+#include "ws_proxy_handler.h"
+
+// Local debug macro
+#define LOG_WSEP_DEBUG(x) do { m_logger->log(eLOG_DEBUG,"%s\n",x); } while (0)
+#define LOG_WSEP_DEBUG_FMT(format,args...) do {m_logger->log(eLOG_DEBUG,format "\n",args);} while (0)
+
+#define LOG_WSEP_NOTICE(x) do { m_logger->log(eLOG_NOTICE,"%s\n",x); } while (0)
+#define LOG_WSEP_NOTICE_FMT(format,args...) do { m_logger->log(eLOG_NOTICE,format "\n",args); printf(format "\n",args);} while (0)
+
+#define LOG_WSEP_WARN(x) do { m_logger->log(eLOG_WARN,"%s\n",x); } while (0)
+#define LOG_WSEP_WARN_FMT(format,args...) do { m_logger->log(eLOG_WARN,format "\n",args); printf(format "\n",args);} while (0)
+
+#define LOG_WSEP_ERROR(x) do { m_logger->log(eLOG_ERROR,"%s\n",x); } while (0)
+#define LOG_WSEP_ERROR_FMT(format,args...) do { m_logger->log(eLOG_ERROR,format "\n",args); } while (0)
+
+static ws_log *m_logger = ws_log::getInstance("ws_proxy_endpoint");
+
+using namespace aeb::net;
+typedef aeb::net::io_dispatcher<aeb::net::detail::socket_type,ip::tcp> io_service;
+typedef aeb::net::basic_socket<ip::tcp> tcp_socket;
+
+ws_proxy_endpoint::ws_proxy_endpoint(const std::string &ws_proxy,const std::string &host,unsigned int port)
+ : m_endpoint(host.c_str(),port), m_stream(),m_ref(0), event_handler<detail::socket_type,ip::tcp>(-1)
+{
+ LOG_WSEP_DEBUG_FMT("ws_proxy_endpoint::ws_proxy_endpoint host=%s port=%d",host.c_str(),port);
+}
+
+ws_proxy_endpoint::ws_proxy_endpoint(const wsproxy::ReverseProxyType_skel::application_sptr &app,const handler_ptr &h)
+ : m_application(app)
+ , m_endpoint(app->attr_host().c_str(),app->attr_port())
+ , m_stream()
+ , m_handler(h)
+ , m_ref(0), event_handler<detail::socket_type,ip::tcp>(-1)
+{
+ LOG_WSEP_DEBUG_FMT("ws_proxy_endpoint::ws_proxy_endpoint host=%s port=%d",app->attr_host().c_str(),app->attr_port());
+}
+
+ws_proxy_endpoint::~ws_proxy_endpoint()
+{
+ LOG_WSEP_DEBUG("ws_proxy_endpoint::~ws_proxy_endpoint");
+ m_stream.close();
+}
+
+int ws_proxy_endpoint::open()
+{
+ LOG_WSEP_DEBUG("ws_proxy_endpoint::open");
+ aeb::net::ip::tcp p = m_endpoint.protocol();
+
+ m_stream.open(p);
+
+ m_stream.connect(m_endpoint);
+
+ this->m_descriptor = m_stream.native();
+ // Register to io_service
+ register_io();
+
+ return 0;
+}
+
+void ws_proxy_endpoint::close()
+{
+ LOG_WSEP_DEBUG("ws_proxy_endpoint::close");
+
+ // Register to io_service
+ unregister_io();
+}
+
+
+
+int ws_proxy_endpoint::send(unsigned char *_b,size_t len)
+{
+ LOG_WSEP_DEBUG_FMT("ws_proxy_endpoint::send len=%d",len);
+ if ( ! m_stream.is_open())
+ {
+ // Do not even try to send anything is the backend is closed
+ LOG_WSEP_ERROR("ws_proxy_endpoint::send bakend is closed try to ropen send");
+ open();
+ }
+ int l = m_stream.send((const char *)_b,len);
+
+ if ( l != len) {
+ LOG_WSEP_ERROR_FMT("ws_proxy_endpoint::send failed: len=%d result=%d",len,l);
+ }
+ return l;
+}
+
+
+#define BUF_SIZE (1024*4)
+void ws_proxy_endpoint::on_read()
+{
+ char buf[BUF_SIZE];
+ LOG_WSEP_DEBUG("ws_proxy_endpoint::on_read");
+ // write data back into proxy_handler (send_frame here, flow attribute is important)
+ memset(buf,0x00,BUF_SIZE);
+ int result = m_stream.receive(buf,BUF_SIZE);
+ if (result < 0 )
+ { // An error ahs occured
+ LOG_WSEP_ERROR_FMT("ws_proxy_endpoint::on_read read %d disconnect %d",result,m_stream.native());
+ } else if (result == 0)
+ { // The socket has been closed. Do the same on my side.
+ LOG_WSEP_WARN_FMT("ws_proxy_endpoint::on_read read 0 disconnect %d",m_stream.native());
+ unregister_io();
+ } else
+ { // Have got the data
+ if (! std::string("text").compare(m_application->attr_flow()))
+ {
+ //if (*m_handler)
+ m_handler->send_frame_text(buf);
+ } else
+ {
+ //if (*m_handler)
+ m_handler->send_frame_binary((unsigned char *)buf,result);
+ }
+ }
+}
+
+void ws_proxy_endpoint::register_io()
+{
+ LOG_WSEP_DEBUG("ws_proxy_endpoint::register_io");
+ io_service *io = aeb::singleton<io_service>::get_instance();
+
+ io->register_handler(0,this);
+}
+void ws_proxy_endpoint::unregister_io()
+{
+ LOG_WSEP_DEBUG("ws_proxy_endpoint::unregister_io");
+ io_service *io = aeb::singleton<io_service>::get_instance();
+ io->unregister_handler(0,this);
+ m_stream.close();
+}
--- /dev/null
+#ifndef WS_PROXY_ENDPOINT_H
+#define WS_PROXY_ENDPOINT_H
+
+// the endpoint needs to send data to the handler.
+// the handler sends data to the endpoint
+// an endpoint is a handler as well
+class ws_proxy_handler;
+
+/**
+ * helper class to find the appropriate application in an array
+ */
+struct find_application {
+ find_application(const std::string &n) : m_name(n) {};
+ bool operator ()(const wsproxy::ReverseProxyType_skel::application_sptr &s) {
+ return ! m_name.compare(s->attr_name());
+ };
+ std::string m_name;
+};
+
+/**
+ *
+ */
+class ws_proxy_endpoint : public event_handler<detail::socket_type,ip::tcp>
+{
+ private:
+ int m_ref;
+ public:
+ typedef aeb::intrusive_ptr<ws_proxy_endpoint> ptr;
+ typedef aeb::intrusive_ptr<ws_proxy_handler> handler_ptr;
+
+ inline void add_ref() {m_ref++;};
+ inline void release() {if (--m_ref == 0) delete this;};
+
+ public:
+ typedef aeb::net::ip::basic_endpoint<ip::tcp> endpoint_type;
+ typedef aeb::net::basic_socket_stream<aeb::net::ip::tcp> stream_type;
+
+ ws_proxy_endpoint(const std::string &ws_protocol,
+ const std::string &host,unsigned int port);
+
+ ws_proxy_endpoint(const wsproxy::ReverseProxyType_skel::application_sptr &app,const handler_ptr &);
+
+ virtual ~ws_proxy_endpoint();
+ // handler functions
+ void on_read() ;
+ void on_write() {};
+ void on_accept() {} ;
+ // Possible actions....
+ int open();
+ void close();
+ int send(unsigned char *,size_t len);
+ protected:
+ void unregister_io();
+ void register_io();
+ protected:
+ wsproxy::ReverseProxyType_skel::application_sptr m_application;
+ handler_ptr m_handler;
+ endpoint_type m_endpoint;
+ stream_type m_stream;
+};
+
+#endif
--- /dev/null
+#include <iostream>
+#include <string>
+#include <iomanip>
+#include <sstream>
+#include <map>
+#include <algorithm> // find_if
+#include <aeb/net/ip/tcp.h>
+#include <aeb/net/ip/basic_endpoint.h>
+#include <aeb/net/socket_base.h>
+#include <aeb/net/io_dispatcher.h>
+#include <aeb/net/basic_socket_acceptor.h>
+#include <aeb/net/basic_socket_stream.h>
+#include <aeb/pointer/intrusive_ptr.h>
+#if defined(__MINGW32__)
+#include <aeb/net/detail/winsock_init.h>
+#endif
+
+#include "time.h"
+#include "reverse_proxy.h"
+#include "ws_constant.h"
+#include "ws_log.h"
+#include "ws_frame.h"
+#include "sha1.h"
+#include "ws_base64.h"
+
+// Local debug macro
+#define LOG_WSPH_DEBUG(x) do { m_logger->log(eLOG_DEBUG,"%s\n",x); } while (0);
+#define LOG_WSPH_DEBUG_FMT(format,args...) do {m_logger->log(eLOG_DEBUG,format "\n" ,args);} while (0)
+
+#define LOG_WSPH_NOTICE(x) do {m_logger->log(eLOG_NOTICE,"%s\n",x);} while (0);
+#define LOG_WSPH_NOTICE_FMT(format,args...) do { m_logger->log(eLOG_NOTICE,format "\n",args);} while (0) ;
+
+#define LOG_WSPH_WARN(x) do {m_logger->log(eLOG_WARN,"%s\n",x);} while (0);
+#define LOG_WSPH_WARN_FMT(format,args...) do { m_logger->log(eLOG_WARN,format "\n",args);} while (0) ;
+
+#define LOG_WSPH_ERROR(x) do {m_logger->log(eLOG_ERROR,"%s\n",x);} while (0);
+#define LOG_WSPH_ERROR_FMT(format,args...) do {m_logger->log(eLOG_ERROR,format "\n",args);} while (0) ;
+
+static ws_log *m_logger = ws_log::getInstance("ws_proxy_handler");
+using namespace aeb::net;
+typedef aeb::net::io_dispatcher<aeb::net::detail::socket_type,ip::tcp> io_service;
+#include "ws_proxy_endpoint.h"
+#include "ws_proxy_handler.h"
+
+void proxy_state::onRead(ws_proxy_handler &p,const ws_frame &buffer)
+{
+ ws_frame frame;
+
+ frame.decode((unsigned char *)buffer.data(),buffer.length());
+ //frame.decode()
+ switch (frame.opcode())
+ {
+ case ews_continue:
+ break;
+ case ews_text:
+ this->onTextFrame(p,frame);
+ break;
+ case ews_binary:
+ this->onBinaryFrame(p,frame);
+ break;
+ case ews_close:
+ LOG_WSPH_WARN_FMT("proxy_state::onRead unkown opcode close: %d , value=%d",frame.opcode(),*(short *)frame.data());
+ this->onClose(p,ews_exit_normal);
+ break;
+ case ews_ping:
+ this->onPing(p);
+ break;
+ case ews_pong:
+ this->onPong(p);
+ break;
+ default:
+ LOG_WSPH_WARN_FMT("proxy_state::onRead unkown opcode : %d",frame.opcode());
+ ;
+ }
+}
+
+void proxy_state::doActivity(ws_proxy_handler &p)
+{
+ ;
+}
+
+//
+// CONNECTING
+//
+CONNECTING::CONNECTING()
+ : proxy_state()
+{
+}
+
+CONNECTING * CONNECTING::m_Instance = NULL;
+
+CONNECTING * CONNECTING::getInstance()
+{
+ if (m_Instance == NULL)
+ m_Instance = new CONNECTING();
+ return m_Instance;
+}
+
+void CONNECTING::onRead(ws_proxy_handler &p,const ws_frame &buffer)
+{
+ std::istringstream stream((char *)buffer.data());
+ char result[256];
+
+ LOG_WSPH_DEBUG("CONNECTING::onRead");
+
+ while( ! stream.getline(result,256).eof() ) {
+ int pos;
+ std::string s(result);
+ if ( (pos = s.find(SEC_WEBSOCKET_KEY)) != std::string::npos && (pos == 0)) {
+ p.set_header(std::string(SEC_WEBSOCKET_KEY),s.substr(s.find(":")+1));
+ p.set_challenge(s.substr(s.find(":")+1));
+ }
+ else if ( ((pos = s.find(WS_UPGRADE)) != std::string::npos ) && (pos == 0 )) {
+ p.set_header(WS_UPGRADE,s.substr(s.find(":")+1));
+ }
+ else if ( ((pos = s.find(WS_ORIGIN)) != std::string::npos ) && (pos == 0 )) {
+ p.set_header(WS_ORIGIN,s.substr(s.find(":")+1));
+ }
+ else if ( (pos = s.find(SEC_WEBSOCKET_VERSION)) != std::string::npos && (pos == 0)) {
+ p.set_header(SEC_WEBSOCKET_VERSION,s.substr(s.find(":")+1));
+ }
+ else if ( (pos = s.find(WS_CONNECTION)) != std::string::npos) {
+ p.set_header(WS_CONNECTION,s.substr(s.find(":")+1));
+ }
+ else if ( ((pos = s.find(SEC_WEBSOCKET_PROTOCOL)) != std::string::npos) && (pos == 0)) {
+ p.set_header(SEC_WEBSOCKET_PROTOCOL,s.substr(s.find(":")+1));
+ } else {
+ LOG_WSPH_NOTICE_FMT("CONNECTING::onRead Unknown header: %s",s.c_str());
+ }
+
+ }
+
+ p.send_response(101);
+ p.set_state(OPEN::getInstance());
+ OPEN::getInstance()->doActivity(p);
+}
+
+void CONNECTING::onClose(ws_proxy_handler &p,unsigned short _code)
+{
+}
+
+void CONNECTING::onPing(ws_proxy_handler &p)
+{
+}
+
+void CONNECTING::onPong(ws_proxy_handler &p)
+{
+}
+
+void CONNECTING::onBinaryFrame(ws_proxy_handler &p,const ws_frame &f)
+{
+}
+
+void CONNECTING::onTextFrame(ws_proxy_handler &p,const ws_frame &f)
+{
+}
+
+void CONNECTING::doActivity(ws_proxy_handler &p)
+{
+ ;
+}
+
+
+//OPEN
+OPEN::OPEN()
+{
+}
+
+OPEN * OPEN::m_Instance = NULL;
+
+OPEN * OPEN::getInstance()
+{
+ if (m_Instance == NULL)
+ m_Instance = new OPEN();
+ return m_Instance;
+}
+
+void OPEN::onRead(ws_proxy_handler &p,const ws_frame &buffer)
+{
+ LOG_WSPH_DEBUG("OPEN::onRead");
+ proxy_state::onRead(p,buffer);
+}
+
+void OPEN::onClose(ws_proxy_handler &p,unsigned short _code)
+{
+ std::string s;
+ LOG_WSPH_DEBUG("OPEN::onClose");
+
+ //p.send_close(ews_exit_normal,s);
+ p.send_close(_code,s);
+
+ p.set_state(CLOSING::getInstance());
+ CLOSING::getInstance()->doActivity(p);
+}
+
+void OPEN::onPing(ws_proxy_handler &p)
+{
+}
+
+void OPEN::onPong(ws_proxy_handler &p)
+{
+}
+
+void OPEN::onBinaryFrame(ws_proxy_handler &p,const ws_frame &f)
+{
+ LOG_WSPH_DEBUG_FMT("OPEN::onBinaryFrame got (%s)",f.data());
+}
+
+void OPEN::onTextFrame(ws_proxy_handler &p,const ws_frame &f)
+{
+ std::string proto;
+ LOG_WSPH_DEBUG_FMT("OPEN::onTextFrame got (%s)",f.data());
+ if ( ! p.protocol(proto)) {
+ p.send_frame_text(std::string("Hello"));
+ } else {
+ p.send_backend(f);
+ }
+}
+
+void OPEN::doActivity(ws_proxy_handler &p)
+{
+ // Try to connect to endpoint, ...
+ std::string proto;
+ wsproxy::ReverseProxyType_skel::application_sequence::iterator it;
+ wsproxy::ReverseProxyType_skel::application_sptr appli;
+ if (p.protocol(proto))
+ {
+ if (p.support_application(proto,appli)) {
+ LOG_WSPH_DEBUG_FMT("OPEN::doActivity found protocol %s open host %s port %ld",proto.c_str(),appli->attr_host().c_str(),appli->attr_port());
+ if (appli->attr_max_frame_size() > 0) {
+ long m_max_frame_size = appli->attr_max_frame_size();
+ LOG_WSPH_DEBUG_FMT("ws_proxy_handler::support_application max_frame_size =%d",m_max_frame_size);
+ }
+ p.open_backend(appli);
+ } else
+ {
+ LOG_WSPH_WARN_FMT("OPEN::doActivity Given protocol unsupported %s",proto.c_str());
+ }
+ } else {
+ LOG_WSPH_DEBUG_FMT("OPEN::doActivity (%s)",proto.c_str());
+ }
+}
+
+
+//
+// CLOSING
+//
+CLOSING::CLOSING()
+{
+}
+
+CLOSING * CLOSING::m_Instance = NULL;
+
+CLOSING * CLOSING::getInstance()
+{
+ if (m_Instance == NULL)
+ m_Instance = new CLOSING();
+ return m_Instance;
+}
+
+void CLOSING::onRead(ws_proxy_handler &p,const ws_frame &buffer)
+{
+ LOG_WSPH_DEBUG("CLOSING::onRead");
+ proxy_state::onRead(p,buffer);
+}
+
+void CLOSING::onClose(ws_proxy_handler &p,unsigned short _code)
+{
+ LOG_WSPH_DEBUG("CLOSING::onClose");
+ p.set_state(CLOSED::getInstance());
+ CLOSED::getInstance()->doActivity(p);
+}
+
+void CLOSING::onPing(ws_proxy_handler &p)
+{
+}
+
+void CLOSING::onPong(ws_proxy_handler &p)
+{
+}
+
+void CLOSING::onBinaryFrame(ws_proxy_handler &p,const ws_frame &f)
+{
+}
+
+void CLOSING::onTextFrame(ws_proxy_handler &p,const ws_frame &f)
+{
+}
+
+void CLOSING::doActivity(ws_proxy_handler &p)
+{
+ std::string s("");
+ LOG_WSPH_DEBUG("CLOSING::doActivity");
+ p.close();
+ p.set_state(CLOSED::getInstance());
+}
+
+
+//
+// CLOSED
+//
+CLOSED::CLOSED()
+{
+}
+
+CLOSED * CLOSED::m_Instance = NULL;
+
+CLOSED * CLOSED::getInstance()
+{
+ if (m_Instance == NULL)
+ m_Instance = new CLOSED();
+ return m_Instance;
+}
+
+void CLOSED::onRead(ws_proxy_handler &p,const ws_frame &buffer)
+{
+ proxy_state::onRead(p,buffer);
+}
+
+void CLOSED::onClose(ws_proxy_handler &p,unsigned short _code)
+{
+ LOG_WSPH_DEBUG("CLOSED::onClose");
+ doActivity(p);
+}
+
+void CLOSED::onPing(ws_proxy_handler &p)
+{
+}
+
+void CLOSED::onPong(ws_proxy_handler &p)
+{
+}
+
+void CLOSED::onBinaryFrame(ws_proxy_handler &p,const ws_frame &f)
+{
+}
+
+void CLOSED::onTextFrame(ws_proxy_handler &p,const ws_frame &f)
+{
+}
+
+void CLOSED::doActivity(ws_proxy_handler &p)
+{
+ LOG_WSPH_DEBUG("CLOSED::doActivity");
+ LOG_WSPH_DEBUG("------------------\n");
+ p.close();
+ p.io_unregister();
+ // Here was release
+}
+
+
+//
+// Proxy Handler implementation
+//
+ws_proxy_handler::ws_proxy_handler(detail::socket_type s,const wsproxy::ReverseProxyType_skel::application_sequence &apps)
+//ws_proxy_handler::ws_proxy_handler(detail::socket_type s)
+ : event_handler<detail::socket_type,ip::tcp>(s), m_ref(0), m_stream(s),m_state(NULL)
+ ,m_applications(apps)
+ ,m_max_frame_size(1024*4)
+{
+ LOG_WSPH_DEBUG("ws_proxy_handler::ws_proxy_handler");
+ io_service *io = aeb::singleton<io_service>::get_instance();
+ io->register_handler(0,this);
+ set_state(CONNECTING::getInstance());
+}
+
+ws_proxy_handler::~ws_proxy_handler()
+{
+ LOG_WSPH_DEBUG("ws_proxy_handler::~ws_proxy_handler");
+ io_unregister();
+}
+
+//
+void ws_proxy_handler::io_register()
+{
+ LOG_WSPH_DEBUG("ws_proxy_handler::io_register ");
+ io_service *io = aeb::singleton<io_service>::get_instance();
+ io->register_handler(0,this);
+}
+
+//
+void ws_proxy_handler::io_unregister()
+{
+ LOG_WSPH_DEBUG("ws_proxy_handler::io_unregister ");
+ io_service *io = aeb::singleton<io_service>::get_instance();
+ io->unregister_handler(0,this);
+ m_stream.close();
+}
+
+bool ws_proxy_handler::available () const
+{
+ LOG_WSPH_DEBUG("ws_proxy_handler::available");
+ return ! m_stream.is_open();
+}
+
+// reuse handler with a new socket
+void ws_proxy_handler::reuse(aeb::net::detail::socket_type s)
+{
+ LOG_WSPH_DEBUG_FMT("ws_proxy_handler::reuse handler with s=%d",s);
+ aeb::net::ip::tcp p = m_stream.local_endpoint().protocol();
+ m_stream.assign(p,s);
+
+ set_state(CONNECTING::getInstance());
+
+ io_register();
+}
+
+void
+ws_proxy_handler::on_write()
+{
+}
+
+#define BS (1024*4)
+void
+ws_proxy_handler::on_read()
+{
+ char buf[BS];
+ memset(buf,0x00,BS);
+
+ int res = m_stream.receive(buf,BS);
+ if ( res== 0)
+ {
+ LOG_WSPH_DEBUG("ws_proxy_handler::on_read read 0 close");
+ if (m_state) {
+ onClose(ews_exit_normal);
+ //m_state->onClose(*this);
+ //m_state->doActivity(*this);
+ // release();
+ }
+ } else
+ {
+ ws_frame frame((unsigned char *)buf,res);
+ LOG_WSPH_DEBUG_FMT("ws_proxy_handler::on_read size=%d dispatch",res);
+ if (m_state)
+ m_state->onRead(*this,frame);
+ }
+}
+
+void ws_proxy_handler::on_accept()
+{
+}
+
+void ws_proxy_handler::onRead(ws_proxy_handler &p,const ws_frame &buffer)
+{
+}
+
+void ws_proxy_handler::onClose(unsigned short _code)
+{
+ if (m_state)
+ m_state->onClose(*this,_code);
+}
+
+void ws_proxy_handler::onPing(ws_proxy_handler &p)
+{
+}
+
+void ws_proxy_handler::onPong(ws_proxy_handler &p)
+{
+}
+
+void ws_proxy_handler::onBinaryFrame(ws_proxy_handler &p,const ws_frame &f)
+{
+}
+
+void ws_proxy_handler::onTextFrame(ws_proxy_handler &p,const ws_frame &f)
+{
+}
+
+void
+ws_proxy_handler::set_header(const std::string &key,const std::string &v)
+{
+ std::string s(v.substr(1,v.size()-2));
+ LOG_WSPH_DEBUG_FMT("ws_proxy_handler::set_header key=%s Value(%s)",key.c_str(),s.c_str());
+ m_header[key] = s;
+}
+
+void
+ws_proxy_handler::set_challenge(const std::string &v)
+{
+ unsigned char sh[60];
+ char hexstr[60];
+ std::string complete;
+ std::string vs(v.substr(1,v.size()-2));
+ std::string result;
+ // That's important because you never now the content of uninitialized arrays. Sometimes it works
+ // some times it does not
+ memset(sh,0x00,60);
+ memset(hexstr,0x00,60);
+ LOG_WSPH_DEBUG_FMT("ws_proxy_handler::set_challenge String(%s)",vs.c_str());
+ complete = vs + WS_HANDSHACK;
+
+ LOG_WSPH_DEBUG_FMT("ws_proxy_handler::set_challenge complete<%s>",complete.c_str());
+ sha1::calc(complete.c_str(),complete.size(),sh);
+ sha1::toHexString(sh,hexstr);
+
+ encode_base64((const unsigned char *)sh,20,result);
+ m_header[std::string(SEC_WEBSOCKET_ACCEPT)] = result;
+ LOG_WSPH_NOTICE_FMT("ws_proxy_handler::set_challenge String(%s) Hash=<%s>",vs.c_str(),result.c_str());
+}
+
+
+void ws_proxy_handler::send_response(int status)
+{
+ std::ostringstream os;
+ std::string response;
+ char date[125];
+ time_t t;
+ struct tm *tmp;
+ t = time(NULL);
+ tmp = localtime(&t);
+ os<<"HTTP/1.1 ";
+ switch (status)
+ {
+ case 101:
+ {
+ os<<"101 Web Socket Protocol Handshake\r\n";
+ std::map<std::string,std::string>::iterator it = m_header.find(SEC_WEBSOCKET_PROTOCOL);
+ if (it != m_header.end())
+ os<<SEC_WEBSOCKET_PROTOCOL<<": "<<m_header[SEC_WEBSOCKET_PROTOCOL]<<"\n";
+#if 0
+ os<<"Access-Control-Allow-Headers: content-type\r\n";
+ os<<"Access-Control-Allow-Headers: authorization\r\n";
+ os<<"Access-Control-Allow-Headers: x-websocket-extensions\r\n";
+ os<<"Access-Control-Allow-Headers: x-websocket-version\r\n";
+ os<<"Access-Control-Allow-Headers: x-websocket-protocol\r\n";
+ it = m_header.find(WS_ORIGIN);
+ if (it != m_header.end())
+ os<<"Access-Control-Allow-Origin:"<<it->second.substr(0,it->second.size()-1)<<"\r\n";
+
+#endif
+ //os<<SEC_WEBSOCKET_VERSION<<": 13\r\n";
+ //os<<WS_CONNECTION<<": "<<m_header[WS_CONNECTION]<<"\r\n";
+ strftime(date,125,"Date: %a, %d %b %Y %T GMT\r\n",tmp);
+ os<<date;
+ os<<SEC_WEBSOCKET_ACCEPT<<": "<<m_header[SEC_WEBSOCKET_ACCEPT]<<"\r\n";
+ os<<"Server"<<": WsDede"<<"\r\n";
+ os<<WS_CONNECTION<<": Upgrade"<<"\r\n";
+ os<<WS_UPGRADE<<": websocket\r\n";
+ //Date: Thu, 20 Nov 2014 09:12:40 GMT
+ }
+ break;
+ case 400:
+ default:
+ ;
+ }
+ os<<"\r\n";
+ LOG_WSPH_DEBUG_FMT("ws_proxy_handler::send_response()\n[%s]",os.str().c_str());
+ m_stream.send(os.str().c_str(),os.str().size());
+}
+
+//
+//
+//
+void
+ws_proxy_handler::send_frame_text(const std::string &s)
+{
+ LOG_WSPH_DEBUG_FMT("ws_proxy_handler::send_frame_text(buffer=<%s> size=%lu)",s.c_str(),s.size());
+
+ ws_frame frame;
+ if (! frame.encode((unsigned const char *)s.c_str(),s.size(),ews_text) ) {
+ LOG_WSPH_DEBUG_FMT("ws_proxy_handler::send_frame_binary send buffer length %d",frame.buffer_length());
+ m_stream.send((const char *)frame.buffer(),frame.buffer_length());
+ } else {
+ LOG_WSPH_ERROR("ws_proxy_handler::send_frame_binary Failed encode frame");
+
+ }
+}
+
+
+void
+ws_proxy_handler::send_frame_binary(const unsigned char *_b,int len)
+{
+ ws_frame frame;
+ if (! frame.encode(_b,len) ) {
+ LOG_WSPH_DEBUG_FMT("ws_proxy_handler::send_frame_binary send buffer length %d",frame.buffer_length());
+ m_stream.send((const char *)frame.buffer(),frame.buffer_length());
+ } else {
+ LOG_WSPH_ERROR("ws_proxy_handler::send_frame_binary Failed encode frame");
+
+ }
+}
+
+void ws_proxy_handler::send_close(unsigned short code,const std::string &cause)
+{
+ char buffer[10];
+ memset(buffer,0x00,10);
+ ws_header *h = (ws_header *)buffer;
+ unsigned char *len = (unsigned char *)&buffer[1];
+ unsigned short *c = (unsigned short *)&buffer[2];
+ LOG_WSPH_DEBUG_FMT("ws_proxy_handler::send_close(%d)",code);
+
+ *len = 2;
+#if 0
+ *c = htons(code);
+#else
+ *c = code;
+#endif
+ h->opcode = ews_close;
+ h->fin = 1;
+ m_stream.send(buffer,4);
+}
+
+//
+void
+ws_proxy_handler::close()
+{
+ LOG_WSPH_DEBUG_FMT("ws_proxy_handler::close(%d)",m_stream.native());
+ m_stream.shutdown(SHUT_WR);
+
+ if ( ! ! m_backend)
+ {
+ ws_proxy_endpoint::ptr release_backend(NULL);
+ LOG_WSPH_DEBUG("ws_proxy_handler::close backend");
+ m_backend->close();
+ m_backend = release_backend;
+ }
+}
+
+
+bool ws_proxy_handler::support_application(const std::string n,wsproxy::ReverseProxyType_skel::application_sptr &app) const
+{
+ // Try to connect to endpoint, ...
+ wsproxy::ReverseProxyType_skel::application_sequence::const_iterator it;
+ it = std::find_if(m_applications.begin(),m_applications.end(),find_application(n));
+ if (it != m_applications.end()) {
+ app = *it;
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Tells if ws_protocol was set. If so, set s to the selected protocol
+ */
+bool ws_proxy_handler::protocol(std::string &s)
+{
+ std::map<std::string,std::string>::iterator it = m_header.find(SEC_WEBSOCKET_PROTOCOL);
+ if (it != m_header.end())
+ {
+ s = m_header[SEC_WEBSOCKET_PROTOCOL];
+ return true;
+ } else
+ {
+ return false;
+ }
+}
+
+
+void ws_proxy_handler::open_backend(const wsproxy::ReverseProxyType_skel::application_sptr &app)
+{
+ ptr p(this); // I have a problem with this.
+
+ m_backend = ws_proxy_endpoint::ptr( new ws_proxy_endpoint(app,p));
+ m_backend->open();
+}
+
+void ws_proxy_handler::send_backend(const ws_frame &f)
+{
+ if ( ! m_backend ) {
+ LOG_WSPH_ERROR("ws_proxy_handler::send_backend not available reconnect?");
+#if 0
+ ptr p(this); // I have a problem with this.
+
+ m_backend = ws_proxy_endpoint::ptr( new ws_proxy_endpoint(app,p));
+ m_backend->open();
+#endif
+ } else
+ m_backend->send(f.data(),f.length());
+}
--- /dev/null
+#ifndef WS_PROXY_HANDLER
+#define WS_PROXY_HANDLER
+
+
+class ws_proxy_handler;
+
+class proxy_state {
+ public:
+ proxy_state() {};
+ // Depending on state data from socket have a different meaning
+ virtual void onRead(ws_proxy_handler &p,const ws_frame &buffer);
+ // has a meaning when state is OPEN
+ virtual void onClose(ws_proxy_handler &p,unsigned short _code) {std::cout<<"proxy_state::onClose"<<std::endl;};
+ virtual void onPing(ws_proxy_handler &p) {};
+ virtual void onPong(ws_proxy_handler &p) {};
+ virtual void onBinaryFrame(ws_proxy_handler &p,const ws_frame &f) {};
+ virtual void onTextFrame(ws_proxy_handler &p,const ws_frame &f) {std::cout<<"proxy_state::onTextFrame\n";};
+ virtual void doActivity(ws_proxy_handler &p);
+ protected:
+};
+
+//
+//
+//
+class CONNECTING : public proxy_state {
+ public:
+
+ static CONNECTING *getInstance();
+ virtual void onRead(ws_proxy_handler &p,const ws_frame &buffer);
+ // has a meaning when state is OPEN
+ virtual void onClose(ws_proxy_handler &p,unsigned short _code) ;
+ virtual void onPing(ws_proxy_handler &p) ;
+ virtual void onPong(ws_proxy_handler &p) ;
+ virtual void onBinaryFrame(ws_proxy_handler &p,const ws_frame &f) ;
+ virtual void onTextFrame(ws_proxy_handler &p,const ws_frame &f) ;
+ virtual void doActivity(ws_proxy_handler &p);
+ protected:
+ private:
+ CONNECTING();
+ static CONNECTING *m_Instance;
+};
+
+//
+//
+//
+class OPEN : public proxy_state {
+ public:
+ static OPEN *getInstance();
+ virtual void onRead(ws_proxy_handler &p,const ws_frame &buffer);
+ // has a meaning when state is OPEN
+ virtual void onClose(ws_proxy_handler &p,unsigned short _code) ;
+ virtual void onPing(ws_proxy_handler &p) ;
+ virtual void onPong(ws_proxy_handler &p) ;
+ virtual void onBinaryFrame(ws_proxy_handler &p, const ws_frame &f) ;
+ virtual void onTextFrame(ws_proxy_handler &p, const ws_frame &f) ;
+ virtual void doActivity(ws_proxy_handler &p);
+ protected:
+
+ private:
+ OPEN();
+ static OPEN *m_Instance;
+};
+
+//
+//
+//
+class CLOSING : public proxy_state {
+ public:
+
+ static CLOSING *getInstance();
+
+ virtual void onRead(ws_proxy_handler &p,const ws_frame &buffer);
+ // has a meaning when state is OPEN
+ virtual void onClose(ws_proxy_handler &p,unsigned short _code) ;
+ virtual void onPing(ws_proxy_handler &p) ;
+ virtual void onPong(ws_proxy_handler &p) ;
+ virtual void onBinaryFrame(ws_proxy_handler &p,const ws_frame &f) ;
+ virtual void onTextFrame(ws_proxy_handler &p,const ws_frame &f) ;
+ virtual void doActivity(ws_proxy_handler &p);
+ protected:
+ private:
+ CLOSING();
+ static CLOSING *m_Instance;
+};
+
+//
+//
+//
+class CLOSED : public proxy_state {
+ public:
+
+ static CLOSED *getInstance();
+
+ virtual void onRead(ws_proxy_handler &p,const ws_frame &buffer);
+ // has a meaning when state is OPEN
+ virtual void onClose(ws_proxy_handler &p,unsigned short _code) ;
+ virtual void onPing(ws_proxy_handler &p) ;
+ virtual void onPong(ws_proxy_handler &p) ;
+ virtual void onBinaryFrame(ws_proxy_handler &p,const ws_frame &f) ;
+ virtual void onTextFrame(ws_proxy_handler &p,const ws_frame &f) ;
+ virtual void doActivity(ws_proxy_handler &p);
+ protected:
+ private:
+ CLOSED();
+ static CLOSED *m_Instance;
+};
+
+
+//
+// ws_proxy_handler
+//
+class ws_proxy_handler : public event_handler<detail::socket_type,ip::tcp>
+{
+ private:
+ int m_ref;
+ public:
+ typedef aeb::intrusive_ptr<ws_proxy_handler> ptr;
+
+ inline void add_ref() {m_ref++;};
+ inline void release() {
+#if 0
+ std::cout<<"ws_proy_handler::release ref="<<m_ref<<" before decrement"<<std::endl;
+#endif
+ if (--m_ref == 0) delete this;
+ };
+ bool available () const;
+ public:
+ ws_proxy_handler(detail::socket_type s,const wsproxy::ReverseProxyType_skel::application_sequence &apps);
+ virtual ~ws_proxy_handler() ;
+
+ inline void set_state(proxy_state *_s) {m_state = _s; };
+ inline proxy_state *get_state() const {return m_state;};
+
+ virtual void onRead(ws_proxy_handler &p,const ws_frame &buffer);
+
+ virtual void onClose(unsigned short _code) ;
+ virtual void onPing(ws_proxy_handler &p) ;
+ virtual void onPong(ws_proxy_handler &p) ;
+ virtual void onBinaryFrame(ws_proxy_handler &p,const ws_frame &f) ;
+ virtual void onTextFrame(ws_proxy_handler &p,const ws_frame &f) ;
+
+ void on_write() ;
+ void on_read() ;
+ void on_accept();
+
+ // helper function
+ void set_header(const std::string &key,const std::string &value);
+ void set_challenge(const std::string &value);
+ void send_response(int status);
+ void send_frame_text(const std::string &s);
+ void send_frame_binary(const unsigned char *buffer,int len);
+ void send_close(unsigned short code,const std::string &cause);
+ void close();
+ bool support_application(const std::string n,wsproxy::ReverseProxyType_skel::application_sptr &app) const;
+ void open_backend(const wsproxy::ReverseProxyType_skel::application_sptr &app);
+ void send_backend(const ws_frame &f);
+
+ void io_register();
+ void io_unregister();
+ // reuse handler with a new socket
+ void reuse(aeb::net::detail::socket_type s);
+ /**
+ * Tells if ws_protocol was set. If so, set s to the selected protocol
+ */
+ bool protocol(std::string &s) ;
+ protected:
+ ws_proxy_handler(const ws_proxy_handler &p) ;
+ // Should own an endpoint
+ proxy_state *m_state;
+ basic_socket_stream<ip::tcp> m_stream;
+ std::map<std::string,std::string> m_header;
+ ws_proxy_endpoint::ptr m_backend;
+ wsproxy::ReverseProxyType_skel::application_sequence m_applications;
+ long m_max_frame_size;
+};
+
+/**
+vim:et:sw=2:ts=2
+ */
+#endif
--- /dev/null
+Subproject commit 9bb0eda673f41d6e5125c1606e69f77ec1198198