// vt_gateway.hyp local ; // Define the callback DATA method for incoming HTTP requests. list HTTP_DATA() { //puts "INCOMING HTTP" ; // Display the http attributes //if ( exists ( _http_attr_ ) ) describe _http_attr_ ; //if ( exists ( args ) ) describe args ; if ( exists ( xmldata ) ) { global xmldata ; //describe xmldata ; } // Who is the sender? int id = sender(1) ; // Make sure we can handle this if ( !exists ( _http_verb_ ) ) return BAD_REQUEST(id,"Bad request, no HTTP verb.") ; // Looks good. //LOG ({"Processing ",_http_verb_, // " on ",_http_target_, // " from device id ",id, // " on socket ",int(http_handle(id)) // }) ; if ( _http_verb_ == "POST" ) return INTERCEPT( id, _http_target_, _http_attr_ ) ; if ( _http_verb_ != "GET" ) return BAD_REQUEST( id, "Bad request, HTTP verb is not GET" ) ; if ( _http_verb_ == "HEAD" ) return http_reply ( id, 200, "Ok" ) ; str cash = "'" + _http_target_ + "'" ; if ( exists ( cache.*cash ) ) { //http_reply ( id, 304, "Not modified" ) ; puts ( { "Taking from cache ",_http_target_} ) ; list data = cache.*cash.data ; list attributes = cache.*cash.attributes ; http_reply ( id, 200, "OK", attributes , data ) ; return ; } if ( !doForwarding ) { str resource = doc_root + _http_target_ ; n = strlen resource ; if ( strloc(resource, ".jpg") != n || strloc(resource, ".JPG") != n || strloc(resource, ".gif") != n || strloc(resource, ".GIF") != n ) { ct = "image/gif" ; list data = load_binary resource ; } else { ct = "text/html" ; list data = load resource ; } list attributes = { str 'Server' = "HyperScript-"+version(), str 'Content-Type' = ct } ; if ( count ( data ) > 0 ) { // Cache the result puts ( {"...cacheing ",_http_target_} ) ; list cache.*cash.data = data ; list cache.*cash.attributes = attributes ; } return http_reply ( id, 200, "Ok", attributes, data ) ; } if ( doForwarding ) { // Forward the client's GET requests to the web server and also // forward the web server's replies back to the client str forwardHost = myServer ; str forwardPort = 80 ; // First see if this is the first message for this client connection int id_forward = id + MAX_INCOMING ; idx = id - HTTP_ID_OFFSET ; idx2 = idx + MAX_INCOMING ; if ( channelList[idx] == -1 ) { puts ( {"Opening forwarding connection, id = ",id_forward} ) ; handle hx = http_open ( forwardHost, forwardPort ) ; if ( hx == -1 ) return ERROR(id,"No connection to "+forwardHost+":"+forwardPort ) ; http_assign ( id_forward, hx ) ; // Store handles channelList[idx] = http_handle ( id ) ; channelList[idx2] = hx ; } // We only need to change one attribute value, ie: the Content-Type http_attributes.'Content-Type' = _http_attr_.'Content-Type' ; http_attributes.'Date' = asctime ; if ( !exists ( _http_data_ ) ) _http_data_ = {} ; puts { "Forwarding request for ",_http_target_ } ; ret = http_query ( id_forward, _http_verb_, _http_target_, http_attributes, _http_data_ ) ; if ( !ret ) return BAD_REQUEST( id, {"Failed to forward request: ",ret}) ; // Get the original sender. id = sender(1) ; int httpStatus = ret ; if ( exists ( _http_status_ ) ) httpStatus = int _http_status_ ; if ( httpStatus != 200 ) { return BAD_REQUEST(id,{"Status not as expected: ",httpStatus} ) ; } // Here's where we could rewrite the reply, if we wanted //describe _http_status_ ; //describe _http_text_ ; //describe _http_attr_ ; //describe _http_data_ ; // Cache the contents puts ( {"...cacheing contents for ",_http_target_} ) ; //describe _http_data_ ; list cache.*cash.data = _http_data_ ; list cache.*cash.attributes = _http_attr_ ; puts ( { "Replying back to id ",id} ) ; ret = http_reply ( id, _http_status_, _http_text_, _http_attr_, _http_data_) ; } return ; } ; global HTTP_DATA ; list INTERCEPT( int id, str target, list attributes ) { // We intercept all POST requests. puts ( {"Incoming connection request on ",target," for id ",id } ); n = strlen target ; str resource = strext ( target, 1, n-1 ) ; if ( resource == "" ) resource = "ROOT" ; // List of resources // // 1. CONNECT_VT // args: host, port // // 2. DATA // args: cmd // // 3. DISCONNECT // // 4. GETPAGE // args: pageno // // 5. // // A cookie determines which VT session // the resource refers to. // This is the "session id". // For SSL sessions, a session id is stored in this cookie as well // if ( resource == "CONNECT_VT" ) return CONNECT_VT( id ) ; // To go further, the user must have a cookie. if ( !exists ( attributes.Cookie ) ) return BAD_REQUEST ( id, "No cookie abinitionId supplied" ) ; //LOG ( { "Cookie is ",attributes.Cookie } ) ; int n = strlen ( attributes.Cookie ) ; int s = strloc ( attributes.Cookie, "abinitionId=" ) ; if ( s == n ) return BAD_REQUEST ( id, "Cookie is not abinitionId" ) ; str c = strext ( attributes.Cookie, s ) ; int s = strloc ( c, ";" ) ; str c = strext ( c, 0, s ) ; int abinitionId = -1 ; str s = c + ";" ; *s ; if ( resource == "GET_VT" ) return GET_VT( id, abinitionId ) ; if ( resource == "OUT_VT" ) return OUT_VT( id, abinitionId ) ; if ( resource == "PAGE_VT" ) return PAGE_VT( id, abinitionId ) ; if ( resource == "DISCONNECT_VT" ) return DISCONNECT_VT( id, abinitionId ) ; return ERROR( id, "Unrecognized resource requested" ) ; } ; global INTERCEPT ; list CONNECT_VT( int id ) { // Connect to vt // Find available slot int vt_id ; boolean found = 0 ; int i = 0 ; int idx ; while ( !found && i < MAX_INCOMING ) { if ( sessionId[i] == -1 ) { idx = i ; vt_id = idx + TELNET_ID_OFFSET ; found = 1 ; } else i++ ; } if ( !found ) return ERROR(id,"No more session slots available") ; srandom timestamp ; int abinitionId = random % random % random ; puts ( { "Setting cookie abinitionId = ",abinitionId } ) ; //describe xmldata ; str host = xmldata.host ; int port = xmldata.port ; handle vt_handle = port_open(host,port); if ( vt_handle == -1 ) return ERROR(id,{"Failed to connect to ",host," at ",port} ); LOG ( {"Succesfully connected to ",host," at ",port} ) ; port_assign ( vt_id, vt_handle ) ; port_enable ( IN_VT, vt_id ) ; aid = "'" + abinitionId + "'" ; // Initialize the session structure. handle sessionList.*aid.vt_handle = vt_handle ; str sessionList.*aid.host = host ; int sessionList.*aid.port = port ; int sessionList.*aid.id = vt_id ; handle sessionList.*aid.hd = vt_handle ; boolean sessionList.*aid.replyRequested = 0 ; boolean sessionList.*aid.inPagedMode = 0 ; int sessionList.*aid.replyId = 0 ; boolean sessionList.*aid.replyAvailable = 0 ; int sessionList.*aid.replyTimeout = 0 ; list sessionList.*aid.vtstate = {} ; list sessionList.*aid.actions = {} ; list sessionList.*aid.pages = {} ; list sessionList.*aid.cursor = {} ; ulong sessionList.*aid.bytesIn = 0 ; ulong sessionList.*aid.bytesOut = 0 ; // Store a lookup to the structure using the abinitionId sessionId[idx] = abinitionId ; list attributes = { str 'Content-Type' = "text/xml; charset=iso-8859-1;", str 'Cache-Control' = "nocache; must-revalidate;", str 'Expires' = "0;", str 'Set-Cookie' = "abinitionId="+abinitionId+";" } ; list 'xmldata' = { } ; str xmls = { "", xsdescribe xmldata } ; puts ( "Sending CONNECT_VT reply" ) ; http_reply ( id, 200, "OK", attributes, xmls ) ; // Update the session and channel list on the vt_gateway panel /* list table = { list th = { "SessionId", "ChannelIn", "ChannelOut" } } ; for ( i=0 list tr = list td = sessionId[i], list td = channelId[i], list td = channelId[i+MAX_INCOMING] } ; append ( table, tr ) ; } */ return ; } ; global CONNECT_VT ; list DISCONNECT_VT( int id, int abinitionId ) { str aid = "'" + abinitionId + "'" ; if ( !exists ( sessionList.*aid ) ) { // Close down connection, free up port return BAD_REQUEST(id,"No such session "+abinitionId ); } // Terminate the connection port_close ( sessionList.*aid.hd ) ; // Throw away the session id. int vt_id = sessionList.*aid.id ; // HANGUP does this as well. idx = vt_id - TELNET_ID_OFFSET ; sessionId[idx] = -1 ; if ( exists ( sessionList.*aid ) ) { detach sessionList.*aid ; undef *aid ; } list attributes = { str 'Content-Type' = "text/xml; charset=iso-8859-1;", str 'Cache-Control' = "nocache; must-revalidate;", str 'Expires' = "0;" } ; list 'xmldata' = { list goodbye = "So long", list abc = {1,2,3} } ; puts ( "Sending DISCONNECT reply" ) ; str xmls = { "", xsdescribe xmldata } ; http_reply ( id, 200, "OK", attributes, xmls ) ; return ; } ; global DISCONNECT_VT ; list OUT_VT( int id, int abinitionId ) { str aid = "'" + abinitionId + "'" ; if ( !exists ( sessionList.*aid ) ) { // Close down connection, free up port return BAD_REQUEST(id,"No such session "+abinitionId ); } //describe xmldata ; str cmd = xmldata.cmd ; str format = xmldata.format ; str buf ; if ( format == "KEY" ) { // 'cmd' is in the format { x, y, z } where // x, y, z are byte codes, eg: {98, 101, 33} cmd = "buf = str char byte " + cmd ; *cmd ; } else if ( format == "PUT" ) { // We add \r to 'cmd' . buf = cmd + "\r" ; } else { // format == "GET" // Note: 'cmd' is always "" buf = cmd ; } nBytes = strlen buf ; if ( nBytes > 0 ) { // There is something to send to the telnet session int vt_id = sessionList.*aid.id ; //puts ( { "Sending ",toexternal(buf)," to id ",vt_id} ) ; ret = port_event ( vt_id, buf ) ; if ( !ret ) { LOG ( {"Failed to send data to id ",vt_id} ) ; } if ( sessionList.*aid.inPagedMode ) doReset = 1 ; sessionList.*aid.inPagedMode = 0 ; sessionList.*aid.bytesOut = sessionList.*aid.bytesOut + nBytes ; } // Prepare to reply to the request. list attributes = { str 'Content-Type' = "text/xml; charset=iso-8859-1;", str 'Cache-Control' = "nocache; must-revalidate;", str 'Expires' = "0;" } ; if ( sessionList.*aid.replyAvailable ) { list xmldata = { list status = {"Processed ",format,", nBytes=",nBytes}, list clocktime = asctime, int bytesIn = sessionList.*aid.bytesIn, int bytesOut = sessionList.*aid.bytesOut , list cursor = detach sessionList.*aid.cursor, list actions = detach sessionList.*aid.actions } ; global xmldata ; } else { // There is now reply available. // If the format is GET, we just return because we've // set the "replyRequested" flag. See above. if ( format == "GET" ) { //puts ( {"No reply available for ",format," timeout in 3 seconds"} ) ; // Ask for a reply to be delivered by IN_VT. sessionList.*aid.replyRequested = 1 ; sessionList.*aid.replyId = id ; // After 5 seconds, deliver something sessionList.*aid.replyTimeout = timestamp() + 3 ; return ; } // Otherwise, just return back an empty data set list xmldata = { list status = "No reply available", list clocktime = asctime } ; global xmldata ; } //xdescribe xmldata ; //puts ( {"Sending OUT_VT reply to id ",id," for ",format} ) ; str xmls = { "", xsdescribe xmldata } ; ret = http_reply ( id, 200, "OK", attributes, xmls ) ; //puts { "Send status is '", ret,"'" } ; if ( !ret ) { LOG ( { "Send failed, reason is [", ret,"]" } ) ; if ( sessionList.*aid.replyAvailable ) { // Put it all back!!! sessionList.*aid.cursor = detach xmldata.cursor ; sessionList.*aid.actions = detach xmldata.actions ; return ; } } sessionList.*aid.replyAvailable = 0 ; sessionList.*aid.cursor = {} ; sessionList.*aid.actions = {} ; return ; } ; global OUT_VT ; list VT_ALARM_HANDLER() { alarm 1 ; on_alarm return VT_ALARM_HANDLER() ; int n = count sessionList; int ts = timestamp ; while ( n-- ) { int to = sessionList[0].replyTimeout ; if ( to != 0 && to <= ts ) { int rid = sessionList[0].replyId ; if ( rid != 0 ) { list attributes = { str 'Content-Type' = "text/xml; charset=iso-8859-1;", str 'Cache-Control' = "nocache; must-revalidate;", str 'Expires' = "0;" } ; list xmldata = { list status = "Timeout reply", list clocktime = asctime } ; global xmldata ; //puts ( {"Sending timeout reply to id ",rid} ) ; str xmls = { "", xsdescribe xmldata } ; ret = http_reply ( rid, 200, "OK", attributes, xmls ) ; } sessionList[0].replyId = 0 ; sessionList[0].replyRequested = 0 ; sessionList[0].replyTimeout = 0 ; } next sessionList ; } return "$ACK" ; } ; global VT_ALARM_HANDLER ; list PAGE_VT ( int id, int abinitionId ) { str aid = "'" + abinitionId + "'" ; if ( !exists ( sessionList.*aid ) ) { // Close down connection, free up port return BAD_REQUEST(id,"No such session "+abinitionId ); } //describe xmldata ; int pageNum = int xmldata.pageNum ; int lineNum = int xmldata.lineNum ; int maxPages = count sessionList.*aid.pages ; LOG ( {"Session ",abinitionId," has ",maxPages," pages saved."} ) ; // If the pageNum is 0, then its a way of also // saying page N, where N is the last or current page number. // We also revert back to normal processing. // // Pages 1 through MAX_PAGES are what has already happened. // We pull the page out of the session cache // and we create the actions necessary to display it. // Requesting a page always involves a reset, of course. // // Unlike the OUT_VT method, this one returns the // actions to the ajax client, because we are not // actually getting new raw data from the telnet session. // // We also set a "inPagedMode" where real incoming // raw data will still be processed, but not actually // sent to the client. The mode is cleared whenever: // A). An OUT_VT KEY or ESC format is received, from // keyboard events // B). The user goes back to page 1 // // The "inPagedMode" keeps a special page in the // the cache in which the client can view, // and also scroll up and down // // Get a copy of the page we are interested in. if ( pageNum > 0 && pageNum <= maxPages ) { LOG ( {"Fetching page ",pageNum} ) ; sessionList.*aid.inPagedMode = 1 ; // Transfer the page we are working on into the cache. list pageCache = sessionList.*aid.pages[pageNum-1] ; str raw = "" ; if ( lineNum > 0 ) { raw = "\033[1;1H" ; for ( i=0;i 0 ) { raw = "\033[1;1H" ; for ( i=0;i", xsdescribe xmldata } ; ret = http_reply ( id, 200, "OK", attributes, xmls ) ; } ; global PAGE_VT ; list IN_VT() { // Raw data from the telnet session has arrived. str raw = _data_ ; int nBytes = strlen raw ; int id = sender(1) ; //puts ( {"Received ",nBytes, " bytes from ",id } ) ; // Get the associated session int idx = id - TELNET_ID_OFFSET ; str aid = "'" + sessionId[idx] + "'" ; if ( !exists ( sessionList.*aid ) ) { // Close down connection, free up port LOG ( {"No session by the name of ",sessionId[idx] } ); return ; } // Detach some structures so we can work on them // a little easier. detach sessionList.*aid.actions ; detach sessionList.*aid.vtstate ; detach sessionList.*aid.pages ; detach sessionList.*aid.cursor ; // How many pages are saved in the cache so far? int pageCount = count pages ; //puts {"There are ",pageCount," pages"} ; // Do we have too many actions? // If so, a reset may be required. // (A reset repaints the entire page). int n = count actions ; if ( n > MIN_ACTION_COUNT ) doReset = 1 ; // If we are resetting, throw away any saved actions if ( doReset ) actions = {} ; // Process the raw input, creating the actions // necessary to update the page, plus update the // "vtstate" structure/ //describe vtstate ; list newActions = vt2html ( raw, vtstate, doReset, doDebug ) ; // Remove the emits from the actions. // (They are of no use to the client.) int n = count newActions ; str a ; list emits = {} ; while ( n-- ) { a = newActions[0] ; if ( a == "emit" ) { append ( emits, detach newActions[0] ) ; } else next newActions ; } // Process the emits, which means to send the // emit data to the telnet session int vt_id = sessionList.*aid.id ; n = count emits ; for ( i=0;i 0 ) { //puts {"sending ",toexternal(cmd)," to ",vt_id}; ret = port_event ( vt_id, cmd ) ; if ( !ret ) break ; } } // Add the new actions to any actions that // have not been processed. appendval ( actions, newActions ) ; lastActionCount = count actions ; // Reset doReset doReset = 0 ; // Also, save any back pages that may have been // generated. This is indicated by the presense // of the 'page.screen' sub-variable. if ( exists ( vtstate.page.screen ) ) { detach vtstate.page ; //describe page ; append ( pages, page ) ; LOG ( "Generating a new page" ) ; if ( pageCount > MAX_PAGES ) remove ( pages ) ; } // Generate the cursor info list cursor = { int row = vtstate.state.row, int col = vtstate.state.col, char chr = vtstate.state.chr, ubyte rendition = vtstate.state.rendition, ubyte graphics = vtstate.state.graphics, ubyte flags3 = vtstate.state.flags3 } ; //describe cursor ; sessionList.*aid.replyAvailable = 1 ; sessionList.*aid.bytesIn = sessionList.*aid.bytesIn + nBytes ; // Reattach the vtstate and pages append ( sessionList.*aid, vtstate ) ; append ( sessionList.*aid, pages ) ; if ( sessionList.*aid.replyRequested && !sessionList.*aid.inPagedMode ) { sessionList.*aid.actions = {} ; sessionList.*aid.cursor = {} ; } else { // No reply requested or inPagedMode //puts {"No reply requested"} ; // Reattach everything append ( sessionList.*aid, actions ) ; append ( sessionList.*aid, cursor ) ; return ; } // Send Reply // Send in XML format // 1. The number of bytes sent and received // 2. The cursor position // 3. The actions for the page list attributes = { str 'Content-Type' = "text/xml; charset=iso-8859-1;", str 'Cache-Control' = "no-cache;" } ; list xmldata = { int bytesIn = sessionList.*aid.bytesIn, int bytesOut = sessionList.*aid.bytesOut, int pageno = pageCount+1, int lineno = cursor.row, list cursor = detach cursor, list actions = detach actions } ; //xdescribe xmldata ; int id = sessionList.*aid.replyId ; //puts ( {"Satisfying the OUT_VT reply to id ",id }) ; str xmls = { "", xsdescribe xmldata } ; ret = http_reply ( id, 200, "OK", attributes, xmls ) ; //puts { "Send status is [", ret,"]" } ; if ( !ret ) { LOG ( { "Send failed. Reason is [", ret,"]" } ) ; sessionList.*aid.cursor = detach xmldata.cursor ; sessionList.*aid.actions = detach xmldata.actions ; } else { sessionList.*aid.replyAvailable = 0 ; } sessionList.*aid.replyTimeout = 0 ; sessionList.*aid.replyRequested = 0 ; sessionList.*aid.replyId = 0 ; return ; } ; global IN_VT ; list ERROR(int device, str text ) { puts text ; list attributes = { str 'Content-Type' = "text/xml; charset=iso-8859-1;", str 'Cache-Control' = "no-cache;" } ; list xmldata = { list actions = { list status = text } } ; list xmldata = {} ; LOG ( {"Bad Request from ",device," : ",text } ) ; str xmls = { "", xsdescribe xmldata } ; ret = http_reply ( device, 200, "OK", attributes, xmls ) ; } ; global ERROR ; list BAD_REQUEST(int device, str text ) { puts text ; list attributes = { str 'Content-Type' = "text/xml; charset=iso-8859-1;", str 'Cache-Control' = "no-cache;" } ; //list xmldata = { // list actions = { // list updatecell = { // int row = 1, // int col = 1, // int colspan = 80, // int rendition = 3, // str text = text + " " // } // } //} ; list xmldata = { list status = text } ; LOG ({"Bad Request from ",device," : ",text }) ; str xmls = { "", xsdescribe xmldata } ; ret = http_reply ( device, 503, text, attributes, xmls ) ; } ; global BAD_REQUEST ; list VT_PIPE_HANDLER() { on_pipe return VT_PIPE_HANDLER() ; return VT_GENERIC_HANDLER( _pipe_status_ ) ; } ; global VT_PIPE_HANDLER ; on_pipe return VT_PIPE_HANDLER() ; list VT_HANGUP_HANDLER() { on_hangup return VT_HANGUP_HANDLER() ; return VT_GENERIC_HANDLER( _hangup_status_ ) ; } ; global VT_HANGUP_HANDLER ; on_hangup return VT_HANGUP_HANDLER() ; list VT_GENERIC_HANDLER( str _generic_status_ ) { list x = strtok _generic_status_ ; if ( count (x) != 4 ) return "$ACK" ; LOG ( {"Event ",x[0]," occurred on socket ",x[1]," through port ",x[2], " for device ",x[3] } ) ; // Which id is hanging up? int id = x[3] ; // id == -1 : hangup of socket without assigned id // id < 1000 : none that we know of // id >= 1000 && id <= 1009 : incoming from AJAX // id >= 1010 && id <= 1019 : outgoing to apache // id >= 23000 : incoming from telnet if ( id == -1 ) return "$ACK" ; if ( id < HTTP_ID_OFFSET ) return "$ACK" ; int idx ; if ( id >= TELNET_ID_OFFSET ) { // Telnet session has hung up idx = id - TELNET_ID_OFFSET ; // If were out of range, then we're done. if ( idx >= MAX_INCOMING ) return "$ACK" ; // Otherwise, we must remove the session str aid = "'" + sessionId[idx] + "'" ; sessionId[idx] = -1 ; // Throw away the session as well. if ( exists ( sessionList.*aid ) ) { detach sessionList.*aid ; undef *aid ; } return "$ACK" ; } ; // Must be channel from the VT Ajax HTTP application idx = id - HTTP_ID_OFFSET ; channelList[idx] = -1 ; int idx2 ; if ( idx < MAX_INCOMING ) { // The incoming channel has closed idx2 = idx + MAX_INCOMING ; } else { // The forward channel has closed idx2 = idx - MAX_INCOMING ; } // Close the other connecting end //if ( channelList[idx2] != -1 ) { // puts {"Closing parallel channel handle ",channelList[idx2] } ; // http_close ( channelList[idx2] ) ; // channelList[idx2] = -1 ; //} return "$ACK" ; } ; global VT_GENERIC_HANDLER ; list VT_CONNECT_HANDLER() { on_connect return VT_CONNECT_HANDLER() ; //puts _connect_status_ ; return "$ACK" ; } ; global VT_CONNECT_HANDLER ; on_connect return VT_CONNECT_HANDLER() ; list VT_MESSAGE_HANDLER() { on_message return VT_MESSAGE_HANDLER() ; return "$ACK" ; } ; global VT_MESSAGE_HANDLER ; on_message return VT_MESSAGE_HANDLER() ; list VT_TIMEOUT_HANDLER() { on_timeout return VT_TIMEOUT_HANDLER() ; return "$ACK" ; return "%TIMEOUT" ; } ; global VT_TIMEOUT_HANDLER ; on_timeout return VT_TIMEOUT_HANDLER() ; list VT_ERROR_HANDLER() { puts STATUS ; on_error return VT_ERROR_HANDLER() ; return "%ERROR - TERMINATE" ; } ; global VT_ERROR_HANDLER ; on_error return VT_ERROR_HANDLER() ; list LOG(str text) { if ( exists ( JSEVAL ) ) { str element = { "appendTextToNode('vt_gateway','",text,"');" } ; JSEVAL ( element ) ; } puts text; } ; global LOG; boolean INITIALIZED = 0 ; global INITIALIZED ; list CONNECT() { if ( ! INITIALIZED ) INITIALIZE() ; INITIALIZED = 1 ; } ; global CONNECT ; enable CONNECT ; list INITIALIZE() { boolean isDOMHS = exists ( JSEVAL ) ; global isDOMHS ; str doc_root = getenv "DOCUMENT_ROOT" ; global doc_root ; puts {"doc_root is ",doc_root}; boolean doForwarding = 0 ; global doForwarding ; if ( isDOMHS ) { doForwarding = 1 ; //debug 130 ; quiet 0 ; list vt_gateway = { list h1 = "VT Gateway", list div = { attr id = "vt_gateway", attr style = "border:outset; overflow:auto; height:300px; width:500px", list p = {} } } ; global vt_gateway ; str nodeName = "dashboard" ; global nodeName ; str innerHTML = xsdescribe vt_gateway ; str element = { "updateNode('", nodeName, "','", innerHTML, "');" } ; JSEVAL ( element ) ; } str SEPARATOR = ( env() == "WINDOWS" ) ? "\\" : ((env() == "UNIX") ? "/" : "") ; global SEPARATOR ; // Just set this next flag to 1 and the server will be https useSSL = 1 ; global useSSL ; // Select which port we are listening on // // For http, 80 or 8080 // For https, 443 or 8443/8444 // int httpPort = 8444 ; global httpPort ; str myServer = localaddr() ; if ( isDOMHS ) myServer = "hyperscript.net" ; global myServer ; timeout 20 ; // Create the service. LOG( {"Creating HTTP listen service on local port ",httpPort } ) ; handle h2 = http_service( httpPort ) ; if ( h2 == -1 ) { puts "Failed http_service" ; exit ; } if ( useSSL ) { // Make all connections secure. str certfile ; str keyfile ; if ( isDOMHS ) { // As a DOMHS server, we need a Certificate-Authority Certificate" // Fetch it from the server, put it in a TEMP directory str tmp = getenv "TMP" ; if ( tmp == "" ) tmp = getenv "TEMP" ; // Grab the certificate and keyfile int CERT_ID = 777 ; global CERT_ID ; handle h_cert = http_open ( myServer, 80 ) ; if ( h_cert != -1 ) { LOG ( {"Fetching certificates from ",myServer} ); list attributes = {} ; int id_cert = CERT_ID ; //puts ( {"Assigning ",h_cert," to ",id_cert} ); http_assign ( id_cert, h_cert ) ; // Even though it may say text/plain, don't interpret cr/lf http_binary ( id_cert, 1 ) ; ret = http_query( id_cert, "GET", "/cacert.pem", attributes ) ; certfile = tmp + SEPARATOR + "cacert.pem" ; LOG ( {"Certfile is ",certfile} ); handle hf = fopen(certfile,"wb"); if ( hf == -1 ) LOG ( {"Cannot open cert file"}); fwrite ( _http_data_, hf ) ; fclose hf ; ret = http_query( id_cert, "GET", "/privkey.pem", attributes ) ; keyfile = tmp + SEPARATOR + "privkey.pem" ; LOG ( {"Keyfile is ",keyfile} ); handle hf = fopen(keyfile,"wb"); if ( hf == -1 ) LOG ( {"Cannot open key file"} ); fwrite ( _http_data_, hf ) ; fclose hf ; } } else { // We're being launched str docRoot = getenv ( "DOCUMENT_ROOT" ) ; if ( docRoot != "" ) docRoot = docRoot + SEPARATOR ; certfile = docRoot + "cacert.pem" ; keyfile = docRoot + "privkey.pem" ; } handle hSSL = ssl_new ( certfile, keyfile ); ssl_assign ( hSSL, h2 ) ; ssl_enableSessions ( hSSL ) ; } list sessionList = {} ; hashed sessionList ; global sessionList ; // Cache list cache = {} ; global cache ; hashed cache ; list http_attributes = { str 'Date' = asctime, str 'Server' = "HyperScript-"+version(), str 'Content-Type' = "text/html", str 'Host' = myServer+":"+httpPort } ; global http_attributes ; // Allow up to MAX_CONNECTIONS connections at a time. int MAX_CONNECTIONS = 12 ; global MAX_CONNECTIONS; int channelList[MAX_CONNECTIONS] ; global channelList ; for ( i=0; i