1 /* Copyright (C) 2010-2018 The RetroArch team
2 *
3 * ---------------------------------------------------------------------------------------
4 * The following license statement only applies to this file (net_http.c).
5 * ---------------------------------------------------------------------------------------
6 *
7 * Permission is hereby granted, free of charge,
8 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation the rights to
10 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
11 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 */
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <ctype.h>
26
27 #include <net/net_http.h>
28 #include <net/net_compat.h>
29 #include <net/net_socket.h>
30 #ifdef HAVE_SSL
31 #include <net/net_socket_ssl.h>
32 #endif
33 #include <compat/strl.h>
34 #include <string/stdstring.h>
35 #include <string.h>
36 #include <retro_common_api.h>
37 #include <retro_miscellaneous.h>
38
39 enum
40 {
41 P_HEADER_TOP = 0,
42 P_HEADER,
43 P_BODY,
44 P_BODY_CHUNKLEN,
45 P_DONE,
46 P_ERROR
47 };
48
49 enum
50 {
51 T_FULL = 0,
52 T_LEN,
53 T_CHUNK
54 };
55
56 struct http_socket_state_t
57 {
58 int fd;
59 bool ssl;
60 void *ssl_ctx;
61 };
62
63 struct http_t
64 {
65 int status;
66
67 char part;
68 char bodytype;
69 bool error;
70
71 size_t pos;
72 size_t len;
73 size_t buflen;
74 char *data;
75 struct http_socket_state_t sock_state;
76 };
77
78 struct http_connection_t
79 {
80 char *domain;
81 char *location;
82 char *urlcopy;
83 char *scan;
84 char *methodcopy;
85 char *contenttypecopy;
86 char *postdatacopy;
87 int port;
88 struct http_socket_state_t sock_state;
89 };
90
91 static char urlencode_lut[256];
92 static bool urlencode_lut_inited = false;
93
urlencode_lut_init(void)94 void urlencode_lut_init(void)
95 {
96 unsigned i;
97
98 urlencode_lut_inited = true;
99
100 for (i = 0; i < 256; i++)
101 {
102 urlencode_lut[i] = isalnum(i) || i == '*' || i == '-' || i == '.' || i == '_' || i == '/' ? i : 0;
103 }
104 }
105
106 /* URL Encode a string
107 caller is responsible for deleting the destination buffer */
net_http_urlencode(char ** dest,const char * source)108 void net_http_urlencode(char **dest, const char *source)
109 {
110 char *enc = NULL;
111 /* Assume every character will be encoded, so we need 3 times the space. */
112 size_t len = strlen(source) * 3 + 1;
113 size_t count = len;
114
115 if (!urlencode_lut_inited)
116 urlencode_lut_init();
117
118 enc = (char*)calloc(1, len);
119
120 *dest = enc;
121
122 for (; *source; source++)
123 {
124 int written = 0;
125
126 /* any non-ascii character will just be encoded without question */
127 if ((unsigned)*source < sizeof(urlencode_lut) && urlencode_lut[(unsigned)*source])
128 written = snprintf(enc, count, "%c", urlencode_lut[(unsigned)*source]);
129 else
130 written = snprintf(enc, count, "%%%02X", *source & 0xFF);
131
132 if (written > 0)
133 count -= written;
134
135 while (*++enc);
136 }
137
138 (*dest)[len - 1] = '\0';
139 }
140
141 /* Re-encode a full URL */
net_http_urlencode_full(char * dest,const char * source,size_t size)142 void net_http_urlencode_full(char *dest,
143 const char *source, size_t size)
144 {
145 char *tmp = NULL;
146 char url_domain[PATH_MAX_LENGTH] = {0};
147 char url_path[PATH_MAX_LENGTH] = {0};
148 int count = 0;
149
150 strlcpy(url_path, source, sizeof(url_path));
151 tmp = url_path;
152
153 while (count < 3 && tmp[0] != '\0')
154 {
155 tmp = strchr(tmp, '/');
156 count++;
157 tmp++;
158 }
159
160 strlcpy(url_domain, source, tmp - url_path);
161
162 strlcpy(url_path,
163 source + strlen(url_domain) + 1,
164 strlen(tmp) + 1
165 );
166
167 tmp = NULL;
168 net_http_urlencode(&tmp, url_path);
169 snprintf(dest, size, "%s/%s", url_domain, tmp);
170 free (tmp);
171 }
172
net_http_new_socket(struct http_connection_t * conn)173 static int net_http_new_socket(struct http_connection_t *conn)
174 {
175 int ret;
176 struct addrinfo *addr = NULL, *next_addr = NULL;
177 int fd = socket_init(
178 (void**)&addr, conn->port, conn->domain, SOCKET_TYPE_STREAM);
179 #ifdef HAVE_SSL
180 if (conn->sock_state.ssl)
181 {
182 if (!(conn->sock_state.ssl_ctx = ssl_socket_init(fd, conn->domain)))
183 return -1;
184 }
185 #endif
186
187 next_addr = addr;
188 while(fd >= 0)
189 {
190 #ifdef HAVE_SSL
191 if (conn->sock_state.ssl)
192 {
193 ret = ssl_socket_connect(conn->sock_state.ssl_ctx,
194 (void*)next_addr, true, true);
195
196 if (ret >= 0)
197 break;
198
199 ssl_socket_close(conn->sock_state.ssl_ctx);
200 }
201 else
202 #endif
203 {
204 ret = socket_connect(fd, (void*)next_addr, true);
205
206 if (ret >= 0 && socket_nonblock(fd))
207 break;
208
209 socket_close(fd);
210 }
211
212 fd = socket_next((void**)&next_addr);
213 }
214
215 if (addr)
216 freeaddrinfo_retro(addr);
217
218 conn->sock_state.fd = fd;
219
220 return fd;
221 }
222
net_http_send_str(struct http_socket_state_t * sock_state,bool * error,const char * text)223 static void net_http_send_str(
224 struct http_socket_state_t *sock_state, bool *error, const char *text)
225 {
226 size_t text_size;
227 if (*error)
228 return;
229 text_size = strlen(text);
230 #ifdef HAVE_SSL
231 if (sock_state->ssl)
232 {
233 if (!ssl_socket_send_all_blocking(
234 sock_state->ssl_ctx, text, text_size, true))
235 *error = true;
236 }
237 else
238 #endif
239 {
240 if (!socket_send_all_blocking(
241 sock_state->fd, text, text_size, true))
242 *error = true;
243 }
244 }
245
net_http_connection_new(const char * url,const char * method,const char * data)246 struct http_connection_t *net_http_connection_new(const char *url,
247 const char *method, const char *data)
248 {
249 char new_domain[2048];
250 bool error = false;
251 char **domain = NULL;
252 char *uri = NULL;
253 char *url_dup = NULL;
254 char *domain_port = NULL;
255 char *domain_port2 = NULL;
256 char *url_port = NULL;
257
258 struct http_connection_t *conn = (struct http_connection_t*)calloc(1,
259 sizeof(*conn));
260
261 if (!conn)
262 return NULL;
263
264 if (!url)
265 {
266 free(conn);
267 return NULL;
268 }
269
270 conn->urlcopy = strdup(url);
271
272 if (method)
273 conn->methodcopy = strdup(method);
274
275 if (data)
276 conn->postdatacopy = strdup(data);
277
278 if (!conn->urlcopy)
279 goto error;
280
281 if (!strncmp(url, "http://", STRLEN_CONST("http://")))
282 conn->scan = conn->urlcopy + STRLEN_CONST("http://");
283 else if (!strncmp(url, "https://", STRLEN_CONST("https://")))
284 {
285 conn->scan = conn->urlcopy + STRLEN_CONST("https://");
286 conn->sock_state.ssl = true;
287 }
288 else
289 error = true;
290
291 if (string_is_empty(conn->scan))
292 goto error;
293
294 /* Get the port here from the url if it's specified.
295 does not work on username password urls: user:pass@domain.com
296
297 This code is not supposed to be needed, since the port
298 should be gotten elsewhere when the url is being scanned
299 for ":", but for whatever reason, it's not working correctly.
300 */
301
302 uri = strchr(conn->scan, (char) '/');
303
304 if (strchr(conn->scan, (char) ':') != NULL)
305 {
306 url_dup = strdup(conn->scan);
307 domain_port = strtok(url_dup, ":");
308 domain_port2 = strtok(NULL, ":");
309 url_port = domain_port2;
310 if (strchr(domain_port2, (char) '/') != NULL)
311 url_port = strtok(domain_port2, "/");
312
313 if (url_port != NULL)
314 conn->port = atoi(url_port);
315
316 strlcpy(new_domain, domain_port, sizeof(new_domain));
317
318 free(url_dup);
319
320 if (uri != NULL)
321 {
322 if (strchr(uri, (char) '/') == NULL)
323 strlcat(new_domain, uri, sizeof(new_domain));
324 else
325 {
326 strlcat(new_domain, "/", sizeof(new_domain));
327 strlcat(new_domain, strchr(uri, (char) '/')+sizeof(char), sizeof(new_domain));
328 }
329 strlcpy(conn->scan, new_domain, strlen(conn->scan) + 1);
330 }
331 }
332 /* end of port-fetching from url */
333 if (error)
334 goto error;
335
336 domain = &conn->domain;
337 *domain = conn->scan;
338
339 return conn;
340
341 error:
342 if (conn->urlcopy)
343 free(conn->urlcopy);
344 if (conn->methodcopy)
345 free(conn->methodcopy);
346 if (conn->postdatacopy)
347 free(conn->postdatacopy);
348 conn->urlcopy = NULL;
349 conn->methodcopy = NULL;
350 conn->postdatacopy = NULL;
351 free(conn);
352 return NULL;
353 }
354
net_http_connection_iterate(struct http_connection_t * conn)355 bool net_http_connection_iterate(struct http_connection_t *conn)
356 {
357 if (!conn)
358 return false;
359
360 while (*conn->scan != '/' && *conn->scan != ':' && *conn->scan != '\0')
361 conn->scan++;
362
363 return true;
364 }
365
net_http_connection_done(struct http_connection_t * conn)366 bool net_http_connection_done(struct http_connection_t *conn)
367 {
368 char **location = NULL;
369
370 if (!conn)
371 return false;
372
373 location = &conn->location;
374
375 if (*conn->scan == '\0')
376 return false;
377 *conn->scan = '\0';
378
379 if (conn->port == 0)
380 {
381 if (conn->sock_state.ssl)
382 conn->port = 443;
383 else
384 conn->port = 80;
385 }
386
387 if (*conn->scan == ':')
388 {
389 if (!isdigit((int)conn->scan[1]))
390 return false;
391
392 conn->port = (int)strtoul(conn->scan + 1, &conn->scan, 10);
393
394 if (*conn->scan != '/')
395 return false;
396 }
397
398 *location = conn->scan + 1;
399
400 return true;
401 }
402
net_http_connection_free(struct http_connection_t * conn)403 void net_http_connection_free(struct http_connection_t *conn)
404 {
405 if (!conn)
406 return;
407
408 if (conn->urlcopy)
409 free(conn->urlcopy);
410
411 if (conn->methodcopy)
412 free(conn->methodcopy);
413
414 if (conn->contenttypecopy)
415 free(conn->contenttypecopy);
416
417 if (conn->postdatacopy)
418 free(conn->postdatacopy);
419
420 conn->urlcopy = NULL;
421 conn->methodcopy = NULL;
422 conn->contenttypecopy = NULL;
423 conn->postdatacopy = NULL;
424
425 free(conn);
426 }
427
net_http_connection_url(struct http_connection_t * conn)428 const char *net_http_connection_url(struct http_connection_t *conn)
429 {
430 return conn->urlcopy;
431 }
432
net_http_new(struct http_connection_t * conn)433 struct http_t *net_http_new(struct http_connection_t *conn)
434 {
435 bool error = false;
436 int fd = -1;
437 struct http_t *state = NULL;
438
439 if (!conn)
440 goto error;
441
442 fd = net_http_new_socket(conn);
443
444 if (fd < 0)
445 goto error;
446
447 error = false;
448
449 /* This is a bit lazy, but it works. */
450 if (conn->methodcopy)
451 {
452 net_http_send_str(&conn->sock_state, &error, conn->methodcopy);
453 net_http_send_str(&conn->sock_state, &error, " /");
454 }
455 else
456 {
457 net_http_send_str(&conn->sock_state, &error, "GET /");
458 }
459
460 net_http_send_str(&conn->sock_state, &error, conn->location);
461 net_http_send_str(&conn->sock_state, &error, " HTTP/1.1\r\n");
462
463 net_http_send_str(&conn->sock_state, &error, "Host: ");
464 net_http_send_str(&conn->sock_state, &error, conn->domain);
465
466 if (!conn->port)
467 {
468 char portstr[16];
469
470 portstr[0] = '\0';
471
472 snprintf(portstr, sizeof(portstr), ":%i", conn->port);
473 net_http_send_str(&conn->sock_state, &error, portstr);
474 }
475
476 net_http_send_str(&conn->sock_state, &error, "\r\n");
477
478 /* this is not being set anywhere yet */
479 if (conn->contenttypecopy)
480 {
481 net_http_send_str(&conn->sock_state, &error, "Content-Type: ");
482 net_http_send_str(&conn->sock_state, &error, conn->contenttypecopy);
483 net_http_send_str(&conn->sock_state, &error, "\r\n");
484 }
485
486 if (conn->methodcopy && (string_is_equal(conn->methodcopy, "POST")))
487 {
488 size_t post_len, len;
489 char *len_str = NULL;
490
491 if (!conn->postdatacopy)
492 goto error;
493
494 if (!conn->contenttypecopy)
495 net_http_send_str(&conn->sock_state, &error,
496 "Content-Type: application/x-www-form-urlencoded\r\n");
497
498 net_http_send_str(&conn->sock_state, &error, "Content-Length: ");
499
500 post_len = strlen(conn->postdatacopy);
501 #ifdef _WIN32
502 len = snprintf(NULL, 0, "%" PRIuPTR, post_len);
503 len_str = (char*)malloc(len + 1);
504 snprintf(len_str, len + 1, "%" PRIuPTR, post_len);
505 #else
506 len = snprintf(NULL, 0, "%llu", (long long unsigned)post_len);
507 len_str = (char*)malloc(len + 1);
508 snprintf(len_str, len + 1, "%llu", (long long unsigned)post_len);
509 #endif
510
511 len_str[len] = '\0';
512
513 net_http_send_str(&conn->sock_state, &error, len_str);
514 net_http_send_str(&conn->sock_state, &error, "\r\n");
515
516 free(len_str);
517 }
518
519 net_http_send_str(&conn->sock_state, &error, "User-Agent: libretro\r\n");
520 net_http_send_str(&conn->sock_state, &error, "Connection: close\r\n");
521 net_http_send_str(&conn->sock_state, &error, "\r\n");
522
523 if (conn->methodcopy && (string_is_equal(conn->methodcopy, "POST")))
524 net_http_send_str(&conn->sock_state, &error, conn->postdatacopy);
525
526 if (error)
527 goto error;
528
529 state = (struct http_t*)malloc(sizeof(struct http_t));
530 state->sock_state = conn->sock_state;
531 state->status = -1;
532 state->data = NULL;
533 state->part = P_HEADER_TOP;
534 state->bodytype = T_FULL;
535 state->error = false;
536 state->pos = 0;
537 state->len = 0;
538 state->buflen = 512;
539 state->data = (char*)malloc(state->buflen);
540
541 if (!state->data)
542 goto error;
543
544 return state;
545
546 error:
547 if (conn)
548 {
549 if (conn->methodcopy)
550 free(conn->methodcopy);
551 if (conn->contenttypecopy)
552 free(conn->contenttypecopy);
553 conn->methodcopy = NULL;
554 conn->contenttypecopy = NULL;
555 conn->postdatacopy = NULL;
556 }
557 #ifdef HAVE_SSL
558 if (conn && conn->sock_state.ssl && conn->sock_state.ssl_ctx && fd >= 0)
559 {
560 ssl_socket_close(conn->sock_state.ssl_ctx);
561 ssl_socket_free(conn->sock_state.ssl_ctx);
562 conn->sock_state.ssl_ctx = NULL;
563 }
564 #else
565 if (fd >= 0)
566 socket_close(fd);
567 #endif
568 if (state)
569 free(state);
570 return NULL;
571 }
572
net_http_fd(struct http_t * state)573 int net_http_fd(struct http_t *state)
574 {
575 if (!state)
576 return -1;
577 return state->sock_state.fd;
578 }
579
net_http_update(struct http_t * state,size_t * progress,size_t * total)580 bool net_http_update(struct http_t *state, size_t* progress, size_t* total)
581 {
582 ssize_t newlen = 0;
583
584 if (!state || state->error)
585 goto fail;
586
587 if (state->part < P_BODY)
588 {
589 if (state->error)
590 newlen = -1;
591 else
592 {
593 #ifdef HAVE_SSL
594 if (state->sock_state.ssl && state->sock_state.ssl_ctx)
595 newlen = ssl_socket_receive_all_nonblocking(state->sock_state.ssl_ctx, &state->error,
596 (uint8_t*)state->data + state->pos,
597 state->buflen - state->pos);
598 else
599 #endif
600 newlen = socket_receive_all_nonblocking(state->sock_state.fd, &state->error,
601 (uint8_t*)state->data + state->pos,
602 state->buflen - state->pos);
603 }
604
605 if (newlen < 0)
606 goto fail;
607
608 if (state->pos + newlen >= state->buflen - 64)
609 {
610 state->buflen *= 2;
611 state->data = (char*)realloc(state->data, state->buflen);
612 }
613 state->pos += newlen;
614
615 while (state->part < P_BODY)
616 {
617 char *dataend = state->data + state->pos;
618 char *lineend = (char*)memchr(state->data, '\n', state->pos);
619
620 if (!lineend)
621 break;
622
623 *lineend='\0';
624
625 if (lineend != state->data && lineend[-1]=='\r')
626 lineend[-1]='\0';
627
628 if (state->part == P_HEADER_TOP)
629 {
630 if (strncmp(state->data, "HTTP/1.", STRLEN_CONST("HTTP/1."))!=0)
631 goto fail;
632 state->status = (int)strtoul(state->data
633 + STRLEN_CONST("HTTP/1.1 "), NULL, 10);
634 state->part = P_HEADER;
635 }
636 else
637 {
638 if (!strncmp(state->data, "Content-Length: ",
639 STRLEN_CONST("Content-Length: ")))
640 {
641 state->bodytype = T_LEN;
642 state->len = strtol(state->data +
643 STRLEN_CONST("Content-Length: "), NULL, 10);
644 }
645 if (string_is_equal(state->data, "Transfer-Encoding: chunked"))
646 state->bodytype = T_CHUNK;
647
648 /* TODO: save headers somewhere */
649 if (state->data[0]=='\0')
650 {
651 state->part = P_BODY;
652 if (state->bodytype == T_CHUNK)
653 state->part = P_BODY_CHUNKLEN;
654 }
655 }
656
657 memmove(state->data, lineend + 1, dataend-(lineend+1));
658 state->pos = (dataend-(lineend + 1));
659 }
660 if (state->part >= P_BODY)
661 {
662 newlen = state->pos;
663 state->pos = 0;
664 }
665 }
666
667 if (state->part >= P_BODY && state->part < P_DONE)
668 {
669 if (!newlen)
670 {
671 if (state->error)
672 newlen = -1;
673 else
674 {
675 #ifdef HAVE_SSL
676 if (state->sock_state.ssl && state->sock_state.ssl_ctx)
677 newlen = ssl_socket_receive_all_nonblocking(
678 state->sock_state.ssl_ctx,
679 &state->error,
680 (uint8_t*)state->data + state->pos,
681 state->buflen - state->pos);
682 else
683 #endif
684 newlen = socket_receive_all_nonblocking(
685 state->sock_state.fd,
686 &state->error,
687 (uint8_t*)state->data + state->pos,
688 state->buflen - state->pos);
689 }
690
691 if (newlen < 0)
692 {
693 if (state->bodytype == T_FULL)
694 {
695 state->part = P_DONE;
696 state->data = (char*)realloc(state->data, state->len);
697 }
698 else
699 goto fail;
700 newlen=0;
701 }
702
703 if (state->pos + newlen >= state->buflen - 64)
704 {
705 state->buflen *= 2;
706 state->data = (char*)realloc(state->data, state->buflen);
707 }
708 }
709
710 parse_again:
711 if (state->bodytype == T_CHUNK)
712 {
713 if (state->part == P_BODY_CHUNKLEN)
714 {
715 state->pos += newlen;
716 if (state->pos - state->len >= 2)
717 {
718 /*
719 * len=start of chunk including \r\n
720 * pos=end of data
721 */
722
723 char *fullend = state->data + state->pos;
724 char *end = (char*)memchr(state->data + state->len + 2, '\n',
725 state->pos - state->len - 2);
726
727 if (end)
728 {
729 size_t chunklen = strtoul(state->data+state->len, NULL, 16);
730 state->pos = state->len;
731 end++;
732
733 memmove(state->data+state->len, end, fullend-end);
734
735 state->len = chunklen;
736 newlen = (fullend - end);
737
738 /*
739 len=num bytes
740 newlen=unparsed bytes after \n
741 pos=start of chunk including \r\n
742 */
743
744 state->part = P_BODY;
745 if (state->len == 0)
746 {
747 state->part = P_DONE;
748 state->len = state->pos;
749 state->data = (char*)realloc(state->data, state->len);
750 }
751 goto parse_again;
752 }
753 }
754 }
755 else if (state->part == P_BODY)
756 {
757 if ((size_t)newlen >= state->len)
758 {
759 state->pos += state->len;
760 newlen -= state->len;
761 state->len = state->pos;
762 state->part = P_BODY_CHUNKLEN;
763 goto parse_again;
764 }
765 else
766 {
767 state->pos += newlen;
768 state->len -= newlen;
769 }
770 }
771 }
772 else
773 {
774 state->pos += newlen;
775
776 if (state->pos == state->len)
777 {
778 state->part = P_DONE;
779 state->data = (char*)realloc(state->data, state->len);
780 }
781 if (state->pos > state->len)
782 goto fail;
783 }
784 }
785
786 if (progress)
787 *progress = state->pos;
788
789 if (total)
790 {
791 if (state->bodytype == T_LEN)
792 *total=state->len;
793 else
794 *total=0;
795 }
796
797 return (state->part == P_DONE);
798
799 fail:
800 if (state)
801 {
802 state->error = true;
803 state->part = P_ERROR;
804 state->status = -1;
805 }
806
807 return true;
808 }
809
net_http_status(struct http_t * state)810 int net_http_status(struct http_t *state)
811 {
812 if (!state)
813 return -1;
814 return state->status;
815 }
816
net_http_data(struct http_t * state,size_t * len,bool accept_error)817 uint8_t* net_http_data(struct http_t *state, size_t* len, bool accept_error)
818 {
819 if (!state)
820 return NULL;
821
822 if (!accept_error && net_http_error(state))
823 {
824 if (len)
825 *len=0;
826 return NULL;
827 }
828
829 if (len)
830 *len=state->len;
831
832 return (uint8_t*)state->data;
833 }
834
net_http_delete(struct http_t * state)835 void net_http_delete(struct http_t *state)
836 {
837 if (!state)
838 return;
839
840 if (state->sock_state.fd >= 0)
841 {
842 socket_close(state->sock_state.fd);
843 #ifdef HAVE_SSL
844 if (state->sock_state.ssl && state->sock_state.ssl_ctx)
845 {
846 ssl_socket_free(state->sock_state.ssl_ctx);
847 state->sock_state.ssl_ctx = NULL;
848 }
849 #endif
850 }
851 free(state);
852 }
853
net_http_error(struct http_t * state)854 bool net_http_error(struct http_t *state)
855 {
856 return (state->error || state->status<200 || state->status>299);
857 }
858