ROME 2 Lab5

HTTPServer.cpp

Committer:
oehlemar
Date:
2020-06-12
Revision:
1:5201940a41c1
Parent:
0:893a1e710078

File content as of revision 1:5201940a41c1:

/*
 * HTTPServer.cpp
 * Copyright (c) 2020, ZHAW
 * All rights reserved.
 */

#include <algorithm>
#include "HTTPScript.h"
#include "HTTPServer.h"

using namespace std;

inline string int2String(int i) {
    
    char buffer[32];
    sprintf(buffer, "%d", i);
    
    return string(buffer);
}

const unsigned int HTTPServer::INPUT_BUFFER_SIZE = 1024;    // size of receive buffer, given in [bytes]
const int HTTPServer::SOCKET_TIMEOUT = 1000;                // timeout of socket, given in [ms]

/**
 * Create and initialize an http server object.
 * @param ethernet a reference to the embedded TCP/IP stack to use.
 */
HTTPServer::HTTPServer(EthernetInterface& ethernet) : ethernet(ethernet), thread(osPriorityNormal, STACK_SIZE) {
    
    // start thread
    
    thread.start(callback(this, &HTTPServer::run));
}

/**
 * Delete the http server object.
 */
HTTPServer::~HTTPServer() {}

/**
 * Registers the given script with the http server.
 * This allows to call a method of this script object
 * through virtual cgi-bin requests from a remote system.
 */
void HTTPServer::add(string name, HTTPScript* httpScript) {
    
    httpScriptNames.push_back(name);
    httpScripts.push_back(httpScript);
}

/**
 * Decodes a given URL string into a standard text string.
 */
string HTTPServer::urlDecoder(string url) {
    
    size_t pos = 0;
    while ((pos = url.find("+")) != string::npos) url = url.substr(0, pos)+" "+url.substr(pos+1);
    while ((pos = url.find("%08")) != string::npos) url = url.substr(0, pos)+"\b"+url.substr(pos+3);
    while ((pos = url.find("%09")) != string::npos) url = url.substr(0, pos)+"\t"+url.substr(pos+3);
    while ((pos = url.find("%0A")) != string::npos) url = url.substr(0, pos)+"\n"+url.substr(pos+3);
    while ((pos = url.find("%0D")) != string::npos) url = url.substr(0, pos)+"\r"+url.substr(pos+3);
    while ((pos = url.find("%20")) != string::npos) url = url.substr(0, pos)+" "+url.substr(pos+3);
    while ((pos = url.find("%22")) != string::npos) url = url.substr(0, pos)+"\""+url.substr(pos+3);
    while ((pos = url.find("%23")) != string::npos) url = url.substr(0, pos)+"#"+url.substr(pos+3);
    while ((pos = url.find("%24")) != string::npos) url = url.substr(0, pos)+"$"+url.substr(pos+3);
    while ((pos = url.find("%25")) != string::npos) url = url.substr(0, pos)+"%"+url.substr(pos+3);
    while ((pos = url.find("%26")) != string::npos) url = url.substr(0, pos)+"&"+url.substr(pos+3);
    while ((pos = url.find("%2B")) != string::npos) url = url.substr(0, pos)+"+"+url.substr(pos+3);
    while ((pos = url.find("%2C")) != string::npos) url = url.substr(0, pos)+","+url.substr(pos+3);
    while ((pos = url.find("%2F")) != string::npos) url = url.substr(0, pos)+"/"+url.substr(pos+3);
    while ((pos = url.find("%3A")) != string::npos) url = url.substr(0, pos)+":"+url.substr(pos+3);
    while ((pos = url.find("%3B")) != string::npos) url = url.substr(0, pos)+";"+url.substr(pos+3);
    while ((pos = url.find("%3C")) != string::npos) url = url.substr(0, pos)+"<"+url.substr(pos+3);
    while ((pos = url.find("%3D")) != string::npos) url = url.substr(0, pos)+"="+url.substr(pos+3);
    while ((pos = url.find("%3E")) != string::npos) url = url.substr(0, pos)+">"+url.substr(pos+3);
    while ((pos = url.find("%3F")) != string::npos) url = url.substr(0, pos)+"?"+url.substr(pos+3);
    while ((pos = url.find("%40")) != string::npos) url = url.substr(0, pos)+"@"+url.substr(pos+3);
    
    return url;
}

