Marco Oehler
/
Lab5
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(ðernet); 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 }