]> git.gir.st - tmk_keyboard.git/blob - tmk_core/tool/mbed/mbed-sdk/libraries/tests/net/protocols/HTTPClient_HelloWorld/HTTPClient/HTTPClient.cpp
Merge commit '1fe4406f374291ab2e86e95a97341fd9c475fcb8'
[tmk_keyboard.git] / tmk_core / tool / mbed / mbed-sdk / libraries / tests / net / protocols / HTTPClient_HelloWorld / HTTPClient / HTTPClient.cpp
1 /* HTTPClient.cpp */
2 /* Copyright (C) 2012 mbed.org, MIT License
3 *
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:
9 *
10 * The above copyright notice and this permission notice shall be included in all copies or
11 * substantial portions of the Software.
12 *
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.
18 */
19
20 //Debug is disabled by default
21 #if 1
22 //Enable debug
23 #include <cstdio>
24 //#define DBG(x, ...) std::printf("[HTTPClient : DBG]"x"\r\n", ##__VA_ARGS__);
25 #define DBG(x, ...)
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__);
28
29 #else
30 //Disable debug
31 #define DBG(x, ...)
32 #define WARN(x, ...)
33 #define ERR(x, ...)
34
35 #endif
36
37 #define HTTP_PORT 80
38
39 #define OK 0
40
41 #define MIN(x,y) (((x)<(y))?(x):(y))
42 #define MAX(x,y) (((x)>(y))?(x):(y))
43
44 #define CHUNK_SIZE 256
45
46 #include <cstring>
47
48 #include "HTTPClient.h"
49
50 HTTPClient::HTTPClient() :
51 m_sock(), m_basicAuthUser(NULL), m_basicAuthPassword(NULL), m_httpResponseCode(0)
52 {
53
54 }
55
56 HTTPClient::~HTTPClient()
57 {
58
59 }
60
61 #if 0
62 void HTTPClient::basicAuth(const char* user, const char* password) //Basic Authentification
63 {
64 m_basicAuthUser = user;
65 m_basicAuthPassword = password;
66 }
67 #endif
68
69 HTTPResult HTTPClient::get(const char* url, IHTTPDataIn* pDataIn, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
70 {
71 return connect(url, HTTP_GET, NULL, pDataIn, timeout);
72 }
73
74 HTTPResult HTTPClient::get(const char* url, char* result, size_t maxResultLen, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
75 {
76 HTTPText str(result, maxResultLen);
77 return get(url, &str, timeout);
78 }
79
80 HTTPResult HTTPClient::post(const char* url, const IHTTPDataOut& dataOut, IHTTPDataIn* pDataIn, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
81 {
82 return connect(url, HTTP_POST, (IHTTPDataOut*)&dataOut, pDataIn, timeout);
83 }
84
85 int HTTPClient::getHTTPResponseCode()
86 {
87 return m_httpResponseCode;
88 }
89
90 #define CHECK_CONN_ERR(ret) \
91 do{ \
92 if(ret) { \
93 m_sock.close(); \
94 ERR("Connection error (%d)", ret); \
95 return HTTP_CONN; \
96 } \
97 } while(0)
98
99 #define PRTCL_ERR() \
100 do{ \
101 m_sock.close(); \
102 ERR("Protocol error"); \
103 return HTTP_PRTCL; \
104 } while(0)
105
106 HTTPResult HTTPClient::connect(const char* url, HTTP_METH method, IHTTPDataOut* pDataOut, IHTTPDataIn* pDataIn, int timeout) //Execute request
107 {
108 m_httpResponseCode = 0; //Invalidate code
109 m_timeout = timeout;
110
111 char scheme[8];
112 uint16_t port;
113 char host[32];
114 char path[64];
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));
117 if(res != HTTP_OK)
118 {
119 ERR("parseURL returned %d", res);
120 return res;
121 }
122
123 if(port == 0) //TODO do handle HTTPS->443
124 {
125 port = 80;
126 }
127
128 DBG("Scheme: %s", scheme);
129 DBG("Host: %s", host);
130 DBG("Port: %d", port);
131 DBG("Path: %s", path);
132
133 //Connect
134 DBG("Connecting socket to server");
135 int ret = m_sock.connect(host, port);
136 if (ret < 0)
137 {
138 m_sock.close();
139 ERR("Could not connect");
140 return HTTP_CONN;
141 }
142
143 //Send request
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
148 ret = send(buf);
149 if(ret)
150 {
151 m_sock.close();
152 ERR("Could not write request");
153 return HTTP_CONN;
154 }
155
156 //Send all headers
157
158 //Send default headers
159 DBG("Sending headers");
160 if( (method == HTTP_POST) && (pDataOut != NULL) )
161 {
162 if( pDataOut->getIsChunked() )
163 {
164 ret = send("Transfer-Encoding: chunked\r\n");
165 CHECK_CONN_ERR(ret);
166 }
167 else
168 {
169 snprintf(buf, sizeof(buf), "Content-Length: %d\r\n", pDataOut->getDataLen());
170 ret = send(buf);
171 CHECK_CONN_ERR(ret);
172 }
173 char type[48];
174 if( pDataOut->getDataType(type, 48) == HTTP_OK )
175 {
176 snprintf(buf, sizeof(buf), "Content-Type: %s\r\n", type);
177 ret = send(buf);
178 CHECK_CONN_ERR(ret);
179 }
180 }
181
182 //Close headers
183 DBG("Headers sent");
184 ret = send("\r\n");
185 CHECK_CONN_ERR(ret);
186
187 size_t trfLen;
188
189 //Send data (if POST)
190 if( (method == HTTP_POST) && (pDataOut != NULL) )
191 {
192 DBG("Sending data");
193 while(true)
194 {
195 size_t writtenLen = 0;
196 pDataOut->read(buf, CHUNK_SIZE, &trfLen);
197 if( pDataOut->getIsChunked() )
198 {
199 //Write chunk header
200 char chunkHeader[16];
201 snprintf(chunkHeader, sizeof(chunkHeader), "%X\r\n", trfLen); //In hex encoding
202 ret = send(chunkHeader);
203 CHECK_CONN_ERR(ret);
204 }
205 else if( trfLen == 0 )
206 {
207 break;
208 }
209 if( trfLen != 0 )
210 {
211 ret = send(buf, trfLen);
212 CHECK_CONN_ERR(ret);
213 }
214
215 if( pDataOut->getIsChunked() )
216 {
217 ret = send("\r\n"); //Chunk-terminating CRLF
218 CHECK_CONN_ERR(ret);
219 }
220 else
221 {
222 writtenLen += trfLen;
223 if( writtenLen >= pDataOut->getDataLen() )
224 {
225 break;
226 }
227 }
228
229 if( trfLen == 0 )
230 {
231 break;
232 }
233 }
234
235 }
236
237 //Receive response
238 DBG("Receiving response");
239 ret = recv(buf, CHUNK_SIZE - 1, CHUNK_SIZE - 1, &trfLen); //Read n bytes
240 CHECK_CONN_ERR(ret);
241
242 buf[trfLen] = '\0';
243
244 char* crlfPtr = strstr(buf, "\r\n");
245 if(crlfPtr == NULL)
246 {
247 PRTCL_ERR();
248 }
249
250 int crlfPos = crlfPtr - buf;
251 buf[crlfPos] = '\0';
252
253 //Parse HTTP response
254 if( sscanf(buf, "HTTP/%*d.%*d %d %*[^\r\n]", &m_httpResponseCode) != 1 )
255 {
256 //Cannot match string, error
257 ERR("Not a correct HTTP answer : %s\n", buf);
258 PRTCL_ERR();
259 }
260
261 if(m_httpResponseCode != 200)
262 {
263 //Cannot match string, error
264 WARN("Response code %d", m_httpResponseCode);
265 PRTCL_ERR();
266 }
267
268 DBG("Reading headers");
269
270 memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2) + 1); //Be sure to move NULL-terminating char as well
271 trfLen -= (crlfPos + 2);
272
273 size_t recvContentLength = 0;
274 bool recvChunked = false;
275 //Now get headers
276 while( true )
277 {
278 crlfPtr = strstr(buf, "\r\n");
279 if(crlfPtr == NULL)
280 {
281 if( trfLen < CHUNK_SIZE - 1 )
282 {
283 size_t newTrfLen;
284 ret = recv(buf + trfLen, 1, CHUNK_SIZE - trfLen - 1, &newTrfLen);
285 trfLen += newTrfLen;
286 buf[trfLen] = '\0';
287 DBG("Read %d chars; In buf: [%s]", newTrfLen, buf);
288 CHECK_CONN_ERR(ret);
289 continue;
290 }
291 else
292 {
293 PRTCL_ERR();
294 }
295 }
296
297 crlfPos = crlfPtr - buf;
298
299 if(crlfPos == 0) //End of headers
300 {
301 DBG("Headers read");
302 memmove(buf, &buf[2], trfLen - 2 + 1); //Be sure to move NULL-terminating char as well
303 trfLen -= 2;
304 break;
305 }
306
307 buf[crlfPos] = '\0';
308
309 char key[64] = {0};
310 char value[32] = {0};
311
312 int n = sscanf(buf, "%63[^:]: %31[^\r\n]", key, value);
313
314 if ( n == 2 )
315 {
316 DBG("Read header : %s: %s\n", key, value);
317 if( !strcmp(key, "Content-Length") )
318 {
319 sscanf(value, "%d", &recvContentLength);
320 pDataIn->setDataLen(recvContentLength);
321 }
322 else if( !strcmp(key, "Transfer-Encoding") )
323 {
324 if( !strcmp(value, "Chunked") || !strcmp(value, "chunked") )
325 {
326 recvChunked = true;
327 pDataIn->setIsChunked(true);
328 }
329 }
330 else if( !strcmp(key, "Content-Type") )
331 {
332 pDataIn->setDataType(value);
333 }
334
335 memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2) + 1); //Be sure to move NULL-terminating char as well
336 trfLen -= (crlfPos + 2);
337
338 }
339 else
340 {
341 ERR("Could not parse header");
342 PRTCL_ERR();
343 }
344
345 }
346
347 //Receive data
348 DBG("Receiving data");
349 while(true)
350 {
351 size_t readLen = 0;
352
353 if( recvChunked )
354 {
355 //Read chunk header
356 crlfPos=0;
357 for(crlfPos++; crlfPos < trfLen - 2; crlfPos++)
358 {
359 if( buf[crlfPos] == '\r' && buf[crlfPos + 1] == '\n' )
360 {
361 break;
362 }
363 }
364 if(crlfPos >= trfLen - 2) //Try to read more
365 {
366 if( trfLen < CHUNK_SIZE )
367 {
368 size_t newTrfLen;
369 ret = recv(buf + trfLen, 0, CHUNK_SIZE - trfLen - 1, &newTrfLen);
370 trfLen += newTrfLen;
371 CHECK_CONN_ERR(ret);
372 continue;
373 }
374 else
375 {
376 PRTCL_ERR();
377 }
378 }
379 buf[crlfPos] = '\0';
380 int n = sscanf(buf, "%x", &readLen);
381 if(n!=1)
382 {
383 ERR("Could not read chunk length");
384 PRTCL_ERR();
385 }
386
387 memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2)); //Not need to move NULL-terminating char any more
388 trfLen -= (crlfPos + 2);
389
390 if( readLen == 0 )
391 {
392 //Last chunk
393 break;
394 }
395 }
396 else
397 {
398 readLen = recvContentLength;
399 }
400
401 DBG("Retrieving %d bytes", readLen);
402
403 do
404 {
405 pDataIn->write(buf, MIN(trfLen, readLen));
406 if( trfLen > readLen )
407 {
408 memmove(buf, &buf[readLen], trfLen - readLen);
409 trfLen -= readLen;
410 readLen = 0;
411 }
412 else
413 {
414 readLen -= trfLen;
415 }
416
417 if(readLen)
418 {
419 ret = recv(buf, 1, CHUNK_SIZE - trfLen - 1, &trfLen);
420 CHECK_CONN_ERR(ret);
421 }
422 } while(readLen);
423
424 if( recvChunked )
425 {
426 if(trfLen < 2)
427 {
428 size_t newTrfLen;
429 //Read missing chars to find end of chunk
430 ret = recv(buf, 2 - trfLen, CHUNK_SIZE, &newTrfLen);
431 CHECK_CONN_ERR(ret);
432 trfLen += newTrfLen;
433 }
434 if( (buf[0] != '\r') || (buf[1] != '\n') )
435 {
436 ERR("Format error");
437 PRTCL_ERR();
438 }
439 memmove(buf, &buf[2], trfLen - 2);
440 trfLen -= 2;
441 }
442 else
443 {
444 break;
445 }
446
447 }
448
449 m_sock.close();
450 DBG("Completed HTTP transaction");
451
452 return HTTP_OK;
453 }
454
455 HTTPResult HTTPClient::recv(char* buf, size_t minLen, size_t maxLen, size_t* pReadLen) //0 on success, err code on failure
456 {
457 DBG("Trying to read between %d and %d bytes", minLen, maxLen);
458 size_t readLen = 0;
459
460 if(!m_sock.is_connected())
461 {
462 WARN("Connection was closed by server");
463 return HTTP_CLOSED; //Connection was closed by server
464 }
465
466 int ret;
467 while(readLen < maxLen)
468 {
469 if(readLen < minLen)
470 {
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);
474 }
475 else
476 {
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);
480 }
481
482 if( ret > 0)
483 {
484 readLen += ret;
485 }
486 else if( ret == 0 )
487 {
488 break;
489 }
490 else
491 {
492 if(!m_sock.is_connected())
493 {
494 ERR("Connection error (recv returned %d)", ret);
495 *pReadLen = readLen;
496 return HTTP_CONN;
497 }
498 else
499 {
500 break;
501 }
502 }
503
504 if(!m_sock.is_connected())
505 {
506 break;
507 }
508 }
509 DBG("Read %d bytes", readLen);
510 *pReadLen = readLen;
511 return HTTP_OK;
512 }
513
514 HTTPResult HTTPClient::send(char* buf, size_t len) //0 on success, err code on failure
515 {
516 if(len == 0)
517 {
518 len = strlen(buf);
519 }
520 DBG("Trying to write %d bytes", len);
521 size_t writtenLen = 0;
522
523 if(!m_sock.is_connected())
524 {
525 WARN("Connection was closed by server");
526 return HTTP_CLOSED; //Connection was closed by server
527 }
528
529 m_sock.set_blocking(false, m_timeout);
530 int ret = m_sock.send_all(buf, len);
531 if(ret > 0)
532 {
533 writtenLen += ret;
534 }
535 else if( ret == 0 )
536 {
537 WARN("Connection was closed by server");
538 return HTTP_CLOSED; //Connection was closed by server
539 }
540 else
541 {
542 ERR("Connection error (send returned %d)", ret);
543 return HTTP_CONN;
544 }
545
546 DBG("Written %d bytes", writtenLen);
547 return HTTP_OK;
548 }
549
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
551 {
552 char* schemePtr = (char*) url;
553 char* hostPtr = (char*) strstr(url, "://");
554 if(hostPtr == NULL)
555 {
556 WARN("Could not find host");
557 return HTTP_PARSE; //URL is invalid
558 }
559
560 if( maxSchemeLen < hostPtr - schemePtr + 1 ) //including NULL-terminating char
561 {
562 WARN("Scheme str is too small (%d >= %d)", maxSchemeLen, hostPtr - schemePtr + 1);
563 return HTTP_PARSE;
564 }
565 memcpy(scheme, schemePtr, hostPtr - schemePtr);
566 scheme[hostPtr - schemePtr] = '\0';
567
568 hostPtr+=3;
569
570 size_t hostLen = 0;
571
572 char* portPtr = strchr(hostPtr, ':');
573 if( portPtr != NULL )
574 {
575 hostLen = portPtr - hostPtr;
576 portPtr++;
577 if( sscanf(portPtr, "%hu", port) != 1)
578 {
579 WARN("Could not find port");
580 return HTTP_PARSE;
581 }
582 }
583 else
584 {
585 *port=0;
586 }
587 char* pathPtr = strchr(hostPtr, '/');
588 if( hostLen == 0 )
589 {
590 hostLen = pathPtr - hostPtr;
591 }
592
593 if( maxHostLen < hostLen + 1 ) //including NULL-terminating char
594 {
595 WARN("Host str is too small (%d >= %d)", maxHostLen, hostLen + 1);
596 return HTTP_PARSE;
597 }
598 memcpy(host, hostPtr, hostLen);
599 host[hostLen] = '\0';
600
601 size_t pathLen;
602 char* fragmentPtr = strchr(hostPtr, '#');
603 if(fragmentPtr != NULL)
604 {
605 pathLen = fragmentPtr - pathPtr;
606 }
607 else
608 {
609 pathLen = strlen(pathPtr);
610 }
611
612 if( maxPathLen < pathLen + 1 ) //including NULL-terminating char
613 {
614 WARN("Path str is too small (%d >= %d)", maxPathLen, pathLen + 1);
615 return HTTP_PARSE;
616 }
617 memcpy(path, pathPtr, pathLen);
618 path[pathLen] = '\0';
619
620 return HTTP_OK;
621 }
Imprint / Impressum