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 static void SetHttpResponseToDefaults(struct HttpResponse *response)
38 response->head[0] = '\0';
39 response->body[0] = '\0';
40 response->body_size = 0;
42 response->status_code = 0;
43 response->status_text[0] = '\0';
46 static boolean SetHTTPResponseCode(struct HttpResponse *response, char *buffer)
48 char *prefix = "HTTP/1.1 ";
49 char *prefix_start = strstr(buffer, prefix);
51 if (prefix_start == NULL)
54 char *status_code_start = prefix_start + strlen(prefix);
55 char *status_code_end = strstr(status_code_start, " ");
57 if (status_code_end == NULL)
60 int status_code_size = status_code_end - status_code_start;
62 if (status_code_size != 3) // status code must have three digits
65 char status_code[status_code_size + 1];
67 strncpy(status_code, status_code_start, status_code_size);
68 status_code[status_code_size] = '\0';
70 response->status_code = atoi(status_code);
72 char *status_text_start = status_code_end + 1;
73 char *status_text_end = strstr(status_text_start, "\r\n");
75 if (status_text_end == NULL)
78 int status_text_size = status_text_end - status_text_start;
80 if (status_text_size > MAX_HTTP_ERROR_SIZE)
83 strncpy(response->status_text, status_text_start, status_text_size);
84 response->status_text[status_text_size] = '\0';
89 static boolean SetHTTPResponseHead(struct HttpResponse *response, char *buffer)
91 char *separator = "\r\n\r\n";
92 char *separator_start = strstr(buffer, separator);
94 if (separator_start == NULL)
97 int head_size = separator_start - buffer;
99 if (head_size > MAX_HTTP_HEAD_SIZE)
102 strncpy(response->head, buffer, head_size);
103 response->head[head_size] = '\0';
108 static boolean SetHTTPResponseBody(struct HttpResponse *response, char *buffer,
111 char *separator = "\r\n\r\n";
112 char *separator_start = strstr(buffer, separator);
114 if (separator_start == NULL)
117 int separator_size = strlen(separator);
118 int full_head_size = separator_start + separator_size - buffer;
119 int body_size = buffer_size - full_head_size;
121 if (body_size > MAX_HTTP_BODY_SIZE)
124 memcpy(response->body, buffer + full_head_size, body_size);
125 response->body[body_size] = '\0';
126 response->body_size = body_size;
131 boolean DoHttpRequest(struct HttpRequest *request,
132 struct HttpResponse *response)
134 SDLNet_SocketSet socket_set;
139 SetHttpResponseToDefaults(response);
141 socket_set = SDLNet_AllocSocketSet(1);
143 if (socket_set == NULL)
145 SetHttpError("cannot allocate socket set");
150 SDLNet_ResolveHost(&ip, request->hostname, request->port);
152 if (ip.host == INADDR_NONE)
154 SetHttpError("cannot resolve hostname '%s'", request->hostname);
159 server_host = SDLNet_Read32(&ip.host);
161 Debug("network:http", "trying to connect to server at %d.%d.%d.%d ...",
162 (server_host >> 24) & 0xff,
163 (server_host >> 16) & 0xff,
164 (server_host >> 8) & 0xff,
165 (server_host >> 0) & 0xff);
167 socket = SDLNet_TCP_Open(&ip);
171 SetHttpError("cannot connect to host '%s': %s", request->hostname,
177 if (SDLNet_TCP_AddSocket(socket_set, socket) == -1)
179 SetHttpError("cannot add socket to socket set");
184 Debug("network:http", "successfully connected to server");
186 snprintf(request->head, MAX_HTTP_HEAD_SIZE,
189 "X-Requested-With: XMLHttpRequest\r\n"
190 "Content-Type: application/json\r\n"
191 "Connection: close\r\n"
192 "Content-Length: %d\r\n",
196 (int)strlen(request->body));
198 int max_http_buffer_size = MAX_HTTP_HEAD_SIZE + MAX_HTTP_BODY_SIZE;
199 char send_buffer[max_http_buffer_size + 1];
200 char recv_buffer[max_http_buffer_size + 1];
202 snprintf(send_buffer, max_http_buffer_size,
203 "%s\r\n%s", request->head, request->body);
205 Debug("network:http", "client request:\n--- snip ---\n%s\n--- snip ---",
208 int send_bytes = SDLNet_TCP_Send(socket, send_buffer, strlen(send_buffer));
210 if (send_bytes != strlen(send_buffer))
212 SetHttpError("sending request to server failed");
217 int recv_bytes = SDLNet_TCP_Recv(socket, recv_buffer, max_http_buffer_size);
221 SetHttpError("receiving response from server failed");
226 recv_buffer[recv_bytes] = '\0';
228 Debug("network:http", "server response:\n--- snip ---\n%s\n--- snip ---",
231 if (!SetHTTPResponseCode(response, recv_buffer))
233 SetHttpError("malformed HTTP response");
238 if (!SetHTTPResponseHead(response, recv_buffer))
240 SetHttpError("invalid HTTP response header");
245 if (!SetHTTPResponseBody(response, recv_buffer, recv_bytes))
247 SetHttpError("invalid HTTP response body");
252 SDLNet_TCP_DelSocket(socket_set, socket);
253 SDLNet_TCP_Close(socket);
255 Debug("network:http", "server response: %d %s",
256 response->status_code,
257 response->status_text);