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.
services/http/client/HTTPClient.cpp@1:3ee499525aa5, 2010-06-14 (annotated)
- Committer:
- iva2k
- Date:
- Mon Jun 14 03:24:33 2010 +0000
- Revision:
- 1:3ee499525aa5
- Parent:
- 0:e614f7875b60
Who changed what in which revision?
User | Revision | Line number | New 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 | } |