Simple Webserver example for wiznet W5500 (SPI Ethernet chip) mbed OS 5 - HTTP 1.1 and multi-threaded
Dependencies: W5500Interface easy-connect mbed-http
Fork of http-webserver-example by
mbed-os-example-http-server
This application demonstrates how to run an HTTP server on an mbed OS 5 device & WIZnet W5500 Ethernet components.
Request parsing is done through [nodejs/http-parser](https://github.com/nodejs/http-parser).
To build
- Open ``mbed_app.json`` and change the `network-interface` option to your connectivity method ([more info](https://github.com/ARMmbed/easy-connect)).
- Build the project in the online compiler or using mbed CLI.
- Flash the project to your development board.
- Attach a serial monitor to your board to see the debug messages.
Tested on
- NUCLEO_F411RE with W5500 Ethernet shield.
- For W5500, you need the following libraries.
Import libraryW5500Interface
W5500 driver for mbed OS 5
Import libraryeasy-connect
Modified version of easy-connect for W5500 Ethernet components
But every networking stack that supports the [mbed OS 5 NetworkInterface API](https://docs.mbed.com/docs/mbed-os-api-reference/en/latest/APIs/communication/network_sockets/) should work.
Test Result
Diff: source/http_response_builder.h
- Revision:
- 0:41f820ea137a
- Child:
- 1:fe3df398bdf5
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/http_response_builder.h Mon Jul 31 15:41:22 2017 +0200 @@ -0,0 +1,181 @@ +/* + * PackageLicenseDeclared: Apache-2.0 + * Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _MBED_HTTP_RESPONSE_BUILDER_ +#define _MBED_HTTP_RESPONSE_BUILDER_ + +#include <string> +#include <map> +#include "http_parser.h" +#include "http_parsed_url.h" + +static const char* get_http_status_string(uint16_t status_code) { + switch (status_code) { + case 100: return "Continue"; + case 101: return "Switching Protocols"; + case 102: return "Processing"; + case 200: return "OK"; + case 201: return "Created"; + case 202: return "Accepted"; + case 203: return "Non-Authoritative Information"; + case 204: return "No Content"; + case 205: return "Reset Content"; + case 206: return "Partial Content"; + case 207: return "Multi-Status"; + case 208: return "Already Reported"; + case 226: return "IM Used"; + case 300: return "Multiple Choices"; + case 301: return "Moved Permanently"; + case 302: return "Found"; + case 303: return "See Other"; + case 304: return "Not Modified"; + case 305: return "Use Proxy"; + case 307: return "Temporary Redirect"; + case 308: return "Permanent Redirect"; + case 400: return "Bad Request"; + case 401: return "Unauthorized"; + case 402: return "Payment Required"; + case 403: return "Forbidden"; + case 404: return "Not Found"; + case 405: return "Method Not Allowed"; + case 406: return "Not Acceptable"; + case 407: return "Proxy Authentication Required"; + case 408: return "Request Timeout"; + case 409: return "Conflict"; + case 410: return "Gone"; + case 411: return "Length Required"; + case 412: return "Precondition Failed"; + case 413: return "Payload Too Large"; + case 414: return "URI Too Long"; + case 415: return "Unsupported Media Type"; + case 416: return "Range Not Satisfiable"; + case 417: return "Expectation Failed"; + case 421: return "Misdirected Request"; + case 422: return "Unprocessable Entity"; + case 423: return "Locked"; + case 424: return "Failed Dependency"; + case 426: return "Upgrade Required"; + case 428: return "Precondition Required"; + case 429: return "Too Many Requests"; + case 431: return "Request Header Fields Too Large"; + case 451: return "Unavailable For Legal Reasons"; + case 500: return "Internal Server Error"; + case 501: return "Not Implemented"; + case 502: return "Bad Gateway"; + case 503: return "Service Unavailable"; + case 504: return "Gateway Timeout"; + case 505: return "HTTP Version Not Supported"; + case 506: return "Variant Also Negotiates"; + case 507: return "Insufficient Storage"; + case 508: return "Loop Detected"; + case 510: return "Not Extended"; + case 511: return "Network Authentication Required"; + default : return "Unknown"; + } +} + +class HttpResponseBuilder { +public: + HttpResponseBuilder(uint16_t a_status_code) + : status_code(a_status_code), status_message(get_http_status_string(a_status_code)) + { + } + + /** + * Set a header for the request + * If the key already exists, it will be overwritten... + */ + void set_header(string key, string value) { + map<string, string>::iterator it = headers.find(key); + + if (it != headers.end()) { + it->second = value; + } + else { + headers.insert(headers.end(), pair<string, string>(key, value)); + } + } + + char* build(const void* body, size_t body_size, size_t* size) { + char buffer[10]; + snprintf(buffer, sizeof(buffer), "%d", body_size); + set_header("Content-Length", string(buffer)); + + char status_code_buffer[5]; + snprintf(status_code_buffer, sizeof(status_code_buffer), "%d", status_code /* max 5 digits */); + + *size = 0; + + // first line is HTTP/1.1 200 OK\r\n + *size += 8 + 1 + strlen(status_code_buffer) + 1 + strlen(status_message) + 2; + + // after that we'll do the headers + typedef map<string, string>::iterator it_type; + for(it_type it = headers.begin(); it != headers.end(); it++) { + // line is KEY: VALUE\r\n + *size += it->first.length() + 1 + 1 + it->second.length() + 2; + } + + // then the body, first an extra newline + *size += 2; + + // body + *size += body_size; + + // Now let's print it + char* res = (char*)calloc(*size + 1, 1); + char* originalRes = res; + + res += sprintf(res, "HTTP/1.1 %s %s\r\n", status_code_buffer, status_message); + + typedef map<string, string>::iterator it_type; + for(it_type it = headers.begin(); it != headers.end(); it++) { + // line is KEY: VALUE\r\n + res += sprintf(res, "%s: %s\r\n", it->first.c_str(), it->second.c_str()); + } + + res += sprintf(res, "\r\n"); + + if (body_size > 0) { + memcpy(res, body, body_size); + } + res += body_size; + + // Uncomment to debug... + // printf("----- BEGIN RESPONSE -----\n"); + // printf("%s", originalRes); + // printf("----- END RESPONSE -----\n"); + + return originalRes; + } + + nsapi_error_t send(TCPSocket* socket, const void* body, size_t body_size) { + if (!socket) return NSAPI_ERROR_NO_SOCKET; + + size_t res_size; + char* response = build(body, body_size, &res_size); + + return socket->send(response, res_size); + } + +private: + uint16_t status_code; + const char* status_message; + map<string, string> headers; +}; + +#endif // _MBED_HTTP_RESPONSE_BUILDER_