]>
git.gir.st - tmk_keyboard.git/blob - tmk_core/tool/mbed/mbed-sdk/libraries/tests/net/cellular/http/common/HTTPClient/HTTPClient.cpp
2 /* Copyright (C) 2012 mbed.org, MIT License
4 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
5 * and associated documentation files (the "Software"), to deal in the Software without restriction,
6 * including without limitation the rights to use, copy, modify, merge, publish, distribute,
7 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
8 * furnished to do so, subject to the following conditions:
10 * The above copyright notice and this permission notice shall be included in all copies or
11 * substantial portions of the Software.
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
14 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
16 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
17 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 //Debug is disabled by default
24 #define DBG(x, ...) std::printf("[HTTPClient : DBG]"x"\r\n", ##__VA_ARGS__);
25 #define WARN(x, ...) std::printf("[HTTPClient : WARN]"x"\r\n", ##__VA_ARGS__);
26 #define ERR(x, ...) std::printf("[HTTPClient : ERR]"x"\r\n", ##__VA_ARGS__);
40 #define MIN(x,y) (((x)<(y))?(x):(y))
41 #define MAX(x,y) (((x)>(y))?(x):(y))
43 #define CHUNK_SIZE 256
47 #include "HTTPClient.h"
49 HTTPClient::HTTPClient() :
50 m_sock(), m_basicAuthUser(NULL
), m_basicAuthPassword(NULL
), m_httpResponseCode(0)
55 HTTPClient::~HTTPClient()
61 void HTTPClient::basicAuth(const char* user
, const char* password
) //Basic Authentification
63 m_basicAuthUser
= user
;
64 m_basicAuthPassword
= password
;
68 HTTPResult
HTTPClient::get(const char* url
, IHTTPDataIn
* pDataIn
, int timeout
/*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
70 return connect(url
, HTTP_GET
, NULL
, pDataIn
, timeout
);
73 HTTPResult
HTTPClient::get(const char* url
, char* result
, size_t maxResultLen
, int timeout
/*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
75 HTTPText
str(result
, maxResultLen
);
76 return get(url
, &str
, timeout
);
79 HTTPResult
HTTPClient::post(const char* url
, const IHTTPDataOut
& dataOut
, IHTTPDataIn
* pDataIn
, int timeout
/*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
81 return connect(url
, HTTP_POST
, (IHTTPDataOut
*)&dataOut
, pDataIn
, timeout
);
84 HTTPResult
HTTPClient::put(const char* url
, const IHTTPDataOut
& dataOut
, IHTTPDataIn
* pDataIn
, int timeout
/*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
86 return connect(url
, HTTP_PUT
, (IHTTPDataOut
*)&dataOut
, pDataIn
, timeout
);
89 HTTPResult
HTTPClient::del(const char* url
, IHTTPDataIn
* pDataIn
, int timeout
/*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
91 return connect(url
, HTTP_DELETE
, NULL
, pDataIn
, timeout
);
95 int HTTPClient::getHTTPResponseCode()
97 return m_httpResponseCode
;
100 #define CHECK_CONN_ERR(ret) \
104 ERR("Connection error (%d)", ret); \
109 #define PRTCL_ERR() \
112 ERR("Protocol error"); \
116 HTTPResult
HTTPClient::connect(const char* url
, HTTP_METH method
, IHTTPDataOut
* pDataOut
, IHTTPDataIn
* pDataIn
, int timeout
) //Execute request
118 m_httpResponseCode
= 0; //Invalidate code
121 pDataIn
->writeReset();
124 pDataOut
->readReset();
131 //First we need to parse the url (http[s]://host[:port][/[path]]) -- HTTPS not supported (yet?)
132 HTTPResult res
= parseURL(url
, scheme
, sizeof(scheme
), host
, sizeof(host
), &port
, path
, sizeof(path
));
135 ERR("parseURL returned %d", res
);
139 if(port
== 0) //TODO do handle HTTPS->443
144 DBG("Scheme: %s", scheme
);
145 DBG("Host: %s", host
);
146 DBG("Port: %d", port
);
147 DBG("Path: %s", path
);
150 DBG("Connecting socket to server");
151 int ret
= m_sock
.connect(host
, port
);
155 ERR("Could not connect");
160 DBG("Sending request");
161 char buf
[CHUNK_SIZE
];
162 const char* meth
= (method
==HTTP_GET
)?"GET":(method
==HTTP_POST
)?"POST":(method
==HTTP_PUT
)?"PUT":(method
==HTTP_DELETE
)?"DELETE":"";
163 snprintf(buf
, sizeof(buf
), "%s %s HTTP/1.1\r\nHost: %s\r\n", meth
, path
, host
); //Write request
168 ERR("Could not write request");
174 //Send default headers
175 DBG("Sending headers");
176 if( pDataOut
!= NULL
)
178 if( pDataOut
->getIsChunked() )
180 ret
= send("Transfer-Encoding: chunked\r\n");
185 snprintf(buf
, sizeof(buf
), "Content-Length: %d\r\n", pDataOut
->getDataLen());
190 if( pDataOut
->getDataType(type
, 48) == HTTP_OK
)
192 snprintf(buf
, sizeof(buf
), "Content-Type: %s\r\n", type
);
205 //Send data (if available)
206 if( pDataOut
!= NULL
)
211 size_t writtenLen
= 0;
212 pDataOut
->read(buf
, CHUNK_SIZE
, &trfLen
);
213 if( pDataOut
->getIsChunked() )
216 char chunkHeader
[16];
217 snprintf(chunkHeader
, sizeof(chunkHeader
), "%X\r\n", trfLen
); //In hex encoding
218 ret
= send(chunkHeader
);
221 else if( trfLen
== 0 )
227 ret
= send(buf
, trfLen
);
231 if( pDataOut
->getIsChunked() )
233 ret
= send("\r\n"); //Chunk-terminating CRLF
238 writtenLen
+= trfLen
;
239 if( writtenLen
>= pDataOut
->getDataLen() )
254 DBG("Receiving response");
255 ret
= recv(buf
, CHUNK_SIZE
- 1, CHUNK_SIZE
- 1, &trfLen
); //Read n bytes
260 char* crlfPtr
= strstr(buf
, "\r\n");
266 int crlfPos
= crlfPtr
- buf
;
269 //Parse HTTP response
270 if( sscanf(buf
, "HTTP/%*d.%*d %d %*[^\r\n]", &m_httpResponseCode
) != 1 )
272 //Cannot match string, error
273 ERR("Not a correct HTTP answer : %s\n", buf
);
277 if( (m_httpResponseCode
< 200) || (m_httpResponseCode
>= 300) )
279 //Did not return a 2xx code; TODO fetch headers/(&data?) anyway and implement a mean of writing/reading headers
280 WARN("Response code %d", m_httpResponseCode
);
284 DBG("Reading headers");
286 memmove(buf
, &buf
[crlfPos
+2], trfLen
- (crlfPos
+ 2) + 1); //Be sure to move NULL-terminating char as well
287 trfLen
-= (crlfPos
+ 2);
289 size_t recvContentLength
= 0;
290 bool recvChunked
= false;
294 crlfPtr
= strstr(buf
, "\r\n");
297 if( trfLen
< CHUNK_SIZE
- 1 )
300 ret
= recv(buf
+ trfLen
, 1, CHUNK_SIZE
- trfLen
- 1, &newTrfLen
);
303 DBG("Read %d chars; In buf: [%s]", newTrfLen
, buf
);
313 crlfPos
= crlfPtr
- buf
;
315 if(crlfPos
== 0) //End of headers
318 memmove(buf
, &buf
[2], trfLen
- 2 + 1); //Be sure to move NULL-terminating char as well
331 int n
= sscanf(buf
, "%31[^:]: %31[^\r\n]", key
, value
);
334 DBG("Read header : %s: %s\n", key
, value
);
335 if( !strcmp(key
, "Content-Length") )
337 sscanf(value
, "%d", &recvContentLength
);
338 pDataIn
->setDataLen(recvContentLength
);
340 else if( !strcmp(key
, "Transfer-Encoding") )
342 if( !strcmp(value
, "Chunked") || !strcmp(value
, "chunked") )
345 pDataIn
->setIsChunked(true);
348 else if( !strcmp(key
, "Content-Type") )
350 pDataIn
->setDataType(value
);
353 memmove(buf
, &buf
[crlfPos
+2], trfLen
- (crlfPos
+ 2) + 1); //Be sure to move NULL-terminating char as well
354 trfLen
-= (crlfPos
+ 2);
359 ERR("Could not parse header");
366 DBG("Receiving data");
382 for(; crlfPos
< trfLen
- 2; crlfPos
++)
384 if( buf
[crlfPos
] == '\r' && buf
[crlfPos
+ 1] == '\n' )
391 if(!foundCrlf
) //Try to read more
393 if( trfLen
< CHUNK_SIZE
)
396 ret
= recv(buf
+ trfLen
, 0, CHUNK_SIZE
- trfLen
- 1, &newTrfLen
);
408 int n
= sscanf(buf
, "%x", &readLen
);
411 ERR("Could not read chunk length");
415 memmove(buf
, &buf
[crlfPos
+2], trfLen
- (crlfPos
+ 2)); //Not need to move NULL-terminating char any more
416 trfLen
-= (crlfPos
+ 2);
426 readLen
= recvContentLength
;
429 DBG("Retrieving %d bytes", readLen
);
433 pDataIn
->write(buf
, MIN(trfLen
, readLen
));
434 if( trfLen
> readLen
)
436 memmove(buf
, &buf
[readLen
], trfLen
- readLen
);
447 ret
= recv(buf
, 1, CHUNK_SIZE
- trfLen
- 1, &trfLen
);
457 //Read missing chars to find end of chunk
458 ret
= recv(buf
+ trfLen
, 2 - trfLen
, CHUNK_SIZE
- trfLen
- 1, &newTrfLen
);
462 if( (buf
[0] != '\r') || (buf
[1] != '\n') )
467 memmove(buf
, &buf
[2], trfLen
- 2);
478 DBG("Completed HTTP transaction");
483 HTTPResult
HTTPClient::recv(char* buf
, size_t minLen
, size_t maxLen
, size_t* pReadLen
) //0 on success, err code on failure
485 DBG("Trying to read between %d and %d bytes", minLen
, maxLen
);
488 if(!m_sock
.is_connected())
490 WARN("Connection was closed by server");
491 return HTTP_CLOSED
; //Connection was closed by server
495 while(readLen
< maxLen
)
499 DBG("Trying to read at most %d bytes [Blocking]", minLen
- readLen
);
500 m_sock
.set_blocking(false, m_timeout
);
501 ret
= m_sock
.receive_all(buf
+ readLen
, minLen
- readLen
);
505 DBG("Trying to read at most %d bytes [Not blocking]", maxLen
- readLen
);
506 m_sock
.set_blocking(false, 0);
507 ret
= m_sock
.receive(buf
+ readLen
, maxLen
- readLen
);
520 if(!m_sock
.is_connected())
522 ERR("Connection error (recv returned %d)", ret
);
532 if(!m_sock
.is_connected())
537 DBG("Read %d bytes", readLen
);
542 HTTPResult
HTTPClient::send(char* buf
, size_t len
) //0 on success, err code on failure
548 DBG("Trying to write %d bytes", len
);
549 size_t writtenLen
= 0;
551 if(!m_sock
.is_connected())
553 WARN("Connection was closed by server");
554 return HTTP_CLOSED
; //Connection was closed by server
557 m_sock
.set_blocking(false, m_timeout
);
558 int ret
= m_sock
.send_all(buf
, len
);
565 WARN("Connection was closed by server");
566 return HTTP_CLOSED
; //Connection was closed by server
570 ERR("Connection error (send returned %d)", ret
);
574 DBG("Written %d bytes", writtenLen
);
578 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
580 char* schemePtr
= (char*) url
;
581 char* hostPtr
= (char*) strstr(url
, "://");
584 WARN("Could not find host");
585 return HTTP_PARSE
; //URL is invalid
588 if( maxSchemeLen
< hostPtr
- schemePtr
+ 1 ) //including NULL-terminating char
590 WARN("Scheme str is too small (%d >= %d)", maxSchemeLen
, hostPtr
- schemePtr
+ 1);
593 memcpy(scheme
, schemePtr
, hostPtr
- schemePtr
);
594 scheme
[hostPtr
- schemePtr
] = '\0';
600 char* portPtr
= strchr(hostPtr
, ':');
601 if( portPtr
!= NULL
)
603 hostLen
= portPtr
- hostPtr
;
605 if( sscanf(portPtr
, "%hu", port
) != 1)
607 WARN("Could not find port");
615 char* pathPtr
= strchr(hostPtr
, '/');
618 hostLen
= pathPtr
- hostPtr
;
621 if( maxHostLen
< hostLen
+ 1 ) //including NULL-terminating char
623 WARN("Host str is too small (%d >= %d)", maxHostLen
, hostLen
+ 1);
626 memcpy(host
, hostPtr
, hostLen
);
627 host
[hostLen
] = '\0';
630 char* fragmentPtr
= strchr(hostPtr
, '#');
631 if(fragmentPtr
!= NULL
)
633 pathLen
= fragmentPtr
- pathPtr
;
637 pathLen
= strlen(pathPtr
);
640 if( maxPathLen
< pathLen
+ 1 ) //including NULL-terminating char
642 WARN("Path str is too small (%d >= %d)", maxPathLen
, pathLen
+ 1);
645 memcpy(path
, pathPtr
, pathLen
);
646 path
[pathLen
] = '\0';