1 /* Copyright (C) 2010-2020 The RetroArch team
3 * ---------------------------------------------------------------------------------------
4 * The following license statement only applies to this file (net_http.c).
5 * ---------------------------------------------------------------------------------------
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:
13 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
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.
27 #include <net/net_http.h>
28 #include <net/net_compat.h>
29 #include <net/net_socket.h>
31 #include <net/net_socket_ssl.h>
33 #include <compat/strl.h>
34 #include <string/stdstring.h>
36 #include <retro_common_api.h>
37 #include <retro_miscellaneous.h>
56 struct http_socket_state_t
66 struct http_socket_state_t sock_state; /* ptr alignment */
76 struct http_connection_t
83 char *contenttypecopy;
87 struct http_socket_state_t sock_state; /* ptr alignment */
95 * caller is responsible for deleting the destination buffer
97 void net_http_urlencode(char **dest, const char *source)
99 static const char urlencode_lut[256] =
359 /* Assume every character will be encoded, so we need 3 times the space. */
360 size_t len = strlen(source) * 3 + 1;
362 char *enc = (char*)calloc(1, len);
365 for (; *source; source++)
369 /* any non-ASCII character will just be encoded without question */
370 if ((unsigned)*source < sizeof(urlencode_lut) && urlencode_lut[(unsigned)*source])
371 written = snprintf(enc, count, "%c", urlencode_lut[(unsigned)*source]);
373 written = snprintf(enc, count, "%%%02X", *source & 0xFF);
381 (*dest)[len - 1] = '\0';
385 * net_http_urlencode_full:
387 * Re-encode a full URL
389 void net_http_urlencode_full(char *dest,
390 const char *source, size_t size)
393 size_t url_domain_len;
394 char url_domain[256];
395 char url_path[PATH_MAX_LENGTH];
400 strlcpy(url_path, source, sizeof(url_path));
403 while (count < 3 && tmp[0] != '\0')
405 tmp = strchr(tmp, '/');
410 tmp_len = strlen(tmp);
411 url_domain_len = ((strlcpy(url_domain, source, tmp - url_path)) - tmp_len) - 1;
413 source + url_domain_len + 1,
418 net_http_urlencode(&tmp, url_path);
419 buf_pos = strlcpy(dest, url_domain, size);
421 dest[buf_pos+1] = '\0';
422 strlcat(dest, tmp, size);
426 static int net_http_new_socket(struct http_connection_t *conn)
428 struct addrinfo *addr = NULL, *next_addr = NULL;
429 int fd = socket_init(
430 (void**)&addr, conn->port, conn->domain, SOCKET_TYPE_STREAM, 0);
432 if (conn->sock_state.ssl)
437 if (!(conn->sock_state.ssl_ctx = ssl_socket_init(fd, conn->domain)))
444 /* TODO: Properly figure out what's going wrong when the newer
445 timeout/poll code interacts with mbed and winsock
446 https://github.com/libretro/RetroArch/issues/14742 */
448 /* Temp fix, don't use new timeout/poll code for cheevos http requests */
451 if (!strcmp(conn->domain, "retroachievements.org"))
455 if (ssl_socket_connect(conn->sock_state.ssl_ctx, addr, timeout, true)
464 for (next_addr = addr; fd >= 0; fd = socket_next((void**)&next_addr))
466 if (socket_connect_with_timeout(fd, next_addr, 5000))
476 freeaddrinfo_retro(addr);
478 conn->sock_state.fd = fd;
483 static void net_http_send_str(
484 struct http_socket_state_t *sock_state, bool *error,
485 const char *text, size_t text_size)
492 if (!ssl_socket_send_all_blocking(
493 sock_state->ssl_ctx, text, text_size, true))
499 if (!socket_send_all_blocking(
500 sock_state->fd, text, text_size, true))
505 struct http_connection_t *net_http_connection_new(const char *url,
506 const char *method, const char *data)
508 struct http_connection_t *conn = NULL;
512 if (!(conn = (struct http_connection_t*)malloc(
517 conn->location = NULL;
518 conn->urlcopy = NULL;
520 conn->methodcopy = NULL;
521 conn->contenttypecopy = NULL;
522 conn->postdatacopy = NULL;
523 conn->useragentcopy = NULL;
524 conn->headerscopy = NULL;
526 conn->sock_state.fd = 0;
527 conn->sock_state.ssl = false;
528 conn->sock_state.ssl_ctx= NULL;
531 conn->methodcopy = strdup(method);
534 conn->postdatacopy = strdup(data);
536 if (!(conn->urlcopy = strdup(url)))
539 if (!strncmp(url, "http://", STRLEN_CONST("http://")))
540 conn->scan = conn->urlcopy + STRLEN_CONST("http://");
541 else if (!strncmp(url, "https://", STRLEN_CONST("https://")))
543 conn->scan = conn->urlcopy + STRLEN_CONST("https://");
544 conn->sock_state.ssl = true;
549 if (string_is_empty(conn->scan))
552 conn->domain = conn->scan;
559 if (conn->methodcopy)
560 free(conn->methodcopy);
561 if (conn->postdatacopy)
562 free(conn->postdatacopy);
563 conn->urlcopy = NULL;
564 conn->methodcopy = NULL;
565 conn->postdatacopy = NULL;
571 * net_http_connection_iterate:
575 bool net_http_connection_iterate(struct http_connection_t *conn)
580 while (*conn->scan != '/' && *conn->scan != ':' && *conn->scan != '\0')
586 bool net_http_connection_done(struct http_connection_t *conn)
590 if (!conn || !conn->domain || !*conn->domain)
593 if (*conn->scan == ':')
595 /* domain followed by port, split off the port */
596 *conn->scan++ = '\0';
598 if (!isdigit((int)(*conn->scan)))
601 conn->port = (int)strtoul(conn->scan, &conn->scan, 10);
604 else if (conn->port == 0)
606 /* port not specified, default to standard HTTP or HTTPS port */
607 if (conn->sock_state.ssl)
613 if (*conn->scan == '/')
615 /* domain followed by location - split off the location */
616 /* site.com/path.html or site.com:80/path.html */
618 conn->location = conn->scan + 1;
621 else if (!*conn->scan)
623 /* domain with no location - point location at empty string */
624 /* site.com or site.com:80 */
625 conn->location = conn->scan;
628 else if (*conn->scan == '?')
630 /* domain with no location, but still has query parms - point location at the query parms */
631 /* site.com?param=3 or site.com:80?param=3 */
634 /* if there wasn't a port, we have to expand the urlcopy so we can separate the two parts */
635 size_t domain_len = strlen(conn->domain);
636 size_t location_len = strlen(conn->scan);
637 char* urlcopy = (char*)malloc(domain_len + location_len + 2);
638 memcpy(urlcopy, conn->domain, domain_len);
639 urlcopy[domain_len] = '\0';
640 memcpy(urlcopy + domain_len + 1, conn->scan, location_len + 1);
643 conn->domain = conn->urlcopy = urlcopy;
644 conn->location = conn->scan = urlcopy + domain_len + 1;
646 else /* There was a port, so overwriting the : will terminate the domain and we can just point at the ? */
647 conn->location = conn->scan;
652 /* invalid character after domain/port */
656 void net_http_connection_free(struct http_connection_t *conn)
664 if (conn->methodcopy)
665 free(conn->methodcopy);
667 if (conn->contenttypecopy)
668 free(conn->contenttypecopy);
670 if (conn->postdatacopy)
671 free(conn->postdatacopy);
673 if (conn->useragentcopy)
674 free(conn->useragentcopy);
676 if (conn->headerscopy)
677 free(conn->headerscopy);
679 conn->urlcopy = NULL;
680 conn->methodcopy = NULL;
681 conn->contenttypecopy = NULL;
682 conn->postdatacopy = NULL;
683 conn->useragentcopy = NULL;
684 conn->headerscopy = NULL;
689 void net_http_connection_set_user_agent(
690 struct http_connection_t *conn, const char *user_agent)
692 if (conn->useragentcopy)
693 free(conn->useragentcopy);
695 conn->useragentcopy = user_agent ? strdup(user_agent) : NULL;
698 void net_http_connection_set_headers(
699 struct http_connection_t *conn, const char *headers)
701 if (conn->headerscopy)
702 free(conn->headerscopy);
704 conn->headerscopy = headers ? strdup(headers) : NULL;
707 const char *net_http_connection_url(struct http_connection_t *conn)
709 return conn->urlcopy;
712 const char* net_http_connection_method(struct http_connection_t* conn)
714 return conn->methodcopy;
717 struct http_t *net_http_new(struct http_connection_t *conn)
721 struct http_t *state = NULL;
726 if ((fd = net_http_new_socket(conn)) < 0)
731 /* This is a bit lazy, but it works. */
732 if (conn->methodcopy)
734 net_http_send_str(&conn->sock_state, &error, conn->methodcopy,
735 strlen(conn->methodcopy));
736 net_http_send_str(&conn->sock_state, &error, " /",
741 net_http_send_str(&conn->sock_state, &error, "GET /",
742 STRLEN_CONST("GET /"));
745 net_http_send_str(&conn->sock_state, &error, conn->location,
746 strlen(conn->location));
747 net_http_send_str(&conn->sock_state, &error, " HTTP/1.1\r\n",
748 STRLEN_CONST(" HTTP/1.1\r\n"));
750 net_http_send_str(&conn->sock_state, &error, "Host: ",
751 STRLEN_CONST("Host: "));
752 net_http_send_str(&conn->sock_state, &error, conn->domain,
753 strlen(conn->domain));
761 snprintf(portstr, sizeof(portstr), ":%i", conn->port);
762 net_http_send_str(&conn->sock_state, &error, portstr,
766 net_http_send_str(&conn->sock_state, &error, "\r\n",
767 STRLEN_CONST("\r\n"));
769 /* Pre-formatted headers */
770 if (conn->headerscopy)
771 net_http_send_str(&conn->sock_state, &error, conn->headerscopy,
772 strlen(conn->headerscopy));
773 /* This is not being set anywhere yet */
774 else if (conn->contenttypecopy)
776 net_http_send_str(&conn->sock_state, &error, "Content-Type: ",
777 STRLEN_CONST("Content-Type: "));
778 net_http_send_str(&conn->sock_state, &error,
779 conn->contenttypecopy, strlen(conn->contenttypecopy));
780 net_http_send_str(&conn->sock_state, &error, "\r\n",
781 STRLEN_CONST("\r\n"));
784 if (conn->methodcopy && (string_is_equal(conn->methodcopy, "POST")))
786 size_t post_len, len;
787 char *len_str = NULL;
789 if (!conn->postdatacopy)
792 if (!conn->headerscopy)
794 if (!conn->contenttypecopy)
795 net_http_send_str(&conn->sock_state, &error,
796 "Content-Type: application/x-www-form-urlencoded\r\n",
798 "Content-Type: application/x-www-form-urlencoded\r\n"
802 net_http_send_str(&conn->sock_state, &error, "Content-Length: ",
803 STRLEN_CONST("Content-Length: "));
805 post_len = strlen(conn->postdatacopy);
807 len = snprintf(NULL, 0, "%" PRIuPTR, post_len);
808 len_str = (char*)malloc(len + 1);
809 snprintf(len_str, len + 1, "%" PRIuPTR, post_len);
811 len = snprintf(NULL, 0, "%llu", (long long unsigned)post_len);
812 len_str = (char*)malloc(len + 1);
813 snprintf(len_str, len + 1, "%llu", (long long unsigned)post_len);
818 net_http_send_str(&conn->sock_state, &error, len_str,
820 net_http_send_str(&conn->sock_state, &error, "\r\n",
821 STRLEN_CONST("\r\n"));
826 net_http_send_str(&conn->sock_state, &error, "User-Agent: ",
827 STRLEN_CONST("User-Agent: "));
828 if (conn->useragentcopy)
829 net_http_send_str(&conn->sock_state, &error,
830 conn->useragentcopy, strlen(conn->useragentcopy));
832 net_http_send_str(&conn->sock_state, &error, "libretro",
833 STRLEN_CONST("libretro"));
834 net_http_send_str(&conn->sock_state, &error, "\r\n",
835 STRLEN_CONST("\r\n"));
837 net_http_send_str(&conn->sock_state, &error,
838 "Connection: close\r\n", STRLEN_CONST("Connection: close\r\n"));
839 net_http_send_str(&conn->sock_state, &error, "\r\n",
840 STRLEN_CONST("\r\n"));
842 if (conn->methodcopy && (string_is_equal(conn->methodcopy, "POST")))
843 net_http_send_str(&conn->sock_state, &error, conn->postdatacopy,
844 strlen(conn->postdatacopy));
849 state = (struct http_t*)malloc(sizeof(struct http_t));
850 state->sock_state = conn->sock_state;
853 state->part = P_HEADER_TOP;
854 state->bodytype = T_FULL;
855 state->error = false;
860 if (!(state->data = (char*)malloc(state->buflen)))
868 if (conn->methodcopy)
869 free(conn->methodcopy);
870 if (conn->contenttypecopy)
871 free(conn->contenttypecopy);
872 conn->methodcopy = NULL;
873 conn->contenttypecopy = NULL;
874 conn->postdatacopy = NULL;
877 if (conn && conn->sock_state.ssl_ctx)
879 ssl_socket_close(conn->sock_state.ssl_ctx);
880 ssl_socket_free(conn->sock_state.ssl_ctx);
881 conn->sock_state.ssl_ctx = NULL;
897 * You can use this to call net_http_update
898 * only when something will happen; select() it for reading.
900 int net_http_fd(struct http_t *state)
904 return state->sock_state.fd;
910 * @return true if it's done, or if something broke.
911 * @total will be 0 if it's not known.
913 bool net_http_update(struct http_t *state, size_t* progress, size_t* total)
921 state->part = P_ERROR;
926 if (state->part < P_BODY)
930 state->part = P_ERROR;
936 if (state->sock_state.ssl && state->sock_state.ssl_ctx)
937 newlen = ssl_socket_receive_all_nonblocking(state->sock_state.ssl_ctx, &state->error,
938 (uint8_t*)state->data + state->pos,
939 state->buflen - state->pos);
942 newlen = socket_receive_all_nonblocking(state->sock_state.fd, &state->error,
943 (uint8_t*)state->data + state->pos,
944 state->buflen - state->pos);
949 state->part = P_ERROR;
954 if (state->pos + newlen >= state->buflen - 64)
957 state->data = (char*)realloc(state->data, state->buflen);
959 state->pos += newlen;
961 while (state->part < P_BODY)
963 char *dataend = state->data + state->pos;
964 char *lineend = (char*)memchr(state->data, '\n', state->pos);
971 if (lineend != state->data && lineend[-1]=='\r')
974 if (state->part == P_HEADER_TOP)
976 if (strncmp(state->data, "HTTP/1.", STRLEN_CONST("HTTP/1."))!=0)
979 state->part = P_ERROR;
983 state->status = (int)strtoul(state->data
984 + STRLEN_CONST("HTTP/1.1 "), NULL, 10);
985 state->part = P_HEADER;
989 if (string_starts_with_case_insensitive(state->data, "Content-Length:"))
991 char* ptr = state->data + STRLEN_CONST("Content-Length:");
992 while (ISSPACE(*ptr))
995 state->bodytype = T_LEN;
996 state->len = strtol(ptr, NULL, 10);
998 if (string_is_equal_case_insensitive(state->data, "Transfer-Encoding: chunked"))
999 state->bodytype = T_CHUNK;
1001 /* TODO: save headers somewhere */
1002 if (state->data[0]=='\0')
1004 state->part = P_BODY;
1005 if (state->bodytype == T_CHUNK)
1006 state->part = P_BODY_CHUNKLEN;
1010 memmove(state->data, lineend + 1, dataend-(lineend+1));
1011 state->pos = (dataend-(lineend + 1));
1014 if (state->part >= P_BODY)
1016 newlen = state->pos;
1021 if (state->part >= P_BODY && state->part < P_DONE)
1030 if (state->sock_state.ssl && state->sock_state.ssl_ctx)
1031 newlen = ssl_socket_receive_all_nonblocking(
1032 state->sock_state.ssl_ctx,
1034 (uint8_t*)state->data + state->pos,
1035 state->buflen - state->pos);
1038 newlen = socket_receive_all_nonblocking(
1039 state->sock_state.fd,
1041 (uint8_t*)state->data + state->pos,
1042 state->buflen - state->pos);
1047 if (state->bodytype != T_FULL)
1049 state->error = true;
1050 state->part = P_ERROR;
1054 state->part = P_DONE;
1055 state->data = (char*)realloc(state->data, state->len);
1059 if (state->pos + newlen >= state->buflen - 64)
1062 state->data = (char*)realloc(state->data, state->buflen);
1067 if (state->bodytype == T_CHUNK)
1069 if (state->part == P_BODY_CHUNKLEN)
1071 state->pos += newlen;
1073 if (state->pos - state->len >= 2)
1076 * len=start of chunk including \r\n
1080 char *fullend = state->data + state->pos;
1081 char *end = (char*)memchr(state->data + state->len + 2, '\n',
1082 state->pos - state->len - 2);
1086 size_t chunklen = strtoul(state->data+state->len, NULL, 16);
1087 state->pos = state->len;
1090 memmove(state->data+state->len, end, fullend-end);
1092 state->len = chunklen;
1093 newlen = (fullend - end);
1097 newlen=unparsed bytes after \n
1098 pos=start of chunk including \r\n
1101 state->part = P_BODY;
1102 if (state->len == 0)
1104 state->part = P_DONE;
1105 state->len = state->pos;
1106 state->data = (char*)realloc(state->data, state->len);
1112 else if (state->part == P_BODY)
1114 if ((size_t)newlen >= state->len)
1116 state->pos += state->len;
1117 newlen -= state->len;
1118 state->len = state->pos;
1119 state->part = P_BODY_CHUNKLEN;
1122 state->pos += newlen;
1123 state->len -= newlen;
1128 state->pos += newlen;
1130 if (state->pos > state->len)
1132 state->error = true;
1133 state->part = P_ERROR;
1137 else if (state->pos == state->len)
1139 state->part = P_DONE;
1140 state->data = (char*)realloc(state->data, state->len);
1146 *progress = state->pos;
1150 if (state->bodytype == T_LEN)
1151 *total = state->len;
1156 return (state->part == P_DONE);
1162 * Report HTTP status. 200, 404, or whatever.
1166 * @return HTTP status code.
1168 int net_http_status(struct http_t *state)
1172 return state->status;
1180 * @return the downloaded data. The returned buffer is owned by the
1181 * HTTP handler; it's freed by net_http_delete().
1182 * If the status is not 20x and accept_error is false, it returns NULL.
1184 uint8_t* net_http_data(struct http_t *state, size_t* len, bool accept_error)
1189 if (!accept_error && (state->error || state->status < 200 || state->status > 299))
1199 return (uint8_t*)state->data;
1205 * Cleans up all memory.
1207 void net_http_delete(struct http_t *state)
1212 if (state->sock_state.fd >= 0)
1215 if (state->sock_state.ssl && state->sock_state.ssl_ctx)
1217 ssl_socket_close(state->sock_state.ssl_ctx);
1218 ssl_socket_free(state->sock_state.ssl_ctx);
1219 state->sock_state.ssl_ctx = NULL;
1223 socket_close(state->sock_state.fd);
1233 bool net_http_error(struct http_t *state)
1235 return (state->error || state->status < 200 || state->status > 299);