Example program with HTTPServer and sensor data streaming over TCPSockets, using Donatien Garnier's Net APIs and services code on top of LWIP. Files StreamServer.h and .cpp encapsulate streaming over TCPSockets. Broadcast is done by sendToAll(), and all incoming data is echoed back to the client. Echo code can be replaced with some remote control of the streaming interface. See main() that shows how to periodically send some data to all subscribed clients. To subscribe, a client should open a socket at <mbed_ip> port 123. I used few lines in TCL code to set up a quick sink for the data. HTTP files are served on port 80 concurrently to the streaming.

Dependencies:   mbed

Committer:
iva2k
Date:
Mon Jun 14 03:24:33 2010 +0000
Revision:
1:3ee499525aa5
Parent:
0:e614f7875b60

        

Who changed what in which revision?

UserRevisionLine numberNew contents of line
iva2k 0:e614f7875b60 1
iva2k 0:e614f7875b60 2 /*
iva2k 0:e614f7875b60 3 Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
iva2k 0:e614f7875b60 4
iva2k 0:e614f7875b60 5 Permission is hereby granted, free of charge, to any person obtaining a copy
iva2k 0:e614f7875b60 6 of this software and associated documentation files (the "Software"), to deal
iva2k 0:e614f7875b60 7 in the Software without restriction, including without limitation the rights
iva2k 0:e614f7875b60 8 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
iva2k 0:e614f7875b60 9 copies of the Software, and to permit persons to whom the Software is
iva2k 0:e614f7875b60 10 furnished to do so, subject to the following conditions:
iva2k 0:e614f7875b60 11
iva2k 0:e614f7875b60 12 The above copyright notice and this permission notice shall be included in
iva2k 0:e614f7875b60 13 all copies or substantial portions of the Software.
iva2k 0:e614f7875b60 14
iva2k 0:e614f7875b60 15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
iva2k 0:e614f7875b60 16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
iva2k 0:e614f7875b60 17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
iva2k 0:e614f7875b60 18 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
iva2k 0:e614f7875b60 19 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
iva2k 0:e614f7875b60 20 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
iva2k 0:e614f7875b60 21 THE SOFTWARE.
iva2k 0:e614f7875b60 22 */
iva2k 0:e614f7875b60 23
iva2k 0:e614f7875b60 24 #include "HTTPRequestDispatcher.h"
iva2k 0:e614f7875b60 25 #include "HTTPRequestHandler.h"
iva2k 0:e614f7875b60 26 #include <string.h>
iva2k 0:e614f7875b60 27
iva2k 0:e614f7875b60 28 //#define __DEBUG
iva2k 0:e614f7875b60 29 #include "dbg/dbg.h"
iva2k 0:e614f7875b60 30
iva2k 0:e614f7875b60 31 HTTPRequestDispatcher::HTTPRequestDispatcher(HTTPServer* pSvr, TCPSocket* pTCPSocket) : NetService(), m_pSvr(pSvr), m_pTCPSocket(pTCPSocket), m_watchdog(), m_closed(false)
iva2k 0:e614f7875b60 32 {
iva2k 0:e614f7875b60 33 m_pTCPSocket->setOnEvent(this, &HTTPRequestDispatcher::onTCPSocketEvent);
iva2k 0:e614f7875b60 34 m_watchdog.attach_us<HTTPRequestDispatcher>(this, &HTTPRequestDispatcher::onTimeout, HTTP_REQUEST_TIMEOUT * 1000);
iva2k 0:e614f7875b60 35 }
iva2k 0:e614f7875b60 36
iva2k 0:e614f7875b60 37 HTTPRequestDispatcher::~HTTPRequestDispatcher()
iva2k 0:e614f7875b60 38 {
iva2k 0:e614f7875b60 39 close();
iva2k 0:e614f7875b60 40 }
iva2k 0:e614f7875b60 41
iva2k 0:e614f7875b60 42 void HTTPRequestDispatcher::dispatchRequest()
iva2k 0:e614f7875b60 43 {
iva2k 0:e614f7875b60 44 string rootPath;
iva2k 0:e614f7875b60 45 string fullPath;
iva2k 0:e614f7875b60 46 string meth;
iva2k 0:e614f7875b60 47 HTTP_METH methCode;
iva2k 0:e614f7875b60 48
iva2k 1:3ee499525aa5 49 DBG("Dispatching req\r\n");
iva2k 0:e614f7875b60 50
iva2k 0:e614f7875b60 51 if( !getRequest(&rootPath, &fullPath, &meth ) )
iva2k 0:e614f7875b60 52 {
iva2k 0:e614f7875b60 53 close();
iva2k 0:e614f7875b60 54 return; //Invalid request
iva2k 0:e614f7875b60 55 }
iva2k 0:e614f7875b60 56
iva2k 0:e614f7875b60 57 if( !meth.compare("GET") )
iva2k 0:e614f7875b60 58 {
iva2k 0:e614f7875b60 59 methCode = HTTP_GET;
iva2k 0:e614f7875b60 60 }
iva2k 0:e614f7875b60 61 else if( !meth.compare("POST") )
iva2k 0:e614f7875b60 62 {
iva2k 0:e614f7875b60 63 methCode = HTTP_POST;
iva2k 0:e614f7875b60 64 }
iva2k 0:e614f7875b60 65 else if( !meth.compare("HEAD") )
iva2k 0:e614f7875b60 66 {
iva2k 0:e614f7875b60 67 methCode = HTTP_HEAD;
iva2k 0:e614f7875b60 68 }
iva2k 0:e614f7875b60 69 else
iva2k 0:e614f7875b60 70 {
iva2k 0:e614f7875b60 71 close(); //Parse error
iva2k 0:e614f7875b60 72 return;
iva2k 0:e614f7875b60 73 }
iva2k 0:e614f7875b60 74
iva2k 1:3ee499525aa5 75 DBG("Looking for a handler\r\n");
iva2k 0:e614f7875b60 76
iva2k 0:e614f7875b60 77 map< string, HTTPRequestHandler*(*)(const char*, const char*, TCPSocket*) >::iterator it;
iva2k 0:e614f7875b60 78 it = m_pSvr->m_lpHandlers.find(rootPath); //We are friends so we can do that
iva2k 0:e614f7875b60 79 if(it == m_pSvr->m_lpHandlers.end())
iva2k 0:e614f7875b60 80 {
iva2k 0:e614f7875b60 81 it = m_pSvr->m_lpHandlers.find(""); //Use default handler if it exists
iva2k 0:e614f7875b60 82 }
iva2k 0:e614f7875b60 83 if(it == m_pSvr->m_lpHandlers.end())
iva2k 0:e614f7875b60 84 {
iva2k 0:e614f7875b60 85 close(); //No handler found
iva2k 0:e614f7875b60 86 return;
iva2k 0:e614f7875b60 87 }
iva2k 0:e614f7875b60 88
iva2k 1:3ee499525aa5 89 DBG("Handler found.\r\n");
iva2k 0:e614f7875b60 90
iva2k 0:e614f7875b60 91 HTTPRequestHandler* pHdlr = (*it).second(rootPath.c_str(), fullPath.c_str(), m_pTCPSocket);
iva2k 0:e614f7875b60 92 m_pTCPSocket = NULL; //We don't own it anymore
iva2k 0:e614f7875b60 93
iva2k 0:e614f7875b60 94 switch(methCode)
iva2k 0:e614f7875b60 95 {
iva2k 0:e614f7875b60 96 case HTTP_GET:
iva2k 0:e614f7875b60 97 pHdlr->doGet();
iva2k 0:e614f7875b60 98 break;
iva2k 0:e614f7875b60 99 case HTTP_POST:
iva2k 0:e614f7875b60 100 pHdlr->doPost();
iva2k 0:e614f7875b60 101 break;
iva2k 0:e614f7875b60 102 case HTTP_HEAD:
iva2k 0:e614f7875b60 103 pHdlr->doHead();
iva2k 0:e614f7875b60 104 break;
iva2k 0:e614f7875b60 105 }
iva2k 0:e614f7875b60 106
iva2k 1:3ee499525aa5 107 DBG("Req handled (or being handled)\r\n");
iva2k 0:e614f7875b60 108 close();
iva2k 0:e614f7875b60 109 }
iva2k 0:e614f7875b60 110
iva2k 0:e614f7875b60 111 void HTTPRequestDispatcher::close() //Close socket and destroy data
iva2k 0:e614f7875b60 112 {
iva2k 0:e614f7875b60 113 if(m_closed)
iva2k 0:e614f7875b60 114 return;
iva2k 0:e614f7875b60 115 m_closed = true; //Prevent recursive calling or calling on an object being destructed by someone else
iva2k 0:e614f7875b60 116 m_watchdog.detach();
iva2k 0:e614f7875b60 117 if(m_pTCPSocket) //m_pTCPSocket Should only be destroyed if ownership not passed to an handler
iva2k 0:e614f7875b60 118 {
iva2k 0:e614f7875b60 119 m_pTCPSocket->resetOnEvent();
iva2k 0:e614f7875b60 120 m_pTCPSocket->close();
iva2k 0:e614f7875b60 121 delete m_pTCPSocket; //This fn might have been called by this socket (through an event), so DO NOT DESTROY IT HERE
iva2k 0:e614f7875b60 122 }
iva2k 0:e614f7875b60 123 NetService::close();
iva2k 0:e614f7875b60 124 }
iva2k 0:e614f7875b60 125
iva2k 0:e614f7875b60 126
iva2k 0:e614f7875b60 127 void HTTPRequestDispatcher::onTimeout() //Connection has timed out
iva2k 0:e614f7875b60 128 {
iva2k 0:e614f7875b60 129 close();
iva2k 0:e614f7875b60 130 }
iva2k 0:e614f7875b60 131
iva2k 0:e614f7875b60 132 bool HTTPRequestDispatcher::getRequest(string* rootPath, string* fullPath, string* meth)
iva2k 0:e614f7875b60 133 {
iva2k 0:e614f7875b60 134 char req[128];
iva2k 0:e614f7875b60 135 char c_path[128];
iva2k 0:e614f7875b60 136 char c_meth[128];
iva2k 0:e614f7875b60 137 const int maxLen = 128;
iva2k 0:e614f7875b60 138 char* p = req;
iva2k 0:e614f7875b60 139 //Read Line
iva2k 0:e614f7875b60 140 int ret;
iva2k 0:e614f7875b60 141 int len = 0;
iva2k 0:e614f7875b60 142 for(int i = 0; i < maxLen - 1; i++)
iva2k 0:e614f7875b60 143 {
iva2k 0:e614f7875b60 144 ret = m_pTCPSocket->recv(p, 1);
iva2k 0:e614f7875b60 145 if(!ret)
iva2k 0:e614f7875b60 146 {
iva2k 0:e614f7875b60 147 break;
iva2k 0:e614f7875b60 148 }
iva2k 0:e614f7875b60 149 if( (len > 1) && *(p-1)=='\r' && *p=='\n' )
iva2k 0:e614f7875b60 150 {
iva2k 0:e614f7875b60 151 p--;
iva2k 0:e614f7875b60 152 len-=2;
iva2k 0:e614f7875b60 153 break;
iva2k 0:e614f7875b60 154 }
iva2k 0:e614f7875b60 155 else if( *p=='\n' )
iva2k 0:e614f7875b60 156 {
iva2k 0:e614f7875b60 157 len--;
iva2k 0:e614f7875b60 158 break;
iva2k 0:e614f7875b60 159 }
iva2k 0:e614f7875b60 160 p++;
iva2k 0:e614f7875b60 161 len++;
iva2k 0:e614f7875b60 162 }
iva2k 0:e614f7875b60 163 *p = 0;
iva2k 0:e614f7875b60 164
iva2k 1:3ee499525aa5 165 DBG("Parsing request : %s\r\n", req);
iva2k 0:e614f7875b60 166
iva2k 0:e614f7875b60 167 ret = sscanf(req, "%s %s HTTP/%*d.%*d", c_meth, c_path);
iva2k 0:e614f7875b60 168 if(ret !=2)
iva2k 0:e614f7875b60 169 return false;
iva2k 0:e614f7875b60 170
iva2k 0:e614f7875b60 171 *meth = string(c_meth);
iva2k 0:e614f7875b60 172 *fullPath = string(c_path);
iva2k 0:e614f7875b60 173
iva2k 0:e614f7875b60 174 c_path[0]= '/';
iva2k 0:e614f7875b60 175 ret = sscanf(req, "%*s /%[^/ ]%*s HTTP/%*d.%*d", c_path+1);
iva2k 0:e614f7875b60 176 if(ret !=1)
iva2k 0:e614f7875b60 177 {
iva2k 0:e614f7875b60 178 //This is the root path
iva2k 0:e614f7875b60 179 }
iva2k 0:e614f7875b60 180
iva2k 0:e614f7875b60 181 *rootPath = string(c_path);
iva2k 0:e614f7875b60 182
iva2k 1:3ee499525aa5 183 DBG("Parse OK :\r\nRoot Path : %s\r\nFull Path : %s\r\nMethod Path : %s\r\n", rootPath->c_str(), fullPath->c_str(), meth->c_str());
iva2k 0:e614f7875b60 184
iva2k 0:e614f7875b60 185 return true;
iva2k 0:e614f7875b60 186
iva2k 0:e614f7875b60 187 }
iva2k 0:e614f7875b60 188
iva2k 0:e614f7875b60 189
iva2k 0:e614f7875b60 190 void HTTPRequestDispatcher::onTCPSocketEvent(TCPSocketEvent e)
iva2k 0:e614f7875b60 191 {
iva2k 0:e614f7875b60 192
iva2k 1:3ee499525aa5 193 DBG("Event %d\r\n", e);
iva2k 0:e614f7875b60 194
iva2k 0:e614f7875b60 195 if(m_closed)
iva2k 0:e614f7875b60 196 {
iva2k 1:3ee499525aa5 197 DBG("WARN: Discarded\r\n");
iva2k 0:e614f7875b60 198 return;
iva2k 0:e614f7875b60 199 }
iva2k 0:e614f7875b60 200
iva2k 0:e614f7875b60 201 switch(e)
iva2k 0:e614f7875b60 202 {
iva2k 0:e614f7875b60 203 case TCPSOCKET_READABLE:
iva2k 0:e614f7875b60 204 m_watchdog.detach();
iva2k 0:e614f7875b60 205 m_pTCPSocket->resetOnEvent();
iva2k 0:e614f7875b60 206 //Req arrived, dispatch :
iva2k 0:e614f7875b60 207 dispatchRequest();
iva2k 0:e614f7875b60 208 break;
iva2k 0:e614f7875b60 209 case TCPSOCKET_CONTIMEOUT:
iva2k 0:e614f7875b60 210 case TCPSOCKET_CONRST:
iva2k 0:e614f7875b60 211 case TCPSOCKET_CONABRT:
iva2k 0:e614f7875b60 212 case TCPSOCKET_ERROR:
iva2k 0:e614f7875b60 213 case TCPSOCKET_DISCONNECTED:
iva2k 0:e614f7875b60 214 close();
iva2k 0:e614f7875b60 215 break;
iva2k 0:e614f7875b60 216 }
iva2k 0:e614f7875b60 217
iva2k 0:e614f7875b60 218 }