1 // ============================================================================
2 // Artsoft Retro-Game Library
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2021 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
20 static char http_error[MAX_HTTP_ERROR_SIZE];
22 static void SetHttpError(char *format, ...)
27 vsnprintf(http_error, MAX_HTTP_ERROR_SIZE, format, ap);
31 char *GetHttpError(void)
36 void ConvertHttpRequestBodyToServerEncoding(struct HttpRequest *request)
38 char *body_utf8 = getUTF8FromLatin1(request->body);
40 strncpy(request->body, body_utf8, MAX_HTTP_BODY_SIZE);
41 request->body[MAX_HTTP_BODY_SIZE] = '\0';
43 checked_free(body_utf8);
46 void ConvertHttpResponseBodyToClientEncoding(struct HttpResponse *response)
48 char *body_latin1 = getLatin1FromUTF8(response->body);
50 strncpy(response->body, body_latin1, MAX_HTTP_BODY_SIZE);
51 response->body[MAX_HTTP_BODY_SIZE] = '\0';
53 response->body_size = strlen(response->body);
55 checked_free(body_latin1);
58 static void SetHttpResponseToDefaults(struct HttpResponse *response)
60 response->head[0] = '\0';
61 response->body[0] = '\0';
62 response->body_size = 0;
64 response->status_code = 0;
65 response->status_text[0] = '\0';
68 struct HttpResponse *GetHttpResponseFromBuffer(void *buffer, int size)
70 if (size > MAX_HTTP_BODY_SIZE)
73 struct HttpResponse *response = checked_calloc(sizeof(struct HttpResponse));
75 SetHttpResponseToDefaults(response);
77 strncpy(response->body, buffer, MAX_HTTP_BODY_SIZE);
78 response->body[MAX_HTTP_BODY_SIZE] = '\0';
79 response->body_size = MIN(size, MAX_HTTP_BODY_SIZE);
84 static boolean SetHTTPResponseCode(struct HttpResponse *response, char *buffer)
86 char *prefix = "HTTP/1.1 ";
87 char *prefix_start = strstr(buffer, prefix);
89 if (prefix_start == NULL)
92 char *status_code_start = prefix_start + strlen(prefix);
93 char *status_code_end = strstr(status_code_start, " ");
95 if (status_code_end == NULL)
98 int status_code_size = status_code_end - status_code_start;
100 if (status_code_size != 3) // status code must have three digits
103 char status_code[status_code_size + 1];
105 strncpy(status_code, status_code_start, status_code_size);
106 status_code[status_code_size] = '\0';
108 response->status_code = atoi(status_code);
110 char *status_text_start = status_code_end + 1;
111 char *status_text_end = strstr(status_text_start, "\r\n");
113 if (status_text_end == NULL)
116 int status_text_size = status_text_end - status_text_start;
118 if (status_text_size > MAX_HTTP_ERROR_SIZE)
121 strncpy(response->status_text, status_text_start, status_text_size);
122 response->status_text[status_text_size] = '\0';
127 static boolean SetHTTPResponseHead(struct HttpResponse *response, char *buffer)
129 char *separator = "\r\n\r\n";
130 char *separator_start = strstr(buffer, separator);
132 if (separator_start == NULL)
135 int head_size = separator_start - buffer;
137 if (head_size > MAX_HTTP_HEAD_SIZE)
140 strncpy(response->head, buffer, head_size);
141 response->head[head_size] = '\0';
146 static boolean SetHTTPResponseBody(struct HttpResponse *response, char *buffer,
149 char *separator = "\r\n\r\n";
150 char *separator_start = strstr(buffer, separator);
152 if (separator_start == NULL)
155 int separator_size = strlen(separator);
156 int full_head_size = separator_start + separator_size - buffer;
157 int body_size = buffer_size - full_head_size;
159 if (body_size > MAX_HTTP_BODY_SIZE)
162 memcpy(response->body, buffer + full_head_size, body_size);
163 response->body[body_size] = '\0';
164 response->body_size = body_size;
169 static boolean DoHttpRequestExt(struct HttpRequest *request,
170 struct HttpResponse *response,
173 int max_http_buffer_size,
174 SDLNet_SocketSet *socket_set,
180 SetHttpResponseToDefaults(response);
182 *socket_set = SDLNet_AllocSocketSet(1);
184 if (*socket_set == NULL)
186 SetHttpError("cannot allocate socket set");
191 SDLNet_ResolveHost(&ip, request->hostname, request->port);
193 if (ip.host == INADDR_NONE)
195 SetHttpError("cannot resolve hostname '%s'", request->hostname);
200 server_host = SDLNet_Read32(&ip.host);
202 Debug("network:http", "trying to connect to server at %d.%d.%d.%d ...",
203 (server_host >> 24) & 0xff,
204 (server_host >> 16) & 0xff,
205 (server_host >> 8) & 0xff,
206 (server_host >> 0) & 0xff);
208 *socket = SDLNet_TCP_Open(&ip);
212 SetHttpError("cannot connect to host '%s': %s", request->hostname,
218 if (SDLNet_TCP_AddSocket(*socket_set, *socket) == -1)
220 SetHttpError("cannot add socket to socket set");
225 Debug("network:http", "successfully connected to server");
227 snprintf(request->head, MAX_HTTP_HEAD_SIZE,
230 "X-Requested-With: XMLHttpRequest\r\n"
231 "Content-Type: application/json\r\n"
232 "Connection: close\r\n"
233 "Content-Length: %d\r\n",
237 (int)strlen(request->body));
239 snprintf(send_buffer, max_http_buffer_size,
240 "%s\r\n%s", request->head, request->body);
242 Debug("network:http", "client request:\n--- snip ---\n%s\n--- snip ---",
245 int send_bytes = SDLNet_TCP_Send(*socket, send_buffer, strlen(send_buffer));
247 if (send_bytes != strlen(send_buffer))
249 SetHttpError("sending request to server failed");
254 int recv_bytes = SDLNet_TCP_Recv(*socket, recv_buffer, max_http_buffer_size);
258 SetHttpError("receiving response from server failed");
263 recv_buffer[recv_bytes] = '\0';
265 Debug("network:http", "server response:\n--- snip ---\n%s\n--- snip ---",
268 if (!SetHTTPResponseCode(response, recv_buffer))
270 SetHttpError("malformed HTTP response");
275 if (!SetHTTPResponseHead(response, recv_buffer))
277 SetHttpError("invalid HTTP response header");
282 if (!SetHTTPResponseBody(response, recv_buffer, recv_bytes))
284 SetHttpError("invalid HTTP response body");
289 Debug("network:http", "server response: %d %s",
290 response->status_code,
291 response->status_text);
296 boolean DoHttpRequest(struct HttpRequest *request,
297 struct HttpResponse *response)
299 int max_http_buffer_size = MAX_HTTP_HEAD_SIZE + MAX_HTTP_BODY_SIZE;
300 char *send_buffer = checked_malloc(max_http_buffer_size + 1);
301 char *recv_buffer = checked_malloc(max_http_buffer_size + 1);
302 SDLNet_SocketSet socket_set = NULL;
303 TCPsocket socket = NULL;
305 boolean success = DoHttpRequestExt(request, response,
306 send_buffer, recv_buffer,
307 max_http_buffer_size,
308 &socket_set, &socket);
309 if (socket_set != NULL)
313 SDLNet_TCP_DelSocket(socket_set, socket);
314 SDLNet_TCP_Close(socket);
317 SDLNet_FreeSocketSet(socket_set);
320 checked_free(send_buffer);
321 checked_free(recv_buffer);
323 runtime.use_api_server = success;