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 "HTTPClient.h"
iva2k 0:e614f7875b60 25 #include "../util/base64.h"
iva2k 0:e614f7875b60 26 #include "../util/url.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 #define HTTP_REQUEST_TIMEOUT 30000//15000
iva2k 0:e614f7875b60 32 #define HTTP_PORT 80
iva2k 0:e614f7875b60 33
iva2k 0:e614f7875b60 34 #define CHUNK_SIZE 256
iva2k 0:e614f7875b60 35
iva2k 0:e614f7875b60 36 HTTPClient::HTTPClient() : NetService(false) /*Not owned by the pool*/, m_meth(HTTP_GET), m_pCbItem(NULL), m_pCbMeth(NULL), m_pCb(NULL),
iva2k 0:e614f7875b60 37 m_watchdog(), m_timeout(0), m_pDnsReq(NULL), m_server(), m_path(),
iva2k 0:e614f7875b60 38 m_closed(true), m_state(HTTP_CLOSED),
iva2k 0:e614f7875b60 39 m_pDataOut(NULL), m_pDataIn(NULL), m_dataChunked(false), m_dataPos(0), m_dataLen(0), m_httpResponseCode(0), m_blockingResult(HTTP_PROCESSING)
iva2k 0:e614f7875b60 40
iva2k 0:e614f7875b60 41 {
iva2k 0:e614f7875b60 42 setTimeout(HTTP_REQUEST_TIMEOUT);
iva2k 0:e614f7875b60 43 m_buf = new char[CHUNK_SIZE];
iva2k 1:3ee499525aa5 44 DBG("New HTTPClient %p\r\n",this);
iva2k 0:e614f7875b60 45 }
iva2k 0:e614f7875b60 46
iva2k 0:e614f7875b60 47 HTTPClient::~HTTPClient()
iva2k 0:e614f7875b60 48 {
iva2k 0:e614f7875b60 49 close();
iva2k 0:e614f7875b60 50 delete[] m_buf;
iva2k 0:e614f7875b60 51 }
iva2k 0:e614f7875b60 52
iva2k 0:e614f7875b60 53 void HTTPClient::basicAuth(const char* user, const char* password) //Basic Authentification
iva2k 0:e614f7875b60 54 {
iva2k 0:e614f7875b60 55 if(user==NULL)
iva2k 0:e614f7875b60 56 {
iva2k 0:e614f7875b60 57 m_reqHeaders.erase("Authorization"); //Remove auth str
iva2k 0:e614f7875b60 58 return;
iva2k 0:e614f7875b60 59 }
iva2k 0:e614f7875b60 60 string auth = "Basic ";
iva2k 0:e614f7875b60 61 string decStr = user;
iva2k 0:e614f7875b60 62 decStr += ":";
iva2k 0:e614f7875b60 63 decStr += password;
iva2k 0:e614f7875b60 64 auth.append( Base64::encode(decStr) );
iva2k 1:3ee499525aa5 65 DBG("Auth str is %s\r\n", auth.c_str());
iva2k 0:e614f7875b60 66 m_reqHeaders["Authorization"] = auth;
iva2k 0:e614f7875b60 67 }
iva2k 0:e614f7875b60 68
iva2k 0:e614f7875b60 69 //High Level setup functions
iva2k 0:e614f7875b60 70 HTTPResult HTTPClient::get(const char* uri, HTTPData* pDataIn) //Blocking
iva2k 0:e614f7875b60 71 {
iva2k 0:e614f7875b60 72 doGet(uri, pDataIn);
iva2k 0:e614f7875b60 73 return blockingProcess();
iva2k 0:e614f7875b60 74 }
iva2k 0:e614f7875b60 75
iva2k 0:e614f7875b60 76 HTTPResult HTTPClient::get(const char* uri, HTTPData* pDataIn, void (*pMethod)(HTTPResult)) //Non blocking
iva2k 0:e614f7875b60 77 {
iva2k 0:e614f7875b60 78 setOnResult(pMethod);
iva2k 0:e614f7875b60 79 doGet(uri, pDataIn);
iva2k 0:e614f7875b60 80 return HTTP_PROCESSING;
iva2k 0:e614f7875b60 81 }
iva2k 0:e614f7875b60 82
iva2k 0:e614f7875b60 83 #ifdef __LINKER_BUG_SOLVED__
iva2k 0:e614f7875b60 84 template<class T>
iva2k 0:e614f7875b60 85 HTTPResult HTTPClient::get(const char* uri, HTTPData* pDataIn, T* pItem, void (T::*pMethod)(HTTPResult)) //Non blocking
iva2k 0:e614f7875b60 86 {
iva2k 0:e614f7875b60 87 setOnResult(pItem, pMethod);
iva2k 0:e614f7875b60 88 doGet(uri, pDataIn);
iva2k 0:e614f7875b60 89 return HTTP_PROCESSING;
iva2k 0:e614f7875b60 90 }
iva2k 0:e614f7875b60 91 #endif
iva2k 0:e614f7875b60 92
iva2k 0:e614f7875b60 93 HTTPResult HTTPClient::post(const char* uri, const HTTPData& dataOut, HTTPData* pDataIn) //Blocking
iva2k 0:e614f7875b60 94 {
iva2k 0:e614f7875b60 95 doPost(uri, dataOut, pDataIn);
iva2k 0:e614f7875b60 96 return blockingProcess();
iva2k 0:e614f7875b60 97 }
iva2k 0:e614f7875b60 98
iva2k 0:e614f7875b60 99 HTTPResult HTTPClient::post(const char* uri, const HTTPData& dataOut, HTTPData* pDataIn, void (*pMethod)(HTTPResult)) //Non blocking
iva2k 0:e614f7875b60 100 {
iva2k 0:e614f7875b60 101 setOnResult(pMethod);
iva2k 0:e614f7875b60 102 doPost(uri, dataOut, pDataIn);
iva2k 0:e614f7875b60 103 return HTTP_PROCESSING;
iva2k 0:e614f7875b60 104 }
iva2k 0:e614f7875b60 105
iva2k 0:e614f7875b60 106 #ifdef __LINKER_BUG_SOLVED__
iva2k 0:e614f7875b60 107 template<class T>
iva2k 0:e614f7875b60 108 HTTPResult HTTPClient::post(const char* uri, const HTTPData& dataOut, HTTPData* pDataIn, T* pItem, void (T::*pMethod)(HTTPResult)) //Non blocking
iva2k 0:e614f7875b60 109 {
iva2k 0:e614f7875b60 110 setOnResult(pItem, pMethod);
iva2k 0:e614f7875b60 111 doPost(uri, dataOut, pDataIn);
iva2k 0:e614f7875b60 112 return HTTP_PROCESSING;
iva2k 0:e614f7875b60 113 }
iva2k 0:e614f7875b60 114 #endif
iva2k 0:e614f7875b60 115
iva2k 0:e614f7875b60 116 void HTTPClient::doGet(const char* uri, HTTPData* pDataIn)
iva2k 0:e614f7875b60 117 {
iva2k 0:e614f7875b60 118 m_meth = HTTP_GET;
iva2k 0:e614f7875b60 119 setup(uri, NULL, pDataIn);
iva2k 0:e614f7875b60 120 }
iva2k 0:e614f7875b60 121
iva2k 0:e614f7875b60 122 void HTTPClient::doPost(const char* uri, const HTTPData& dataOut, HTTPData* pDataIn)
iva2k 0:e614f7875b60 123 {
iva2k 0:e614f7875b60 124 m_meth = HTTP_POST;
iva2k 0:e614f7875b60 125 setup(uri, (HTTPData*) &dataOut, pDataIn);
iva2k 0:e614f7875b60 126 }
iva2k 0:e614f7875b60 127
iva2k 0:e614f7875b60 128 void HTTPClient::setOnResult( void (*pMethod)(HTTPResult) )
iva2k 0:e614f7875b60 129 {
iva2k 0:e614f7875b60 130 m_pCb = pMethod;
iva2k 0:e614f7875b60 131 m_pCbItem = NULL;
iva2k 0:e614f7875b60 132 m_pCbMeth = NULL;
iva2k 0:e614f7875b60 133 }
iva2k 0:e614f7875b60 134
iva2k 0:e614f7875b60 135 #ifdef __LINKER_BUG_SOLVED__
iva2k 0:e614f7875b60 136 template<class T>
iva2k 0:e614f7875b60 137 void HTTPClient::setOnResult( T* pItem, void (T::*pMethod)(NtpResult) )
iva2k 0:e614f7875b60 138 {
iva2k 0:e614f7875b60 139 m_pCb = NULL;
iva2k 0:e614f7875b60 140 m_pCbItem = (CDummy*) pItem;
iva2k 0:e614f7875b60 141 m_pCbMeth = (void (CDummy::*)(NtpResult)) pMethod;
iva2k 0:e614f7875b60 142 }
iva2k 0:e614f7875b60 143 #endif
iva2k 0:e614f7875b60 144
iva2k 0:e614f7875b60 145 void HTTPClient::setTimeout(int ms)
iva2k 0:e614f7875b60 146 {
iva2k 0:e614f7875b60 147 m_timeout = 1000*ms;
iva2k 0:e614f7875b60 148 //resetTimeout();
iva2k 0:e614f7875b60 149 }
iva2k 0:e614f7875b60 150
iva2k 0:e614f7875b60 151 void HTTPClient::poll() //Called by NetServices
iva2k 0:e614f7875b60 152 {
iva2k 0:e614f7875b60 153 if(m_closed)
iva2k 0:e614f7875b60 154 {
iva2k 0:e614f7875b60 155 return;
iva2k 0:e614f7875b60 156 }
iva2k 0:e614f7875b60 157 if(m_watchdog.read_us()>m_timeout)
iva2k 0:e614f7875b60 158 {
iva2k 0:e614f7875b60 159 onTimeout();
iva2k 0:e614f7875b60 160 }
iva2k 0:e614f7875b60 161 else if(m_state == HTTP_READ_DATA_INCOMPLETE)
iva2k 0:e614f7875b60 162 {
iva2k 0:e614f7875b60 163 readData(); //Try to read more data
iva2k 0:e614f7875b60 164 if( m_state == HTTP_DONE )
iva2k 0:e614f7875b60 165 {
iva2k 0:e614f7875b60 166 //All data has been read, close w/ success :)
iva2k 1:3ee499525aa5 167 DBG("Done :)!\r\n");
iva2k 0:e614f7875b60 168 onResult(HTTP_OK);
iva2k 0:e614f7875b60 169 close();
iva2k 0:e614f7875b60 170 }
iva2k 0:e614f7875b60 171 }
iva2k 0:e614f7875b60 172
iva2k 0:e614f7875b60 173 }
iva2k 0:e614f7875b60 174
iva2k 0:e614f7875b60 175 int HTTPClient::getHTTPResponseCode()
iva2k 0:e614f7875b60 176 {
iva2k 0:e614f7875b60 177 return m_httpResponseCode;
iva2k 0:e614f7875b60 178 }
iva2k 0:e614f7875b60 179
iva2k 0:e614f7875b60 180 void HTTPClient::setRequestHeader(const string& header, const string& value)
iva2k 0:e614f7875b60 181 {
iva2k 0:e614f7875b60 182 m_reqHeaders[header] = value;
iva2k 0:e614f7875b60 183 }
iva2k 0:e614f7875b60 184
iva2k 0:e614f7875b60 185 string& HTTPClient::getResponseHeader(const string& header)
iva2k 0:e614f7875b60 186 {
iva2k 0:e614f7875b60 187 return m_respHeaders[header];
iva2k 0:e614f7875b60 188 }
iva2k 0:e614f7875b60 189
iva2k 0:e614f7875b60 190 void HTTPClient::resetRequestHeaders()
iva2k 0:e614f7875b60 191 {
iva2k 0:e614f7875b60 192 m_reqHeaders.clear();
iva2k 0:e614f7875b60 193 }
iva2k 0:e614f7875b60 194
iva2k 0:e614f7875b60 195 void HTTPClient::resetTimeout()
iva2k 0:e614f7875b60 196 {
iva2k 0:e614f7875b60 197 m_watchdog.reset();
iva2k 0:e614f7875b60 198 m_watchdog.start();
iva2k 0:e614f7875b60 199 }
iva2k 0:e614f7875b60 200
iva2k 0:e614f7875b60 201 void HTTPClient::init() //Create and setup socket if needed
iva2k 0:e614f7875b60 202 {
iva2k 0:e614f7875b60 203 close(); //Remove previous elements
iva2k 0:e614f7875b60 204 if(!m_closed) //Already opened
iva2k 0:e614f7875b60 205 return;
iva2k 0:e614f7875b60 206 m_state = HTTP_WRITE_HEADERS;
iva2k 0:e614f7875b60 207 m_pTCPSocket = new TCPSocket;
iva2k 0:e614f7875b60 208 m_pTCPSocket->setOnEvent(this, &HTTPClient::onTCPSocketEvent);
iva2k 0:e614f7875b60 209 m_closed = false;
iva2k 0:e614f7875b60 210 m_httpResponseCode = 0;
iva2k 0:e614f7875b60 211 }
iva2k 0:e614f7875b60 212
iva2k 0:e614f7875b60 213 void HTTPClient::close()
iva2k 0:e614f7875b60 214 {
iva2k 0:e614f7875b60 215 if(m_closed)
iva2k 0:e614f7875b60 216 return;
iva2k 0:e614f7875b60 217 m_state = HTTP_CLOSED;
iva2k 0:e614f7875b60 218 //Now Request headers are kept btw requests unless resetRequestHeaders() is called
iva2k 0:e614f7875b60 219 //m_reqHeaders.clear(); //Clear headers for next requests
iva2k 0:e614f7875b60 220 m_closed = true; //Prevent recursive calling or calling on an object being destructed by someone else
iva2k 0:e614f7875b60 221 m_watchdog.stop(); //Stop timeout
iva2k 0:e614f7875b60 222 m_watchdog.reset();
iva2k 0:e614f7875b60 223 m_pTCPSocket->resetOnEvent();
iva2k 0:e614f7875b60 224 m_pTCPSocket->close();
iva2k 0:e614f7875b60 225 delete m_pTCPSocket;
iva2k 0:e614f7875b60 226 m_pTCPSocket = NULL;
iva2k 0:e614f7875b60 227 if( m_pDnsReq )
iva2k 0:e614f7875b60 228 {
iva2k 0:e614f7875b60 229 m_pDnsReq->close();
iva2k 0:e614f7875b60 230 delete m_pDnsReq;
iva2k 0:e614f7875b60 231 m_pDnsReq = NULL;
iva2k 0:e614f7875b60 232 }
iva2k 0:e614f7875b60 233 }
iva2k 0:e614f7875b60 234
iva2k 0:e614f7875b60 235 void HTTPClient::setup(const char* uri, HTTPData* pDataOut, HTTPData* pDataIn) //Setup request, make DNS Req if necessary
iva2k 0:e614f7875b60 236 {
iva2k 0:e614f7875b60 237 init(); //Initialize client in known state, create socket
iva2k 0:e614f7875b60 238 m_pDataOut = pDataOut;
iva2k 0:e614f7875b60 239 m_pDataIn = pDataIn;
iva2k 0:e614f7875b60 240 resetTimeout();
iva2k 0:e614f7875b60 241
iva2k 0:e614f7875b60 242 //Erase previous headers
iva2k 0:e614f7875b60 243 //Do NOT clear m_reqHeaders as they might have already set before connecting
iva2k 0:e614f7875b60 244 m_respHeaders.clear();
iva2k 0:e614f7875b60 245
iva2k 0:e614f7875b60 246 //Erase response buffer
iva2k 0:e614f7875b60 247 if(m_pDataIn)
iva2k 0:e614f7875b60 248 m_pDataIn->clear();
iva2k 0:e614f7875b60 249
iva2k 0:e614f7875b60 250 //Assert that buffers are initialized properly
iva2k 0:e614f7875b60 251 m_dataLen = 0;
iva2k 0:e614f7875b60 252 m_bufRemainingLen = 0;
iva2k 0:e614f7875b60 253
iva2k 0:e614f7875b60 254 Url url;
iva2k 0:e614f7875b60 255 url.fromString(uri);
iva2k 0:e614f7875b60 256
iva2k 0:e614f7875b60 257 m_path = url.getPath();
iva2k 0:e614f7875b60 258
iva2k 0:e614f7875b60 259 m_server.setName(url.getHost().c_str());
iva2k 0:e614f7875b60 260
iva2k 0:e614f7875b60 261 if( url.getPort() > 0 )
iva2k 0:e614f7875b60 262 {
iva2k 0:e614f7875b60 263 m_server.setPort( url.getPort() );
iva2k 0:e614f7875b60 264 }
iva2k 0:e614f7875b60 265 else
iva2k 0:e614f7875b60 266 {
iva2k 0:e614f7875b60 267 m_server.setPort( HTTP_PORT );
iva2k 0:e614f7875b60 268 }
iva2k 0:e614f7875b60 269
iva2k 1:3ee499525aa5 270 DBG("URL parsed,\r\nHost: %s\r\nPort: %d\r\nPath: %s\r\n", url.getHost().c_str(), url.getPort(), url.getPath().c_str());
iva2k 0:e614f7875b60 271
iva2k 0:e614f7875b60 272 IpAddr ip;
iva2k 0:e614f7875b60 273 if( url.getHostIp(&ip) )
iva2k 0:e614f7875b60 274 {
iva2k 0:e614f7875b60 275 m_server.setIp(ip);
iva2k 0:e614f7875b60 276 connect();
iva2k 0:e614f7875b60 277 }
iva2k 0:e614f7875b60 278 else
iva2k 0:e614f7875b60 279 {
iva2k 1:3ee499525aa5 280 DBG("DNS Query...\r\n");
iva2k 0:e614f7875b60 281 m_pDnsReq = new DNSRequest();
iva2k 0:e614f7875b60 282 m_pDnsReq->setOnReply(this, &HTTPClient::onDNSReply);
iva2k 0:e614f7875b60 283 m_pDnsReq->resolve(&m_server);
iva2k 1:3ee499525aa5 284 DBG("HTTPClient : DNSRequest %p\r\n", m_pDnsReq);
iva2k 0:e614f7875b60 285 }
iva2k 0:e614f7875b60 286
iva2k 0:e614f7875b60 287 }
iva2k 0:e614f7875b60 288
iva2k 0:e614f7875b60 289 void HTTPClient::connect() //Start Connection
iva2k 0:e614f7875b60 290 {
iva2k 0:e614f7875b60 291 resetTimeout();
iva2k 1:3ee499525aa5 292 DBG("Connecting...\r\n");
iva2k 0:e614f7875b60 293 m_pTCPSocket->connect(m_server);
iva2k 0:e614f7875b60 294 }
iva2k 0:e614f7875b60 295
iva2k 0:e614f7875b60 296 #define MIN(a,b) ((a)<(b)?(a):(b))
iva2k 0:e614f7875b60 297 #define ABS(a) (((a)>0)?(a):0)
iva2k 0:e614f7875b60 298 int HTTPClient::tryRead() //Try to read data from tcp packet and put in the HTTPData object
iva2k 0:e614f7875b60 299 {
iva2k 0:e614f7875b60 300 int len = 0;
iva2k 0:e614f7875b60 301 int readLen;
iva2k 0:e614f7875b60 302 do
iva2k 0:e614f7875b60 303 {
iva2k 0:e614f7875b60 304 if(m_state == HTTP_READ_DATA_INCOMPLETE) //First try to complete buffer copy
iva2k 0:e614f7875b60 305 {
iva2k 0:e614f7875b60 306 readLen = m_bufRemainingLen;
iva2k 0:e614f7875b60 307 if (readLen == 0)
iva2k 0:e614f7875b60 308 {
iva2k 0:e614f7875b60 309 m_state = HTTP_READ_DATA;
iva2k 0:e614f7875b60 310 continue;
iva2k 0:e614f7875b60 311 }
iva2k 0:e614f7875b60 312 }
iva2k 0:e614f7875b60 313 else
iva2k 0:e614f7875b60 314 {
iva2k 0:e614f7875b60 315 readLen = m_pTCPSocket->recv(m_buf, MIN(ABS(m_dataLen-m_dataPos),CHUNK_SIZE));
iva2k 0:e614f7875b60 316 if(readLen < 0) //Error
iva2k 0:e614f7875b60 317 {
iva2k 0:e614f7875b60 318 return readLen;
iva2k 0:e614f7875b60 319 }
iva2k 0:e614f7875b60 320 m_pBufRemaining = m_buf;
iva2k 0:e614f7875b60 321 }
iva2k 0:e614f7875b60 322 /* if (readLen == 0)
iva2k 0:e614f7875b60 323 {
iva2k 0:e614f7875b60 324 m_state = HTTP_READ_DATA;
iva2k 0:e614f7875b60 325 return len;
iva2k 0:e614f7875b60 326 }*/
iva2k 0:e614f7875b60 327
iva2k 0:e614f7875b60 328 int writtenLen = m_pDataIn->write(m_pBufRemaining, readLen);
iva2k 0:e614f7875b60 329 m_dataPos += writtenLen;
iva2k 0:e614f7875b60 330
iva2k 0:e614f7875b60 331 if(writtenLen<readLen) //Data was not completely written
iva2k 0:e614f7875b60 332 {
iva2k 0:e614f7875b60 333 m_pBufRemaining += writtenLen;
iva2k 0:e614f7875b60 334 m_bufRemainingLen = readLen - writtenLen;
iva2k 0:e614f7875b60 335 m_state = HTTP_READ_DATA_INCOMPLETE;
iva2k 0:e614f7875b60 336 return len + writtenLen;
iva2k 0:e614f7875b60 337 }
iva2k 0:e614f7875b60 338 else
iva2k 0:e614f7875b60 339 {
iva2k 0:e614f7875b60 340 m_state = HTTP_READ_DATA;
iva2k 0:e614f7875b60 341 }
iva2k 0:e614f7875b60 342 len += readLen;
iva2k 0:e614f7875b60 343 } while(readLen>0);
iva2k 0:e614f7875b60 344
iva2k 0:e614f7875b60 345 return len;
iva2k 0:e614f7875b60 346 }
iva2k 0:e614f7875b60 347
iva2k 0:e614f7875b60 348 void HTTPClient::readData() //Data has been read
iva2k 0:e614f7875b60 349 {
iva2k 0:e614f7875b60 350 if(m_pDataIn == NULL) //Nothing to read (in HEAD for instance, not supported now)
iva2k 0:e614f7875b60 351 {
iva2k 0:e614f7875b60 352 m_state = HTTP_DONE;
iva2k 0:e614f7875b60 353 return;
iva2k 0:e614f7875b60 354 }
iva2k 1:3ee499525aa5 355 DBG("Reading response...\r\n");
iva2k 0:e614f7875b60 356 int len = 0;
iva2k 0:e614f7875b60 357 do
iva2k 0:e614f7875b60 358 {
iva2k 0:e614f7875b60 359 if(m_dataChunked && (m_state != HTTP_READ_DATA_INCOMPLETE))
iva2k 0:e614f7875b60 360 {
iva2k 0:e614f7875b60 361 if(m_dataLen==0)
iva2k 0:e614f7875b60 362 {
iva2k 1:3ee499525aa5 363 DBG("Reading chunk length...\r\n");
iva2k 0:e614f7875b60 364 //New block
iva2k 0:e614f7875b60 365 static char chunkHeader[16];
iva2k 0:e614f7875b60 366 //We use m_dataPos to retain the read position in chunkHeader, it has been set to 0 before the first call of readData()
iva2k 0:e614f7875b60 367 m_dataPos += readLine(chunkHeader + m_dataPos, ABS(16 - m_dataPos));
iva2k 0:e614f7875b60 368 if( m_dataPos > 0 )
iva2k 0:e614f7875b60 369 {
iva2k 0:e614f7875b60 370 if( chunkHeader[strlen(chunkHeader)-1] == 0x0d )
iva2k 0:e614f7875b60 371 {
iva2k 0:e614f7875b60 372 sscanf(chunkHeader, "%x%*[^\r\n]", &m_dataLen);
iva2k 1:3ee499525aa5 373 DBG("Chunk length is %d\r\n", m_dataLen);
iva2k 0:e614f7875b60 374 m_dataPos = 0;
iva2k 0:e614f7875b60 375 }
iva2k 0:e614f7875b60 376 else
iva2k 0:e614f7875b60 377 {
iva2k 0:e614f7875b60 378 //Wait for end of line
iva2k 1:3ee499525aa5 379 DBG("Wait for CRLF\r\n");
iva2k 0:e614f7875b60 380 return;
iva2k 0:e614f7875b60 381 }
iva2k 0:e614f7875b60 382 }
iva2k 0:e614f7875b60 383 else
iva2k 0:e614f7875b60 384 {
iva2k 1:3ee499525aa5 385 DBG("Wait for data\r\n");
iva2k 0:e614f7875b60 386 //Wait for data
iva2k 0:e614f7875b60 387 return;
iva2k 0:e614f7875b60 388 }
iva2k 0:e614f7875b60 389 }
iva2k 0:e614f7875b60 390 }
iva2k 0:e614f7875b60 391
iva2k 0:e614f7875b60 392 //Proper data recovery
iva2k 0:e614f7875b60 393 len = tryRead();
iva2k 0:e614f7875b60 394 if(len<0) //Error
iva2k 0:e614f7875b60 395 {
iva2k 0:e614f7875b60 396 onResult(HTTP_CONN);
iva2k 0:e614f7875b60 397 return;
iva2k 0:e614f7875b60 398 }
iva2k 0:e614f7875b60 399
iva2k 0:e614f7875b60 400 if(m_state == HTTP_READ_DATA_INCOMPLETE)
iva2k 0:e614f7875b60 401 return;
iva2k 0:e614f7875b60 402
iva2k 0:e614f7875b60 403 //Chunk Tail
iva2k 0:e614f7875b60 404 if(m_dataChunked)
iva2k 0:e614f7875b60 405 {
iva2k 0:e614f7875b60 406 if(m_dataPos >= m_dataLen)
iva2k 0:e614f7875b60 407 {
iva2k 1:3ee499525aa5 408 DBG("Chunk read, wait for CRLF\r\n");
iva2k 0:e614f7875b60 409 char chunkTail[3];
iva2k 0:e614f7875b60 410 m_dataPos += readLine(chunkTail, 3);
iva2k 0:e614f7875b60 411 }
iva2k 0:e614f7875b60 412
iva2k 0:e614f7875b60 413 if(m_dataPos >= m_dataLen + 1) //1 == strlen("\n"),
iva2k 0:e614f7875b60 414 {
iva2k 1:3ee499525aa5 415 DBG("End of chunk\r\n");
iva2k 0:e614f7875b60 416 if(m_dataLen==0)
iva2k 0:e614f7875b60 417 {
iva2k 1:3ee499525aa5 418 DBG("End of file\r\n");
iva2k 0:e614f7875b60 419 //End of file
iva2k 0:e614f7875b60 420 m_state = HTTP_DONE; //Done
iva2k 0:e614f7875b60 421 }
iva2k 0:e614f7875b60 422 m_dataLen = 0;
iva2k 0:e614f7875b60 423 m_dataPos = 0;
iva2k 0:e614f7875b60 424 }
iva2k 0:e614f7875b60 425 }
iva2k 0:e614f7875b60 426
iva2k 0:e614f7875b60 427 } while(len>0);
iva2k 0:e614f7875b60 428
iva2k 0:e614f7875b60 429
iva2k 0:e614f7875b60 430 if(!m_dataChunked && (m_dataPos >= m_dataLen)) //All Data has been received
iva2k 0:e614f7875b60 431 {
iva2k 1:3ee499525aa5 432 DBG("End of file\r\n");
iva2k 0:e614f7875b60 433 m_state = HTTP_DONE; //Done
iva2k 0:e614f7875b60 434 }
iva2k 0:e614f7875b60 435 }
iva2k 0:e614f7875b60 436
iva2k 0:e614f7875b60 437 void HTTPClient::writeData() //Data has been written & buf is free
iva2k 0:e614f7875b60 438 {
iva2k 0:e614f7875b60 439 if(m_pDataOut == NULL) //Nothing to write (in POST for instance)
iva2k 0:e614f7875b60 440 {
iva2k 0:e614f7875b60 441 m_dataLen = 0; //Reset Data Length
iva2k 0:e614f7875b60 442 m_state = HTTP_READ_HEADERS;
iva2k 0:e614f7875b60 443 return;
iva2k 0:e614f7875b60 444 }
iva2k 0:e614f7875b60 445 int len = m_pDataOut->read(m_buf, CHUNK_SIZE);
iva2k 0:e614f7875b60 446 if( m_dataChunked )
iva2k 0:e614f7875b60 447 {
iva2k 0:e614f7875b60 448 //Write chunk header
iva2k 0:e614f7875b60 449 char chunkHeader[16];
iva2k 0:e614f7875b60 450 sprintf(chunkHeader, "%d\r\n", len);
iva2k 0:e614f7875b60 451 int ret = m_pTCPSocket->send(chunkHeader, strlen(chunkHeader));
iva2k 0:e614f7875b60 452 if(ret < 0)//Error
iva2k 0:e614f7875b60 453 {
iva2k 0:e614f7875b60 454 onResult(HTTP_CONN);
iva2k 0:e614f7875b60 455 return;
iva2k 0:e614f7875b60 456 }
iva2k 0:e614f7875b60 457 }
iva2k 0:e614f7875b60 458 m_pTCPSocket->send(m_buf, len);
iva2k 0:e614f7875b60 459 m_dataPos+=len;
iva2k 0:e614f7875b60 460 if( m_dataChunked )
iva2k 0:e614f7875b60 461 {
iva2k 0:e614f7875b60 462 m_pTCPSocket->send("\r\n", 2); //Chunk terminating CRLF
iva2k 0:e614f7875b60 463 }
iva2k 0:e614f7875b60 464 if( ( !m_dataChunked && (m_dataPos >= m_dataLen) )
iva2k 0:e614f7875b60 465 || ( m_dataChunked && !len ) ) //All Data has been sent
iva2k 0:e614f7875b60 466 {
iva2k 0:e614f7875b60 467 m_dataLen = 0; //Reset Data Length
iva2k 0:e614f7875b60 468 m_state = HTTP_READ_HEADERS; //Wait for resp
iva2k 0:e614f7875b60 469 }
iva2k 0:e614f7875b60 470 }
iva2k 0:e614f7875b60 471
iva2k 0:e614f7875b60 472 void HTTPClient::onTCPSocketEvent(TCPSocketEvent e)
iva2k 0:e614f7875b60 473 {
iva2k 1:3ee499525aa5 474 DBG("Event %d in HTTPClient::onTCPSocketEvent()\r\n", e);
iva2k 0:e614f7875b60 475
iva2k 0:e614f7875b60 476 if(m_closed)
iva2k 0:e614f7875b60 477 {
iva2k 1:3ee499525aa5 478 DBG("WARN: Discarded\r\n");
iva2k 0:e614f7875b60 479 return;
iva2k 0:e614f7875b60 480 }
iva2k 0:e614f7875b60 481
iva2k 0:e614f7875b60 482 switch(e)
iva2k 0:e614f7875b60 483 {
iva2k 0:e614f7875b60 484 case TCPSOCKET_READABLE: //Incoming data
iva2k 0:e614f7875b60 485 resetTimeout();
iva2k 0:e614f7875b60 486 switch(m_state)
iva2k 0:e614f7875b60 487 {
iva2k 0:e614f7875b60 488 case HTTP_READ_HEADERS:
iva2k 0:e614f7875b60 489 if( !readHeaders() )
iva2k 0:e614f7875b60 490 {
iva2k 0:e614f7875b60 491 return; //Connection has been closed or incomplete data
iva2k 0:e614f7875b60 492 }
iva2k 0:e614f7875b60 493 if( m_pDataIn )
iva2k 0:e614f7875b60 494 {
iva2k 0:e614f7875b60 495 //Data chunked?
iva2k 0:e614f7875b60 496 if(m_respHeaders["Transfer-Encoding"].find("chunked")!=string::npos)
iva2k 0:e614f7875b60 497 {
iva2k 0:e614f7875b60 498 m_dataChunked = true;
iva2k 0:e614f7875b60 499 m_dataPos = 0;
iva2k 0:e614f7875b60 500 m_dataLen = 0;
iva2k 1:3ee499525aa5 501 DBG("Encoding is chunked, Content-Type is %s\r\n", m_respHeaders["Content-Type"].c_str() );
iva2k 0:e614f7875b60 502 }
iva2k 0:e614f7875b60 503 else
iva2k 0:e614f7875b60 504 {
iva2k 0:e614f7875b60 505 m_dataChunked = false;
iva2k 0:e614f7875b60 506 int len = 0;
iva2k 1:3ee499525aa5 507 //DBG("Preparing read... len = %s\r\n", m_respHeaders["Content-Length"].c_str());
iva2k 0:e614f7875b60 508 sscanf(m_respHeaders["Content-Length"].c_str(), "%d", &len);
iva2k 0:e614f7875b60 509 m_pDataIn->setDataLen( len );
iva2k 0:e614f7875b60 510 m_dataPos = 0;
iva2k 0:e614f7875b60 511 m_dataLen = len;
iva2k 1:3ee499525aa5 512 DBG("Content-Length is %d, Content-Type is %s\r\n", len, m_respHeaders["Content-Type"].c_str() );
iva2k 0:e614f7875b60 513 }
iva2k 0:e614f7875b60 514 m_pDataIn->setDataType( m_respHeaders["Content-Type"] );
iva2k 0:e614f7875b60 515 }
iva2k 0:e614f7875b60 516 case HTTP_READ_DATA:
iva2k 0:e614f7875b60 517 readData();
iva2k 0:e614f7875b60 518 break;
iva2k 0:e614f7875b60 519 case HTTP_READ_DATA_INCOMPLETE:
iva2k 0:e614f7875b60 520 break; //We need to handle previously received data first
iva2k 0:e614f7875b60 521 default:
iva2k 0:e614f7875b60 522 //Should not receive data now, req is not complete
iva2k 0:e614f7875b60 523 onResult(HTTP_PRTCL);
iva2k 0:e614f7875b60 524 }
iva2k 0:e614f7875b60 525 //All data has been read, close w/ success :)
iva2k 0:e614f7875b60 526 if( m_state == HTTP_DONE )
iva2k 0:e614f7875b60 527 {
iva2k 1:3ee499525aa5 528 DBG("Done :)!\r\n");
iva2k 0:e614f7875b60 529 onResult(HTTP_OK);
iva2k 0:e614f7875b60 530 }
iva2k 0:e614f7875b60 531 break;
iva2k 0:e614f7875b60 532 case TCPSOCKET_CONNECTED:
iva2k 0:e614f7875b60 533 case TCPSOCKET_WRITEABLE: //We can send data
iva2k 0:e614f7875b60 534 resetTimeout();
iva2k 0:e614f7875b60 535 switch(m_state)
iva2k 0:e614f7875b60 536 {
iva2k 0:e614f7875b60 537 case HTTP_WRITE_HEADERS:
iva2k 0:e614f7875b60 538 //Update headers fields according to m_pDataOut
iva2k 0:e614f7875b60 539 if( m_pDataOut )
iva2k 0:e614f7875b60 540 {
iva2k 0:e614f7875b60 541 //Data is chunked?
iva2k 0:e614f7875b60 542 if(m_pDataOut->getIsChunked())
iva2k 0:e614f7875b60 543 {
iva2k 0:e614f7875b60 544 m_dataChunked = true;
iva2k 0:e614f7875b60 545 m_reqHeaders.erase("Content-Length");
iva2k 0:e614f7875b60 546 m_reqHeaders["Transfer-Encoding"] = "chunked";
iva2k 0:e614f7875b60 547 }
iva2k 0:e614f7875b60 548 else
iva2k 0:e614f7875b60 549 {
iva2k 0:e614f7875b60 550 m_dataChunked = false;
iva2k 0:e614f7875b60 551 char c_len[16] = "0";
iva2k 0:e614f7875b60 552 int len = m_pDataOut->getDataLen();
iva2k 0:e614f7875b60 553 sprintf(c_len, "%d", len);
iva2k 0:e614f7875b60 554 m_dataPos = 0;
iva2k 0:e614f7875b60 555 m_dataLen = len;
iva2k 0:e614f7875b60 556 m_reqHeaders.erase("Transfer-Encoding");
iva2k 0:e614f7875b60 557 m_reqHeaders["Content-Length"] = string(c_len);
iva2k 0:e614f7875b60 558 }
iva2k 0:e614f7875b60 559 string type = m_pDataOut->getDataType();
iva2k 0:e614f7875b60 560 if(!type.empty())
iva2k 0:e614f7875b60 561 {
iva2k 0:e614f7875b60 562 m_reqHeaders["Content-Type"] = type;
iva2k 0:e614f7875b60 563 }
iva2k 0:e614f7875b60 564 else
iva2k 0:e614f7875b60 565 {
iva2k 0:e614f7875b60 566 m_reqHeaders.erase("Content-Type");
iva2k 0:e614f7875b60 567 }
iva2k 0:e614f7875b60 568 }
iva2k 0:e614f7875b60 569 if( !writeHeaders() )
iva2k 0:e614f7875b60 570 {
iva2k 0:e614f7875b60 571 return; //Connection has been closed
iva2k 0:e614f7875b60 572 }
iva2k 0:e614f7875b60 573 break; //Wait for writeable event before sending payload
iva2k 0:e614f7875b60 574 case HTTP_WRITE_DATA:
iva2k 0:e614f7875b60 575 writeData();
iva2k 0:e614f7875b60 576 break;
iva2k 0:e614f7875b60 577 }
iva2k 0:e614f7875b60 578 //Otherwise request has been sent, now wait for resp
iva2k 0:e614f7875b60 579 break;
iva2k 0:e614f7875b60 580 case TCPSOCKET_CONTIMEOUT:
iva2k 0:e614f7875b60 581 case TCPSOCKET_CONRST:
iva2k 0:e614f7875b60 582 case TCPSOCKET_CONABRT:
iva2k 0:e614f7875b60 583 case TCPSOCKET_ERROR:
iva2k 1:3ee499525aa5 584 DBG("Connection error.\r\n");
iva2k 0:e614f7875b60 585 onResult(HTTP_CONN);
iva2k 0:e614f7875b60 586 case TCPSOCKET_DISCONNECTED:
iva2k 0:e614f7875b60 587 //There might still be some data available for reading
iva2k 0:e614f7875b60 588 //So if we are in a reading state, do not close the socket yet
iva2k 0:e614f7875b60 589 if( (m_state != HTTP_READ_DATA_INCOMPLETE) && (m_state != HTTP_DONE) && (m_state != HTTP_CLOSED) )
iva2k 0:e614f7875b60 590 {
iva2k 0:e614f7875b60 591 onResult(HTTP_CONN);
iva2k 0:e614f7875b60 592 }
iva2k 1:3ee499525aa5 593 DBG("Connection closed by remote host.\r\n");
iva2k 0:e614f7875b60 594 break;
iva2k 0:e614f7875b60 595 }
iva2k 0:e614f7875b60 596 }
iva2k 0:e614f7875b60 597
iva2k 0:e614f7875b60 598 void HTTPClient::onDNSReply(DNSReply r)
iva2k 0:e614f7875b60 599 {
iva2k 0:e614f7875b60 600 if(m_closed)
iva2k 0:e614f7875b60 601 {
iva2k 1:3ee499525aa5 602 DBG("WARN: Discarded\r\n");
iva2k 0:e614f7875b60 603 return;
iva2k 0:e614f7875b60 604 }
iva2k 0:e614f7875b60 605
iva2k 0:e614f7875b60 606 if( r != DNS_FOUND )
iva2k 0:e614f7875b60 607 {
iva2k 1:3ee499525aa5 608 DBG("Could not resolve hostname.\r\n");
iva2k 0:e614f7875b60 609 onResult(HTTP_DNS);
iva2k 0:e614f7875b60 610 return;
iva2k 0:e614f7875b60 611 }
iva2k 0:e614f7875b60 612
iva2k 1:3ee499525aa5 613 DBG("DNS Resolved to %d.%d.%d.%d.\r\n",m_server.getIp()[0],m_server.getIp()[1],m_server.getIp()[2],m_server.getIp()[3]);
iva2k 0:e614f7875b60 614 //If no error, m_server has been updated by m_pDnsReq so we're set to go !
iva2k 0:e614f7875b60 615 m_pDnsReq->close();
iva2k 0:e614f7875b60 616 delete m_pDnsReq;
iva2k 0:e614f7875b60 617 m_pDnsReq = NULL;
iva2k 0:e614f7875b60 618 connect();
iva2k 0:e614f7875b60 619 }
iva2k 0:e614f7875b60 620
iva2k 0:e614f7875b60 621 void HTTPClient::onResult(HTTPResult r) //Called when exchange completed or on failure
iva2k 0:e614f7875b60 622 {
iva2k 0:e614f7875b60 623 if(m_pCbItem && m_pCbMeth)
iva2k 0:e614f7875b60 624 (m_pCbItem->*m_pCbMeth)(r);
iva2k 0:e614f7875b60 625 else if(m_pCb)
iva2k 0:e614f7875b60 626 m_pCb(r);
iva2k 0:e614f7875b60 627 m_blockingResult = r; //Blocking mode
iva2k 0:e614f7875b60 628 close(); //FIXME:Remove suppl. close() calls
iva2k 0:e614f7875b60 629 }
iva2k 0:e614f7875b60 630
iva2k 0:e614f7875b60 631 void HTTPClient::onTimeout() //Connection has timed out
iva2k 0:e614f7875b60 632 {
iva2k 1:3ee499525aa5 633 DBG("Timed out.\n");
iva2k 0:e614f7875b60 634 onResult(HTTP_TIMEOUT);
iva2k 0:e614f7875b60 635 close();
iva2k 0:e614f7875b60 636 }
iva2k 0:e614f7875b60 637
iva2k 0:e614f7875b60 638 //Headers
iva2k 0:e614f7875b60 639
iva2k 0:e614f7875b60 640 //TODO: Factorize w/ HTTPRequestHandler in a single HTTPHeader class
iva2k 0:e614f7875b60 641
iva2k 0:e614f7875b60 642 HTTPResult HTTPClient::blockingProcess() //Called in blocking mode, calls Net::poll() until return code is available
iva2k 0:e614f7875b60 643 {
iva2k 0:e614f7875b60 644 //Disable callbacks
iva2k 0:e614f7875b60 645 m_pCb = NULL;
iva2k 0:e614f7875b60 646 m_pCbItem = NULL;
iva2k 0:e614f7875b60 647 m_pCbMeth = NULL;
iva2k 0:e614f7875b60 648 m_blockingResult = HTTP_PROCESSING;
iva2k 0:e614f7875b60 649 do
iva2k 0:e614f7875b60 650 {
iva2k 0:e614f7875b60 651 Net::poll();
iva2k 0:e614f7875b60 652 } while(m_blockingResult == HTTP_PROCESSING);
iva2k 0:e614f7875b60 653 Net::poll(); //Necessary for cleanup
iva2k 0:e614f7875b60 654 return m_blockingResult;
iva2k 0:e614f7875b60 655 }
iva2k 0:e614f7875b60 656
iva2k 0:e614f7875b60 657 bool HTTPClient::readHeaders()
iva2k 0:e614f7875b60 658 {
iva2k 0:e614f7875b60 659 static char* line = m_buf;
iva2k 0:e614f7875b60 660 static char key[128];
iva2k 0:e614f7875b60 661 static char value[128];
iva2k 0:e614f7875b60 662 if(!m_dataLen) //No incomplete header in buffer, this is the first time we read data
iva2k 0:e614f7875b60 663 {
iva2k 0:e614f7875b60 664 if( readLine(line, 128) > 0 )
iva2k 0:e614f7875b60 665 {
iva2k 0:e614f7875b60 666 //Check RC
iva2k 0:e614f7875b60 667 m_httpResponseCode = 0;
iva2k 0:e614f7875b60 668 if( sscanf(line, "HTTP/%*d.%*d %d %*[^\r\n]", &m_httpResponseCode) != 1 )
iva2k 0:e614f7875b60 669 {
iva2k 0:e614f7875b60 670 //Cannot match string, error
iva2k 1:3ee499525aa5 671 DBG("Not a correct HTTP answer : %s\r\n", line);
iva2k 0:e614f7875b60 672 onResult(HTTP_PRTCL);
iva2k 0:e614f7875b60 673 close();
iva2k 0:e614f7875b60 674 return false;
iva2k 0:e614f7875b60 675 }
iva2k 0:e614f7875b60 676
iva2k 0:e614f7875b60 677 if(m_httpResponseCode != 200)
iva2k 0:e614f7875b60 678 {
iva2k 1:3ee499525aa5 679 DBG("Response: error code %d\r\n", m_httpResponseCode);
iva2k 0:e614f7875b60 680 HTTPResult res = HTTP_ERROR;
iva2k 0:e614f7875b60 681 switch(m_httpResponseCode)
iva2k 0:e614f7875b60 682 {
iva2k 0:e614f7875b60 683 case 404:
iva2k 0:e614f7875b60 684 res = HTTP_NOTFOUND;
iva2k 0:e614f7875b60 685 break;
iva2k 0:e614f7875b60 686 case 403:
iva2k 0:e614f7875b60 687 res = HTTP_REFUSED;
iva2k 0:e614f7875b60 688 break;
iva2k 0:e614f7875b60 689 default:
iva2k 0:e614f7875b60 690 res = HTTP_ERROR;
iva2k 0:e614f7875b60 691 }
iva2k 0:e614f7875b60 692 onResult(res);
iva2k 0:e614f7875b60 693 close();
iva2k 0:e614f7875b60 694 return false;
iva2k 0:e614f7875b60 695 }
iva2k 1:3ee499525aa5 696 DBG("Response OK\r\n");
iva2k 0:e614f7875b60 697 }
iva2k 0:e614f7875b60 698 else
iva2k 0:e614f7875b60 699 {
iva2k 0:e614f7875b60 700 //Empty packet, weird!
iva2k 1:3ee499525aa5 701 DBG("Empty packet!\r\n");
iva2k 0:e614f7875b60 702 onResult(HTTP_PRTCL);
iva2k 0:e614f7875b60 703 close();
iva2k 0:e614f7875b60 704 return false;
iva2k 0:e614f7875b60 705 }
iva2k 0:e614f7875b60 706 }
iva2k 0:e614f7875b60 707 bool incomplete = false;
iva2k 0:e614f7875b60 708 while( true )
iva2k 0:e614f7875b60 709 {
iva2k 0:e614f7875b60 710 int readLen = readLine(line + m_dataLen, 128 - m_dataLen, &incomplete);
iva2k 0:e614f7875b60 711 m_dataLen = 0;
iva2k 0:e614f7875b60 712 if( readLen <= 2 ) //if == 1 or 2, it is an empty line = end of headers
iva2k 0:e614f7875b60 713 {
iva2k 1:3ee499525aa5 714 DBG("All headers read.\r\n");
iva2k 0:e614f7875b60 715 m_state = HTTP_READ_DATA;
iva2k 0:e614f7875b60 716 break;
iva2k 0:e614f7875b60 717 }
iva2k 0:e614f7875b60 718 else if( incomplete == true )
iva2k 0:e614f7875b60 719 {
iva2k 0:e614f7875b60 720 m_dataLen = readLen;//Sets data length available in buffer
iva2k 0:e614f7875b60 721 return false;
iva2k 0:e614f7875b60 722 }
iva2k 1:3ee499525aa5 723 //DBG("Header : %s\r\n", line);
iva2k 0:e614f7875b60 724 int n = sscanf(line, "%[^:] : %[^\r\n]", key, value);
iva2k 0:e614f7875b60 725 if ( n == 2 )
iva2k 0:e614f7875b60 726 {
iva2k 1:3ee499525aa5 727 DBG("Read header : %s: %s\r\n", key, value);
iva2k 0:e614f7875b60 728 m_respHeaders[key] = value;
iva2k 0:e614f7875b60 729 }
iva2k 0:e614f7875b60 730 //TODO: Impl n==1 case (part 2 of previous header)
iva2k 0:e614f7875b60 731 }
iva2k 0:e614f7875b60 732
iva2k 0:e614f7875b60 733 return true;
iva2k 0:e614f7875b60 734 }
iva2k 0:e614f7875b60 735
iva2k 0:e614f7875b60 736 bool HTTPClient::writeHeaders() //Called at the first writeData call
iva2k 0:e614f7875b60 737 {
iva2k 0:e614f7875b60 738 static char* line = m_buf;
iva2k 0:e614f7875b60 739 const char* HTTP_METH_STR[] = {"GET", "POST", "HEAD"};
iva2k 0:e614f7875b60 740
iva2k 0:e614f7875b60 741 //Req
iva2k 0:e614f7875b60 742 sprintf(line, "%s %s HTTP/1.1\r\nHost: %s\r\n", HTTP_METH_STR[m_meth], m_path.c_str(), m_server.getName()); //Write request
iva2k 0:e614f7875b60 743 m_pTCPSocket->send(line, strlen(line));
iva2k 1:3ee499525aa5 744 DBG("Request: %s\r\n", line);
iva2k 0:e614f7875b60 745
iva2k 1:3ee499525aa5 746 DBG("Writing headers:\r\n");
iva2k 0:e614f7875b60 747 map<string,string>::iterator it;
iva2k 0:e614f7875b60 748 for( it = m_reqHeaders.begin(); it != m_reqHeaders.end(); it++ )
iva2k 0:e614f7875b60 749 {
iva2k 0:e614f7875b60 750 sprintf(line, "%s: %s\r\n", (*it).first.c_str(), (*it).second.c_str() );
iva2k 1:3ee499525aa5 751 DBG("%s", line);
iva2k 0:e614f7875b60 752 m_pTCPSocket->send(line, strlen(line));
iva2k 0:e614f7875b60 753 }
iva2k 0:e614f7875b60 754 m_pTCPSocket->send("\r\n",2); //End of head
iva2k 0:e614f7875b60 755 m_state = HTTP_WRITE_DATA;
iva2k 0:e614f7875b60 756 return true;
iva2k 0:e614f7875b60 757 }
iva2k 0:e614f7875b60 758
iva2k 0:e614f7875b60 759 int HTTPClient::readLine(char* str, int maxLen, bool* pIncomplete /* = NULL*/)
iva2k 0:e614f7875b60 760 {
iva2k 0:e614f7875b60 761 int ret;
iva2k 0:e614f7875b60 762 int len = 0;
iva2k 0:e614f7875b60 763 if(pIncomplete)
iva2k 0:e614f7875b60 764 *pIncomplete = false;
iva2k 0:e614f7875b60 765 for(int i = 0; i < maxLen - 1; i++)
iva2k 0:e614f7875b60 766 {
iva2k 0:e614f7875b60 767 ret = m_pTCPSocket->recv(str, 1);
iva2k 0:e614f7875b60 768 if(ret != 1)
iva2k 0:e614f7875b60 769 {
iva2k 0:e614f7875b60 770 if(pIncomplete)
iva2k 0:e614f7875b60 771 *pIncomplete = true;
iva2k 0:e614f7875b60 772 break;
iva2k 0:e614f7875b60 773 }
iva2k 0:e614f7875b60 774 if( (len > 1) && *(str-1)=='\r' && *str=='\n' )
iva2k 0:e614f7875b60 775 {
iva2k 0:e614f7875b60 776 break;
iva2k 0:e614f7875b60 777 }
iva2k 0:e614f7875b60 778 else if( *str=='\n' )
iva2k 0:e614f7875b60 779 {
iva2k 0:e614f7875b60 780 break;
iva2k 0:e614f7875b60 781 }
iva2k 0:e614f7875b60 782 str++;
iva2k 0:e614f7875b60 783 len++;
iva2k 0:e614f7875b60 784 }
iva2k 0:e614f7875b60 785 *str = 0;
iva2k 0:e614f7875b60 786 return len;
iva2k 0:e614f7875b60 787 }