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

Dependencies:   mbed-http

For Mbed OS 6, please use the NuMaker-simple-httpd example.

This application demonstrates how to run an HTTP server on an mbed OS 5 device. It is derived from http-webserver-example Request parsing is done through nodejs/http-parser.

Fixed for Mbed OS 5.15 or later

Tested on

NuMaker IoT-M487 with Ethernet NuMaker PFM-M487 with Ethernet

Committer:
morgandu
Date:
Mon Jun 29 08:17:16 2020 +0000
Revision:
2:ff1a293c4df3
Parent:
1:fe3df398bdf5
Webserver example for mbed OS 5 - HTTP 1.1 and multi-threaded; Fix for mbed OS 5.15 and clean unnecessary libraries; Add fixed IP address option; Test on Nuvoton NuMaker boards.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Jan Jongboom 0:41f820ea137a 1 /*
Jan Jongboom 0:41f820ea137a 2 * PackageLicenseDeclared: Apache-2.0
Jan Jongboom 0:41f820ea137a 3 * Copyright (c) 2017 ARM Limited
Jan Jongboom 0:41f820ea137a 4 *
Jan Jongboom 0:41f820ea137a 5 * Licensed under the Apache License, Version 2.0 (the "License");
Jan Jongboom 0:41f820ea137a 6 * you may not use this file except in compliance with the License.
Jan Jongboom 0:41f820ea137a 7 * You may obtain a copy of the License at
Jan Jongboom 0:41f820ea137a 8 *
Jan Jongboom 0:41f820ea137a 9 * http://www.apache.org/licenses/LICENSE-2.0
Jan Jongboom 0:41f820ea137a 10 *
Jan Jongboom 0:41f820ea137a 11 * Unless required by applicable law or agreed to in writing, software
Jan Jongboom 0:41f820ea137a 12 * distributed under the License is distributed on an "AS IS" BASIS,
Jan Jongboom 0:41f820ea137a 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Jan Jongboom 0:41f820ea137a 14 * See the License for the specific language governing permissions and
Jan Jongboom 0:41f820ea137a 15 * limitations under the License.
Jan Jongboom 0:41f820ea137a 16 */
Jan Jongboom 0:41f820ea137a 17
Jan Jongboom 0:41f820ea137a 18 #ifndef _MBED_HTTP_RESPONSE_BUILDER_
Jan Jongboom 0:41f820ea137a 19 #define _MBED_HTTP_RESPONSE_BUILDER_
Jan Jongboom 0:41f820ea137a 20
Jan Jongboom 0:41f820ea137a 21 #include <string>
Jan Jongboom 0:41f820ea137a 22 #include <map>
Jan Jongboom 0:41f820ea137a 23 #include "http_parser.h"
Jan Jongboom 0:41f820ea137a 24 #include "http_parsed_url.h"
Jan Jongboom 0:41f820ea137a 25
Jan Jongboom 0:41f820ea137a 26 static const char* get_http_status_string(uint16_t status_code) {
Jan Jongboom 0:41f820ea137a 27 switch (status_code) {
Jan Jongboom 0:41f820ea137a 28 case 100: return "Continue";
Jan Jongboom 0:41f820ea137a 29 case 101: return "Switching Protocols";
Jan Jongboom 0:41f820ea137a 30 case 102: return "Processing";
Jan Jongboom 0:41f820ea137a 31 case 200: return "OK";
Jan Jongboom 0:41f820ea137a 32 case 201: return "Created";
Jan Jongboom 0:41f820ea137a 33 case 202: return "Accepted";
Jan Jongboom 0:41f820ea137a 34 case 203: return "Non-Authoritative Information";
Jan Jongboom 0:41f820ea137a 35 case 204: return "No Content";
Jan Jongboom 0:41f820ea137a 36 case 205: return "Reset Content";
Jan Jongboom 0:41f820ea137a 37 case 206: return "Partial Content";
Jan Jongboom 0:41f820ea137a 38 case 207: return "Multi-Status";
Jan Jongboom 0:41f820ea137a 39 case 208: return "Already Reported";
Jan Jongboom 0:41f820ea137a 40 case 226: return "IM Used";
Jan Jongboom 0:41f820ea137a 41 case 300: return "Multiple Choices";
Jan Jongboom 0:41f820ea137a 42 case 301: return "Moved Permanently";
Jan Jongboom 0:41f820ea137a 43 case 302: return "Found";
Jan Jongboom 0:41f820ea137a 44 case 303: return "See Other";
Jan Jongboom 0:41f820ea137a 45 case 304: return "Not Modified";
Jan Jongboom 0:41f820ea137a 46 case 305: return "Use Proxy";
Jan Jongboom 0:41f820ea137a 47 case 307: return "Temporary Redirect";
Jan Jongboom 0:41f820ea137a 48 case 308: return "Permanent Redirect";
Jan Jongboom 0:41f820ea137a 49 case 400: return "Bad Request";
Jan Jongboom 0:41f820ea137a 50 case 401: return "Unauthorized";
Jan Jongboom 0:41f820ea137a 51 case 402: return "Payment Required";
Jan Jongboom 0:41f820ea137a 52 case 403: return "Forbidden";
Jan Jongboom 0:41f820ea137a 53 case 404: return "Not Found";
Jan Jongboom 0:41f820ea137a 54 case 405: return "Method Not Allowed";
Jan Jongboom 0:41f820ea137a 55 case 406: return "Not Acceptable";
Jan Jongboom 0:41f820ea137a 56 case 407: return "Proxy Authentication Required";
Jan Jongboom 0:41f820ea137a 57 case 408: return "Request Timeout";
Jan Jongboom 0:41f820ea137a 58 case 409: return "Conflict";
Jan Jongboom 0:41f820ea137a 59 case 410: return "Gone";
Jan Jongboom 0:41f820ea137a 60 case 411: return "Length Required";
Jan Jongboom 0:41f820ea137a 61 case 412: return "Precondition Failed";
Jan Jongboom 0:41f820ea137a 62 case 413: return "Payload Too Large";
Jan Jongboom 0:41f820ea137a 63 case 414: return "URI Too Long";
Jan Jongboom 0:41f820ea137a 64 case 415: return "Unsupported Media Type";
Jan Jongboom 0:41f820ea137a 65 case 416: return "Range Not Satisfiable";
Jan Jongboom 0:41f820ea137a 66 case 417: return "Expectation Failed";
Jan Jongboom 0:41f820ea137a 67 case 421: return "Misdirected Request";
Jan Jongboom 0:41f820ea137a 68 case 422: return "Unprocessable Entity";
Jan Jongboom 0:41f820ea137a 69 case 423: return "Locked";
Jan Jongboom 0:41f820ea137a 70 case 424: return "Failed Dependency";
Jan Jongboom 0:41f820ea137a 71 case 426: return "Upgrade Required";
Jan Jongboom 0:41f820ea137a 72 case 428: return "Precondition Required";
Jan Jongboom 0:41f820ea137a 73 case 429: return "Too Many Requests";
Jan Jongboom 0:41f820ea137a 74 case 431: return "Request Header Fields Too Large";
Jan Jongboom 0:41f820ea137a 75 case 451: return "Unavailable For Legal Reasons";
Jan Jongboom 0:41f820ea137a 76 case 500: return "Internal Server Error";
Jan Jongboom 0:41f820ea137a 77 case 501: return "Not Implemented";
Jan Jongboom 0:41f820ea137a 78 case 502: return "Bad Gateway";
Jan Jongboom 0:41f820ea137a 79 case 503: return "Service Unavailable";
Jan Jongboom 0:41f820ea137a 80 case 504: return "Gateway Timeout";
Jan Jongboom 0:41f820ea137a 81 case 505: return "HTTP Version Not Supported";
Jan Jongboom 0:41f820ea137a 82 case 506: return "Variant Also Negotiates";
Jan Jongboom 0:41f820ea137a 83 case 507: return "Insufficient Storage";
Jan Jongboom 0:41f820ea137a 84 case 508: return "Loop Detected";
Jan Jongboom 0:41f820ea137a 85 case 510: return "Not Extended";
Jan Jongboom 0:41f820ea137a 86 case 511: return "Network Authentication Required";
Jan Jongboom 0:41f820ea137a 87 default : return "Unknown";
Jan Jongboom 0:41f820ea137a 88 }
Jan Jongboom 0:41f820ea137a 89 }
Jan Jongboom 0:41f820ea137a 90
Jan Jongboom 0:41f820ea137a 91 class HttpResponseBuilder {
Jan Jongboom 0:41f820ea137a 92 public:
Jan Jongboom 0:41f820ea137a 93 HttpResponseBuilder(uint16_t a_status_code)
Jan Jongboom 0:41f820ea137a 94 : status_code(a_status_code), status_message(get_http_status_string(a_status_code))
Jan Jongboom 0:41f820ea137a 95 {
Jan Jongboom 0:41f820ea137a 96 }
Jan Jongboom 0:41f820ea137a 97
Jan Jongboom 0:41f820ea137a 98 /**
Jan Jongboom 0:41f820ea137a 99 * Set a header for the request
Jan Jongboom 0:41f820ea137a 100 * If the key already exists, it will be overwritten...
Jan Jongboom 0:41f820ea137a 101 */
Jan Jongboom 0:41f820ea137a 102 void set_header(string key, string value) {
Jan Jongboom 0:41f820ea137a 103 map<string, string>::iterator it = headers.find(key);
Jan Jongboom 0:41f820ea137a 104
Jan Jongboom 0:41f820ea137a 105 if (it != headers.end()) {
Jan Jongboom 0:41f820ea137a 106 it->second = value;
Jan Jongboom 0:41f820ea137a 107 }
Jan Jongboom 0:41f820ea137a 108 else {
Jan Jongboom 0:41f820ea137a 109 headers.insert(headers.end(), pair<string, string>(key, value));
Jan Jongboom 0:41f820ea137a 110 }
Jan Jongboom 0:41f820ea137a 111 }
Jan Jongboom 0:41f820ea137a 112
Jan Jongboom 0:41f820ea137a 113 char* build(const void* body, size_t body_size, size_t* size) {
Jan Jongboom 0:41f820ea137a 114 char buffer[10];
Jan Jongboom 0:41f820ea137a 115 snprintf(buffer, sizeof(buffer), "%d", body_size);
Jan Jongboom 0:41f820ea137a 116 set_header("Content-Length", string(buffer));
Jan Jongboom 0:41f820ea137a 117
Jan Jongboom 0:41f820ea137a 118 char status_code_buffer[5];
Jan Jongboom 0:41f820ea137a 119 snprintf(status_code_buffer, sizeof(status_code_buffer), "%d", status_code /* max 5 digits */);
Jan Jongboom 0:41f820ea137a 120
Jan Jongboom 0:41f820ea137a 121 *size = 0;
Jan Jongboom 0:41f820ea137a 122
Jan Jongboom 0:41f820ea137a 123 // first line is HTTP/1.1 200 OK\r\n
Jan Jongboom 0:41f820ea137a 124 *size += 8 + 1 + strlen(status_code_buffer) + 1 + strlen(status_message) + 2;
Jan Jongboom 0:41f820ea137a 125
Jan Jongboom 0:41f820ea137a 126 // after that we'll do the headers
Jan Jongboom 0:41f820ea137a 127 typedef map<string, string>::iterator it_type;
Jan Jongboom 0:41f820ea137a 128 for(it_type it = headers.begin(); it != headers.end(); it++) {
Jan Jongboom 0:41f820ea137a 129 // line is KEY: VALUE\r\n
Jan Jongboom 0:41f820ea137a 130 *size += it->first.length() + 1 + 1 + it->second.length() + 2;
Jan Jongboom 0:41f820ea137a 131 }
Jan Jongboom 0:41f820ea137a 132
Jan Jongboom 0:41f820ea137a 133 // then the body, first an extra newline
Jan Jongboom 0:41f820ea137a 134 *size += 2;
Jan Jongboom 0:41f820ea137a 135
Jan Jongboom 0:41f820ea137a 136 // body
Jan Jongboom 0:41f820ea137a 137 *size += body_size;
Jan Jongboom 0:41f820ea137a 138
Jan Jongboom 0:41f820ea137a 139 // Now let's print it
Jan Jongboom 0:41f820ea137a 140 char* res = (char*)calloc(*size + 1, 1);
Jan Jongboom 0:41f820ea137a 141 char* originalRes = res;
Jan Jongboom 0:41f820ea137a 142
Jan Jongboom 0:41f820ea137a 143 res += sprintf(res, "HTTP/1.1 %s %s\r\n", status_code_buffer, status_message);
Jan Jongboom 0:41f820ea137a 144
Jan Jongboom 0:41f820ea137a 145 typedef map<string, string>::iterator it_type;
Jan Jongboom 0:41f820ea137a 146 for(it_type it = headers.begin(); it != headers.end(); it++) {
Jan Jongboom 0:41f820ea137a 147 // line is KEY: VALUE\r\n
Jan Jongboom 0:41f820ea137a 148 res += sprintf(res, "%s: %s\r\n", it->first.c_str(), it->second.c_str());
Jan Jongboom 0:41f820ea137a 149 }
Jan Jongboom 0:41f820ea137a 150
Jan Jongboom 0:41f820ea137a 151 res += sprintf(res, "\r\n");
Jan Jongboom 0:41f820ea137a 152
Jan Jongboom 0:41f820ea137a 153 if (body_size > 0) {
Jan Jongboom 0:41f820ea137a 154 memcpy(res, body, body_size);
Jan Jongboom 0:41f820ea137a 155 }
Jan Jongboom 0:41f820ea137a 156 res += body_size;
Jan Jongboom 0:41f820ea137a 157
Jan Jongboom 0:41f820ea137a 158 // Uncomment to debug...
Jan Jongboom 0:41f820ea137a 159 // printf("----- BEGIN RESPONSE -----\n");
Jan Jongboom 0:41f820ea137a 160 // printf("%s", originalRes);
Jan Jongboom 0:41f820ea137a 161 // printf("----- END RESPONSE -----\n");
Jan Jongboom 0:41f820ea137a 162
Jan Jongboom 0:41f820ea137a 163 return originalRes;
Jan Jongboom 0:41f820ea137a 164 }
Jan Jongboom 0:41f820ea137a 165
Jan Jongboom 0:41f820ea137a 166 nsapi_error_t send(TCPSocket* socket, const void* body, size_t body_size) {
Jan Jongboom 0:41f820ea137a 167 if (!socket) return NSAPI_ERROR_NO_SOCKET;
Jan Jongboom 0:41f820ea137a 168
Jan Jongboom 0:41f820ea137a 169 size_t res_size;
Jan Jongboom 0:41f820ea137a 170 char* response = build(body, body_size, &res_size);
Jan Jongboom 0:41f820ea137a 171
Jan Jongboom 1:fe3df398bdf5 172 nsapi_error_t r = socket->send(response, res_size);
Jan Jongboom 1:fe3df398bdf5 173
Jan Jongboom 1:fe3df398bdf5 174 free(response);
Jan Jongboom 1:fe3df398bdf5 175
Jan Jongboom 1:fe3df398bdf5 176 return r;
Jan Jongboom 0:41f820ea137a 177 }
Jan Jongboom 0:41f820ea137a 178
Jan Jongboom 0:41f820ea137a 179 private:
Jan Jongboom 0:41f820ea137a 180 uint16_t status_code;
Jan Jongboom 0:41f820ea137a 181 const char* status_message;
Jan Jongboom 0:41f820ea137a 182 map<string, string> headers;
Jan Jongboom 0:41f820ea137a 183 };
Jan Jongboom 0:41f820ea137a 184
Jan Jongboom 0:41f820ea137a 185 #endif // _MBED_HTTP_RESPONSE_BUILDER_