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.
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 17:34:43 by
1.7.2