/**
 * This <code>run()</code> method binds the TCP/IP server to a given port number
 * and enters an infinite loop that waits for http requests and then processes
 * these requests and returns a response.
 */
void HTTPServer::run() {
    
    // bind the server to a given port number
    
    server.open(&ethernet);
    server.bind(PORT_NUMBER);
    server.listen();
    
    // enter infinite loop
    
    while (true) {
        
        TCPSocket* client = server.accept();
        if (client != NULL) {
            
            client->set_blocking(true);
            client->set_timeout(SOCKET_TIMEOUT); // set timeout of socket
            
            // read input
            
            char buffer[INPUT_BUFFER_SIZE];
            int size = client->recv(buffer, sizeof(buffer));
            
            if (size > 0) {
                
                string input(buffer, size);
                string header;
                string output;
                
                // parse input
                
                if ((input.find("GET") == 0) || (input.find("HEAD") == 0)) {
                    
                    if (input.find("cgi-bin") != string::npos) {
                        
                        // process script request with arguments
                        
                        string script = input.substr(input.find("cgi-bin/")+8, input.find(" ", input.find("cgi-bin/")+8)-input.find("cgi-bin/")-8);
                        string name;
                        vector<string> names;
                        vector<string> values;
                        
                        if (script.find("?") != string::npos) {
                            
                            name = script.substr(0, script.find("?"));
                            script = script.substr(script.find("?")+1);
                            
                            vector<string> arguments;
                            while (script.find("&") != string::npos) {
                                arguments.push_back(script.substr(0, script.find("&")));
                                script = script.substr(script.find("&")+1);
                            }
                            arguments.push_back(script);
                            
                            for (int i = 0; i < arguments.size(); i++) {
                                
                                if (arguments[i].find("=") != string::npos) {
                                    
                                    names.push_back(arguments[i].substr(0, arguments[i].find("=")));
                                    values.push_back(urlDecoder(arguments[i].substr(arguments[i].find("=")+1)));
                                    
                                } else {
                                    
                                    names.push_back(arguments[i]);
                                    values.push_back("");
                                }
                            }
                            
                        } else {
                            
                            name = script;
                        }
                        
                        // look for corresponding script
                        
                        for (int i = 0; i < min(httpScriptNames.size(), httpScripts.size()); i++) {
                            
                            if (httpScriptNames[i].compare(name) == 0) {
                                
                                output  = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n";
                                output += "<!DOCTYPE html>\r\n";
                                output += "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\r\n";
                                output += "<body>\r\n";
                                output += httpScripts[i]->call(names, values);
                                output += "</body>\r\n";
                                output += "</html>\r\n";
                                
                                header  = "HTTP/1.1 200 OK\r\n";
                                header += "Content-Length: "+int2String(output.size())+"\r\n";
                                header += "Content-Type: text/xml\r\n";
                                header += "Expires: 0\r\n";
                                header += "\r\n";
                                
                                output = header+output;
                            }
                        }
                        
                        // requested script was not found on this server
                        
                        if ((output).size() == 0) {
                            
                            output  = "<!DOCTYPE html>\r\n";
                            output += "<html lang=\"en\">\r\n";
                            output += "<head>\r\n";
                            output += "  <title>404 Not Found</title>\r\n";
                            output += "  <style type=\"text/css\">\r\n";
                            output += "    h2 {font-family:Helvetica,Arial,sans-serif; font-size: 24; color:#FFFFFF;}\r\n";
                            output += "    p {font-family:Helvetica,Arial,sans-serif; font-size: 14; color:#444444;}\r\n";
                            output += "  </style>\r\n";
                            output += "</head>\r\n";
                            output += "<body leftmargin=\"0\" topmargin=\"0\" marginwidth=\"0\" marginheight=\"0\">\r\n";
                            output += "  <table width=\"100%\" height=\"100%\" border=\"0\" frame=\"void\" cellspacing=\"0\" cellpadding=\"20\">\r\n";
                            output += "    <tr>\r\n";
                            output += "      <th width=\"100%\" height=\"30\" bgcolor=\"#0064A6\"><h2>400 Bad Request</h2></th>\r\n";
                            output += "    </tr>\r\n";
                            output += "    <tr>\r\n";
                            output += "      <td valign=\"top\">\r\n";
                            output += "      <p>The requested script could not be found on this server!</p>\r\n";
                            output += "      </td>\r\n";
                            output += "    </tr>\r\n";
                            output += "  </table>\r\n";
                            output += "</body>\r\n";
                            output += "</html>\r\n";
                            
                            header  = "HTTP/1.1 404 Not Found\r\n";
                            header += "Content-Length: "+int2String(output.size())+"\r\n";
                            header += "Content-Type: text/html\r\n";
                            header += "\r\n";
                            
                            output = header+output;
                        }
                        
                        // write output
                        
                        void* address = (void*)(output).c_str();
                        int offset = 0;
                        while (offset < (output).size()) offset += client->send((void*)(static_cast<int>(reinterpret_cast<intptr_t>(address))+offset), (output).size()-offset);
                        
                    } else {
                        
                        // transmit static file
                        
                        output  = "<!DOCTYPE html>\r\n";
                        output += "<html lang=\"en\">\r\n";
                        output += "<head>\r\n";
                        output += "  <meta charset=\"utf-8\"/>\r\n";
                        output += "  <title>LIDAR Scan</title>\r\n";
                        output += "  <style type=\"text/css\">\r\n";
                        output += "    html {background-color: #223344;}\r\n";
                        output += "    h2 {font-family:Helvetica,Arial,sans-serif; font-size: 24; color:#FFFFFF;}\r\n";
                        output += "    p {font-family:Helvetica,Arial,sans-serif; font-size: 16; color:#EEEEEE;}\r\n";
                        output += "  </style>\r\n";
                        output += "</head>\r\n";
                        output += "<body leftmargin=\"0\" topmargin=\"0\" marginwidth=\"0\" marginheight=\"0\">\r\n";
                        output += "  <script type=\"text/javascript\">\r\n";
                        output += "  var xmlhttp = null;\r\n";
                        output += "  var task = window.setInterval(\"update()\", 250);\r\n";
                        output += "  function update() {\r\n";
                        output += "    if (window.XMLHttpRequest) {\r\n";
                        output += "      xmlhttp = new XMLHttpRequest();\r\n";
                        output += "    } else if (window.ActiveXObject) {\r\n";
                        output += "      try {\r\n";
                        output += "        xmlhttp = new ActiveXObject(\"Msxml2.XMLHTTP\");\r\n";
                        output += "      } catch (exception) {\r\n";
                        output += "        try {\r\n";
                        output += "          xmlhttp = new ActiveXObject(\"Microsoft.XMLHTTP\");\r\n";
                        output += "        } catch (exception) {}\r\n";
                        output += "      }\r\n";
                        output += "    }\r\n";
                        output += "    xmlhttp.onreadystatechange = refresh;\r\n";
                        output += "    xmlhttp.open(\"GET\", \"/cgi-bin/lidar\");\r\n";
                        output += "    xmlhttp.send(null);\r\n";
                        output += "  }\r\n";
                        output += "  function refresh() {\r\n";
                        output += "    if (xmlhttp.readyState == 4) {\r\n";
                        output += "      var xml = xmlhttp.responseXML;\r\n";
                        output += "      var intValues = xml.getElementsByTagName(\"int\");\r\n";
                        output += "      var floatValues = xml.getElementsByTagName(\"float\");\r\n";
                        output += "      var sizeScan = intValues[0].childNodes[0].nodeValue;\r\n";
                        output += "      var sizeBeacons = intValues[1].childNodes[0].nodeValue;\r\n";
                        output += "      var x = [];\r\n";
                        output += "      var y = [];\r\n";
                        output += "      var xBeacon = [];\r\n";
                        output += "      var yBeacon = [];\r\n";
                        output += "      for (i = 0; i < sizeScan; i++) {\r\n";
                        output += "        x.push(-floatValues[2*i+1].childNodes[0].nodeValue);\r\n";
                        output += "        y.push(floatValues[2*i].childNodes[0].nodeValue);\r\n";
                        output += "      }\r\n";
                        output += "      for (i = 0; i < sizeBeacons; i++) {\r\n";
                        output += "        xBeacon.push(-floatValues[2*i+2*sizeScan+1].childNodes[0].nodeValue);\r\n";
                        output += "        yBeacon.push(floatValues[2*i+2*sizeScan].childNodes[0].nodeValue);\r\n";
                        output += "      }\r\n";
                        output += "      drawScan(\"lidar\", x, y, xBeacon, yBeacon);\r\n";
                        output += "    }\r\n";
                        output += "  }\r\n";
                        output += "  function drawScan(id, x, y, xBeacon, yBeacon) {\r\n";
                        output += "    var canvas = document.getElementById(id);\r\n";
                        output += "    var width = window.innerWidth-50;\r\n";
                        output += "    var height = window.innerHeight-50;\r\n";
                        output += "    canvas.width = 2*width;\r\n";
                        output += "    canvas.height = 2*height;\r\n";
                        output += "    canvas.style.width = width+\"px\";\r\n";
                        output += "    canvas.style.height = height+\"px\";\r\n";
                        output += "    var ctx = canvas.getContext(\"2d\");\r\n";
                        output += "    ctx.scale(2,2);\r\n";
                        output += "    ctx.fillStyle = \"#334455\";\r\n";
                        output += "    ctx.fillRect(0.5, 0.5, width-1, height-1);\r\n";
                        output += "    var xMax = 3.0;\r\n";
                        output += "    var yMax = 3.0;\r\n";
                        output += "    if (width > height) {\r\n";
                        output += "      xMax = yMax*width/height;\r\n";
                        output += "    } else {\r\n";
                        output += "      yMax = xMax*height/width;\r\n";
                        output += "    }\r\n";
                        output += "    ctx.strokeStyle = \"#445566\";\r\n";
                        output += "    ctx.lineWidth = 1;\r\n";
                        output += "    ctx.beginPath();\r\n";
                        output += "    for (xGrid = 0.0; xGrid < xMax; xGrid += 0.1) {\r\n";
                        output += "      ctx.moveTo(width/2.0+xGrid/xMax*width/2.0, 0.0);\r\n";
                        output += "      ctx.lineTo(width/2.0+xGrid/xMax*width/2.0, height);\r\n";
                        output += "      ctx.moveTo(width/2.0-xGrid/xMax*width/2.0, 0.0);\r\n";
                        output += "      ctx.lineTo(width/2.0-xGrid/xMax*width/2.0, height);\r\n";
                        output += "    }\r\n";
                        output += "    for (yGrid = 0.0; yGrid < yMax; yGrid += 0.1) {\r\n";
                        output += "      ctx.moveTo(0.0, height/2.0+yGrid/yMax*height/2.0);\r\n";
                        output += "      ctx.lineTo(width, height/2.0+yGrid/yMax*height/2.0);\r\n";
                        output += "      ctx.moveTo(0.0, height/2.0-yGrid/yMax*height/2.0);\r\n";
                        output += "      ctx.lineTo(width, height/2.0-yGrid/yMax*height/2.0);\r\n";
                        output += "    }\r\n";
                        output += "    ctx.stroke();\r\n";
                        output += "    ctx.strokeStyle = \"#667788\";\r\n";
                        output += "    ctx.lineWidth = 1;\r\n";
                        output += "    ctx.beginPath();\r\n";
                        output += "    for (xGrid = 0.0; xGrid < xMax; xGrid += 1.0) {\r\n";
                        output += "      ctx.moveTo(width/2.0+xGrid/xMax*width/2.0, 0.0);\r\n";
                        output += "      ctx.lineTo(width/2.0+xGrid/xMax*width/2.0, height);\r\n";
                        output += "      ctx.moveTo(width/2.0-xGrid/xMax*width/2.0, 0.0);\r\n";
                        output += "      ctx.lineTo(width/2.0-xGrid/xMax*width/2.0, height);\r\n";
                        output += "    }\r\n";
                        output += "    for (yGrid = 0.0; yGrid < yMax; yGrid += 1.0) {\r\n";
                        output += "      ctx.moveTo(0.0, height/2.0+yGrid/yMax*height/2.0);\r\n";
                        output += "      ctx.lineTo(width, height/2.0+yGrid/yMax*height/2.0);\r\n";
                        output += "      ctx.moveTo(0.0, height/2.0-yGrid/yMax*height/2.0);\r\n";
                        output += "      ctx.lineTo(width, height/2.0-yGrid/yMax*height/2.0);\r\n";
                        output += "    }\r\n";
                        output += "    ctx.stroke();\r\n";
                        output += "    ctx.strokeStyle = \"white\";\r\n";
                        output += "    ctx.lineWidth = 0;\r\n";
                        output += "    ctx.fillStyle = \"white\";\r\n";
                        output += "    ctx.beginPath();\r\n";
                        output += "    ctx.moveTo(width/2.0, height/2.0);\r\n";
                        output += "    ctx.arc(width/2.0, height/2.0, 8, 0, 2*Math.PI, false);\r\n";
                        output += "    ctx.moveTo(width/2.0, height/2.0+8);\r\n";
                        output += "    ctx.arc(width/2.0, height/2.0+8, 5, 0, 2*Math.PI, false);\r\n";
                        output += "    ctx.fill();\r\n";
                        output += "    ctx.stroke();\r\n";
                        output += "    ctx.strokeStyle = \"#FF0000\";\r\n";
                        output += "    ctx.fillStyle = \"#FF0000\";\r\n";
                        output += "    ctx.beginPath();\r\n";
                        output += "    for (i = 0; i < Math.min(xBeacon.length, yBeacon.length); i++) {\r\n";
                        output += "      ctx.moveTo(width/2.0+xBeacon[i]/xMax*width/2.0, height/2.0-yBeacon[i]/yMax*height/2.0);\r\n";
                        output += "      ctx.arc(width/2.0+xBeacon[i]/xMax*width/2.0, height/2.0-yBeacon[i]/yMax*height/2.0, 5, 0, 2*Math.PI, false);\r\n";
                        output += "    }\r\n";
                        output += "    ctx.fill();\r\n";
                        output += "    ctx.stroke();\r\n";
                        output += "    ctx.strokeStyle = \"#FFFF00\";\r\n";
                        output += "    ctx.lineWidth = 0.5;\r\n";
                        output += "    ctx.beginPath();\r\n";
                        output += "    ctx.moveTo(width/2.0+x[0]/xMax*width/2.0, height/2.0-y[0]/yMax*height/2.0);\r\n";
                        output += "    for (i = 1; i < Math.min(x.length, y.length); i++) {\r\n";
                        output += "      ctx.lineTo(width/2.0+x[i]/xMax*width/2.0, height/2.0-y[i]/yMax*height/2.0);\r\n";
                        output += "    }\r\n";
                        output += "    ctx.lineTo(width/2.0+x[0]/xMax*width/2.0, height/2.0-y[0]/yMax*height/2.0);\r\n";
                        output += "    ctx.stroke();\r\n";
                        output += "    ctx.fillStyle = \"#FFFF00\";\r\n";
                        output += "    ctx.beginPath();\r\n";
                        output += "    for (i = 0; i < Math.min(x.length, y.length); i++) {\r\n";
                        output += "      ctx.moveTo(width/2.0+x[i]/xMax*width/2.0, height/2.0-y[i]/yMax*height/2.0);\r\n";
                        output += "      ctx.arc(width/2.0+x[i]/xMax*width/2.0, height/2.0-y[i]/yMax*height/2.0, 2, 0, 2*Math.PI, false);\r\n";
                        output += "    }\r\n";
                        output += "    ctx.fill();\r\n";
                        output += "    ctx.stroke();\r\n";
                        output += "    ctx.strokeStyle = \"white\";\r\n";
                        output += "    ctx.lineWidth = 1;\r\n";
                        output += "    ctx.strokeRect(0.5, 0.5, width-1, height-1);\r\n";
                        output += "  }\r\n";
                        output += "  </script>\r\n";
                        output += "  <table width=\"100%\" height=\"100%\" border=\"0\" frame=\"void\" cellspacing=\"20\" cellpadding=\"0\">\r\n";
                        output += "    <tr>\r\n";
                        output += "      <th><canvas id=\"lidar\"></canvas></th>\r\n";
                        output += "    </tr>\r\n";
                        output += "  </table>\r\n";
                        output += "</body>\r\n";
                        output += "</html>\r\n";
                        
                        header  = "HTTP/1.1 404 Not Found\r\n";
                        header += "Content-Length: "+int2String(output.size())+"\r\n";
                        header += "Content-Type: text/html\r\n";
                        header += "\r\n";
                        
                        output = header+output;
                        
                        // write output
                        
                        void* address = (void*)output.c_str();
                        int offset = 0;
                        while (offset < output.size()) offset += client->send((void*)(static_cast<int>(reinterpret_cast<intptr_t>(address))+offset), output.size()-offset);
                    }
                    
                } else {
                    
                    // the http method is not known
                    
                    output  = "<!DOCTYPE html>\r\n";
                    output += "<html lang=\"en\">\r\n";
                    output += "<head>\r\n";
                    output += "  <title>400 Bad Request</title>\r\n";
                    output += "  <style type=\"text/css\">\r\n";
                    output += "    h2 {font-family:Helvetica,Arial,sans-serif; font-size: 24; color:#FFFFFF;}\r\n";
                    output += "    p {font-family:Helvetica,Arial,sans-serif; font-size: 14; color:#444444;}\r\n";
                    output += "  </style>\r\n";
                    output += "</head>\r\n";
                    output += "<body leftmargin=\"0\" topmargin=\"0\" marginwidth=\"0\" marginheight=\"0\">\r\n";
                    output += "  <table width=\"100%\" height=\"100%\" border=\"0\" frame=\"void\" cellspacing=\"0\" cellpadding=\"20\">\r\n";
                    output += "    <tr>\r\n";
                    output += "      <th width=\"100%\" height=\"30\" bgcolor=\"#0064A6\"><h2>400 Bad Request</h2></th>\r\n";
                    output += "    </tr>\r\n";
                    output += "    <tr>\r\n";
                    output += "      <td valign=\"top\">\r\n";
                    output += "      <p>The requested method is not supported by this server!</p>\r\n";
                    output += "      </td>\r\n";
                    output += "    </tr>\r\n";
                    output += "  </table>\r\n";
                    output += "</body>\r\n";
                    output += "</html>\r\n";
                    
                    header  = "HTTP/1.1 400 Bad Request\r\n";
                    header += "Content-Length: "+int2String(output.size())+"\r\n";
                    header += "Content-Type: text/html\r\n";
                    header += "\r\n";
                    
                    output = header+output;
                    
                    // write output
                    
                    void* address = (void*)output.c_str();
                    int offset = 0;
                    while (offset < output.size()) offset += client->send((void*)(static_cast<int>(reinterpret_cast<intptr_t>(address))+offset), output.size()-offset);
                }
            }
            
            client->close();
            
        } // client != NULL
        
    } // infinite while loop
}