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