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