HTTP Client Library
Dependents: EthernetHTTPClientTest
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 #define CHECK_CONN_ERR(ret) \ 00081 do{ \ 00082 if(ret != OK) { \ 00083 ::close(m_sock); \ 00084 ERR("Connection error (%d)", ret); \ 00085 return NET_CONN; \ 00086 } \ 00087 } while(0) 00088 00089 #define PRTCL_ERR() \ 00090 do{ \ 00091 ::close(m_sock); \ 00092 ERR("Protocol error"); \ 00093 return NET_PROTOCOL; \ 00094 } while(0) 00095 00096 int HTTPClient::connect(const char* url, HTTP_METH method, IHTTPDataOut* pDataOut, IHTTPDataIn* pDataIn, uint32_t timeout) //Execute request 00097 { 00098 m_httpResponseCode = 0; //Invalidate code 00099 m_timeout = timeout; 00100 00101 char scheme[8]; 00102 uint16_t port; 00103 char host[32]; 00104 char path[64]; 00105 //First we need to parse the url (http[s]://host[:port][/[path]]) -- HTTPS not supported (yet?) 00106 int ret = parseURL(url, scheme, sizeof(scheme), host, sizeof(host), &port, path, sizeof(path)); 00107 if(ret != OK) 00108 { 00109 ERR("parseURL returned %d", ret); 00110 return ret; 00111 } 00112 00113 if(port == 0) //TODO do handle HTTPS->443 00114 { 00115 port = 80; 00116 } 00117 00118 DBG("Scheme: %s", scheme); 00119 DBG("Host: %s", host); 00120 DBG("Port: %d", port); 00121 DBG("Path: %s", path); 00122 00123 //Now populate structure 00124 std::memset(&m_serverAddr, 0, sizeof(struct sockaddr_in)); 00125 00126 //Resolve DNS if needed 00127 00128 DBG("Resolving DNS address or populate hard-coded IP address"); 00129 struct hostent *server = ::gethostbyname(host); 00130 if(server == NULL) 00131 { 00132 return NET_NOTFOUND; //Fail 00133 } 00134 memcpy((char*)&m_serverAddr.sin_addr.s_addr, (char*)server->h_addr_list[0], server->h_length); 00135 00136 m_serverAddr.sin_family = AF_INET; 00137 m_serverAddr.sin_port = htons(port); 00138 00139 //Create socket 00140 DBG("Creating socket"); 00141 m_sock = ::socket(AF_INET, SOCK_STREAM, 0); //UDP socket 00142 if (m_sock < 0) 00143 { 00144 ERR("Could not create socket"); 00145 return NET_OOM; 00146 } 00147 DBG("Handle is %d", m_sock); 00148 00149 //Connect it 00150 DBG("Connecting socket to %s:%d", inet_ntoa(m_serverAddr.sin_addr), ntohs(m_serverAddr.sin_port)); 00151 ret = ::connect(m_sock, (const struct sockaddr *)&m_serverAddr, sizeof(m_serverAddr)); 00152 if (ret < 0) 00153 { 00154 ::close(m_sock); 00155 ERR("Could not connect"); 00156 return NET_CONN; 00157 } 00158 00159 //Send request 00160 DBG("Sending request"); 00161 char line[128]; 00162 const char* meth = (method==HTTP_GET)?"GET":(method==HTTP_POST)?"POST":""; 00163 snprintf(line, sizeof(line), "%s %s HTTP/1.1\r\nHost: %s\r\n", meth, path, host); //Write request 00164 ret = send(line); 00165 if(ret) 00166 { 00167 ::close(m_sock); 00168 ERR("Could not write request"); 00169 return NET_CONN; 00170 } 00171 00172 //Send all headers 00173 00174 //Send default headers 00175 DBG("Sending headers"); 00176 if( (method == HTTP_POST) && (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(line, sizeof(line), "Content-Length: %d\r\n", pDataOut->getDataLen()); 00186 ret = send(line); 00187 CHECK_CONN_ERR(ret); 00188 } 00189 char type[48]; 00190 if( pDataOut->getDataType(type, 48) == OK ) 00191 { 00192 snprintf(line, sizeof(line), "Content-Type: %s\r\n", type); 00193 ret = send(line); 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 char buf[CHUNK_SIZE]; 00204 size_t trfLen; 00205 00206 //Send data (if POST) 00207 if( (method == HTTP_POST) && (pDataOut != NULL) ) 00208 { 00209 DBG("Sending data"); 00210 while(true) 00211 { 00212 size_t writtenLen = 0; 00213 pDataOut->read(buf, CHUNK_SIZE, &trfLen); 00214 if( pDataOut->getIsChunked() ) 00215 { 00216 //Write chunk header 00217 snprintf(line, sizeof(line), "%X\r\n", trfLen); //In hex encoding 00218 ret = send(line); 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) 00278 { 00279 //Cannot match string, error 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 crlfPos=0; 00375 for(crlfPos++; crlfPos < trfLen - 2; crlfPos++) 00376 { 00377 if( buf[crlfPos] == '\r' && buf[crlfPos + 1] == '\n' ) 00378 { 00379 break; 00380 } 00381 } 00382 if(crlfPos >= trfLen - 2) //Try to read more 00383 { 00384 if( trfLen < CHUNK_SIZE ) 00385 { 00386 size_t newTrfLen; 00387 ret = recv(buf + trfLen, 0, CHUNK_SIZE - trfLen - 1, &newTrfLen); 00388 trfLen += newTrfLen; 00389 CHECK_CONN_ERR(ret); 00390 continue; 00391 } 00392 else 00393 { 00394 PRTCL_ERR(); 00395 } 00396 } 00397 buf[crlfPos] = '\0'; 00398 int n = sscanf(buf, "%x", &readLen); 00399 if(n!=1) 00400 { 00401 ERR("Could not read chunk length"); 00402 PRTCL_ERR(); 00403 } 00404 00405 memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2)); //Not need to move NULL-terminating char any more 00406 trfLen -= (crlfPos + 2); 00407 00408 if( readLen == 0 ) 00409 { 00410 //Last chunk 00411 break; 00412 } 00413 } 00414 else 00415 { 00416 readLen = recvContentLength; 00417 } 00418 00419 DBG("Retrieving %d bytes", readLen); 00420 00421 do 00422 { 00423 pDataIn->write(buf, MIN(trfLen, readLen)); 00424 if( trfLen > readLen ) 00425 { 00426 memmove(buf, &buf[readLen], trfLen - readLen); 00427 trfLen -= readLen; 00428 readLen = 0; 00429 } 00430 else 00431 { 00432 readLen -= trfLen; 00433 } 00434 00435 if(readLen) 00436 { 00437 ret = recv(buf, 1, CHUNK_SIZE - trfLen - 1, &trfLen); 00438 CHECK_CONN_ERR(ret); 00439 } 00440 } while(readLen); 00441 00442 if( recvChunked ) 00443 { 00444 if(trfLen < 2) 00445 { 00446 size_t newTrfLen; 00447 //Read missing chars to find end of chunk 00448 ret = recv(buf, 2 - trfLen, CHUNK_SIZE, &newTrfLen); 00449 CHECK_CONN_ERR(ret); 00450 trfLen += newTrfLen; 00451 } 00452 if( (buf[0] != '\r') || (buf[1] != '\n') ) 00453 { 00454 ERR("Format error"); 00455 PRTCL_ERR(); 00456 } 00457 memmove(buf, &buf[2], trfLen - 2); 00458 trfLen -= 2; 00459 } 00460 else 00461 { 00462 break; 00463 } 00464 00465 } 00466 00467 ::close(m_sock); 00468 DBG("Completed HTTP transaction"); 00469 00470 return OK; 00471 } 00472 00473 int HTTPClient::recv(char* buf, size_t minLen, size_t maxLen, size_t* pReadLen) //0 on success, err code on failure 00474 { 00475 DBG("Trying to read between %d and %d bytes", minLen, maxLen); 00476 size_t readLen = 0; 00477 while(readLen < minLen) 00478 { 00479 //Wait for socket to be readable 00480 //Creating FS set 00481 fd_set socksSet; 00482 FD_ZERO(&socksSet); 00483 FD_SET(m_sock, &socksSet); 00484 struct timeval t_val; 00485 t_val.tv_sec = m_timeout / 1000; 00486 t_val.tv_usec = (m_timeout - (t_val.tv_sec * 1000)) * 1000; 00487 int ret = ::select(FD_SETSIZE, &socksSet, NULL, NULL, &t_val); 00488 if(ret <= 0 || !FD_ISSET(m_sock, &socksSet)) 00489 { 00490 WARN("Timeout"); 00491 return NET_TIMEOUT; //Timeout 00492 } 00493 00494 ret = ::recv(m_sock, buf + readLen, maxLen - readLen, 0); 00495 if( ret > 0) 00496 { 00497 readLen += ret; 00498 continue; 00499 } 00500 else if( ret == 0 ) 00501 { 00502 WARN("Connection was closed by server"); 00503 return NET_CLOSED; //Connection was closed by server 00504 } 00505 else 00506 { 00507 ERR("Connection error (recv returned %d)", ret); 00508 return NET_CONN; 00509 } 00510 } 00511 *pReadLen = readLen; 00512 DBG("Read %d bytes", readLen); 00513 return OK; 00514 } 00515 00516 int HTTPClient::send(char* buf, size_t len) //0 on success, err code on failure 00517 { 00518 if(len == 0) 00519 { 00520 len = strlen(buf); 00521 } 00522 DBG("Trying to write %d bytes", len); 00523 size_t writtenLen = 0; 00524 while(writtenLen < len) 00525 { 00526 //Wait for socket to be writeable 00527 //Creating FS set 00528 fd_set socksSet; 00529 FD_ZERO(&socksSet); 00530 FD_SET(m_sock, &socksSet); 00531 struct timeval t_val; 00532 t_val.tv_sec = m_timeout / 1000; 00533 t_val.tv_usec = (m_timeout - (t_val.tv_sec * 1000)) * 1000; 00534 int ret = ::select(FD_SETSIZE, NULL, &socksSet, NULL, &t_val); 00535 if(ret <= 0 || !FD_ISSET(m_sock, &socksSet)) 00536 { 00537 WARN("Timeout"); 00538 return NET_TIMEOUT; //Timeout 00539 } 00540 00541 ret = ::send(m_sock, buf + writtenLen, len - writtenLen, 0); 00542 if( ret > 0) 00543 { 00544 writtenLen += ret; 00545 continue; 00546 } 00547 else if( ret == 0 ) 00548 { 00549 WARN("Connection was closed by server"); 00550 return NET_CLOSED; //Connection was closed by server 00551 } 00552 else 00553 { 00554 ERR("Connection error (recv returned %d)", ret); 00555 return NET_CONN; 00556 } 00557 } 00558 DBG("Written %d bytes", writtenLen); 00559 return OK; 00560 } 00561 00562 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 00563 { 00564 char* schemePtr = (char*) url; 00565 char* hostPtr = (char*) strstr(url, "://"); 00566 if(hostPtr == NULL) 00567 { 00568 WARN("Could not find host"); 00569 return NET_INVALID; //URL is invalid 00570 } 00571 00572 if( maxSchemeLen < hostPtr - schemePtr + 1 ) //including NULL-terminating char 00573 { 00574 WARN("Scheme str is too small (%d >= %d)", maxSchemeLen, hostPtr - schemePtr + 1); 00575 return NET_TOOSMALL; 00576 } 00577 memcpy(scheme, schemePtr, hostPtr - schemePtr); 00578 scheme[hostPtr - schemePtr] = '\0'; 00579 00580 hostPtr+=3; 00581 00582 size_t hostLen = 0; 00583 00584 char* portPtr = strchr(hostPtr, ':'); 00585 if( portPtr != NULL ) 00586 { 00587 hostLen = portPtr - hostPtr; 00588 portPtr++; 00589 if( sscanf(portPtr, "%d", &port) != 1) 00590 { 00591 WARN("Could not find port"); 00592 return NET_INVALID; 00593 } 00594 } 00595 else 00596 { 00597 *port=0; 00598 } 00599 char* pathPtr = strchr(hostPtr, '/'); 00600 if( hostLen == 0 ) 00601 { 00602 hostLen = pathPtr - hostPtr; 00603 } 00604 00605 if( maxHostLen < hostLen + 1 ) //including NULL-terminating char 00606 { 00607 WARN("Host str is too small (%d >= %d)", maxHostLen, hostLen + 1); 00608 return NET_TOOSMALL; 00609 } 00610 memcpy(host, hostPtr, hostLen); 00611 host[hostLen] = '\0'; 00612 00613 size_t pathLen; 00614 char* fragmentPtr = strchr(hostPtr, '#'); 00615 if(fragmentPtr != NULL) 00616 { 00617 pathLen = fragmentPtr - pathPtr; 00618 } 00619 else 00620 { 00621 pathLen = strlen(pathPtr); 00622 } 00623 00624 if( maxPathLen < pathLen + 1 ) //including NULL-terminating char 00625 { 00626 WARN("Path str is too small (%d >= %d)", maxPathLen, pathLen + 1); 00627 return NET_TOOSMALL; 00628 } 00629 memcpy(path, pathPtr, pathLen); 00630 path[pathLen] = '\0'; 00631 00632 return OK; 00633 }
Generated on Wed Jul 13 2022 16:09:36 by 1.7.2