httpclient

Dependents:   Lab_6

Committer:
rr387
Date:
Thu Dec 30 15:21:34 2021 +0000
Revision:
0:a988a72f184b
retes

Who changed what in which revision?

UserRevisionLine numberNew contents of line
rr387 0:a988a72f184b 1 /* HTTPClient.cpp */
rr387 0:a988a72f184b 2 /* Copyright (C) 2012 mbed.org, MIT License
rr387 0:a988a72f184b 3 *
rr387 0:a988a72f184b 4 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
rr387 0:a988a72f184b 5 * and associated documentation files (the "Software"), to deal in the Software without restriction,
rr387 0:a988a72f184b 6 * including without limitation the rights to use, copy, modify, merge, publish, distribute,
rr387 0:a988a72f184b 7 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
rr387 0:a988a72f184b 8 * furnished to do so, subject to the following conditions:
rr387 0:a988a72f184b 9 *
rr387 0:a988a72f184b 10 * The above copyright notice and this permission notice shall be included in all copies or
rr387 0:a988a72f184b 11 * substantial portions of the Software.
rr387 0:a988a72f184b 12 *
rr387 0:a988a72f184b 13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
rr387 0:a988a72f184b 14 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
rr387 0:a988a72f184b 15 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
rr387 0:a988a72f184b 16 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
rr387 0:a988a72f184b 17 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
rr387 0:a988a72f184b 18 */
rr387 0:a988a72f184b 19
rr387 0:a988a72f184b 20 //Debug is disabled by default
rr387 0:a988a72f184b 21 #if 0
rr387 0:a988a72f184b 22 //Enable debug
rr387 0:a988a72f184b 23 #include <cstdio>
rr387 0:a988a72f184b 24 #define DBG(x, ...) std::printf("[HTTPClient : DBG]"x"\r\n", ##__VA_ARGS__);
rr387 0:a988a72f184b 25 #define WARN(x, ...) std::printf("[HTTPClient : WARN]"x"\r\n", ##__VA_ARGS__);
rr387 0:a988a72f184b 26 #define ERR(x, ...) std::printf("[HTTPClient : ERR]"x"\r\n", ##__VA_ARGS__);
rr387 0:a988a72f184b 27
rr387 0:a988a72f184b 28 #else
rr387 0:a988a72f184b 29 //Disable debug
rr387 0:a988a72f184b 30 #define DBG(x, ...)
rr387 0:a988a72f184b 31 #define WARN(x, ...)
rr387 0:a988a72f184b 32 #define ERR(x, ...)
rr387 0:a988a72f184b 33
rr387 0:a988a72f184b 34 #endif
rr387 0:a988a72f184b 35
rr387 0:a988a72f184b 36 #define HTTP_PORT 80
rr387 0:a988a72f184b 37
rr387 0:a988a72f184b 38 #define OK 0
rr387 0:a988a72f184b 39
rr387 0:a988a72f184b 40 #define MIN(x,y) (((x)<(y))?(x):(y))
rr387 0:a988a72f184b 41 #define MAX(x,y) (((x)>(y))?(x):(y))
rr387 0:a988a72f184b 42
rr387 0:a988a72f184b 43 #define CHUNK_SIZE 256
rr387 0:a988a72f184b 44 #define BUF_SIZE 128
rr387 0:a988a72f184b 45
rr387 0:a988a72f184b 46 #include <cstring>
rr387 0:a988a72f184b 47
rr387 0:a988a72f184b 48 #include "HTTPClient.h"
rr387 0:a988a72f184b 49
rr387 0:a988a72f184b 50 HTTPClient::HTTPClient() :
rr387 0:a988a72f184b 51 m_sock(), m_basicAuthUser(NULL), m_basicAuthPassword(NULL), m_httpResponseCode(0)
rr387 0:a988a72f184b 52 {
rr387 0:a988a72f184b 53
rr387 0:a988a72f184b 54 }
rr387 0:a988a72f184b 55
rr387 0:a988a72f184b 56 HTTPClient::~HTTPClient()
rr387 0:a988a72f184b 57 {
rr387 0:a988a72f184b 58
rr387 0:a988a72f184b 59 }
rr387 0:a988a72f184b 60
rr387 0:a988a72f184b 61 #if 0
rr387 0:a988a72f184b 62 void HTTPClient::basicAuth(const char* user, const char* password) //Basic Authentification
rr387 0:a988a72f184b 63 {
rr387 0:a988a72f184b 64 m_basicAuthUser = user;
rr387 0:a988a72f184b 65 m_basicAuthPassword = password;
rr387 0:a988a72f184b 66 }
rr387 0:a988a72f184b 67 #endif
rr387 0:a988a72f184b 68
rr387 0:a988a72f184b 69 HTTPResult HTTPClient::get(const char* url, IHTTPDataIn* pDataIn, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
rr387 0:a988a72f184b 70 {
rr387 0:a988a72f184b 71 return connect(url, HTTP_GET, NULL, pDataIn, timeout);
rr387 0:a988a72f184b 72 }
rr387 0:a988a72f184b 73
rr387 0:a988a72f184b 74 HTTPResult HTTPClient::get(const char* url, char* result, size_t maxResultLen, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
rr387 0:a988a72f184b 75 {
rr387 0:a988a72f184b 76 HTTPText str(result, maxResultLen);
rr387 0:a988a72f184b 77 return get(url, &str, timeout);
rr387 0:a988a72f184b 78 }
rr387 0:a988a72f184b 79
rr387 0:a988a72f184b 80 HTTPResult HTTPClient::post(const char* url, const IHTTPDataOut& dataOut, IHTTPDataIn* pDataIn, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
rr387 0:a988a72f184b 81 {
rr387 0:a988a72f184b 82 return connect(url, HTTP_POST, (IHTTPDataOut*)&dataOut, pDataIn, timeout);
rr387 0:a988a72f184b 83 }
rr387 0:a988a72f184b 84
rr387 0:a988a72f184b 85 HTTPResult HTTPClient::put(const char* url, const IHTTPDataOut& dataOut, IHTTPDataIn* pDataIn, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
rr387 0:a988a72f184b 86 {
rr387 0:a988a72f184b 87 return connect(url, HTTP_PUT, (IHTTPDataOut*)&dataOut, pDataIn, timeout);
rr387 0:a988a72f184b 88 }
rr387 0:a988a72f184b 89
rr387 0:a988a72f184b 90 HTTPResult HTTPClient::del(const char* url, IHTTPDataIn* pDataIn, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
rr387 0:a988a72f184b 91 {
rr387 0:a988a72f184b 92 return connect(url, HTTP_DELETE, NULL, pDataIn, timeout);
rr387 0:a988a72f184b 93 }
rr387 0:a988a72f184b 94
rr387 0:a988a72f184b 95
rr387 0:a988a72f184b 96 int HTTPClient::getHTTPResponseCode()
rr387 0:a988a72f184b 97 {
rr387 0:a988a72f184b 98 return m_httpResponseCode;
rr387 0:a988a72f184b 99 }
rr387 0:a988a72f184b 100
rr387 0:a988a72f184b 101 #define CHECK_CONN_ERR(ret) \
rr387 0:a988a72f184b 102 do{ \
rr387 0:a988a72f184b 103 if(ret) { \
rr387 0:a988a72f184b 104 m_sock.close(); \
rr387 0:a988a72f184b 105 ERR("Connection error (%d)", ret); \
rr387 0:a988a72f184b 106 return HTTP_CONN; \
rr387 0:a988a72f184b 107 } \
rr387 0:a988a72f184b 108 } while(0)
rr387 0:a988a72f184b 109
rr387 0:a988a72f184b 110 #define PRTCL_ERR() \
rr387 0:a988a72f184b 111 do{ \
rr387 0:a988a72f184b 112 m_sock.close(); \
rr387 0:a988a72f184b 113 ERR("Protocol error"); \
rr387 0:a988a72f184b 114 return HTTP_PRTCL; \
rr387 0:a988a72f184b 115 } while(0)
rr387 0:a988a72f184b 116
rr387 0:a988a72f184b 117 HTTPResult HTTPClient::connect(const char* url, HTTP_METH method, IHTTPDataOut* pDataOut, IHTTPDataIn* pDataIn, int timeout) //Execute request
rr387 0:a988a72f184b 118 {
rr387 0:a988a72f184b 119 m_httpResponseCode = 0; //Invalidate code
rr387 0:a988a72f184b 120 m_timeout = timeout;
rr387 0:a988a72f184b 121
rr387 0:a988a72f184b 122 pDataIn->writeReset();
rr387 0:a988a72f184b 123 if( pDataOut )
rr387 0:a988a72f184b 124 {
rr387 0:a988a72f184b 125 pDataOut->readReset();
rr387 0:a988a72f184b 126 }
rr387 0:a988a72f184b 127
rr387 0:a988a72f184b 128 char scheme[8];
rr387 0:a988a72f184b 129 uint16_t port;
rr387 0:a988a72f184b 130 char host[32];
rr387 0:a988a72f184b 131 char path[64];
rr387 0:a988a72f184b 132 //First we need to parse the url (http[s]://host[:port][/[path]]) -- HTTPS not supported (yet?)
rr387 0:a988a72f184b 133 HTTPResult res = parseURL(url, scheme, sizeof(scheme), host, sizeof(host), &port, path, sizeof(path));
rr387 0:a988a72f184b 134 if(res != HTTP_OK)
rr387 0:a988a72f184b 135 {
rr387 0:a988a72f184b 136 ERR("parseURL returned %d", res);
rr387 0:a988a72f184b 137 return res;
rr387 0:a988a72f184b 138 }
rr387 0:a988a72f184b 139
rr387 0:a988a72f184b 140 if(port == 0) //TODO do handle HTTPS->443
rr387 0:a988a72f184b 141 {
rr387 0:a988a72f184b 142 port = 80;
rr387 0:a988a72f184b 143 }
rr387 0:a988a72f184b 144
rr387 0:a988a72f184b 145 DBG("Scheme: %s", scheme);
rr387 0:a988a72f184b 146 DBG("Host: %s", host);
rr387 0:a988a72f184b 147 DBG("Port: %d", port);
rr387 0:a988a72f184b 148 DBG("Path: %s", path);
rr387 0:a988a72f184b 149
rr387 0:a988a72f184b 150 //Connect
rr387 0:a988a72f184b 151 DBG("Connecting socket to server");
rr387 0:a988a72f184b 152 int ret = m_sock.connect(host, port);
rr387 0:a988a72f184b 153 if (ret < 0)
rr387 0:a988a72f184b 154 {
rr387 0:a988a72f184b 155 m_sock.close();
rr387 0:a988a72f184b 156 ERR("Could not connect");
rr387 0:a988a72f184b 157 return HTTP_CONN;
rr387 0:a988a72f184b 158 }
rr387 0:a988a72f184b 159
rr387 0:a988a72f184b 160 //Send request
rr387 0:a988a72f184b 161 DBG("Sending request");
rr387 0:a988a72f184b 162 char buf[CHUNK_SIZE];
rr387 0:a988a72f184b 163 const char* meth = (method==HTTP_GET)?"GET":(method==HTTP_POST)?"POST":(method==HTTP_PUT)?"PUT":(method==HTTP_DELETE)?"DELETE":"";
rr387 0:a988a72f184b 164 snprintf(buf, sizeof(buf), "%s %s HTTP/1.1\r\nHost: %s\r\n", meth, path, host); //Write request
rr387 0:a988a72f184b 165 ret = send(buf);
rr387 0:a988a72f184b 166 if(ret)
rr387 0:a988a72f184b 167 {
rr387 0:a988a72f184b 168 m_sock.close();
rr387 0:a988a72f184b 169 ERR("Could not write request");
rr387 0:a988a72f184b 170 return HTTP_CONN;
rr387 0:a988a72f184b 171 }
rr387 0:a988a72f184b 172
rr387 0:a988a72f184b 173 //Send all headers
rr387 0:a988a72f184b 174
rr387 0:a988a72f184b 175 //Send default headers
rr387 0:a988a72f184b 176 DBG("Sending headers");
rr387 0:a988a72f184b 177 if( pDataOut != NULL )
rr387 0:a988a72f184b 178 {
rr387 0:a988a72f184b 179 if( pDataOut->getIsChunked() )
rr387 0:a988a72f184b 180 {
rr387 0:a988a72f184b 181 ret = send("Transfer-Encoding: chunked\r\n");
rr387 0:a988a72f184b 182 CHECK_CONN_ERR(ret);
rr387 0:a988a72f184b 183 }
rr387 0:a988a72f184b 184 else
rr387 0:a988a72f184b 185 {
rr387 0:a988a72f184b 186 snprintf(buf, sizeof(buf), "Content-Length: %d\r\n", pDataOut->getDataLen());
rr387 0:a988a72f184b 187 ret = send(buf);
rr387 0:a988a72f184b 188 CHECK_CONN_ERR(ret);
rr387 0:a988a72f184b 189 }
rr387 0:a988a72f184b 190 char type[48];
rr387 0:a988a72f184b 191 if( pDataOut->getDataType(type, 48) == HTTP_OK )
rr387 0:a988a72f184b 192 {
rr387 0:a988a72f184b 193 snprintf(buf, sizeof(buf), "Content-Type: %s\r\n", type);
rr387 0:a988a72f184b 194 ret = send(buf);
rr387 0:a988a72f184b 195 CHECK_CONN_ERR(ret);
rr387 0:a988a72f184b 196 }
rr387 0:a988a72f184b 197
rr387 0:a988a72f184b 198 //Send specific headers
rr387 0:a988a72f184b 199 while( pDataOut->getHeader(buf, sizeof(buf) - 3) ) //must have space left for CRLF + 0 terminating char
rr387 0:a988a72f184b 200 {
rr387 0:a988a72f184b 201 size_t headerlen = strlen(buf);
rr387 0:a988a72f184b 202 snprintf(buf + headerlen, sizeof(buf) - headerlen, "\r\n");
rr387 0:a988a72f184b 203 ret = send(buf);
rr387 0:a988a72f184b 204 CHECK_CONN_ERR(ret);
rr387 0:a988a72f184b 205 }
rr387 0:a988a72f184b 206 }
rr387 0:a988a72f184b 207
rr387 0:a988a72f184b 208 //Send specific headers
rr387 0:a988a72f184b 209 while( pDataIn->getHeader(buf, sizeof(buf) - 3) )
rr387 0:a988a72f184b 210 {
rr387 0:a988a72f184b 211 size_t headerlen = strlen(buf);
rr387 0:a988a72f184b 212 snprintf(buf + headerlen, sizeof(buf) - headerlen, "\r\n");
rr387 0:a988a72f184b 213 ret = send(buf);
rr387 0:a988a72f184b 214 CHECK_CONN_ERR(ret);
rr387 0:a988a72f184b 215 }
rr387 0:a988a72f184b 216
rr387 0:a988a72f184b 217 //Close headers
rr387 0:a988a72f184b 218 DBG("Headers sent");
rr387 0:a988a72f184b 219 ret = send("\r\n");
rr387 0:a988a72f184b 220 CHECK_CONN_ERR(ret);
rr387 0:a988a72f184b 221
rr387 0:a988a72f184b 222 size_t trfLen;
rr387 0:a988a72f184b 223
rr387 0:a988a72f184b 224 //Send data (if available)
rr387 0:a988a72f184b 225 if( pDataOut != NULL )
rr387 0:a988a72f184b 226 {
rr387 0:a988a72f184b 227 DBG("Sending data");
rr387 0:a988a72f184b 228 while(true)
rr387 0:a988a72f184b 229 {
rr387 0:a988a72f184b 230 size_t writtenLen = 0;
rr387 0:a988a72f184b 231 pDataOut->read(buf, CHUNK_SIZE, &trfLen);
rr387 0:a988a72f184b 232 if( pDataOut->getIsChunked() )
rr387 0:a988a72f184b 233 {
rr387 0:a988a72f184b 234 //Write chunk header
rr387 0:a988a72f184b 235 char chunkHeader[16];
rr387 0:a988a72f184b 236 snprintf(chunkHeader, sizeof(chunkHeader), "%X\r\n", trfLen); //In hex encoding
rr387 0:a988a72f184b 237 ret = send(chunkHeader);
rr387 0:a988a72f184b 238 CHECK_CONN_ERR(ret);
rr387 0:a988a72f184b 239 }
rr387 0:a988a72f184b 240 else if( trfLen == 0 )
rr387 0:a988a72f184b 241 {
rr387 0:a988a72f184b 242 break;
rr387 0:a988a72f184b 243 }
rr387 0:a988a72f184b 244 if( trfLen != 0 )
rr387 0:a988a72f184b 245 {
rr387 0:a988a72f184b 246 ret = send(buf, trfLen);
rr387 0:a988a72f184b 247 CHECK_CONN_ERR(ret);
rr387 0:a988a72f184b 248 }
rr387 0:a988a72f184b 249
rr387 0:a988a72f184b 250 if( pDataOut->getIsChunked() )
rr387 0:a988a72f184b 251 {
rr387 0:a988a72f184b 252 ret = send("\r\n"); //Chunk-terminating CRLF
rr387 0:a988a72f184b 253 CHECK_CONN_ERR(ret);
rr387 0:a988a72f184b 254 }
rr387 0:a988a72f184b 255 else
rr387 0:a988a72f184b 256 {
rr387 0:a988a72f184b 257 writtenLen += trfLen;
rr387 0:a988a72f184b 258 if( writtenLen >= pDataOut->getDataLen() )
rr387 0:a988a72f184b 259 {
rr387 0:a988a72f184b 260 break;
rr387 0:a988a72f184b 261 }
rr387 0:a988a72f184b 262 }
rr387 0:a988a72f184b 263
rr387 0:a988a72f184b 264 if( trfLen == 0 )
rr387 0:a988a72f184b 265 {
rr387 0:a988a72f184b 266 break;
rr387 0:a988a72f184b 267 }
rr387 0:a988a72f184b 268 }
rr387 0:a988a72f184b 269 }
rr387 0:a988a72f184b 270
rr387 0:a988a72f184b 271 //Receive response
rr387 0:a988a72f184b 272 DBG("Receiving response");
rr387 0:a988a72f184b 273 ret = recv(buf, 1, CHUNK_SIZE - 1, &trfLen); //Read n bytes
rr387 0:a988a72f184b 274 CHECK_CONN_ERR(ret);
rr387 0:a988a72f184b 275
rr387 0:a988a72f184b 276 buf[trfLen] = '\0';
rr387 0:a988a72f184b 277
rr387 0:a988a72f184b 278 //Make sure we got the first response line
rr387 0:a988a72f184b 279 char* crlfPtr = NULL;
rr387 0:a988a72f184b 280 while( true )
rr387 0:a988a72f184b 281 {
rr387 0:a988a72f184b 282 crlfPtr = strstr(buf, "\r\n");
rr387 0:a988a72f184b 283 if(crlfPtr == NULL)
rr387 0:a988a72f184b 284 {
rr387 0:a988a72f184b 285 if( trfLen < CHUNK_SIZE - 1 )
rr387 0:a988a72f184b 286 {
rr387 0:a988a72f184b 287 size_t newTrfLen;
rr387 0:a988a72f184b 288 ret = recv(buf + trfLen, 1, CHUNK_SIZE - trfLen - 1, &newTrfLen);
rr387 0:a988a72f184b 289 trfLen += newTrfLen;
rr387 0:a988a72f184b 290 buf[trfLen] = '\0';
rr387 0:a988a72f184b 291 DBG("Read %d chars; In buf: [%s]", newTrfLen, buf);
rr387 0:a988a72f184b 292 CHECK_CONN_ERR(ret);
rr387 0:a988a72f184b 293 continue;
rr387 0:a988a72f184b 294 }
rr387 0:a988a72f184b 295 else
rr387 0:a988a72f184b 296 {
rr387 0:a988a72f184b 297 PRTCL_ERR();
rr387 0:a988a72f184b 298 }
rr387 0:a988a72f184b 299 }
rr387 0:a988a72f184b 300 break;
rr387 0:a988a72f184b 301 }
rr387 0:a988a72f184b 302
rr387 0:a988a72f184b 303 int crlfPos = crlfPtr - buf;
rr387 0:a988a72f184b 304 buf[crlfPos] = '\0';
rr387 0:a988a72f184b 305
rr387 0:a988a72f184b 306 //Parse HTTP response
rr387 0:a988a72f184b 307 //if( sscanf(buf, "HTTP/%*d.%*d %d %*[^\r\n]", &m_httpResponseCode) != 1 )
rr387 0:a988a72f184b 308 if(crlfPos > 13)
rr387 0:a988a72f184b 309 {
rr387 0:a988a72f184b 310 buf[13] = '\0';
rr387 0:a988a72f184b 311 }
rr387 0:a988a72f184b 312 if( sscanf(buf, "HTTP/%*d.%*d %d", &m_httpResponseCode) != 1 ) //Kludge for newlib nano
rr387 0:a988a72f184b 313 {
rr387 0:a988a72f184b 314 //Cannot match string, error
rr387 0:a988a72f184b 315 ERR("Not a correct HTTP answer : %s\n", buf);
rr387 0:a988a72f184b 316 PRTCL_ERR();
rr387 0:a988a72f184b 317 }
rr387 0:a988a72f184b 318
rr387 0:a988a72f184b 319 if( (m_httpResponseCode < 200) || (m_httpResponseCode >= 300) )
rr387 0:a988a72f184b 320 {
rr387 0:a988a72f184b 321 //Did not return a 2xx code; TODO fetch headers/(&data?) anyway and implement a mean of writing/reading headers
rr387 0:a988a72f184b 322 WARN("Response code %d", m_httpResponseCode);
rr387 0:a988a72f184b 323 PRTCL_ERR();
rr387 0:a988a72f184b 324 }
rr387 0:a988a72f184b 325
rr387 0:a988a72f184b 326 DBG("Reading headers");
rr387 0:a988a72f184b 327
rr387 0:a988a72f184b 328 memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2) + 1); //Be sure to move NULL-terminating char as well
rr387 0:a988a72f184b 329 trfLen -= (crlfPos + 2);
rr387 0:a988a72f184b 330
rr387 0:a988a72f184b 331 size_t recvContentLength = 0;
rr387 0:a988a72f184b 332 bool recvChunked = false;
rr387 0:a988a72f184b 333 bool recvLengthUnknown = true;
rr387 0:a988a72f184b 334 //Now get headers
rr387 0:a988a72f184b 335 while( true )
rr387 0:a988a72f184b 336 {
rr387 0:a988a72f184b 337 crlfPtr = strstr(buf, "\r\n");
rr387 0:a988a72f184b 338 if(crlfPtr == NULL)
rr387 0:a988a72f184b 339 {
rr387 0:a988a72f184b 340 if( trfLen < CHUNK_SIZE - 1 )
rr387 0:a988a72f184b 341 {
rr387 0:a988a72f184b 342 size_t newTrfLen;
rr387 0:a988a72f184b 343 ret = recv(buf + trfLen, 1, CHUNK_SIZE - trfLen - 1, &newTrfLen);
rr387 0:a988a72f184b 344 trfLen += newTrfLen;
rr387 0:a988a72f184b 345 buf[trfLen] = '\0';
rr387 0:a988a72f184b 346 DBG("Read %d chars; In buf: [%s]", newTrfLen, buf);
rr387 0:a988a72f184b 347 CHECK_CONN_ERR(ret);
rr387 0:a988a72f184b 348 continue;
rr387 0:a988a72f184b 349 }
rr387 0:a988a72f184b 350 else
rr387 0:a988a72f184b 351 {
rr387 0:a988a72f184b 352 PRTCL_ERR();
rr387 0:a988a72f184b 353 }
rr387 0:a988a72f184b 354 }
rr387 0:a988a72f184b 355
rr387 0:a988a72f184b 356 crlfPos = crlfPtr - buf;
rr387 0:a988a72f184b 357
rr387 0:a988a72f184b 358 if(crlfPos == 0) //End of headers
rr387 0:a988a72f184b 359 {
rr387 0:a988a72f184b 360 DBG("Headers read");
rr387 0:a988a72f184b 361 memmove(buf, &buf[2], trfLen - 2 + 1); //Be sure to move NULL-terminating char as well
rr387 0:a988a72f184b 362 trfLen -= 2;
rr387 0:a988a72f184b 363 break;
rr387 0:a988a72f184b 364 }
rr387 0:a988a72f184b 365
rr387 0:a988a72f184b 366 buf[crlfPos] = '\0';
rr387 0:a988a72f184b 367
rr387 0:a988a72f184b 368 char key[BUF_SIZE];
rr387 0:a988a72f184b 369 char value[BUF_SIZE];
rr387 0:a988a72f184b 370
rr387 0:a988a72f184b 371 //key[31] = '\0';
rr387 0:a988a72f184b 372 //value[31] = '\0';
rr387 0:a988a72f184b 373
rr387 0:a988a72f184b 374 memset(key, 0, BUF_SIZE);
rr387 0:a988a72f184b 375 memset(value, 0, BUF_SIZE);
rr387 0:a988a72f184b 376
rr387 0:a988a72f184b 377 //int n = sscanf(buf, "%31[^:]: %31[^\r\n]", key, value);
rr387 0:a988a72f184b 378
rr387 0:a988a72f184b 379 int n = 0;
rr387 0:a988a72f184b 380
rr387 0:a988a72f184b 381 char* keyEnd = strchr(buf, ':');
rr387 0:a988a72f184b 382 if(keyEnd != NULL)
rr387 0:a988a72f184b 383 {
rr387 0:a988a72f184b 384 *keyEnd = '\0';
rr387 0:a988a72f184b 385 if(strlen(buf) < BUF_SIZE)
rr387 0:a988a72f184b 386 {
rr387 0:a988a72f184b 387 strcpy(key, buf);
rr387 0:a988a72f184b 388 n++;
rr387 0:a988a72f184b 389 char* valueStart = keyEnd + 2;
rr387 0:a988a72f184b 390 if( (valueStart - buf) < crlfPos )
rr387 0:a988a72f184b 391 {
rr387 0:a988a72f184b 392 if(strlen(valueStart) < BUF_SIZE)
rr387 0:a988a72f184b 393 {
rr387 0:a988a72f184b 394 strcpy(value, valueStart);
rr387 0:a988a72f184b 395 n++;
rr387 0:a988a72f184b 396 }
rr387 0:a988a72f184b 397 }
rr387 0:a988a72f184b 398 }
rr387 0:a988a72f184b 399 }
rr387 0:a988a72f184b 400 if ( n == 2 )
rr387 0:a988a72f184b 401 {
rr387 0:a988a72f184b 402 DBG("Read header : %s: %s\n", key, value);
rr387 0:a988a72f184b 403 if( !strcmp(key, "Content-Length") )
rr387 0:a988a72f184b 404 {
rr387 0:a988a72f184b 405 sscanf(value, "%d", &recvContentLength);
rr387 0:a988a72f184b 406 recvLengthUnknown = false;
rr387 0:a988a72f184b 407 pDataIn->setDataLen(recvContentLength);
rr387 0:a988a72f184b 408 }
rr387 0:a988a72f184b 409 else if( !strcmp(key, "Transfer-Encoding") )
rr387 0:a988a72f184b 410 {
rr387 0:a988a72f184b 411 if( !strcmp(value, "Chunked") || !strcmp(value, "chunked") )
rr387 0:a988a72f184b 412 {
rr387 0:a988a72f184b 413 recvChunked = true;
rr387 0:a988a72f184b 414 recvLengthUnknown = false;
rr387 0:a988a72f184b 415 pDataIn->setIsChunked(true);
rr387 0:a988a72f184b 416 }
rr387 0:a988a72f184b 417 }
rr387 0:a988a72f184b 418 else if( !strcmp(key, "Content-Type") )
rr387 0:a988a72f184b 419 {
rr387 0:a988a72f184b 420 pDataIn->setDataType(value);
rr387 0:a988a72f184b 421 }
rr387 0:a988a72f184b 422
rr387 0:a988a72f184b 423 memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2) + 1); //Be sure to move NULL-terminating char as well
rr387 0:a988a72f184b 424 trfLen -= (crlfPos + 2);
rr387 0:a988a72f184b 425
rr387 0:a988a72f184b 426 }
rr387 0:a988a72f184b 427 else
rr387 0:a988a72f184b 428 {
rr387 0:a988a72f184b 429 ERR("Could not parse header");
rr387 0:a988a72f184b 430 PRTCL_ERR();
rr387 0:a988a72f184b 431 }
rr387 0:a988a72f184b 432
rr387 0:a988a72f184b 433 }
rr387 0:a988a72f184b 434
rr387 0:a988a72f184b 435 //Receive data
rr387 0:a988a72f184b 436 DBG("Receiving data");
rr387 0:a988a72f184b 437 while(true)
rr387 0:a988a72f184b 438 {
rr387 0:a988a72f184b 439 size_t readLen = 0;
rr387 0:a988a72f184b 440
rr387 0:a988a72f184b 441 if( recvChunked )
rr387 0:a988a72f184b 442 {
rr387 0:a988a72f184b 443 //Read chunk header
rr387 0:a988a72f184b 444 bool foundCrlf;
rr387 0:a988a72f184b 445 do
rr387 0:a988a72f184b 446 {
rr387 0:a988a72f184b 447 foundCrlf = false;
rr387 0:a988a72f184b 448 crlfPos=0;
rr387 0:a988a72f184b 449 buf[trfLen]=0;
rr387 0:a988a72f184b 450 if(trfLen >= 2)
rr387 0:a988a72f184b 451 {
rr387 0:a988a72f184b 452 for(; crlfPos < trfLen - 2; crlfPos++)
rr387 0:a988a72f184b 453 {
rr387 0:a988a72f184b 454 if( buf[crlfPos] == '\r' && buf[crlfPos + 1] == '\n' )
rr387 0:a988a72f184b 455 {
rr387 0:a988a72f184b 456 foundCrlf = true;
rr387 0:a988a72f184b 457 break;
rr387 0:a988a72f184b 458 }
rr387 0:a988a72f184b 459 }
rr387 0:a988a72f184b 460 }
rr387 0:a988a72f184b 461 if(!foundCrlf) //Try to read more
rr387 0:a988a72f184b 462 {
rr387 0:a988a72f184b 463 if( trfLen < CHUNK_SIZE )
rr387 0:a988a72f184b 464 {
rr387 0:a988a72f184b 465 size_t newTrfLen;
rr387 0:a988a72f184b 466 ret = recv(buf + trfLen, 0, CHUNK_SIZE - trfLen - 1, &newTrfLen);
rr387 0:a988a72f184b 467 trfLen += newTrfLen;
rr387 0:a988a72f184b 468 CHECK_CONN_ERR(ret);
rr387 0:a988a72f184b 469 continue;
rr387 0:a988a72f184b 470 }
rr387 0:a988a72f184b 471 else
rr387 0:a988a72f184b 472 {
rr387 0:a988a72f184b 473 PRTCL_ERR();
rr387 0:a988a72f184b 474 }
rr387 0:a988a72f184b 475 }
rr387 0:a988a72f184b 476 } while(!foundCrlf);
rr387 0:a988a72f184b 477 buf[crlfPos] = '\0';
rr387 0:a988a72f184b 478 int n = sscanf(buf, "%x", &readLen);
rr387 0:a988a72f184b 479 if(n!=1)
rr387 0:a988a72f184b 480 {
rr387 0:a988a72f184b 481 ERR("Could not read chunk length");
rr387 0:a988a72f184b 482 PRTCL_ERR();
rr387 0:a988a72f184b 483 }
rr387 0:a988a72f184b 484
rr387 0:a988a72f184b 485 memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2)); //Not need to move NULL-terminating char any more
rr387 0:a988a72f184b 486 trfLen -= (crlfPos + 2);
rr387 0:a988a72f184b 487
rr387 0:a988a72f184b 488 if( readLen == 0 )
rr387 0:a988a72f184b 489 {
rr387 0:a988a72f184b 490 //Last chunk
rr387 0:a988a72f184b 491 break;
rr387 0:a988a72f184b 492 }
rr387 0:a988a72f184b 493 }
rr387 0:a988a72f184b 494 else
rr387 0:a988a72f184b 495 {
rr387 0:a988a72f184b 496 readLen = recvContentLength;
rr387 0:a988a72f184b 497 }
rr387 0:a988a72f184b 498
rr387 0:a988a72f184b 499 DBG("Retrieving %d bytes (%d bytes in buffer)", readLen, trfLen);
rr387 0:a988a72f184b 500
rr387 0:a988a72f184b 501 do
rr387 0:a988a72f184b 502 {
rr387 0:a988a72f184b 503 if(recvLengthUnknown )
rr387 0:a988a72f184b 504 {
rr387 0:a988a72f184b 505 readLen = trfLen;
rr387 0:a988a72f184b 506 }
rr387 0:a988a72f184b 507 pDataIn->write(buf, MIN(trfLen, readLen));
rr387 0:a988a72f184b 508 if(!recvLengthUnknown)
rr387 0:a988a72f184b 509 {
rr387 0:a988a72f184b 510 if( trfLen > readLen )
rr387 0:a988a72f184b 511 {
rr387 0:a988a72f184b 512 memmove(buf, &buf[readLen], trfLen - readLen);
rr387 0:a988a72f184b 513 trfLen -= readLen;
rr387 0:a988a72f184b 514 readLen = 0;
rr387 0:a988a72f184b 515 }
rr387 0:a988a72f184b 516 else
rr387 0:a988a72f184b 517 {
rr387 0:a988a72f184b 518 readLen -= trfLen;
rr387 0:a988a72f184b 519 }
rr387 0:a988a72f184b 520 }
rr387 0:a988a72f184b 521 else
rr387 0:a988a72f184b 522 {
rr387 0:a988a72f184b 523 trfLen = 0;
rr387 0:a988a72f184b 524 }
rr387 0:a988a72f184b 525
rr387 0:a988a72f184b 526 if(readLen || recvLengthUnknown)
rr387 0:a988a72f184b 527 {
rr387 0:a988a72f184b 528 ret = recv(buf, 1, CHUNK_SIZE - trfLen - 1, &trfLen);
rr387 0:a988a72f184b 529 if(recvLengthUnknown && (ret == HTTP_CLOSED))
rr387 0:a988a72f184b 530 {
rr387 0:a988a72f184b 531 //Write and exit
rr387 0:a988a72f184b 532 pDataIn->write(buf, trfLen);
rr387 0:a988a72f184b 533 break;
rr387 0:a988a72f184b 534 }
rr387 0:a988a72f184b 535 CHECK_CONN_ERR(ret);
rr387 0:a988a72f184b 536 if(recvLengthUnknown && (trfLen == 0))
rr387 0:a988a72f184b 537 {
rr387 0:a988a72f184b 538 break;
rr387 0:a988a72f184b 539 }
rr387 0:a988a72f184b 540 }
rr387 0:a988a72f184b 541 } while(readLen || recvLengthUnknown);
rr387 0:a988a72f184b 542
rr387 0:a988a72f184b 543 if( recvChunked )
rr387 0:a988a72f184b 544 {
rr387 0:a988a72f184b 545 if(trfLen < 2)
rr387 0:a988a72f184b 546 {
rr387 0:a988a72f184b 547 size_t newTrfLen;
rr387 0:a988a72f184b 548 //Read missing chars to find end of chunk
rr387 0:a988a72f184b 549 ret = recv(buf + trfLen, 2 - trfLen, CHUNK_SIZE - trfLen - 1, &newTrfLen);
rr387 0:a988a72f184b 550 CHECK_CONN_ERR(ret);
rr387 0:a988a72f184b 551 trfLen += newTrfLen;
rr387 0:a988a72f184b 552 }
rr387 0:a988a72f184b 553 if( (buf[0] != '\r') || (buf[1] != '\n') )
rr387 0:a988a72f184b 554 {
rr387 0:a988a72f184b 555 ERR("Format error");
rr387 0:a988a72f184b 556 PRTCL_ERR();
rr387 0:a988a72f184b 557 }
rr387 0:a988a72f184b 558 memmove(buf, &buf[2], trfLen - 2);
rr387 0:a988a72f184b 559 trfLen -= 2;
rr387 0:a988a72f184b 560 }
rr387 0:a988a72f184b 561 else
rr387 0:a988a72f184b 562 {
rr387 0:a988a72f184b 563 break;
rr387 0:a988a72f184b 564 }
rr387 0:a988a72f184b 565
rr387 0:a988a72f184b 566 }
rr387 0:a988a72f184b 567
rr387 0:a988a72f184b 568 m_sock.close();
rr387 0:a988a72f184b 569 DBG("Completed HTTP transaction");
rr387 0:a988a72f184b 570
rr387 0:a988a72f184b 571 return HTTP_OK;
rr387 0:a988a72f184b 572 }
rr387 0:a988a72f184b 573
rr387 0:a988a72f184b 574 HTTPResult HTTPClient::recv(char* buf, size_t minLen, size_t maxLen, size_t* pReadLen) //0 on success, err code on failure
rr387 0:a988a72f184b 575 {
rr387 0:a988a72f184b 576 DBG("Trying to read between %d and %d bytes", minLen, maxLen);
rr387 0:a988a72f184b 577 size_t readLen = 0;
rr387 0:a988a72f184b 578
rr387 0:a988a72f184b 579 if(!m_sock.is_connected())
rr387 0:a988a72f184b 580 {
rr387 0:a988a72f184b 581 WARN("Connection was closed by server");
rr387 0:a988a72f184b 582 return HTTP_CLOSED; //Connection was closed by server
rr387 0:a988a72f184b 583 }
rr387 0:a988a72f184b 584
rr387 0:a988a72f184b 585 int ret;
rr387 0:a988a72f184b 586 while(readLen < maxLen)
rr387 0:a988a72f184b 587 {
rr387 0:a988a72f184b 588 if(readLen < minLen)
rr387 0:a988a72f184b 589 {
rr387 0:a988a72f184b 590 DBG("Trying to read at most %d bytes [Blocking]", minLen - readLen);
rr387 0:a988a72f184b 591 m_sock.set_blocking(false, m_timeout);
rr387 0:a988a72f184b 592 ret = m_sock.receive_all(buf + readLen, minLen - readLen);
rr387 0:a988a72f184b 593 }
rr387 0:a988a72f184b 594 else
rr387 0:a988a72f184b 595 {
rr387 0:a988a72f184b 596 DBG("Trying to read at most %d bytes [Not blocking]", maxLen - readLen);
rr387 0:a988a72f184b 597 m_sock.set_blocking(false, 0);
rr387 0:a988a72f184b 598 ret = m_sock.receive(buf + readLen, maxLen - readLen);
rr387 0:a988a72f184b 599 }
rr387 0:a988a72f184b 600
rr387 0:a988a72f184b 601 if( ret > 0)
rr387 0:a988a72f184b 602 {
rr387 0:a988a72f184b 603 readLen += ret;
rr387 0:a988a72f184b 604 }
rr387 0:a988a72f184b 605 else if( ret == 0 )
rr387 0:a988a72f184b 606 {
rr387 0:a988a72f184b 607 break;
rr387 0:a988a72f184b 608 }
rr387 0:a988a72f184b 609 else
rr387 0:a988a72f184b 610 {
rr387 0:a988a72f184b 611 if(!m_sock.is_connected())
rr387 0:a988a72f184b 612 {
rr387 0:a988a72f184b 613 ERR("Connection error (recv returned %d)", ret);
rr387 0:a988a72f184b 614 *pReadLen = readLen;
rr387 0:a988a72f184b 615 return HTTP_CONN;
rr387 0:a988a72f184b 616 }
rr387 0:a988a72f184b 617 else
rr387 0:a988a72f184b 618 {
rr387 0:a988a72f184b 619 break;
rr387 0:a988a72f184b 620 }
rr387 0:a988a72f184b 621 }
rr387 0:a988a72f184b 622
rr387 0:a988a72f184b 623 if(!m_sock.is_connected())
rr387 0:a988a72f184b 624 {
rr387 0:a988a72f184b 625 break;
rr387 0:a988a72f184b 626 }
rr387 0:a988a72f184b 627 }
rr387 0:a988a72f184b 628 DBG("Read %d bytes", readLen);
rr387 0:a988a72f184b 629 *pReadLen = readLen;
rr387 0:a988a72f184b 630 return HTTP_OK;
rr387 0:a988a72f184b 631 }
rr387 0:a988a72f184b 632
rr387 0:a988a72f184b 633 HTTPResult HTTPClient::send(char* buf, size_t len) //0 on success, err code on failure
rr387 0:a988a72f184b 634 {
rr387 0:a988a72f184b 635 if(len == 0)
rr387 0:a988a72f184b 636 {
rr387 0:a988a72f184b 637 len = strlen(buf);
rr387 0:a988a72f184b 638 }
rr387 0:a988a72f184b 639 DBG("Trying to write %d bytes", len);
rr387 0:a988a72f184b 640 size_t writtenLen = 0;
rr387 0:a988a72f184b 641
rr387 0:a988a72f184b 642 if(!m_sock.is_connected())
rr387 0:a988a72f184b 643 {
rr387 0:a988a72f184b 644 WARN("Connection was closed by server");
rr387 0:a988a72f184b 645 return HTTP_CLOSED; //Connection was closed by server
rr387 0:a988a72f184b 646 }
rr387 0:a988a72f184b 647
rr387 0:a988a72f184b 648 m_sock.set_blocking(false, m_timeout);
rr387 0:a988a72f184b 649 int ret = m_sock.send_all(buf, len);
rr387 0:a988a72f184b 650 if(ret > 0)
rr387 0:a988a72f184b 651 {
rr387 0:a988a72f184b 652 writtenLen += ret;
rr387 0:a988a72f184b 653 }
rr387 0:a988a72f184b 654 else if( ret == 0 )
rr387 0:a988a72f184b 655 {
rr387 0:a988a72f184b 656 WARN("Connection was closed by server");
rr387 0:a988a72f184b 657 return HTTP_CLOSED; //Connection was closed by server
rr387 0:a988a72f184b 658 }
rr387 0:a988a72f184b 659 else
rr387 0:a988a72f184b 660 {
rr387 0:a988a72f184b 661 ERR("Connection error (send returned %d)", ret);
rr387 0:a988a72f184b 662 return HTTP_CONN;
rr387 0:a988a72f184b 663 }
rr387 0:a988a72f184b 664
rr387 0:a988a72f184b 665 DBG("Written %d bytes", writtenLen);
rr387 0:a988a72f184b 666 return HTTP_OK;
rr387 0:a988a72f184b 667 }
rr387 0:a988a72f184b 668
rr387 0:a988a72f184b 669 HTTPResult HTTPClient::parseURL(const char* url, char* scheme, size_t maxSchemeLen, char* host, size_t maxHostLen, uint16_t* port, char* path, size_t maxPathLen) //Parse URL
rr387 0:a988a72f184b 670 {
rr387 0:a988a72f184b 671 char* schemePtr = (char*) url;
rr387 0:a988a72f184b 672 char* hostPtr = (char*) strstr(url, "://");
rr387 0:a988a72f184b 673 if(hostPtr == NULL)
rr387 0:a988a72f184b 674 {
rr387 0:a988a72f184b 675 WARN("Could not find host");
rr387 0:a988a72f184b 676 return HTTP_PARSE; //URL is invalid
rr387 0:a988a72f184b 677 }
rr387 0:a988a72f184b 678
rr387 0:a988a72f184b 679 if( maxSchemeLen < hostPtr - schemePtr + 1 ) //including NULL-terminating char
rr387 0:a988a72f184b 680 {
rr387 0:a988a72f184b 681 WARN("Scheme str is too small (%d >= %d)", maxSchemeLen, hostPtr - schemePtr + 1);
rr387 0:a988a72f184b 682 return HTTP_PARSE;
rr387 0:a988a72f184b 683 }
rr387 0:a988a72f184b 684 memcpy(scheme, schemePtr, hostPtr - schemePtr);
rr387 0:a988a72f184b 685 scheme[hostPtr - schemePtr] = '\0';
rr387 0:a988a72f184b 686
rr387 0:a988a72f184b 687 hostPtr+=3;
rr387 0:a988a72f184b 688
rr387 0:a988a72f184b 689 size_t hostLen = 0;
rr387 0:a988a72f184b 690
rr387 0:a988a72f184b 691 char* portPtr = strchr(hostPtr, ':');
rr387 0:a988a72f184b 692 if( portPtr != NULL )
rr387 0:a988a72f184b 693 {
rr387 0:a988a72f184b 694 hostLen = portPtr - hostPtr;
rr387 0:a988a72f184b 695 portPtr++;
rr387 0:a988a72f184b 696 if( sscanf(portPtr, "%hu", port) != 1)
rr387 0:a988a72f184b 697 {
rr387 0:a988a72f184b 698 WARN("Could not find port");
rr387 0:a988a72f184b 699 return HTTP_PARSE;
rr387 0:a988a72f184b 700 }
rr387 0:a988a72f184b 701 }
rr387 0:a988a72f184b 702 else
rr387 0:a988a72f184b 703 {
rr387 0:a988a72f184b 704 *port=0;
rr387 0:a988a72f184b 705 }
rr387 0:a988a72f184b 706 char* pathPtr = strchr(hostPtr, '/');
rr387 0:a988a72f184b 707 if( hostLen == 0 )
rr387 0:a988a72f184b 708 {
rr387 0:a988a72f184b 709 hostLen = pathPtr - hostPtr;
rr387 0:a988a72f184b 710 }
rr387 0:a988a72f184b 711
rr387 0:a988a72f184b 712 if( maxHostLen < hostLen + 1 ) //including NULL-terminating char
rr387 0:a988a72f184b 713 {
rr387 0:a988a72f184b 714 WARN("Host str is too small (%d >= %d)", maxHostLen, hostLen + 1);
rr387 0:a988a72f184b 715 return HTTP_PARSE;
rr387 0:a988a72f184b 716 }
rr387 0:a988a72f184b 717 memcpy(host, hostPtr, hostLen);
rr387 0:a988a72f184b 718 host[hostLen] = '\0';
rr387 0:a988a72f184b 719
rr387 0:a988a72f184b 720 size_t pathLen;
rr387 0:a988a72f184b 721 char* fragmentPtr = strchr(hostPtr, '#');
rr387 0:a988a72f184b 722 if(fragmentPtr != NULL)
rr387 0:a988a72f184b 723 {
rr387 0:a988a72f184b 724 pathLen = fragmentPtr - pathPtr;
rr387 0:a988a72f184b 725 }
rr387 0:a988a72f184b 726 else
rr387 0:a988a72f184b 727 {
rr387 0:a988a72f184b 728 pathLen = strlen(pathPtr);
rr387 0:a988a72f184b 729 }
rr387 0:a988a72f184b 730
rr387 0:a988a72f184b 731 if( maxPathLen < pathLen + 1 ) //including NULL-terminating char
rr387 0:a988a72f184b 732 {
rr387 0:a988a72f184b 733 WARN("Path str is too small (%d >= %d)", maxPathLen, pathLen + 1);
rr387 0:a988a72f184b 734 return HTTP_PARSE;
rr387 0:a988a72f184b 735 }
rr387 0:a988a72f184b 736 memcpy(path, pathPtr, pathLen);
rr387 0:a988a72f184b 737 path[pathLen] = '\0';
rr387 0:a988a72f184b 738
rr387 0:a988a72f184b 739 return HTTP_OK;
rr387 0:a988a72f184b 740 }