X-Git-Url: https://git.artsoft.org/?a=blobdiff_plain;f=src%2Flibgame%2Fhttp.c;h=0064bc14e5e3bdf01001dedfeba1ca1ea16c0c32;hb=HEAD;hp=74fafb379713e61b5d4d7a9edf768da73090a896;hpb=db269a1ab34d16c03e8087eaf786e7b3c9a135e4;p=rocksndiamonds.git diff --git a/src/libgame/http.c b/src/libgame/http.c index 74fafb37..c83c91f7 100644 --- a/src/libgame/http.c +++ b/src/libgame/http.c @@ -37,7 +37,9 @@ void ConvertHttpRequestBodyToServerEncoding(struct HttpRequest *request) { char *body_utf8 = getUTF8FromLatin1(request->body); - strcpy(request->body, body_utf8); + strncpy(request->body, body_utf8, MAX_HTTP_BODY_SIZE); + request->body[MAX_HTTP_BODY_SIZE] = '\0'; + checked_free(body_utf8); } @@ -45,10 +47,12 @@ void ConvertHttpResponseBodyToClientEncoding(struct HttpResponse *response) { char *body_latin1 = getLatin1FromUTF8(response->body); - strcpy(response->body, body_latin1); - checked_free(body_latin1); + strncpy(response->body, body_latin1, MAX_HTTP_BODY_SIZE); + response->body[MAX_HTTP_BODY_SIZE] = '\0'; response->body_size = strlen(response->body); + + checked_free(body_latin1); } static void SetHttpResponseToDefaults(struct HttpResponse *response) @@ -61,6 +65,22 @@ static void SetHttpResponseToDefaults(struct HttpResponse *response) response->status_text[0] = '\0'; } +struct HttpResponse *GetHttpResponseFromBuffer(void *buffer, int body_size) +{ + if (body_size > MAX_HTTP_BODY_SIZE) + return NULL; + + struct HttpResponse *response = checked_calloc(sizeof(struct HttpResponse)); + + SetHttpResponseToDefaults(response); + + memcpy(response->body, buffer, body_size); + response->body[body_size] = '\0'; + response->body_size = body_size; + + return response; +} + static boolean SetHTTPResponseCode(struct HttpResponse *response, char *buffer) { char *prefix = "HTTP/1.1 "; @@ -146,19 +166,119 @@ static boolean SetHTTPResponseBody(struct HttpResponse *response, char *buffer, return TRUE; } -boolean DoHttpRequest(struct HttpRequest *request, - struct HttpResponse *response) +static int GetHttpResponse(TCPsocket socket, char *buffer, int max_buffer_size) +{ + char *buffer_ptr = buffer; + int buffer_left = max_buffer_size; + int buffer_size = 0; + int response_size = 0; + + while (1) + { + // read as many bytes to the buffer as possible + int bytes = SDLNet_TCP_Recv(socket, buffer_ptr, buffer_left); + + if (bytes <= 0) + { + SetHttpError("receiving response from server failed"); + + return -1; + } + + buffer_ptr += bytes; + buffer_size += bytes; + buffer_left -= bytes; + + // check if response size was already determined + if (response_size > 0) + { + // check if response data was completely received + if (buffer_size >= response_size) + break; + + // continue reading response body from server + continue; + } + + char *separator = "\r\n\r\n"; + char *separator_start = strstr(buffer, separator); + int separator_size = strlen(separator); + + // check if response header was completely received + if (separator_start == NULL) + { + // continue reading response header from server + continue; + } + + char *content_length = "Content-Length: "; + char *content_length_start = strstr(buffer, content_length); + int head_size = separator_start - buffer; + + // check if response header contains content length header + if (content_length_start == NULL || + content_length_start >= buffer + head_size) + { + SetHttpError("receiving 'Content-Length' header from server failed"); + + return -1; + } + + char *content_length_value = content_length_start + strlen(content_length); + char *content_length_end = strstr(content_length_value, "\r\n"); + + // check if content length header has line termination + if (content_length_end == NULL) + { + SetHttpError("receiving 'Content-Length' value from server failed"); + + return -1; + } + + int value_len = content_length_end - content_length_value; + int max_value_len = 10; + + // check if content length header has valid size + if (value_len > max_value_len) + { + SetHttpError("received invalid 'Content-Length' value from server"); + + return -1; + } + + char value_str[value_len + 1]; + + strncpy(value_str, content_length_value, value_len); + value_str[value_len] = '\0'; + + int body_size = atoi(value_str); + + response_size = head_size + separator_size + body_size; + + // check if response data was completely received + if (buffer_size >= response_size) + break; + } + + return buffer_size; +} + +static boolean DoHttpRequestExt(struct HttpRequest *request, + struct HttpResponse *response, + char *send_buffer, + char *recv_buffer, + int max_http_buffer_size, + SDLNet_SocketSet *socket_set, + TCPsocket *socket) { - SDLNet_SocketSet socket_set; - TCPsocket socket; IPaddress ip; int server_host; SetHttpResponseToDefaults(response); - socket_set = SDLNet_AllocSocketSet(1); + *socket_set = SDLNet_AllocSocketSet(1); - if (socket_set == NULL) + if (*socket_set == NULL) { SetHttpError("cannot allocate socket set"); @@ -182,9 +302,9 @@ boolean DoHttpRequest(struct HttpRequest *request, (server_host >> 8) & 0xff, (server_host >> 0) & 0xff); - socket = SDLNet_TCP_Open(&ip); + *socket = SDLNet_TCP_Open(&ip); - if (socket == NULL) + if (*socket == NULL) { SetHttpError("cannot connect to host '%s': %s", request->hostname, SDLNet_GetError()); @@ -192,7 +312,7 @@ boolean DoHttpRequest(struct HttpRequest *request, return FALSE; } - if (SDLNet_TCP_AddSocket(socket_set, socket) == -1) + if (SDLNet_TCP_AddSocket(*socket_set, *socket) == -1) { SetHttpError("cannot add socket to socket set"); @@ -213,17 +333,13 @@ boolean DoHttpRequest(struct HttpRequest *request, request->hostname, (int)strlen(request->body)); - int max_http_buffer_size = MAX_HTTP_HEAD_SIZE + MAX_HTTP_BODY_SIZE; - char send_buffer[max_http_buffer_size + 1]; - char recv_buffer[max_http_buffer_size + 1]; - snprintf(send_buffer, max_http_buffer_size, "%s\r\n%s", request->head, request->body); Debug("network:http", "client request:\n--- snip ---\n%s\n--- snip ---", send_buffer); - int send_bytes = SDLNet_TCP_Send(socket, send_buffer, strlen(send_buffer)); + int send_bytes = SDLNet_TCP_Send(*socket, send_buffer, strlen(send_buffer)); if (send_bytes != strlen(send_buffer)) { @@ -232,11 +348,11 @@ boolean DoHttpRequest(struct HttpRequest *request, return FALSE; } - int recv_bytes = SDLNet_TCP_Recv(socket, recv_buffer, max_http_buffer_size); + int recv_bytes = GetHttpResponse(*socket, recv_buffer, max_http_buffer_size); if (recv_bytes <= 0) { - SetHttpError("receiving response from server failed"); + // HTTP error already set in GetHttpResponse() return FALSE; } @@ -267,12 +383,41 @@ boolean DoHttpRequest(struct HttpRequest *request, return FALSE; } - SDLNet_TCP_DelSocket(socket_set, socket); - SDLNet_TCP_Close(socket); - Debug("network:http", "server response: %d %s", response->status_code, response->status_text); return TRUE; } + +boolean DoHttpRequest(struct HttpRequest *request, + struct HttpResponse *response) +{ + int max_http_buffer_size = MAX_HTTP_HEAD_SIZE + 2 + MAX_HTTP_BODY_SIZE + 1; + char *send_buffer = checked_malloc(max_http_buffer_size); + char *recv_buffer = checked_malloc(max_http_buffer_size); + SDLNet_SocketSet socket_set = NULL; + TCPsocket socket = NULL; + + boolean success = DoHttpRequestExt(request, response, + send_buffer, recv_buffer, + max_http_buffer_size, + &socket_set, &socket); + if (socket_set != NULL) + { + if (socket != NULL) + { + SDLNet_TCP_DelSocket(socket_set, socket); + SDLNet_TCP_Close(socket); + } + + SDLNet_FreeSocketSet(socket_set); + } + + checked_free(send_buffer); + checked_free(recv_buffer); + + runtime.use_api_server = success; + + return success; +}