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