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 _HTTP_SERVER_
Jan Jongboom 0:41f820ea137a 19 #define _HTTP_SERVER_
Jan Jongboom 0:41f820ea137a 20
Jan Jongboom 0:41f820ea137a 21 #include "mbed.h"
Jan Jongboom 0:41f820ea137a 22 #include "http_request_parser.h"
Jan Jongboom 0:41f820ea137a 23 #include "http_response.h"
Jan Jongboom 0:41f820ea137a 24 #include "http_response_builder.h"
Jan Jongboom 0:41f820ea137a 25
Jan Jongboom 1:fe3df398bdf5 26 #ifndef HTTP_SERVER_MAX_CONCURRENT
Jan Jongboom 0:41f820ea137a 27 #define HTTP_SERVER_MAX_CONCURRENT 5
Jan Jongboom 1:fe3df398bdf5 28 #endif
Jan Jongboom 0:41f820ea137a 29
Jan Jongboom 0:41f820ea137a 30 typedef HttpResponse ParsedHttpRequest;
Jan Jongboom 0:41f820ea137a 31
Jan Jongboom 0:41f820ea137a 32 /**
Jan Jongboom 0:41f820ea137a 33 * \brief HttpServer implements the logic for setting up an HTTP server.
Jan Jongboom 0:41f820ea137a 34 */
Jan Jongboom 0:41f820ea137a 35 class HttpServer {
Jan Jongboom 0:41f820ea137a 36 public:
Jan Jongboom 0:41f820ea137a 37 /**
Jan Jongboom 0:41f820ea137a 38 * HttpRequest Constructor
Jan Jongboom 0:41f820ea137a 39 *
Jan Jongboom 0:41f820ea137a 40 * @param[in] network The network interface
Jan Jongboom 0:41f820ea137a 41 */
Jan Jongboom 1:fe3df398bdf5 42 HttpServer(NetworkInterface* network) {
Jan Jongboom 1:fe3df398bdf5 43 _network = network;
Jan Jongboom 0:41f820ea137a 44 }
Jan Jongboom 0:41f820ea137a 45
Jan Jongboom 0:41f820ea137a 46 ~HttpServer() {
Jan Jongboom 0:41f820ea137a 47 for (size_t ix = 0; ix < HTTP_SERVER_MAX_CONCURRENT; ix++) {
Jan Jongboom 0:41f820ea137a 48 if (socket_threads[ix]) {
Jan Jongboom 0:41f820ea137a 49 delete socket_threads[ix];
Jan Jongboom 0:41f820ea137a 50 }
Jan Jongboom 0:41f820ea137a 51 }
Jan Jongboom 0:41f820ea137a 52 }
Jan Jongboom 0:41f820ea137a 53
Jan Jongboom 0:41f820ea137a 54 /**
Jan Jongboom 0:41f820ea137a 55 * Start running the server (it will run on it's own thread)
Jan Jongboom 0:41f820ea137a 56 */
Jan Jongboom 0:41f820ea137a 57 nsapi_error_t start(uint16_t port, Callback<void(ParsedHttpRequest* request, TCPSocket* socket)> a_handler) {
morgandu 2:ff1a293c4df3 58 server = new TCPSocket();
Jan Jongboom 0:41f820ea137a 59
Jan Jongboom 1:fe3df398bdf5 60 nsapi_error_t ret;
Jan Jongboom 1:fe3df398bdf5 61
Jan Jongboom 1:fe3df398bdf5 62 ret = server->open(_network);
Jan Jongboom 0:41f820ea137a 63 if (ret != NSAPI_ERROR_OK) {
Jan Jongboom 0:41f820ea137a 64 return ret;
Jan Jongboom 0:41f820ea137a 65 }
Jan Jongboom 0:41f820ea137a 66
Jan Jongboom 1:fe3df398bdf5 67 ret = server->bind(port);
Jan Jongboom 1:fe3df398bdf5 68 if (ret != NSAPI_ERROR_OK) {
Jan Jongboom 1:fe3df398bdf5 69 return ret;
Jan Jongboom 1:fe3df398bdf5 70 }
Jan Jongboom 1:fe3df398bdf5 71
Jan Jongboom 1:fe3df398bdf5 72 server->listen(HTTP_SERVER_MAX_CONCURRENT); // max. concurrent connections...
Jan Jongboom 1:fe3df398bdf5 73
Jan Jongboom 0:41f820ea137a 74 handler = a_handler;
Jan Jongboom 0:41f820ea137a 75
Jan Jongboom 0:41f820ea137a 76 main_thread.start(callback(this, &HttpServer::main));
Jan Jongboom 0:41f820ea137a 77
Jan Jongboom 0:41f820ea137a 78 return NSAPI_ERROR_OK;
Jan Jongboom 0:41f820ea137a 79 }
Jan Jongboom 0:41f820ea137a 80
Jan Jongboom 0:41f820ea137a 81 private:
Jan Jongboom 0:41f820ea137a 82
Jan Jongboom 0:41f820ea137a 83 void receive_data() {
Jan Jongboom 0:41f820ea137a 84 // UNSAFE: should Mutex around it or something
Jan Jongboom 0:41f820ea137a 85 TCPSocket* socket = sockets.back();
Jan Jongboom 0:41f820ea137a 86
Jan Jongboom 0:41f820ea137a 87 // needs to keep running until the socket gets closed
Jan Jongboom 0:41f820ea137a 88 while (1) {
Jan Jongboom 0:41f820ea137a 89
Jan Jongboom 0:41f820ea137a 90 ParsedHttpRequest* response = new ParsedHttpRequest();
Jan Jongboom 0:41f820ea137a 91 HttpParser* parser = new HttpParser(response, HTTP_REQUEST);
Jan Jongboom 0:41f820ea137a 92
Jan Jongboom 0:41f820ea137a 93 // Set up a receive buffer (on the heap)
Jan Jongboom 0:41f820ea137a 94 uint8_t* recv_buffer = (uint8_t*)malloc(HTTP_RECEIVE_BUFFER_SIZE);
Jan Jongboom 0:41f820ea137a 95
Jan Jongboom 0:41f820ea137a 96 // TCPSocket::recv is called until we don't have any data anymore
Jan Jongboom 0:41f820ea137a 97 nsapi_size_or_error_t recv_ret;
Jan Jongboom 0:41f820ea137a 98 while ((recv_ret = socket->recv(recv_buffer, HTTP_RECEIVE_BUFFER_SIZE)) > 0) {
Jan Jongboom 0:41f820ea137a 99 // Pass the chunk into the http_parser
Jan Jongboom 0:41f820ea137a 100 size_t nparsed = parser->execute((const char*)recv_buffer, recv_ret);
Jan Jongboom 0:41f820ea137a 101 if (nparsed != recv_ret) {
Jan Jongboom 0:41f820ea137a 102 printf("Parsing failed... parsed %d bytes, received %d bytes\n", nparsed, recv_ret);
Jan Jongboom 0:41f820ea137a 103 recv_ret = -2101;
Jan Jongboom 0:41f820ea137a 104 break;
Jan Jongboom 0:41f820ea137a 105 }
Jan Jongboom 0:41f820ea137a 106
Jan Jongboom 0:41f820ea137a 107 if (response->is_message_complete()) {
Jan Jongboom 0:41f820ea137a 108 break;
Jan Jongboom 0:41f820ea137a 109 }
Jan Jongboom 0:41f820ea137a 110 }
Jan Jongboom 0:41f820ea137a 111 // error?
Jan Jongboom 1:fe3df398bdf5 112 if (recv_ret <= 0) {
Jan Jongboom 1:fe3df398bdf5 113 if (recv_ret < 0) {
Jan Jongboom 1:fe3df398bdf5 114 printf("Error reading from socket %d\n", recv_ret);
Jan Jongboom 1:fe3df398bdf5 115 }
Jan Jongboom 0:41f820ea137a 116
Jan Jongboom 0:41f820ea137a 117 // error = recv_ret;
Jan Jongboom 0:41f820ea137a 118 delete response;
Jan Jongboom 0:41f820ea137a 119 delete parser;
Jan Jongboom 0:41f820ea137a 120 free(recv_buffer);
Jan Jongboom 0:41f820ea137a 121
Jan Jongboom 0:41f820ea137a 122 // q; should we always break out of the thread or only if NO_SOCKET ?
Jan Jongboom 0:41f820ea137a 123 // should we delete socket here? the socket seems already gone...
Jan Jongboom 1:fe3df398bdf5 124 if (recv_ret < -3000 || recv_ret == 0) {
Jan Jongboom 0:41f820ea137a 125 return;
Jan Jongboom 0:41f820ea137a 126 }
Jan Jongboom 0:41f820ea137a 127 else {
Jan Jongboom 0:41f820ea137a 128 continue;
Jan Jongboom 0:41f820ea137a 129 }
Jan Jongboom 0:41f820ea137a 130 }
Jan Jongboom 0:41f820ea137a 131
Jan Jongboom 0:41f820ea137a 132 // When done, call parser.finish()
Jan Jongboom 0:41f820ea137a 133 parser->finish();
Jan Jongboom 0:41f820ea137a 134
Jan Jongboom 0:41f820ea137a 135 // Free the receive buffer
Jan Jongboom 0:41f820ea137a 136 free(recv_buffer);
Jan Jongboom 0:41f820ea137a 137
Jan Jongboom 0:41f820ea137a 138 // Let user application handle the request, if user needs a handle to response they need to memcpy themselves
Jan Jongboom 1:fe3df398bdf5 139 if (recv_ret > 0) {
Jan Jongboom 1:fe3df398bdf5 140 handler(response, socket);
Jan Jongboom 1:fe3df398bdf5 141 }
Jan Jongboom 0:41f820ea137a 142
Jan Jongboom 0:41f820ea137a 143 // Free the response and parser
Jan Jongboom 0:41f820ea137a 144 delete response;
Jan Jongboom 0:41f820ea137a 145 delete parser;
Jan Jongboom 0:41f820ea137a 146 }
Jan Jongboom 0:41f820ea137a 147 }
Jan Jongboom 0:41f820ea137a 148
Jan Jongboom 0:41f820ea137a 149 void main() {
Jan Jongboom 0:41f820ea137a 150 while (1) {
morgandu 2:ff1a293c4df3 151 TCPSocket* clt_sock; // = new TCPSocket(); // Q: when should these be cleared? When not connected anymore?
morgandu 2:ff1a293c4df3 152 //SocketAddress clt_addr;
morgandu 2:ff1a293c4df3 153 nsapi_error_t accept_res;
morgandu 2:ff1a293c4df3 154
morgandu 2:ff1a293c4df3 155 //nsapi_error_t accept_res = server->accept(clt_sock, &clt_addr);
morgandu 2:ff1a293c4df3 156 clt_sock = server->accept(&accept_res);
Jan Jongboom 0:41f820ea137a 157 if (accept_res == NSAPI_ERROR_OK) {
Jan Jongboom 0:41f820ea137a 158 sockets.push_back(clt_sock); // so we can clear our disconnected sockets
Jan Jongboom 0:41f820ea137a 159
Jan Jongboom 0:41f820ea137a 160 // and start listening for events there
Jan Jongboom 0:41f820ea137a 161 Thread* t = new Thread(osPriorityNormal, 2048);
Jan Jongboom 0:41f820ea137a 162 t->start(callback(this, &HttpServer::receive_data));
Jan Jongboom 0:41f820ea137a 163
Jan Jongboom 1:fe3df398bdf5 164 socket_thread_metadata_t* m = new socket_thread_metadata_t();
Jan Jongboom 1:fe3df398bdf5 165 m->socket = clt_sock;
Jan Jongboom 1:fe3df398bdf5 166 m->thread = t;
Jan Jongboom 1:fe3df398bdf5 167 socket_threads.push_back(m);
Jan Jongboom 1:fe3df398bdf5 168 }
Jan Jongboom 1:fe3df398bdf5 169 else {
morgandu 2:ff1a293c4df3 170 //delete clt_sock;
Jan Jongboom 0:41f820ea137a 171 }
Jan Jongboom 0:41f820ea137a 172
Jan Jongboom 0:41f820ea137a 173 for (size_t ix = 0; ix < socket_threads.size(); ix++) {
Jan Jongboom 1:fe3df398bdf5 174 if (socket_threads[ix]->thread->get_state() == Thread::Deleted) {
Jan Jongboom 1:fe3df398bdf5 175 socket_threads[ix]->thread->terminate();
Jan Jongboom 1:fe3df398bdf5 176 delete socket_threads[ix]->thread;
morgandu 2:ff1a293c4df3 177 //delete socket_threads[ix]->socket; // does this work on esp8266?
morgandu 2:ff1a293c4df3 178 socket_threads[ix]->socket->close();
Jan Jongboom 0:41f820ea137a 179 socket_threads.erase(socket_threads.begin() + ix); // what if there are two?
Jan Jongboom 0:41f820ea137a 180 }
Jan Jongboom 0:41f820ea137a 181 }
Jan Jongboom 0:41f820ea137a 182
Jan Jongboom 0:41f820ea137a 183 }
Jan Jongboom 0:41f820ea137a 184 }
Jan Jongboom 0:41f820ea137a 185
Jan Jongboom 1:fe3df398bdf5 186 typedef struct {
Jan Jongboom 1:fe3df398bdf5 187 TCPSocket* socket;
Jan Jongboom 1:fe3df398bdf5 188 Thread* thread;
Jan Jongboom 1:fe3df398bdf5 189 } socket_thread_metadata_t;
Jan Jongboom 1:fe3df398bdf5 190
morgandu 2:ff1a293c4df3 191 TCPSocket* server;
Jan Jongboom 1:fe3df398bdf5 192 NetworkInterface* _network;
Jan Jongboom 0:41f820ea137a 193 Thread main_thread;
Jan Jongboom 0:41f820ea137a 194 vector<TCPSocket*> sockets;
Jan Jongboom 1:fe3df398bdf5 195 vector<socket_thread_metadata_t*> socket_threads;
Jan Jongboom 1:fe3df398bdf5 196 Callback<void(ParsedHttpRequest* request, TCPSocket* socket)> handler;
Jan Jongboom 0:41f820ea137a 197 };
Jan Jongboom 0:41f820ea137a 198
Jan Jongboom 0:41f820ea137a 199 #endif // _HTTP_SERVER