Webserver example for Nuvoton NuMaker boards and mbed OS 5.15 - HTTP 1.1 and multi-threaded.

Dependencies:   mbed-http

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers http_response_builder.h Source File

http_response_builder.h

00001 /*
00002  * PackageLicenseDeclared: Apache-2.0
00003  * Copyright (c) 2017 ARM Limited
00004  *
00005  * Licensed under the Apache License, Version 2.0 (the "License");
00006  * you may not use this file except in compliance with the License.
00007  * You may obtain a copy of the License at
00008  *
00009  *     http://www.apache.org/licenses/LICENSE-2.0
00010  *
00011  * Unless required by applicable law or agreed to in writing, software
00012  * distributed under the License is distributed on an "AS IS" BASIS,
00013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00014  * See the License for the specific language governing permissions and
00015  * limitations under the License.
00016  */
00017 
00018 #ifndef _MBED_HTTP_RESPONSE_BUILDER_
00019 #define _MBED_HTTP_RESPONSE_BUILDER_
00020 
00021 #include <string>
00022 #include <map>
00023 #include "http_parser.h"
00024 #include "http_parsed_url.h"
00025 
00026 static const char* get_http_status_string(uint16_t status_code) {
00027     switch (status_code) {
00028         case 100: return "Continue";
00029         case 101: return "Switching Protocols";
00030         case 102: return "Processing";
00031         case 200: return "OK";
00032         case 201: return "Created";
00033         case 202: return "Accepted";
00034         case 203: return "Non-Authoritative Information";
00035         case 204: return "No Content";
00036         case 205: return "Reset Content";
00037         case 206: return "Partial Content";
00038         case 207: return "Multi-Status";
00039         case 208: return "Already Reported";
00040         case 226: return "IM Used";
00041         case 300: return "Multiple Choices";
00042         case 301: return "Moved Permanently";
00043         case 302: return "Found";
00044         case 303: return "See Other";
00045         case 304: return "Not Modified";
00046         case 305: return "Use Proxy";
00047         case 307: return "Temporary Redirect";
00048         case 308: return "Permanent Redirect";
00049         case 400: return "Bad Request";
00050         case 401: return "Unauthorized";
00051         case 402: return "Payment Required";
00052         case 403: return "Forbidden";
00053         case 404: return "Not Found";
00054         case 405: return "Method Not Allowed";
00055         case 406: return "Not Acceptable";
00056         case 407: return "Proxy Authentication Required";
00057         case 408: return "Request Timeout";
00058         case 409: return "Conflict";
00059         case 410: return "Gone";
00060         case 411: return "Length Required";
00061         case 412: return "Precondition Failed";
00062         case 413: return "Payload Too Large";
00063         case 414: return "URI Too Long";
00064         case 415: return "Unsupported Media Type";
00065         case 416: return "Range Not Satisfiable";
00066         case 417: return "Expectation Failed";
00067         case 421: return "Misdirected Request";
00068         case 422: return "Unprocessable Entity";
00069         case 423: return "Locked";
00070         case 424: return "Failed Dependency";
00071         case 426: return "Upgrade Required";
00072         case 428: return "Precondition Required";
00073         case 429: return "Too Many Requests";
00074         case 431: return "Request Header Fields Too Large";
00075         case 451: return "Unavailable For Legal Reasons";
00076         case 500: return "Internal Server Error";
00077         case 501: return "Not Implemented";
00078         case 502: return "Bad Gateway";
00079         case 503: return "Service Unavailable";
00080         case 504: return "Gateway Timeout";
00081         case 505: return "HTTP Version Not Supported";
00082         case 506: return "Variant Also Negotiates";
00083         case 507: return "Insufficient Storage";
00084         case 508: return "Loop Detected";
00085         case 510: return "Not Extended";
00086         case 511: return "Network Authentication Required";
00087         default : return "Unknown";
00088     }
00089 }
00090 
00091 class HttpResponseBuilder {
00092 public:
00093     HttpResponseBuilder(uint16_t a_status_code)
00094         : status_code(a_status_code), status_message(get_http_status_string(a_status_code))
00095     {
00096     }
00097 
00098     /**
00099      * Set a header for the request
00100      * If the key already exists, it will be overwritten...
00101      */
00102     void set_header(string key, string value) {
00103         map<string, string>::iterator it = headers.find(key);
00104 
00105         if (it != headers.end()) {
00106             it->second = value;
00107         }
00108         else {
00109             headers.insert(headers.end(), pair<string, string>(key, value));
00110         }
00111     }
00112 
00113     char* build(const void* body, size_t body_size, size_t* size) {
00114         char buffer[10];
00115         snprintf(buffer, sizeof(buffer), "%d", body_size);
00116         set_header("Content-Length", string(buffer));
00117 
00118         char status_code_buffer[5];
00119         snprintf(status_code_buffer, sizeof(status_code_buffer), "%d", status_code /* max 5 digits */);
00120 
00121         *size = 0;
00122 
00123         // first line is HTTP/1.1 200 OK\r\n
00124         *size += 8 + 1 + strlen(status_code_buffer) + 1 + strlen(status_message) + 2;
00125 
00126         // after that we'll do the headers
00127         typedef map<string, string>::iterator it_type;
00128         for(it_type it = headers.begin(); it != headers.end(); it++) {
00129             // line is KEY: VALUE\r\n
00130             *size += it->first.length() + 1 + 1 + it->second.length() + 2;
00131         }
00132 
00133         // then the body, first an extra newline
00134         *size += 2;
00135 
00136         // body
00137         *size += body_size;
00138 
00139         // Now let's print it
00140         char* res = (char*)calloc(*size + 1, 1);
00141         char* originalRes = res;
00142 
00143         res += sprintf(res, "HTTP/1.1 %s %s\r\n", status_code_buffer, status_message);
00144 
00145         typedef map<string, string>::iterator it_type;
00146         for(it_type it = headers.begin(); it != headers.end(); it++) {
00147             // line is KEY: VALUE\r\n
00148             res += sprintf(res, "%s: %s\r\n", it->first.c_str(), it->second.c_str());
00149         }
00150 
00151         res += sprintf(res, "\r\n");
00152 
00153         if (body_size > 0) {
00154             memcpy(res, body, body_size);
00155         }
00156         res += body_size;
00157 
00158         // Uncomment to debug...
00159         // printf("----- BEGIN RESPONSE -----\n");
00160         // printf("%s", originalRes);
00161         // printf("----- END RESPONSE -----\n");
00162 
00163         return originalRes;
00164     }
00165 
00166     nsapi_error_t send(TCPSocket* socket, const void* body, size_t body_size) {
00167         if (!socket) return NSAPI_ERROR_NO_SOCKET;
00168 
00169         size_t res_size;
00170         char* response = build(body, body_size, &res_size);
00171 
00172         nsapi_error_t r = socket->send(response, res_size);
00173 
00174         free(response);
00175 
00176         return r;
00177     }
00178 
00179 private:
00180     uint16_t status_code;
00181     const char* status_message;
00182     map<string, string> headers;
00183 };
00184 
00185 #endif // _MBED_HTTP_RESPONSE_BUILDER_