HTTP Client library
Dependents: weather_LCD_display News_LCD_display TwitterExample_1 GeoLocation_LCD_Display ... more
HTTPClient.cpp
00001 /* HTTPClient.cpp */ 00002 /* 00003 Copyright (C) 2012 ARM Limited. 00004 00005 Permission is hereby granted, free of charge, to any person obtaining a copy of 00006 this software and associated documentation files (the "Software"), to deal in 00007 the Software without restriction, including without limitation the rights to 00008 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 00009 of the Software, and to permit persons to whom the Software is furnished to do 00010 so, subject to the following conditions: 00011 00012 The above copyright notice and this permission notice shall be included in all 00013 copies or substantial portions of the Software. 00014 00015 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 00016 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 00017 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 00018 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 00019 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 00020 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 00021 SOFTWARE. 00022 */ 00023 00024 #define __DEBUG__ 4 //Maximum verbosity 00025 #ifndef __MODULE__ 00026 #define __MODULE__ "HTTPClient.cpp" 00027 #endif 00028 00029 #include "core/fwk.h" 00030 00031 #include "HTTPClient.h" 00032 00033 #define HTTP_REQUEST_TIMEOUT 30000 00034 #define HTTP_PORT 80 00035 00036 #define CHUNK_SIZE 256 00037 00038 #include <cstring> 00039 00040 HTTPClient::HTTPClient() : 00041 m_basicAuthUser(NULL), m_basicAuthPassword(NULL), m_httpResponseCode(0) 00042 { 00043 00044 } 00045 00046 HTTPClient::~HTTPClient() 00047 { 00048 00049 } 00050 00051 #if 0 00052 void HTTPClient::basicAuth(const char* user, const char* password) //Basic Authentification 00053 { 00054 m_basicAuthUser = user; 00055 m_basicAuthPassword = password; 00056 } 00057 #endif 00058 00059 int HTTPClient::get(const char* url, IHTTPDataIn* pDataIn, uint32_t timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking 00060 { 00061 return connect(url, HTTP_GET, NULL, pDataIn, timeout); 00062 } 00063 00064 int HTTPClient::get(const char* url, char* result, size_t maxResultLen, uint32_t timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking 00065 { 00066 HTTPText str(result, maxResultLen); 00067 return get(url, &str, timeout); 00068 } 00069 00070 int HTTPClient::post(const char* url, const IHTTPDataOut& dataOut, IHTTPDataIn* pDataIn, uint32_t timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking 00071 { 00072 return connect(url, HTTP_POST, (IHTTPDataOut*)&dataOut, pDataIn, timeout); 00073 } 00074 00075 int HTTPClient::getHTTPResponseCode() 00076 { 00077 return m_httpResponseCode; 00078 } 00079 00080 00081 int HTTPClient::connect(const char* url, HTTP_METH method, IHTTPDataOut* pDataOut, IHTTPDataIn* pDataIn, uint32_t timeout) //Execute request 00082 { 00083 m_httpResponseCode = 0; //Invalidate code 00084 m_timeout = timeout; 00085 00086 char scheme[8]; 00087 uint16_t port; 00088 char host[32]; 00089 char path[64]; 00090 //First we need to parse the url (http[s]://host[:port][/[path]]) -- HTTPS not supported (yet?) 00091 int ret = parseURL(url, scheme, sizeof(scheme), host, sizeof(host), &port, path, sizeof(path)); 00092 if(ret != OK) 00093 { 00094 ERR("parseURL returned %d", ret); 00095 return ret; 00096 } 00097 00098 if(port == 0) //TODO do handle HTTPS->443 00099 { 00100 port = 80; 00101 } 00102 00103 DBG("Scheme: %s", scheme); 00104 DBG("Host: %s", host); 00105 DBG("Port: %d", port); 00106 DBG("Path: %s", path); 00107 00108 //Now populate structure 00109 std::memset(&m_serverAddr, 0, sizeof(struct sockaddr_in)); 00110 00111 //Resolve DNS if needed 00112 00113 DBG("Resolving DNS address or populate hard-coded IP address"); 00114 struct hostent *server = socket::gethostbyname(host); 00115 if(server == NULL) 00116 { 00117 return NET_NOTFOUND; //Fail 00118 } 00119 memcpy((char*)&m_serverAddr.sin_addr.s_addr, (char*)server->h_addr_list[0], server->h_length); 00120 00121 m_serverAddr.sin_family = AF_INET; 00122 m_serverAddr.sin_port = htons(port); 00123 00124 //Create socket 00125 DBG("Creating socket"); 00126 m_sock = socket::socket(AF_INET, SOCK_STREAM, 0); //UDP socket 00127 if (m_sock < 0) 00128 { 00129 ERR("Could not create socket"); 00130 return NET_OOM; 00131 } 00132 DBG("Handle is %d", m_sock); 00133 00134 //Connect it 00135 DBG("Connecting socket to %s:%d", inet_ntoa(m_serverAddr.sin_addr), ntohs(m_serverAddr.sin_port)); 00136 ret = socket::connect(m_sock, (const struct sockaddr *)&m_serverAddr, sizeof(m_serverAddr)); 00137 if (ret < 0) 00138 { 00139 socket::close(m_sock); 00140 ERR("Could not connect"); 00141 return NET_CONN; 00142 } 00143 00144 //Send request 00145 DBG("Sending request"); 00146 char line[128]; 00147 const char* meth = (method==HTTP_GET)?"GET":(method==HTTP_POST)?"POST":""; 00148 snprintf(line, sizeof(line), "%s %s HTTP/1.1\r\nHost: %s\r\n", meth, path, host); //Write request 00149 ret = send(line); 00150 if(ret) 00151 { 00152 socket::close(m_sock); 00153 ERR("Could not write request"); 00154 return NET_CONN; 00155 } 00156 00157 //Send all headers 00158 00159 //Send default headers 00160 DBG("Sending headers"); 00161 if( (method == HTTP_POST) && (pDataOut != NULL) ) 00162 { 00163 if( pDataOut->getIsChunked() ) 00164 { 00165 ret = send("Transfer-Encoding: chunked\r\n"); 00166 if(ret != OK) goto connerr; 00167 } 00168 else 00169 { 00170 snprintf(line, sizeof(line), "Content-Length: %d\r\n", pDataOut->getDataLen()); 00171 ret = send(line); 00172 if(ret != OK) goto connerr; 00173 } 00174 char type[48]; 00175 if( pDataOut->getDataType(type, 48) == OK ) 00176 { 00177 snprintf(line, sizeof(line), "Content-Type: %s\r\n", type); 00178 ret = send(line); 00179 if(ret != OK) goto connerr; 00180 } 00181 } 00182 00183 //Close headers 00184 DBG("Headers sent"); 00185 ret = send("\r\n"); 00186 if(ret != OK) goto connerr; 00187 00188 char buf[CHUNK_SIZE]; 00189 size_t trfLen; 00190 00191 //Send data (if POST) 00192 if( (method == HTTP_POST) && (pDataOut != NULL) ) 00193 { 00194 DBG("Sending data"); 00195 while(true) 00196 { 00197 size_t writtenLen = 0; 00198 pDataOut->read(buf, CHUNK_SIZE, &trfLen); 00199 if( pDataOut->getIsChunked() ) 00200 { 00201 //Write chunk header 00202 snprintf(line, sizeof(line), "%X\r\n", trfLen); //In hex encoding 00203 ret = send(line); 00204 if(ret != OK) goto connerr; 00205 } 00206 else if( trfLen == 0 ) 00207 { 00208 break; 00209 } 00210 if( trfLen != 0 ) 00211 { 00212 ret = send(buf, trfLen); 00213 if(ret != OK) goto connerr; 00214 } 00215 00216 if( pDataOut->getIsChunked() ) 00217 { 00218 ret = send("\r\n"); //Chunk-terminating CRLF 00219 if(ret != OK) goto connerr; 00220 } 00221 else 00222 { 00223 writtenLen += trfLen; 00224 if( writtenLen >= pDataOut->getDataLen() ) 00225 { 00226 break; 00227 } 00228 } 00229 00230 if( trfLen == 0 ) 00231 { 00232 break; 00233 } 00234 } 00235 00236 } 00237 00238 //Receive response 00239 DBG("Receiving response"); 00240 ret = recv(buf, CHUNK_SIZE, CHUNK_SIZE, &trfLen); //Read n bytes 00241 if(ret != OK) goto connerr; 00242 00243 buf[trfLen] = '\0'; 00244 00245 char* crlfPtr = strstr(buf, "\r\n"); 00246 if(crlfPtr == NULL) 00247 { 00248 goto prtclerr; 00249 } 00250 00251 int crlfPos = crlfPtr - buf; 00252 buf[crlfPos] = '\0'; 00253 00254 //Parse HTTP response 00255 if( sscanf(buf, "HTTP/%*d.%*d %d %*[^\r\n]", &m_httpResponseCode) != 1 ) 00256 { 00257 //Cannot match string, error 00258 ERR("Not a correct HTTP answer : %s\n", buf); 00259 goto prtclerr; 00260 } 00261 00262 if(m_httpResponseCode != 200) 00263 { 00264 //Cannot match string, error 00265 WARN("Response code %d", m_httpResponseCode); 00266 goto prtclerr; 00267 } 00268 00269 DBG("Reading headers"); 00270 00271 memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2)); 00272 trfLen -= (crlfPos + 2); 00273 00274 size_t recvContentLength = 0; 00275 bool recvChunked = false; 00276 //Now get headers 00277 while( true ) 00278 { 00279 crlfPtr = strstr(buf, "\r\n"); 00280 if(crlfPtr == NULL) 00281 { 00282 if( trfLen < CHUNK_SIZE ) 00283 { 00284 size_t newTrfLen; 00285 ret = recv(buf + trfLen, 1, CHUNK_SIZE - trfLen - 1, &newTrfLen); 00286 trfLen += newTrfLen; 00287 buf[trfLen] = '\0'; 00288 DBG("In buf: [%s]", buf); 00289 if(ret != OK) goto connerr; 00290 continue; 00291 } 00292 else 00293 { 00294 goto prtclerr; 00295 } 00296 } 00297 00298 crlfPos = crlfPtr - buf; 00299 00300 if(crlfPos == 0) //End of headers 00301 { 00302 DBG("Headers read"); 00303 memmove(buf, &buf[2], trfLen - 2); 00304 trfLen -= 2; 00305 break; 00306 } 00307 00308 buf[crlfPos] = '\0'; 00309 00310 char key[16]; 00311 char value[16]; 00312 00313 int n = sscanf(buf, "%16[^:]: %16[^\r\n]", key, value); 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)); 00336 trfLen -= (crlfPos + 2); 00337 00338 } 00339 else 00340 { 00341 ERR("Could not parse header"); 00342 goto prtclerr; 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 if(ret != OK) goto connerr; 00372 continue; 00373 } 00374 else 00375 { 00376 goto prtclerr; 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 goto prtclerr; 00385 } 00386 00387 memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2)); 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 if(ret != OK) goto connerr; 00421 00422 } 00423 } while(readLen); 00424 00425 if( recvChunked ) 00426 { 00427 if(trfLen < 2) 00428 { 00429 size_t newTrfLen; 00430 //Read missing chars to find end of chunk 00431 ret = recv(buf, 2 - trfLen, CHUNK_SIZE, &newTrfLen); 00432 if(ret != OK) goto connerr; 00433 trfLen += newTrfLen; 00434 } 00435 if( (buf[0] != '\r') || (buf[1] != '\n') ) 00436 { 00437 ERR("Format error"); 00438 goto prtclerr; 00439 } 00440 memmove(buf, &buf[2], trfLen - 2); 00441 trfLen -= 2; 00442 } 00443 else 00444 { 00445 break; 00446 } 00447 00448 } 00449 00450 socket::close(m_sock); 00451 DBG("Completed HTTP transaction"); 00452 00453 return OK; 00454 00455 connerr: 00456 socket::close(m_sock); 00457 ERR("Connection error (%d)", ret); 00458 return NET_CONN; 00459 00460 prtclerr: 00461 socket::close(m_sock); 00462 ERR("Protocol error"); 00463 return NET_PROTOCOL; 00464 00465 } 00466 00467 int HTTPClient::recv(char* buf, size_t minLen, size_t maxLen, size_t* pReadLen) //0 on success, err code on failure 00468 { 00469 DBG("Trying to read between %d and %d bytes", minLen, maxLen); 00470 size_t readLen = 0; 00471 while(readLen < minLen) 00472 { 00473 //Wait for socket to be readable 00474 //Creating FS set 00475 fd_set socksSet; 00476 FD_ZERO(&socksSet); 00477 FD_SET(m_sock, &socksSet); 00478 struct timeval t_val; 00479 t_val.tv_sec = m_timeout / 1000; 00480 t_val.tv_usec = (m_timeout - (t_val.tv_sec * 1000)) * 1000; 00481 int ret = socket::select(FD_SETSIZE, &socksSet, NULL, NULL, &t_val); 00482 if(ret <= 0 || !FD_ISSET(m_sock, &socksSet)) 00483 { 00484 WARN("Timeout"); 00485 return NET_TIMEOUT; //Timeout 00486 } 00487 00488 ret = socket::recv(m_sock, buf + readLen, maxLen - readLen, 0); 00489 if( ret > 0) 00490 { 00491 readLen += ret; 00492 continue; 00493 } 00494 else if( ret == 0 ) 00495 { 00496 WARN("Connection was closed by server"); 00497 return NET_CLOSED; //Connection was closed by server 00498 } 00499 else 00500 { 00501 ERR("Connection error (recv returned %d)", ret); 00502 return NET_CONN; 00503 } 00504 } 00505 *pReadLen = readLen; 00506 DBG("Read %d bytes", readLen); 00507 return OK; 00508 } 00509 00510 int HTTPClient::send(char* buf, size_t len) //0 on success, err code on failure 00511 { 00512 if(len == 0) 00513 { 00514 len = strlen(buf); 00515 } 00516 DBG("Trying to write %d bytes", len); 00517 size_t writtenLen = 0; 00518 while(writtenLen < len) 00519 { 00520 //Wait for socket to be writeable 00521 //Creating FS set 00522 fd_set socksSet; 00523 FD_ZERO(&socksSet); 00524 FD_SET(m_sock, &socksSet); 00525 struct timeval t_val; 00526 t_val.tv_sec = m_timeout / 1000; 00527 t_val.tv_usec = (m_timeout - (t_val.tv_sec * 1000)) * 1000; 00528 int ret = socket::select(FD_SETSIZE, NULL, &socksSet, NULL, &t_val); 00529 if(ret <= 0 || !FD_ISSET(m_sock, &socksSet)) 00530 { 00531 WARN("Timeout"); 00532 return NET_TIMEOUT; //Timeout 00533 } 00534 00535 ret = socket::send(m_sock, buf + writtenLen, len - writtenLen, 0); 00536 if( ret > 0) 00537 { 00538 writtenLen += ret; 00539 continue; 00540 } 00541 else if( ret == 0 ) 00542 { 00543 WARN("Connection was closed by server"); 00544 return NET_CLOSED; //Connection was closed by server 00545 } 00546 else 00547 { 00548 ERR("Connection error (recv returned %d)", ret); 00549 return NET_CONN; 00550 } 00551 } 00552 DBG("Written %d bytes", writtenLen); 00553 return OK; 00554 } 00555 00556 int 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 00557 { 00558 char* schemePtr = (char*) url; 00559 char* hostPtr = (char*) strstr(url, "://"); 00560 if(hostPtr == NULL) 00561 { 00562 WARN("Could not find host"); 00563 return NET_INVALID; //URL is invalid 00564 } 00565 00566 if( maxSchemeLen < hostPtr - schemePtr + 1 ) //including NULL-terminating char 00567 { 00568 WARN("Scheme str is too small (%d >= %d)", maxSchemeLen, hostPtr - schemePtr + 1); 00569 return NET_TOOSMALL; 00570 } 00571 memcpy(scheme, schemePtr, hostPtr - schemePtr); 00572 scheme[hostPtr - schemePtr] = '\0'; 00573 00574 hostPtr+=3; 00575 00576 size_t hostLen = 0; 00577 00578 char* portPtr = strchr(hostPtr, ':'); 00579 if( portPtr != NULL ) 00580 { 00581 hostLen = portPtr - hostPtr; 00582 portPtr++; 00583 if( sscanf(portPtr, "%d", &port) != 1) 00584 { 00585 WARN("Could not find port"); 00586 return NET_INVALID; 00587 } 00588 } 00589 else 00590 { 00591 *port=0; 00592 } 00593 char* pathPtr = strchr(hostPtr, '/'); 00594 if( hostLen == 0 ) 00595 { 00596 hostLen = pathPtr - hostPtr; 00597 } 00598 00599 if( maxHostLen < hostLen + 1 ) //including NULL-terminating char 00600 { 00601 WARN("Host str is too small (%d >= %d)", maxHostLen, hostLen + 1); 00602 return NET_TOOSMALL; 00603 } 00604 memcpy(host, hostPtr, hostLen); 00605 host[hostLen] = '\0'; 00606 00607 size_t pathLen; 00608 char* fragmentPtr = strchr(hostPtr, '#'); 00609 if(fragmentPtr != NULL) 00610 { 00611 pathLen = fragmentPtr - pathPtr; 00612 } 00613 else 00614 { 00615 pathLen = strlen(pathPtr); 00616 } 00617 00618 if( maxPathLen < pathLen + 1 ) //including NULL-terminating char 00619 { 00620 WARN("Path str is too small (%d >= %d)", maxPathLen, pathLen + 1); 00621 return NET_TOOSMALL; 00622 } 00623 memcpy(path, pathPtr, pathLen); 00624 path[pathLen] = '\0'; 00625 00626 return OK; 00627 } 00628
Generated on Tue Jul 12 2022 20:54:55 by 1.7.2