Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: WiFlyHTTPServer WiflyInterface mbed-rpc mbed-rtos mbed
HttpServer.cpp
- Committer:
- leihen
- Date:
- 2013-06-26
- Revision:
- 0:9c6ebc97c758
File content as of revision 0:9c6ebc97c758:
#include "mbed.h"
#include "HttpServer.h"
#define DEBUG
#include "debug.h"
#define EVENT_DATA_READY 0x05
DigitalOut ledRX(LED4);
typedef struct {
char c;
} message_t;
Queue<char, 256> m_queue;
typedef struct {
const char* method;
msg_t type;
} methodType_t;
const methodType_t supportedOps[] = {
{ "GET", msg_get },
{ "POST", msg_post },
{ "PUT", msg_put },
{ "HEAD", msg_head},
{ "CONNECT", msg_connect},
{ "DELETE", msg_delete},
{ "TRACE", msg_trace},
{ "OPTIONS", msg_options}
};
Queue<request_msg_t, 5> m_requestQueue; // Do not allow more than 5 concurrent requests
MemoryPool<request_msg_t, 5> m_requestPool;
map<string, string> messageHeaders;
map<string, HTTPRequestHandler* (*)(const char*, const char*, HTTPConnection::HTTPMessage&), HttpServer::handlersComp> HttpServer::m_lpHandlers;
/* Constructor will create and initialize all objects excep the threads */
HttpServer::HttpServer(PinName tx, PinName rx, PinName rst, PinName tcp_status, const char * ssid, const char * phrase, Security sec, Wifly::WiflyBaudrate_t baud)
: Wifly(tx, rx, rst, tcp_status, ssid, phrase, sec, baud), m_listener(NULL), m_worker(NULL)
{
INFO("Initializing wifly\n");
// Initialize the wifly wlan device
reset();
state.dhcp = true;
INFO("Connecting to network...");
// Try join the network
while(!join()) {
INFO("Failed to connect. Trying again\n");
reset();
}
INFO("connected\n");
}
HttpServer::~HttpServer()
{
if (m_listener) {
m_listener->terminate();
delete m_listener;
}
if (m_worker) {
m_worker->terminate();
delete m_worker;
}
}
bool HttpServer::start(int port)
{
// Bind to that port
if (!bind(port)) {
ERR("Failed to bind to port %d\n", port);
return false;
}
// Start the child threads
m_worker = new Thread(HttpServer::worker_thread, NULL, osPriorityAboveNormal, DEFAULT_STACK_SIZE*4);
if (m_worker == NULL) {
ERR("Failed to start server thread !\n");
return false;
}
m_listener = new Thread(&HttpServer::listen_thread, NULL, osPriorityAboveNormal, DEFAULT_STACK_SIZE*2);
if (m_listener == NULL) {
ERR("Failed to start listener thread !\n");
m_worker->terminate();
delete m_worker;
m_worker = NULL;
return false;
}
return true;
}
bool HttpServer::bind(int port)
{
char cmd[20];
// set TCP protocol
setProtocol(TCP);
// set local port
sprintf(cmd, "set i l %d\r", port);
if (!sendCommand(cmd, "AOK"))
return false;
// save
if (!sendCommand("save\r", "Stor"))
return false;
// reboot
reboot();
// connect the network
if (isDHCP()) {
if (!sendCommand("join\r", "DHCP=ON", NULL, 10000))
return false;
} else {
if (!sendCommand("join\r", "Associated", NULL, 10000))
return false;
}
// exit
exit();
Thread::wait(200);
flush();
return true;
}
DigitalOut Led2(LED2);
void HttpServer::handler_rx(void)
{
static char sequence = 0;
//read characters
while (wifi.readable()) {
char c = LPC_UART3->RBR;
ledRX = !ledRX;
switch(sequence) {
case 0 : if (c == 'G') sequence = 1; break;
case 1 : if (c == 'E') sequence = 2; break;
case 2 : if (c == 'T') sequence = 0; Led2 = !Led2;break;
default: break;
}
m_queue.put((char*)(int)c);
}
}
void HttpServer::attach_rx(bool callback)
{
if (!callback)
wifi.attach(NULL);
else
wifi.attach(this, &HttpServer::handler_rx);
}
bool HttpServer::join()
{
return Wifly::join();
}
int HttpServer::send(const char * str, int len, const char * ACK, char * res, int timeout)
{
return Wifly::send(str, len, ACK, res, timeout);
}
request_msg_t* HttpServer::checkMessageReceived(char *data)
{
INFO("Checking for new HTTP request !\n");
char *req = data;
char *uri = NULL;
char *ver = NULL;
while( *data ) {
if (*data == ' ') {
*data = 0;
if (uri == NULL) {
uri = data+1;
} else {
ver = data+1;
break;
}
}
data++;
}
INFO("Detected : %s, %s, %s\n", req, uri, ver);
if ((req != NULL) && (uri != NULL) && (ver != NULL) ) {
for (int i = 0 ; i < sizeof(supportedOps) / sizeof(methodType_t) ; i++) {
if (strcmp(supportedOps[i].method, req) == 0) {
// found the request
INFO("Request valid !!!\n");
request_msg_t* pmsg = m_requestPool.alloc();
pmsg->requestType = supportedOps[i].type;
strncpy(pmsg->requestUri, uri, 255);
return pmsg;
}
}
}
INFO("Invalid request \"%s\"\n", req);
return NULL;
}
void HttpServer::processMessageHeader(char* headerLine, char **fieldname, char **fieldvalue)
{
*fieldname = headerLine;
*fieldvalue = NULL;
while( *headerLine ) {
if (*headerLine == ':') {
*headerLine++ = 0;
while(*headerLine == ' ') headerLine++;
*fieldvalue = headerLine;
return;
}
headerLine++;
}
return ;
}
void HttpServer::listenForRequests()
{
static char data[256];
static int curPos = 0;
int CRLF = 0;
int m_openConnections = 0;
request_msg_t *pMsg = NULL;
INFO("Listener running\n");
bool asteriskReceivedOnce = false;
while(1) {
osEvent evt = m_queue.get();
if (evt.status == osEventMessage) {
char c;
c = (char)(int)evt.value.p;
if ((c!='\n') && (c!='\r')) {
data[curPos++] = c;
data[curPos] = 0;
}
if (pMsg != NULL) { // request was detected and will further be processed completely
// check for CRLF
if (c == '\n') {
CRLF++;
INFO("<CR>(%d)", CRLF);
if (CRLF == 2) { // all message headers received, so send message and be ready for new one
CRLF = 0;
// SPAWN MESSAGE
INFO("REQUEST COMPLETE --> Handing over to worker thread !\n\n\n\n");
m_requestQueue.put(pMsg);
data[0] = 0;
curPos = 0;
asteriskReceivedOnce = false;
pMsg = NULL;
} else { // must be a new header
// char *name, *value;
// INFO("Processing Header !\"%s\"", data);
/* processMessageHeader(data, &name, &value);
if (strncmp(name, "Content-Length", 14 ) == 0) {
// Data will be sent, be ready to receive
} else {
INFO("HEADER: Name=\"%s\", Value=\"%s\"", name, value);
}
*/ data[0] = 0;
curPos = 0;
}
} else {
if (c != '\r')
CRLF = 0;
else
INFO("<LF>");
}
} else if (c == '*') {
CRLF = 0;
if (asteriskReceivedOnce) {
// could be an open, close or read command
if (curPos >= 6) { // only need to process if data is large enough
if ( (data[curPos-6] == '*') && (data[curPos-5] == 'O') && (data[curPos-4] == 'P') && (data[curPos-3] == 'E') && (data[curPos-2] == 'N') && (data[curPos-1] == '*')) {
// Add a connection
INFO("New connection opened (%d)...\n", ++m_openConnections);
data[0] = 0;
curPos = 0;
} else if ( (data[curPos-6] == '*') && (data[curPos-5] == 'C') && (data[curPos-4] == 'L') && (data[curPos-3] == 'O') && (data[curPos-2] == 'S') && (data[curPos-1] == '*')) {
// close a connection
INFO("Connection was closed ...(%d)\n", --m_openConnections);
data[0] = 0;
curPos = 0;
}
}
asteriskReceivedOnce = false;
} else { // set the indicator so that next time we'll check for valid connection commands
asteriskReceivedOnce = true;
}
} else { // first make sure that when no asterisk is received the asteriskReceivedOnce flag will be reset on each newline
if (c == '\n') {
if (m_openConnections > 0) {
// Check to see if we received a valid request
pMsg = checkMessageReceived(data);
if (pMsg == NULL) {
// not received valid stuff, so discard
INFO("Unrecognised data received : \"%s\"\n", data);
} else {
INFO("New request detected ! : \"%s\"\n", data);
}
} else {
INFO("Unrecognised data detected : \"%s\"\n", data);
}
asteriskReceivedOnce = false;
data[0] = 0;
curPos = 0;
CRLF = 1;
}
}
}
// else {
Thread::yield();
// }
}
}
void HttpServer::serveRequests()
{
HTTPConnection::HTTPMessage *myMessage = new HTTPConnection::HTTPMessage;
INFO("Server running\n");
while(1) {
INFO("Listening for new request !");
osEvent evt = m_requestQueue.get();
if (evt.status == osEventMessage) {
request_msg_t* pMsg = (request_msg_t*)evt.value.p;
m_worker->set_priority(osPriorityBelowNormal);
Thread::yield();
switch(pMsg->requestType) {
case msg_get:
INFO("Server received GET message !");
myMessage->request = HTTP_RT_GET;
myMessage->uri = pMsg->requestUri;
HandleRequest(myMessage);
Thread::yield();
break;
case msg_post:
case msg_put:
case msg_head:
case msg_delete:
case msg_trace:
case msg_options:
case msg_connect:
default:
break;
}
m_worker->set_priority(osPriorityNormal);
m_requestPool.free(pMsg);
}
Thread::yield();
}
}
bool HttpServer::parseRequest(char *request)
{
// dissect into : path, file[, [arg, value]1..N ] as "/path/file?arg1=val1&arg2=val2...
// first check for questionmark sign to separate the file and path from any arguments
char* path = request;
char* file = NULL;
char* arglist = NULL;
char* lastPathSep = NULL;
while(*request) {
if (*request == '/' )
lastPathSep = request;
if (*request == '?') {
*request++ = 0;
arglist = request;
}
}
if (arglist == NULL) {
INFO("Request does not have parameters !");
}
if (lastPathSep == NULL)
return false; // no path provided !!!!
// now, whatever is provided to the left including the slash is the 'path', the part to the right is the file. caution: the file may be left blank !
if (lastPathSep != 0) {
// 2 cases to handle :
// 1. : "/blah/" or "/blah/blub/" --> path = "/blah/", file = "index.html"
// 2. : "/blah/blub" or "/blah/blub/blubber" --> path = "/blah/", file = "blub"
} else {
// 2 cases to handle :
// 1. : "/" --> path = "/", file = "index.html"
// 2. : "/blah" --> path = "/", file = "blah"
}
return true;
}
void HttpServer::listen_thread(const void *params)
{
HttpServer* pSvr = (HttpServer*)params;
pSvr->listenForRequests();
}
void HttpServer::worker_thread(const void * params)
{
HttpServer* pSvr = (HttpServer*)params;
pSvr->serveRequests();
}
static const char* szStdErrorPage = "<HTML><HEAD><META content=\"text/html\" http-equiv=Content-Type></HEAD><BODY><h1>Error 404</h1><P>This resource is not available<P></BODY></HTML>\r\n\r\n";
void HttpServer::StdErrorHandler(HTTPConnection::HTTPMessage& msg)
{
char echoHeader[256];
sprintf(echoHeader,"HTTP/1.0 404 Fail\r\nConnection: close\r\nContent-Length: %d\r\nContent-Type: text/html\r\nServer: mbed embedded\r\n\n\r",strlen(szStdErrorPage));
Wifly::getInstance()->sendData(echoHeader, strlen(echoHeader));
Wifly::getInstance()->sendData((char*)szStdErrorPage, strlen(szStdErrorPage));
}
void HttpServer::HandleRequest(HTTPConnection::HTTPMessage* pmsg)
{
static std::string localPath;
static std::map<std::string, HTTPRequestHandler*(*)(const char*, const char*, HTTPConnection::HTTPMessage&), handlersComp>::const_iterator it;
INFO("Trying to handle request");
// Iterate through registered handlers and check if the handler's path is a subset of the requested uri.
for (it = m_lpHandlers.begin() ; it != m_lpHandlers.end() ; it++) {
// check if this entries' path is fully contained at the beginning of the requested path
std::string curpth = it->first;
if (pmsg->uri.find(curpth) == 0) {
// firts matching handler found, we just take it and we'll be happy
localPath = pmsg->uri.substr(curpth.length());
break;
}
}
if (it == m_lpHandlers.end()) {
// There is no such handler, so return invalid
INFO("Webrequest left unhandled.");
m_pErrorHandler(*pmsg);
} else {
// Valid handler was found
INFO("Routing webrequest !");
// Instantiate the handler object (handling will be done from withing the object's constructor
HTTPRequestHandler *phdl = (*it->second)(it->first.c_str(), localPath.c_str(), *pmsg);
// now we can delete the object, because handling is completed.
if (phdl != NULL)
delete phdl;
}
}