]>
git.gir.st - tmk_keyboard.git/blob - tmk_core/tool/mbed/mbed-sdk/libraries/tests/net/protocols/HTTPClient_HelloWorld/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__);
26 #define WARN(x, ...) std::printf("[HTTPClient : WARN]"x"\r\n", ##__VA_ARGS__);
27 #define ERR(x, ...) std::printf("[HTTPClient : ERR]"x"\r\n", ##__VA_ARGS__);
41 #define MIN(x,y) (((x)<(y))?(x):(y))
42 #define MAX(x,y) (((x)>(y))?(x):(y))
44 #define CHUNK_SIZE 256
48 #include "HTTPClient.h"
50 HTTPClient::HTTPClient() :
51 m_sock(), m_basicAuthUser(NULL
), m_basicAuthPassword(NULL
), m_httpResponseCode(0)
56 HTTPClient::~HTTPClient()
62 void HTTPClient::basicAuth(const char* user
, const char* password
) //Basic Authentification
64 m_basicAuthUser
= user
;
65 m_basicAuthPassword
= password
;
69 HTTPResult
HTTPClient::get(const char* url
, IHTTPDataIn
* pDataIn
, int timeout
/*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
71 return connect(url
, HTTP_GET
, NULL
, pDataIn
, timeout
);
74 HTTPResult
HTTPClient::get(const char* url
, char* result
, size_t maxResultLen
, int timeout
/*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
76 HTTPText
str(result
, maxResultLen
);
77 return get(url
, &str
, timeout
);
80 HTTPResult
HTTPClient::post(const char* url
, const IHTTPDataOut
& dataOut
, IHTTPDataIn
* pDataIn
, int timeout
/*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
82 return connect(url
, HTTP_POST
, (IHTTPDataOut
*)&dataOut
, pDataIn
, timeout
);
85 int HTTPClient::getHTTPResponseCode()
87 return m_httpResponseCode
;
90 #define CHECK_CONN_ERR(ret) \
94 ERR("Connection error (%d)", ret); \
102 ERR("Protocol error"); \
106 HTTPResult
HTTPClient::connect(const char* url
, HTTP_METH method
, IHTTPDataOut
* pDataOut
, IHTTPDataIn
* pDataIn
, int timeout
) //Execute request
108 m_httpResponseCode
= 0; //Invalidate code
115 //First we need to parse the url (http[s]://host[:port][/[path]]) -- HTTPS not supported (yet?)
116 HTTPResult res
= parseURL(url
, scheme
, sizeof(scheme
), host
, sizeof(host
), &port
, path
, sizeof(path
));
119 ERR("parseURL returned %d", res
);
123 if(port
== 0) //TODO do handle HTTPS->443
128 DBG("Scheme: %s", scheme
);
129 DBG("Host: %s", host
);
130 DBG("Port: %d", port
);
131 DBG("Path: %s", path
);
134 DBG("Connecting socket to server");
135 int ret
= m_sock
.connect(host
, port
);
139 ERR("Could not connect");
144 DBG("Sending request");
145 char buf
[CHUNK_SIZE
];
146 const char* meth
= (method
==HTTP_GET
)?"GET":(method
==HTTP_POST
)?"POST":"";
147 snprintf(buf
, sizeof(buf
), "%s %s HTTP/1.1\r\nHost: %s\r\n", meth
, path
, host
); //Write request
152 ERR("Could not write request");
158 //Send default headers
159 DBG("Sending headers");
160 if( (method
== HTTP_POST
) && (pDataOut
!= NULL
) )
162 if( pDataOut
->getIsChunked() )
164 ret
= send("Transfer-Encoding: chunked\r\n");
169 snprintf(buf
, sizeof(buf
), "Content-Length: %d\r\n", pDataOut
->getDataLen());
174 if( pDataOut
->getDataType(type
, 48) == HTTP_OK
)
176 snprintf(buf
, sizeof(buf
), "Content-Type: %s\r\n", type
);
189 //Send data (if POST)
190 if( (method
== HTTP_POST
) && (pDataOut
!= NULL
) )
195 size_t writtenLen
= 0;
196 pDataOut
->read(buf
, CHUNK_SIZE
, &trfLen
);
197 if( pDataOut
->getIsChunked() )
200 char chunkHeader
[16];
201 snprintf(chunkHeader
, sizeof(chunkHeader
), "%X\r\n", trfLen
); //In hex encoding
202 ret
= send(chunkHeader
);
205 else if( trfLen
== 0 )
211 ret
= send(buf
, trfLen
);
215 if( pDataOut
->getIsChunked() )
217 ret
= send("\r\n"); //Chunk-terminating CRLF
222 writtenLen
+= trfLen
;
223 if( writtenLen
>= pDataOut
->getDataLen() )
238 DBG("Receiving response");
239 ret
= recv(buf
, CHUNK_SIZE
- 1, CHUNK_SIZE
- 1, &trfLen
); //Read n bytes
244 char* crlfPtr
= strstr(buf
, "\r\n");
250 int crlfPos
= crlfPtr
- buf
;
253 //Parse HTTP response
254 if( sscanf(buf
, "HTTP/%*d.%*d %d %*[^\r\n]", &m_httpResponseCode
) != 1 )
256 //Cannot match string, error
257 ERR("Not a correct HTTP answer : %s\n", buf
);
261 if(m_httpResponseCode
!= 200)
263 //Cannot match string, error
264 WARN("Response code %d", m_httpResponseCode
);
268 DBG("Reading headers");
270 memmove(buf
, &buf
[crlfPos
+2], trfLen
- (crlfPos
+ 2) + 1); //Be sure to move NULL-terminating char as well
271 trfLen
-= (crlfPos
+ 2);
273 size_t recvContentLength
= 0;
274 bool recvChunked
= false;
278 crlfPtr
= strstr(buf
, "\r\n");
281 if( trfLen
< CHUNK_SIZE
- 1 )
284 ret
= recv(buf
+ trfLen
, 1, CHUNK_SIZE
- trfLen
- 1, &newTrfLen
);
287 DBG("Read %d chars; In buf: [%s]", newTrfLen
, buf
);
297 crlfPos
= crlfPtr
- buf
;
299 if(crlfPos
== 0) //End of headers
302 memmove(buf
, &buf
[2], trfLen
- 2 + 1); //Be sure to move NULL-terminating char as well
310 char value
[32] = {0};
312 int n
= sscanf(buf
, "%63[^:]: %31[^\r\n]", key
, value
);
316 DBG("Read header : %s: %s\n", key
, value
);
317 if( !strcmp(key
, "Content-Length") )
319 sscanf(value
, "%d", &recvContentLength
);
320 pDataIn
->setDataLen(recvContentLength
);
322 else if( !strcmp(key
, "Transfer-Encoding") )
324 if( !strcmp(value
, "Chunked") || !strcmp(value
, "chunked") )
327 pDataIn
->setIsChunked(true);
330 else if( !strcmp(key
, "Content-Type") )
332 pDataIn
->setDataType(value
);
335 memmove(buf
, &buf
[crlfPos
+2], trfLen
- (crlfPos
+ 2) + 1); //Be sure to move NULL-terminating char as well
336 trfLen
-= (crlfPos
+ 2);
341 ERR("Could not parse header");
348 DBG("Receiving data");
357 for(crlfPos
++; crlfPos
< trfLen
- 2; crlfPos
++)
359 if( buf
[crlfPos
] == '\r' && buf
[crlfPos
+ 1] == '\n' )
364 if(crlfPos
>= trfLen
- 2) //Try to read more
366 if( trfLen
< CHUNK_SIZE
)
369 ret
= recv(buf
+ trfLen
, 0, CHUNK_SIZE
- trfLen
- 1, &newTrfLen
);
380 int n
= sscanf(buf
, "%x", &readLen
);
383 ERR("Could not read chunk length");
387 memmove(buf
, &buf
[crlfPos
+2], trfLen
- (crlfPos
+ 2)); //Not need to move NULL-terminating char any more
388 trfLen
-= (crlfPos
+ 2);
398 readLen
= recvContentLength
;
401 DBG("Retrieving %d bytes", readLen
);
405 pDataIn
->write(buf
, MIN(trfLen
, readLen
));
406 if( trfLen
> readLen
)
408 memmove(buf
, &buf
[readLen
], trfLen
- readLen
);
419 ret
= recv(buf
, 1, CHUNK_SIZE
- trfLen
- 1, &trfLen
);
429 //Read missing chars to find end of chunk
430 ret
= recv(buf
, 2 - trfLen
, CHUNK_SIZE
, &newTrfLen
);
434 if( (buf
[0] != '\r') || (buf
[1] != '\n') )
439 memmove(buf
, &buf
[2], trfLen
- 2);
450 DBG("Completed HTTP transaction");
455 HTTPResult
HTTPClient::recv(char* buf
, size_t minLen
, size_t maxLen
, size_t* pReadLen
) //0 on success, err code on failure
457 DBG("Trying to read between %d and %d bytes", minLen
, maxLen
);
460 if(!m_sock
.is_connected())
462 WARN("Connection was closed by server");
463 return HTTP_CLOSED
; //Connection was closed by server
467 while(readLen
< maxLen
)
471 DBG("Trying to read at most %d bytes [Blocking]", minLen
- readLen
);
472 m_sock
.set_blocking(false, m_timeout
);
473 ret
= m_sock
.receive_all(buf
+ readLen
, minLen
- readLen
);
477 DBG("Trying to read at most %d bytes [Not blocking]", maxLen
- readLen
);
478 m_sock
.set_blocking(false, 0);
479 ret
= m_sock
.receive(buf
+ readLen
, maxLen
- readLen
);
492 if(!m_sock
.is_connected())
494 ERR("Connection error (recv returned %d)", ret
);
504 if(!m_sock
.is_connected())
509 DBG("Read %d bytes", readLen
);
514 HTTPResult
HTTPClient::send(char* buf
, size_t len
) //0 on success, err code on failure
520 DBG("Trying to write %d bytes", len
);
521 size_t writtenLen
= 0;
523 if(!m_sock
.is_connected())
525 WARN("Connection was closed by server");
526 return HTTP_CLOSED
; //Connection was closed by server
529 m_sock
.set_blocking(false, m_timeout
);
530 int ret
= m_sock
.send_all(buf
, len
);
537 WARN("Connection was closed by server");
538 return HTTP_CLOSED
; //Connection was closed by server
542 ERR("Connection error (send returned %d)", ret
);
546 DBG("Written %d bytes", writtenLen
);
550 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
552 char* schemePtr
= (char*) url
;
553 char* hostPtr
= (char*) strstr(url
, "://");
556 WARN("Could not find host");
557 return HTTP_PARSE
; //URL is invalid
560 if( maxSchemeLen
< hostPtr
- schemePtr
+ 1 ) //including NULL-terminating char
562 WARN("Scheme str is too small (%d >= %d)", maxSchemeLen
, hostPtr
- schemePtr
+ 1);
565 memcpy(scheme
, schemePtr
, hostPtr
- schemePtr
);
566 scheme
[hostPtr
- schemePtr
] = '\0';
572 char* portPtr
= strchr(hostPtr
, ':');
573 if( portPtr
!= NULL
)
575 hostLen
= portPtr
- hostPtr
;
577 if( sscanf(portPtr
, "%hu", port
) != 1)
579 WARN("Could not find port");
587 char* pathPtr
= strchr(hostPtr
, '/');
590 hostLen
= pathPtr
- hostPtr
;
593 if( maxHostLen
< hostLen
+ 1 ) //including NULL-terminating char
595 WARN("Host str is too small (%d >= %d)", maxHostLen
, hostLen
+ 1);
598 memcpy(host
, hostPtr
, hostLen
);
599 host
[hostLen
] = '\0';
602 char* fragmentPtr
= strchr(hostPtr
, '#');
603 if(fragmentPtr
!= NULL
)
605 pathLen
= fragmentPtr
- pathPtr
;
609 pathLen
= strlen(pathPtr
);
612 if( maxPathLen
< pathLen
+ 1 ) //including NULL-terminating char
614 WARN("Path str is too small (%d >= %d)", maxPathLen
, pathLen
+ 1);
617 memcpy(path
, pathPtr
, pathLen
);
618 path
[pathLen
] = '\0';