Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: mbed-TFT-example-NCS36510 mbed-Accelerometer-example-NCS36510 mbed-Accelerometer-example-NCS36510
HTTPClient.cpp
00001 /* HTTPClient.cpp */ 00002 /* Copyright (C) 2012 mbed.org, MIT License 00003 * 00004 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 00005 * and associated documentation files (the "Software"), to deal in the Software without restriction, 00006 * including without limitation the rights to use, copy, modify, merge, publish, distribute, 00007 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 00008 * furnished to do so, subject to the following conditions: 00009 * 00010 * The above copyright notice and this permission notice shall be included in all copies or 00011 * substantial portions of the Software. 00012 * 00013 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 00014 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 00015 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 00016 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 00017 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 00018 */ 00019 00020 //Debug is disabled by default 00021 #if 1 00022 //Enable debug 00023 #include <cstdio> 00024 //#define DBG(x, ...) std::printf("[HTTPClient : DBG]"x"\r\n", ##__VA_ARGS__); 00025 #define DBG(x, ...) 00026 #define WARN(x, ...) std::printf("[HTTPClient : WARN]"x"\r\n", ##__VA_ARGS__); 00027 #define ERR(x, ...) std::printf("[HTTPClient : ERR]"x"\r\n", ##__VA_ARGS__); 00028 00029 #else 00030 //Disable debug 00031 #define DBG(x, ...) 00032 #define WARN(x, ...) 00033 #define ERR(x, ...) 00034 00035 #endif 00036 00037 #define HTTP_PORT 80 00038 00039 #define OK 0 00040 00041 #define MIN(x,y) (((x)<(y))?(x):(y)) 00042 #define MAX(x,y) (((x)>(y))?(x):(y)) 00043 00044 #define CHUNK_SIZE 256 00045 00046 #include <cstring> 00047 00048 #include "HTTPClient.h" 00049 00050 HTTPClient::HTTPClient() : 00051 m_sock(), m_basicAuthUser(NULL), m_basicAuthPassword(NULL), m_httpResponseCode(0) 00052 { 00053 00054 } 00055 00056 HTTPClient::~HTTPClient() 00057 { 00058 00059 } 00060 00061 #if 0 00062 void HTTPClient::basicAuth(const char* user, const char* password) //Basic Authentification 00063 { 00064 m_basicAuthUser = user; 00065 m_basicAuthPassword = password; 00066 } 00067 #endif 00068 00069 HTTPResult HTTPClient::get(const char* url, IHTTPDataIn* pDataIn, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking 00070 { 00071 return connect(url, HTTP_GET, NULL, pDataIn, timeout); 00072 } 00073 00074 HTTPResult HTTPClient::get(const char* url, char* result, size_t maxResultLen, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking 00075 { 00076 HTTPText str(result, maxResultLen); 00077 return get(url, &str, timeout); 00078 } 00079 00080 HTTPResult HTTPClient::post(const char* url, const IHTTPDataOut& dataOut, IHTTPDataIn* pDataIn, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking 00081 { 00082 return connect(url, HTTP_POST, (IHTTPDataOut*)&dataOut, pDataIn, timeout); 00083 } 00084 00085 int HTTPClient::getHTTPResponseCode() 00086 { 00087 return m_httpResponseCode; 00088 } 00089 00090 #define CHECK_CONN_ERR(ret) \ 00091 do{ \ 00092 if(ret) { \ 00093 m_sock.close(); \ 00094 ERR("Connection error (%d)", ret); \ 00095 return HTTP_CONN; \ 00096 } \ 00097 } while(0) 00098 00099 #define PRTCL_ERR() \ 00100 do{ \ 00101 m_sock.close(); \ 00102 ERR("Protocol error"); \ 00103 return HTTP_PRTCL; \ 00104 } while(0) 00105 00106 HTTPResult HTTPClient::connect(const char* url, HTTP_METH method, IHTTPDataOut* pDataOut, IHTTPDataIn* pDataIn, int timeout) //Execute request 00107 { 00108 m_httpResponseCode = 0; //Invalidate code 00109 m_timeout = timeout; 00110 00111 char scheme[8]; 00112 uint16_t port; 00113 char host[32]; 00114 char path[64]; 00115 //First we need to parse the url (http[s]://host[:port][/[path]]) -- HTTPS not supported (yet?) 00116 HTTPResult res = parseURL(url, scheme, sizeof(scheme), host, sizeof(host), &port, path, sizeof(path)); 00117 if(res != HTTP_OK) 00118 { 00119 ERR("parseURL returned %d", res); 00120 return res; 00121 } 00122 00123 if(port == 0) //TODO do handle HTTPS->443 00124 { 00125 port = 80; 00126 } 00127 00128 DBG("Scheme: %s", scheme); 00129 DBG("Host: %s", host); 00130 DBG("Port: %d", port); 00131 DBG("Path: %s", path); 00132 00133 //Connect 00134 DBG("Connecting socket to server"); 00135 int ret = m_sock.connect(host, port); 00136 if (ret < 0) 00137 { 00138 m_sock.close(); 00139 ERR("Could not connect"); 00140 return HTTP_CONN; 00141 } 00142 00143 //Send request 00144 DBG("Sending request"); 00145 char buf[CHUNK_SIZE]; 00146 const char* meth = (method==HTTP_GET)?"GET":(method==HTTP_POST)?"POST":""; 00147 snprintf(buf, sizeof(buf), "%s %s HTTP/1.1\r\nHost: %s\r\n", meth, path, host); //Write request 00148 ret = send(buf); 00149 if(ret) 00150 { 00151 m_sock.close(); 00152 ERR("Could not write request"); 00153 return HTTP_CONN; 00154 } 00155 00156 //Send all headers 00157 00158 //Send default headers 00159 DBG("Sending headers"); 00160 if( (method == HTTP_POST) && (pDataOut != NULL) ) 00161 { 00162 if( pDataOut->getIsChunked() ) 00163 { 00164 ret = send("Transfer-Encoding: chunked\r\n"); 00165 CHECK_CONN_ERR(ret); 00166 } 00167 else 00168 { 00169 snprintf(buf, sizeof(buf), "Content-Length: %d\r\n", pDataOut->getDataLen()); 00170 ret = send(buf); 00171 CHECK_CONN_ERR(ret); 00172 } 00173 char type[48]; 00174 if( pDataOut->getDataType(type, 48) == HTTP_OK ) 00175 { 00176 snprintf(buf, sizeof(buf), "Content-Type: %s\r\n", type); 00177 ret = send(buf); 00178 CHECK_CONN_ERR(ret); 00179 } 00180 } 00181 00182 //Close headers 00183 DBG("Headers sent"); 00184 ret = send("\r\n"); 00185 CHECK_CONN_ERR(ret); 00186 00187 size_t trfLen; 00188 00189 //Send data (if POST) 00190 if( (method == HTTP_POST) && (pDataOut != NULL) ) 00191 { 00192 DBG("Sending data"); 00193 while(true) 00194 { 00195 size_t writtenLen = 0; 00196 pDataOut->read(buf, CHUNK_SIZE, &trfLen); 00197 if( pDataOut->getIsChunked() ) 00198 { 00199 //Write chunk header 00200 char chunkHeader[16]; 00201 snprintf(chunkHeader, sizeof(chunkHeader), "%X\r\n", trfLen); //In hex encoding 00202 ret = send(chunkHeader); 00203 CHECK_CONN_ERR(ret); 00204 } 00205 else if( trfLen == 0 ) 00206 { 00207 break; 00208 } 00209 if( trfLen != 0 ) 00210 { 00211 ret = send(buf, trfLen); 00212 CHECK_CONN_ERR(ret); 00213 } 00214 00215 if( pDataOut->getIsChunked() ) 00216 { 00217 ret = send("\r\n"); //Chunk-terminating CRLF 00218 CHECK_CONN_ERR(ret); 00219 } 00220 else 00221 { 00222 writtenLen += trfLen; 00223 if( writtenLen >= pDataOut->getDataLen() ) 00224 { 00225 break; 00226 } 00227 } 00228 00229 if( trfLen == 0 ) 00230 { 00231 break; 00232 } 00233 } 00234 00235 } 00236 00237 //Receive response 00238 DBG("Receiving response"); 00239 ret = recv(buf, CHUNK_SIZE - 1, CHUNK_SIZE - 1, &trfLen); //Read n bytes 00240 CHECK_CONN_ERR(ret); 00241 00242 buf[trfLen] = '\0'; 00243 00244 char* crlfPtr = strstr(buf, "\r\n"); 00245 if(crlfPtr == NULL) 00246 { 00247 PRTCL_ERR(); 00248 } 00249 00250 int crlfPos = crlfPtr - buf; 00251 buf[crlfPos] = '\0'; 00252 00253 //Parse HTTP response 00254 if( sscanf(buf, "HTTP/%*d.%*d %d %*[^\r\n]", &m_httpResponseCode) != 1 ) 00255 { 00256 //Cannot match string, error 00257 ERR("Not a correct HTTP answer : %s\n", buf); 00258 PRTCL_ERR(); 00259 } 00260 00261 if(m_httpResponseCode != 200) 00262 { 00263 //Cannot match string, error 00264 WARN("Response code %d", m_httpResponseCode); 00265 PRTCL_ERR(); 00266 } 00267 00268 DBG("Reading headers"); 00269 00270 memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2) + 1); //Be sure to move NULL-terminating char as well 00271 trfLen -= (crlfPos + 2); 00272 00273 size_t recvContentLength = 0; 00274 bool recvChunked = false; 00275 //Now get headers 00276 while( true ) 00277 { 00278 crlfPtr = strstr(buf, "\r\n"); 00279 if(crlfPtr == NULL) 00280 { 00281 if( trfLen < CHUNK_SIZE - 1 ) 00282 { 00283 size_t newTrfLen; 00284 ret = recv(buf + trfLen, 1, CHUNK_SIZE - trfLen - 1, &newTrfLen); 00285 trfLen += newTrfLen; 00286 buf[trfLen] = '\0'; 00287 DBG("Read %d chars; In buf: [%s]", newTrfLen, buf); 00288 CHECK_CONN_ERR(ret); 00289 continue; 00290 } 00291 else 00292 { 00293 PRTCL_ERR(); 00294 } 00295 } 00296 00297 crlfPos = crlfPtr - buf; 00298 00299 if(crlfPos == 0) //End of headers 00300 { 00301 DBG("Headers read"); 00302 memmove(buf, &buf[2], trfLen - 2 + 1); //Be sure to move NULL-terminating char as well 00303 trfLen -= 2; 00304 break; 00305 } 00306 00307 buf[crlfPos] = '\0'; 00308 00309 char key[64] = {0}; 00310 char value[32] = {0}; 00311 00312 int n = sscanf(buf, "%63[^:]: %31[^\r\n]", key, value); 00313 00314 if ( n == 2 ) 00315 { 00316 DBG("Read header : %s: %s\n", key, value); 00317 if( !strcmp(key, "Content-Length") ) 00318 { 00319 sscanf(value, "%d", &recvContentLength); 00320 pDataIn->setDataLen(recvContentLength); 00321 } 00322 else if( !strcmp(key, "Transfer-Encoding") ) 00323 { 00324 if( !strcmp(value, "Chunked") || !strcmp(value, "chunked") ) 00325 { 00326 recvChunked = true; 00327 pDataIn->setIsChunked(true); 00328 } 00329 } 00330 else if( !strcmp(key, "Content-Type") ) 00331 { 00332 pDataIn->setDataType(value); 00333 } 00334 00335 memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2) + 1); //Be sure to move NULL-terminating char as well 00336 trfLen -= (crlfPos + 2); 00337 00338 } 00339 else 00340 { 00341 ERR("Could not parse header"); 00342 PRTCL_ERR(); 00343 } 00344 00345 } 00346 00347 //Receive data 00348 DBG("Receiving data"); 00349 while(true) 00350 { 00351 size_t readLen = 0; 00352 00353 if( recvChunked ) 00354 { 00355 //Read chunk header 00356 crlfPos=0; 00357 for(crlfPos++; crlfPos < trfLen - 2; crlfPos++) 00358 { 00359 if( buf[crlfPos] == '\r' && buf[crlfPos + 1] == '\n' ) 00360 { 00361 break; 00362 } 00363 } 00364 if(crlfPos >= trfLen - 2) //Try to read more 00365 { 00366 if( trfLen < CHUNK_SIZE ) 00367 { 00368 size_t newTrfLen; 00369 ret = recv(buf + trfLen, 0, CHUNK_SIZE - trfLen - 1, &newTrfLen); 00370 trfLen += newTrfLen; 00371 CHECK_CONN_ERR(ret); 00372 continue; 00373 } 00374 else 00375 { 00376 PRTCL_ERR(); 00377 } 00378 } 00379 buf[crlfPos] = '\0'; 00380 int n = sscanf(buf, "%x", &readLen); 00381 if(n!=1) 00382 { 00383 ERR("Could not read chunk length"); 00384 PRTCL_ERR(); 00385 } 00386 00387 memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2)); //Not need to move NULL-terminating char any more 00388 trfLen -= (crlfPos + 2); 00389 00390 if( readLen == 0 ) 00391 { 00392 //Last chunk 00393 break; 00394 } 00395 } 00396 else 00397 { 00398 readLen = recvContentLength; 00399 } 00400 00401 DBG("Retrieving %d bytes", readLen); 00402 00403 do 00404 { 00405 pDataIn->write(buf, MIN(trfLen, readLen)); 00406 if( trfLen > readLen ) 00407 { 00408 memmove(buf, &buf[readLen], trfLen - readLen); 00409 trfLen -= readLen; 00410 readLen = 0; 00411 } 00412 else 00413 { 00414 readLen -= trfLen; 00415 } 00416 00417 if(readLen) 00418 { 00419 ret = recv(buf, 1, CHUNK_SIZE - trfLen - 1, &trfLen); 00420 CHECK_CONN_ERR(ret); 00421 } 00422 } while(readLen); 00423 00424 if( recvChunked ) 00425 { 00426 if(trfLen < 2) 00427 { 00428 size_t newTrfLen; 00429 //Read missing chars to find end of chunk 00430 ret = recv(buf, 2 - trfLen, CHUNK_SIZE, &newTrfLen); 00431 CHECK_CONN_ERR(ret); 00432 trfLen += newTrfLen; 00433 } 00434 if( (buf[0] != '\r') || (buf[1] != '\n') ) 00435 { 00436 ERR("Format error"); 00437 PRTCL_ERR(); 00438 } 00439 memmove(buf, &buf[2], trfLen - 2); 00440 trfLen -= 2; 00441 } 00442 else 00443 { 00444 break; 00445 } 00446 00447 } 00448 00449 m_sock.close(); 00450 DBG("Completed HTTP transaction"); 00451 00452 return HTTP_OK; 00453 } 00454 00455 HTTPResult HTTPClient::recv(char* buf, size_t minLen, size_t maxLen, size_t* pReadLen) //0 on success, err code on failure 00456 { 00457 DBG("Trying to read between %d and %d bytes", minLen, maxLen); 00458 size_t readLen = 0; 00459 00460 if(!m_sock.is_connected()) 00461 { 00462 WARN("Connection was closed by server"); 00463 return HTTP_CLOSED; //Connection was closed by server 00464 } 00465 00466 int ret; 00467 while(readLen < maxLen) 00468 { 00469 if(readLen < minLen) 00470 { 00471 DBG("Trying to read at most %d bytes [Blocking]", minLen - readLen); 00472 m_sock.set_blocking(false, m_timeout); 00473 ret = m_sock.receive_all(buf + readLen, minLen - readLen); 00474 } 00475 else 00476 { 00477 DBG("Trying to read at most %d bytes [Not blocking]", maxLen - readLen); 00478 m_sock.set_blocking(false, 0); 00479 ret = m_sock.receive(buf + readLen, maxLen - readLen); 00480 } 00481 00482 if( ret > 0) 00483 { 00484 readLen += ret; 00485 } 00486 else if( ret == 0 ) 00487 { 00488 break; 00489 } 00490 else 00491 { 00492 if(!m_sock.is_connected()) 00493 { 00494 ERR("Connection error (recv returned %d)", ret); 00495 *pReadLen = readLen; 00496 return HTTP_CONN; 00497 } 00498 else 00499 { 00500 break; 00501 } 00502 } 00503 00504 if(!m_sock.is_connected()) 00505 { 00506 break; 00507 } 00508 } 00509 DBG("Read %d bytes", readLen); 00510 *pReadLen = readLen; 00511 return HTTP_OK; 00512 } 00513 00514 HTTPResult HTTPClient::send(char* buf, size_t len) //0 on success, err code on failure 00515 { 00516 if(len == 0) 00517 { 00518 len = strlen(buf); 00519 } 00520 DBG("Trying to write %d bytes", len); 00521 size_t writtenLen = 0; 00522 00523 if(!m_sock.is_connected()) 00524 { 00525 WARN("Connection was closed by server"); 00526 return HTTP_CLOSED; //Connection was closed by server 00527 } 00528 00529 m_sock.set_blocking(false, m_timeout); 00530 int ret = m_sock.send_all(buf, len); 00531 if(ret > 0) 00532 { 00533 writtenLen += ret; 00534 } 00535 else if( ret == 0 ) 00536 { 00537 WARN("Connection was closed by server"); 00538 return HTTP_CLOSED; //Connection was closed by server 00539 } 00540 else 00541 { 00542 ERR("Connection error (send returned %d)", ret); 00543 return HTTP_CONN; 00544 } 00545 00546 DBG("Written %d bytes", writtenLen); 00547 return HTTP_OK; 00548 } 00549 00550 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 00551 { 00552 char* schemePtr = (char*) url; 00553 char* hostPtr = (char*) strstr(url, "://"); 00554 if(hostPtr == NULL) 00555 { 00556 WARN("Could not find host"); 00557 return HTTP_PARSE; //URL is invalid 00558 } 00559 00560 if( maxSchemeLen < hostPtr - schemePtr + 1 ) //including NULL-terminating char 00561 { 00562 WARN("Scheme str is too small (%d >= %d)", maxSchemeLen, hostPtr - schemePtr + 1); 00563 return HTTP_PARSE; 00564 } 00565 memcpy(scheme, schemePtr, hostPtr - schemePtr); 00566 scheme[hostPtr - schemePtr] = '\0'; 00567 00568 hostPtr+=3; 00569 00570 size_t hostLen = 0; 00571 00572 char* portPtr = strchr(hostPtr, ':'); 00573 if( portPtr != NULL ) 00574 { 00575 hostLen = portPtr - hostPtr; 00576 portPtr++; 00577 if( sscanf(portPtr, "%hu", port) != 1) 00578 { 00579 WARN("Could not find port"); 00580 return HTTP_PARSE; 00581 } 00582 } 00583 else 00584 { 00585 *port=0; 00586 } 00587 char* pathPtr = strchr(hostPtr, '/'); 00588 if( hostLen == 0 ) 00589 { 00590 hostLen = pathPtr - hostPtr; 00591 } 00592 00593 if( maxHostLen < hostLen + 1 ) //including NULL-terminating char 00594 { 00595 WARN("Host str is too small (%d >= %d)", maxHostLen, hostLen + 1); 00596 return HTTP_PARSE; 00597 } 00598 memcpy(host, hostPtr, hostLen); 00599 host[hostLen] = '\0'; 00600 00601 size_t pathLen; 00602 char* fragmentPtr = strchr(hostPtr, '#'); 00603 if(fragmentPtr != NULL) 00604 { 00605 pathLen = fragmentPtr - pathPtr; 00606 } 00607 else 00608 { 00609 pathLen = strlen(pathPtr); 00610 } 00611 00612 if( maxPathLen < pathLen + 1 ) //including NULL-terminating char 00613 { 00614 WARN("Path str is too small (%d >= %d)", maxPathLen, pathLen + 1); 00615 return HTTP_PARSE; 00616 } 00617 memcpy(path, pathPtr, pathLen); 00618 path[pathLen] = '\0'; 00619 00620 return HTTP_OK; 00621 }
Generated on Tue Jul 12 2022 11:02:39 by
1.7.2