Embed:
(wiki syntax)
Show/hide line numbers
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 - 1, CHUNK_SIZE - 1, &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) + 1); //Be sure to move NULL-terminating char as well 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 - 1 ) 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("Read %d chars; In buf: [%s]", newTrfLen, 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 + 1); //Be sure to move NULL-terminating char as well 00304 trfLen -= 2; 00305 break; 00306 } 00307 00308 buf[crlfPos] = '\0'; 00309 00310 char key[32]; 00311 char value[32]; 00312 00313 key[31] = '\0'; 00314 value[31] = '\0'; 00315 00316 int n = sscanf(buf, "%31[^:]: %31[^\r\n]", key, value); 00317 if ( n == 2 ) 00318 { 00319 DBG("Read header : %s: %s\n", key, value); 00320 if( !strcmp(key, "Content-Length") ) 00321 { 00322 sscanf(value, "%d", &recvContentLength); 00323 pDataIn->setDataLen(recvContentLength); 00324 } 00325 else if( !strcmp(key, "Transfer-Encoding") ) 00326 { 00327 if( !strcmp(value, "Chunked") || !strcmp(value, "chunked") ) 00328 { 00329 recvChunked = true; 00330 pDataIn->setIsChunked(true); 00331 } 00332 } 00333 else if( !strcmp(key, "Content-Type") ) 00334 { 00335 pDataIn->setDataType(value); 00336 } 00337 00338 memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2) + 1); //Be sure to move NULL-terminating char as well 00339 trfLen -= (crlfPos + 2); 00340 00341 } 00342 else 00343 { 00344 ERR("Could not parse header"); 00345 goto prtclerr; 00346 } 00347 00348 } 00349 00350 //Receive data 00351 DBG("Receiving data"); 00352 while(true) 00353 { 00354 size_t readLen = 0; 00355 00356 if( recvChunked ) 00357 { 00358 //Read chunk header 00359 crlfPos=0; 00360 for(crlfPos++; crlfPos < trfLen - 2; crlfPos++) 00361 { 00362 if( buf[crlfPos] == '\r' && buf[crlfPos + 1] == '\n' ) 00363 { 00364 break; 00365 } 00366 } 00367 if(crlfPos >= trfLen - 2) //Try to read more 00368 { 00369 if( trfLen < CHUNK_SIZE ) 00370 { 00371 size_t newTrfLen; 00372 ret = recv(buf + trfLen, 0, CHUNK_SIZE - trfLen - 1, &newTrfLen); 00373 trfLen += newTrfLen; 00374 if(ret != OK) goto connerr; 00375 continue; 00376 } 00377 else 00378 { 00379 goto prtclerr; 00380 } 00381 } 00382 buf[crlfPos] = '\0'; 00383 int n = sscanf(buf, "%x", &readLen); 00384 if(n!=1) 00385 { 00386 ERR("Could not read chunk length"); 00387 goto prtclerr; 00388 } 00389 00390 memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2)); //Not need to move NULL-terminating char any more 00391 trfLen -= (crlfPos + 2); 00392 00393 if( readLen == 0 ) 00394 { 00395 //Last chunk 00396 break; 00397 } 00398 } 00399 else 00400 { 00401 readLen = recvContentLength; 00402 } 00403 00404 DBG("Retrieving %d bytes", readLen); 00405 00406 do 00407 { 00408 pDataIn->write(buf, MIN(trfLen, readLen)); 00409 if( trfLen > readLen ) 00410 { 00411 memmove(buf, &buf[readLen], trfLen - readLen); 00412 trfLen -= readLen; 00413 readLen = 0; 00414 } 00415 else 00416 { 00417 readLen -= trfLen; 00418 } 00419 00420 if(readLen) 00421 { 00422 ret = recv(buf, 1, CHUNK_SIZE - trfLen - 1, &trfLen); 00423 if(ret != OK) goto connerr; 00424 } 00425 } while(readLen); 00426 00427 if( recvChunked ) 00428 { 00429 if(trfLen < 2) 00430 { 00431 size_t newTrfLen; 00432 //Read missing chars to find end of chunk 00433 ret = recv(buf, 2 - trfLen, CHUNK_SIZE, &newTrfLen); 00434 if(ret != OK) goto connerr; 00435 trfLen += newTrfLen; 00436 } 00437 if( (buf[0] != '\r') || (buf[1] != '\n') ) 00438 { 00439 ERR("Format error"); 00440 goto prtclerr; 00441 } 00442 memmove(buf, &buf[2], trfLen - 2); 00443 trfLen -= 2; 00444 } 00445 else 00446 { 00447 break; 00448 } 00449 00450 } 00451 00452 socket::close(m_sock); 00453 DBG("Completed HTTP transaction"); 00454 00455 return OK; 00456 00457 connerr: 00458 socket::close(m_sock); 00459 ERR("Connection error (%d)", ret); 00460 return NET_CONN; 00461 00462 prtclerr: 00463 socket::close(m_sock); 00464 ERR("Protocol error"); 00465 return NET_PROTOCOL; 00466 00467 } 00468 00469 int HTTPClient::recv(char* buf, size_t minLen, size_t maxLen, size_t* pReadLen) //0 on success, err code on failure 00470 { 00471 DBG("Trying to read between %d and %d bytes", minLen, maxLen); 00472 size_t readLen = 0; 00473 while(readLen < minLen) 00474 { 00475 //Wait for socket to be readable 00476 //Creating FS set 00477 fd_set socksSet; 00478 FD_ZERO(&socksSet); 00479 FD_SET(m_sock, &socksSet); 00480 struct timeval t_val; 00481 t_val.tv_sec = m_timeout / 1000; 00482 t_val.tv_usec = (m_timeout - (t_val.tv_sec * 1000)) * 1000; 00483 int ret = socket::select(FD_SETSIZE, &socksSet, NULL, NULL, &t_val); 00484 if(ret <= 0 || !FD_ISSET(m_sock, &socksSet)) 00485 { 00486 WARN("Timeout"); 00487 return NET_TIMEOUT; //Timeout 00488 } 00489 00490 ret = socket::recv(m_sock, buf + readLen, maxLen - readLen, 0); 00491 if( ret > 0) 00492 { 00493 readLen += ret; 00494 continue; 00495 } 00496 else if( ret == 0 ) 00497 { 00498 WARN("Connection was closed by server"); 00499 return NET_CLOSED; //Connection was closed by server 00500 } 00501 else 00502 { 00503 ERR("Connection error (recv returned %d)", ret); 00504 return NET_CONN; 00505 } 00506 } 00507 *pReadLen = readLen; 00508 DBG("Read %d bytes", readLen); 00509 return OK; 00510 } 00511 00512 int HTTPClient::send(char* buf, size_t len) //0 on success, err code on failure 00513 { 00514 if(len == 0) 00515 { 00516 len = strlen(buf); 00517 } 00518 DBG("Trying to write %d bytes", len); 00519 size_t writtenLen = 0; 00520 while(writtenLen < len) 00521 { 00522 //Wait for socket to be writeable 00523 //Creating FS set 00524 fd_set socksSet; 00525 FD_ZERO(&socksSet); 00526 FD_SET(m_sock, &socksSet); 00527 struct timeval t_val; 00528 t_val.tv_sec = m_timeout / 1000; 00529 t_val.tv_usec = (m_timeout - (t_val.tv_sec * 1000)) * 1000; 00530 int ret = socket::select(FD_SETSIZE, NULL, &socksSet, NULL, &t_val); 00531 if(ret <= 0 || !FD_ISSET(m_sock, &socksSet)) 00532 { 00533 WARN("Timeout"); 00534 return NET_TIMEOUT; //Timeout 00535 } 00536 00537 ret = socket::send(m_sock, buf + writtenLen, len - writtenLen, 0); 00538 if( ret > 0) 00539 { 00540 writtenLen += ret; 00541 continue; 00542 } 00543 else if( ret == 0 ) 00544 { 00545 WARN("Connection was closed by server"); 00546 return NET_CLOSED; //Connection was closed by server 00547 } 00548 else 00549 { 00550 ERR("Connection error (recv returned %d)", ret); 00551 return NET_CONN; 00552 } 00553 } 00554 DBG("Written %d bytes", writtenLen); 00555 return OK; 00556 } 00557 00558 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 00559 { 00560 char* schemePtr = (char*) url; 00561 char* hostPtr = (char*) strstr(url, "://"); 00562 if(hostPtr == NULL) 00563 { 00564 WARN("Could not find host"); 00565 return NET_INVALID; //URL is invalid 00566 } 00567 00568 if( maxSchemeLen < hostPtr - schemePtr + 1 ) //including NULL-terminating char 00569 { 00570 WARN("Scheme str is too small (%d >= %d)", maxSchemeLen, hostPtr - schemePtr + 1); 00571 return NET_TOOSMALL; 00572 } 00573 memcpy(scheme, schemePtr, hostPtr - schemePtr); 00574 scheme[hostPtr - schemePtr] = '\0'; 00575 00576 hostPtr+=3; 00577 00578 size_t hostLen = 0; 00579 00580 char* portPtr = strchr(hostPtr, ':'); 00581 if( portPtr != NULL ) 00582 { 00583 hostLen = portPtr - hostPtr; 00584 portPtr++; 00585 if( sscanf(portPtr, "%hu", port) != 1) 00586 { 00587 WARN("Could not find port"); 00588 return NET_INVALID; 00589 } 00590 } 00591 else 00592 { 00593 *port=0; 00594 } 00595 char* pathPtr = strchr(hostPtr, '/'); 00596 if( hostLen == 0 ) 00597 { 00598 hostLen = pathPtr - hostPtr; 00599 } 00600 00601 if( maxHostLen < hostLen + 1 ) //including NULL-terminating char 00602 { 00603 WARN("Host str is too small (%d >= %d)", maxHostLen, hostLen + 1); 00604 return NET_TOOSMALL; 00605 } 00606 memcpy(host, hostPtr, hostLen); 00607 host[hostLen] = '\0'; 00608 00609 size_t pathLen; 00610 char* fragmentPtr = strchr(hostPtr, '#'); 00611 if(fragmentPtr != NULL) 00612 { 00613 pathLen = fragmentPtr - pathPtr; 00614 } 00615 else 00616 { 00617 pathLen = strlen(pathPtr); 00618 } 00619 00620 if( maxPathLen < pathLen + 1 ) //including NULL-terminating char 00621 { 00622 WARN("Path str is too small (%d >= %d)", maxPathLen, pathLen + 1); 00623 return NET_TOOSMALL; 00624 } 00625 memcpy(path, pathPtr, pathLen); 00626 path[pathLen] = '\0'; 00627 00628 return OK; 00629 }
Generated on Wed Jul 13 2022 00:58:15 by 1.7.2