1
0
mirror of https://github.com/weechat/weechat.git synced 2026-06-28 05:46:38 +02:00

relay: add functions to parse HTTP response (issue #2066)

This commit is contained in:
Sébastien Helleu
2024-03-30 17:45:12 +01:00
parent c4c220f0a3
commit c2343ecb74
4 changed files with 569 additions and 8 deletions
+1 -1
View File
@@ -2206,7 +2206,7 @@ relay_client_print_log ()
weechat_log_printf (" gnutls_handshake_ok . . . : %p", ptr_client->gnutls_handshake_ok);
weechat_log_printf (" websocket . . . . . . . . ; %d", ptr_client->websocket);
relay_websocket_deflate_print_log (ptr_client->ws_deflate, "");
relay_http_print_log (ptr_client->http_req);
relay_http_print_log_request (ptr_client->http_req);
weechat_log_printf (" address . . . . . . . . . : '%s'", ptr_client->address);
weechat_log_printf (" real_ip . . . . . . . . . : '%s'", ptr_client->real_ip);
weechat_log_printf (" status. . . . . . . . . . : %d (%s)",
+269 -3
View File
@@ -344,7 +344,7 @@ relay_http_parse_method_path (struct t_relay_http_request *request,
char **items;
int num_items;
if (!method_path || !method_path[0])
if (!request || !method_path || !method_path[0])
return 0;
weechat_string_dyn_concat (request->raw, method_path, -1);
@@ -1372,19 +1372,265 @@ relay_http_request_free (struct t_relay_http_request *request)
free (request);
}
/*
* Allocates a t_relay_http_response structure.
*/
struct t_relay_http_response *
relay_http_response_alloc ()
{
struct t_relay_http_response *new_response;
new_response = (struct t_relay_http_response *)malloc (sizeof (*new_response));
if (!new_response)
return NULL;
new_response->status = RELAY_HTTP_METHOD;
new_response->http_version = NULL;
new_response->return_code = 0;
new_response->message = NULL;
new_response->headers = weechat_hashtable_new (
32,
WEECHAT_HASHTABLE_STRING,
WEECHAT_HASHTABLE_STRING,
NULL, NULL);
new_response->content_length = 0;
new_response->body_size = 0;
new_response->body = NULL;
return new_response;
}
/*
* Parses and saves response code.
*
* Returns:
* 1: OK, response code and HTTP version saved
* 0: error: invalid format
*/
int
relay_http_parse_response_code (struct t_relay_http_response *response,
const char *response_code)
{
const char *pos, *pos2;
char *error, *return_code;
long value;
if (!response)
return 0;
if (!response_code || !response_code[0])
{
response->status = RELAY_HTTP_END;
return 0;
}
pos = strchr (response_code, ' ');
if (!pos)
goto error;
if (response->http_version)
free (response->http_version);
response->http_version = weechat_strndup (response_code, pos - response_code);
while (pos[0] == ' ')
{
pos++;
}
pos2 = strchr (pos, ' ');
if (pos2)
return_code = weechat_strndup (pos, pos2 - pos);
else
return_code = strdup (pos);
if (!return_code)
goto error;
error = NULL;
value = strtol (return_code, &error, 10);
if (error && !error[0])
response->return_code = (int)value;
free (return_code);
if (pos2)
{
while (pos2[0] == ' ')
{
pos2++;
}
if (response->message)
free (response->message);
response->message = strdup (pos2);
}
response->status = RELAY_HTTP_HEADERS;
return 1;
error:
response->status = RELAY_HTTP_END;
return 0;
}
/*
* Parses and saves a header of a HTTP response in hashtable "headers".
*
* Returns:
* 1: OK, header saved
* 0: error: invalid format
*/
int
relay_http_parse_response_header (struct t_relay_http_response *response,
const char *header)
{
char *pos, *name, *name_lower, *error;
const char *ptr_value;
long number;
/* empty line => end of headers */
if (!header || !header[0])
{
response->status = (response->content_length > 0) ?
RELAY_HTTP_BODY : RELAY_HTTP_END;
return 1;
}
pos = strchr (header, ':');
/* not a valid header */
if (!pos || (pos == header))
return 0;
/* get header name, which is case-insensitive */
name = weechat_strndup (header, pos - header);
if (!name)
return 0;
name_lower = weechat_string_tolower (name);
if (!name_lower)
{
free (name);
return 0;
}
/* get pointer on header value */
ptr_value = pos + 1;
while (ptr_value[0] == ' ')
{
ptr_value++;
}
/* add header in the hashtable */
weechat_hashtable_set (response->headers, name_lower, ptr_value);
/* if header is "Content-Length", save the length */
if (strcmp (name_lower, "content-length") == 0)
{
error = NULL;
number = strtol (ptr_value, &error, 10);
if (error && !error[0])
response->content_length = (int)number;
}
free (name);
free (name_lower);
return 1;
}
/*
* Parses HTTP response with a string.
*
* Returns HTTP request structure, NULL if error.
*/
struct t_relay_http_response *
relay_http_parse_response (const char *data)
{
struct t_relay_http_response *http_resp;
const char *ptr_data, *pos;
char *line;
if (!data || !data[0])
return NULL;
http_resp = relay_http_response_alloc ();
if (!http_resp)
return NULL;
ptr_data = data;
while (ptr_data && ptr_data[0])
{
if ((http_resp->status == RELAY_HTTP_METHOD)
|| (http_resp->status == RELAY_HTTP_HEADERS))
{
pos = strchr (ptr_data, '\r');
if (!pos)
break;
line = weechat_strndup (ptr_data, pos - ptr_data);
if (!line)
break;
if (http_resp->status == RELAY_HTTP_METHOD)
relay_http_parse_response_code (http_resp, line);
else
relay_http_parse_response_header (http_resp, line);
free (line);
ptr_data = pos + 1;
if (ptr_data[0] == '\n')
ptr_data++;
}
else if (http_resp->status == RELAY_HTTP_BODY)
{
http_resp->body_size = strlen (ptr_data);
http_resp->body = malloc (http_resp->body_size);
if (http_resp->body)
memcpy (http_resp->body, ptr_data, http_resp->body_size);
http_resp->status = RELAY_HTTP_END;
}
else
break;
if (http_resp->status == RELAY_HTTP_END)
break;
}
return http_resp;
}
/*
* Frees a HTTP response.
*/
void
relay_http_response_free (struct t_relay_http_response *response)
{
if (response->http_version)
free (response->http_version);
if (response->message)
free (response->message);
if (response->headers)
weechat_hashtable_free (response->headers);
if (response->body)
free (response->body);
free (response);
}
/*
* Prints HTTP request in WeeChat log file (usually for crash dump).
*/
void
relay_http_print_log (struct t_relay_http_request *request)
relay_http_print_log_request (struct t_relay_http_request *request)
{
int i;
weechat_log_printf (" http_request:");
weechat_log_printf (" status. . . . . . . . . : %d", request->status);
weechat_log_printf (" raw . . . . . . . . . . : '%s'",
(request->raw) ? *(request->raw) : NULL);
weechat_log_printf (" status. . . . . . . . . : %d", request->status);
weechat_log_printf (" method. . . . . . . . . : '%s'", request->method);
weechat_log_printf (" path. . . . . . . . . . : '%s'", request->path);
weechat_log_printf (" path_items. . . . . . . : %p", request->path_items);
@@ -1412,3 +1658,23 @@ relay_http_print_log (struct t_relay_http_request *request)
weechat_log_printf (" body_size . . . . . . . : %d", request->body_size);
weechat_log_printf (" body. . . . . . . . . . : '%s'", request->body);
}
/*
* Prints HTTP response in WeeChat log file (usually for crash dump).
*/
void
relay_http_print_log_response (struct t_relay_http_response *response)
{
weechat_log_printf (" http_response:");
weechat_log_printf (" status. . . . . . . . . : %d", response->status);
weechat_log_printf (" http_version. . . . . . : '%s'", response->http_version);
weechat_log_printf (" return_code . . . . . . : %d", response->return_code);
weechat_log_printf (" message . . . . . . . . : '%s'", response->message);
weechat_log_printf (" headers . . . . . . . . : 0x%lx (hashtable: '%s')",
response->headers,
weechat_hashtable_get_string (response->headers, "keys_values"));
weechat_log_printf (" content_length. . . . . : %d", response->content_length);
weechat_log_printf (" body_size . . . . . . . : %d", response->body_size);
weechat_log_printf (" body. . . . . . . . . . : '%s'", response->body);
}
+18 -1
View File
@@ -71,6 +71,19 @@ struct t_relay_http_request
char *body; /* HTTP body (can be NULL) */
};
struct t_relay_http_response
{
enum t_relay_client_http_status status; /* HTTP status */
char *http_version; /* HTTP version (eg: "HTTP/1.1") */
int return_code; /* HTTP return code (eg: 200, 401) */
char *message; /* message after return code */
struct t_hashtable *headers; /* HTTP headers for websocket */
/* and API protocol */
int content_length; /* value of header "Content-Length" */
int body_size; /* size of HTTP body read so far */
char *body; /* HTTP body (can be NULL) */
};
extern void relay_http_request_reinit (struct t_relay_http_request *request);
extern struct t_relay_http_request *relay_http_request_alloc ();
extern int relay_http_get_param_boolean (struct t_relay_http_request *request,
@@ -96,6 +109,10 @@ extern int relay_http_send_error_json (struct t_relay_client *client,
const char *headers,
const char *format, ...);
extern void relay_http_request_free (struct t_relay_http_request *request);
extern void relay_http_print_log (struct t_relay_http_request *request);
extern struct t_relay_http_response *relay_http_response_alloc ();
extern struct t_relay_http_response *relay_http_parse_response (const char *data);
extern void relay_http_response_free (struct t_relay_http_response *response);
extern void relay_http_print_log_request (struct t_relay_http_request *request);
extern void relay_http_print_log_response (struct t_relay_http_response *response);
#endif /* WEECHAT_PLUGIN_RELAY_HTTP_H */
+281 -3
View File
@@ -56,6 +56,10 @@ extern char *relay_http_compress (struct t_relay_http_request *request,
int *compressed_size,
char *http_content_encoding,
int http_content_encoding_size);
extern int relay_http_parse_response_code (struct t_relay_http_response *response,
const char *response_code);
extern int relay_http_parse_response_header (struct t_relay_http_response *response,
const char *header);
}
#define WEE_PARSE_PATH(__path) \
@@ -75,7 +79,7 @@ TEST_GROUP(RelayHttp)
* relay_http_request_free
*/
TEST(RelayHttp, AllocReinitFree)
TEST(RelayHttp, RequestAllocReinitFree)
{
struct t_relay_http_request *request;
@@ -346,6 +350,7 @@ TEST(RelayHttp, ParseMethodPath)
request = relay_http_request_alloc ();
CHECK(request);
relay_http_parse_method_path (NULL, NULL);
relay_http_parse_method_path (request, NULL);
LONGS_EQUAL(RELAY_HTTP_METHOD, request->status);
STRCMP_EQUAL("", *(request->raw));
@@ -1014,10 +1019,283 @@ TEST(RelayHttp, SendErrorJson)
/*
* Tests functions:
* relay_http_print_log
* relay_http_response_alloc
* relay_http_response_free
*/
TEST(RelayHttp, PrintLog)
TEST(RelayHttp, ResponseAllocFree)
{
struct t_relay_http_response *response;
response = relay_http_response_alloc ();
CHECK(response);
LONGS_EQUAL(RELAY_HTTP_METHOD, response->status);
POINTERS_EQUAL(NULL, response->http_version);
LONGS_EQUAL(0, response->return_code);
POINTERS_EQUAL(NULL, response->message);
CHECK(response->headers);
LONGS_EQUAL(0, response->headers->items_count);
LONGS_EQUAL(0, response->content_length);
LONGS_EQUAL(0, response->body_size);
POINTERS_EQUAL(NULL, response->body);
relay_http_response_free (response);
}
/*
* Tests functions:
* relay_http_parse_response_code
*/
TEST(RelayHttp, ParseResponseCode)
{
struct t_relay_http_response *response;
response = relay_http_response_alloc ();
CHECK(response);
relay_http_parse_response_code (NULL, NULL);
LONGS_EQUAL(RELAY_HTTP_METHOD, response->status);
POINTERS_EQUAL(NULL, response->http_version);
LONGS_EQUAL(0, response->return_code);
POINTERS_EQUAL(NULL, response->message);
CHECK(response->headers);
LONGS_EQUAL(0, response->headers->items_count);
LONGS_EQUAL(0, response->content_length);
LONGS_EQUAL(0, response->body_size);
POINTERS_EQUAL(NULL, response->body);
relay_http_response_free (response);
response = relay_http_response_alloc ();
CHECK(response);
relay_http_parse_response_code (response, NULL);
relay_http_parse_response_code (response, "");
LONGS_EQUAL(RELAY_HTTP_END, response->status);
POINTERS_EQUAL(NULL, response->http_version);
LONGS_EQUAL(0, response->return_code);
POINTERS_EQUAL(NULL, response->message);
CHECK(response->headers);
LONGS_EQUAL(0, response->headers->items_count);
LONGS_EQUAL(0, response->content_length);
LONGS_EQUAL(0, response->body_size);
POINTERS_EQUAL(NULL, response->body);
relay_http_response_free (response);
response = relay_http_response_alloc ();
CHECK(response);
relay_http_parse_response_code (response, "HTTP/1.1");
LONGS_EQUAL(RELAY_HTTP_END, response->status);
POINTERS_EQUAL(NULL, response->http_version);
LONGS_EQUAL(0, response->return_code);
POINTERS_EQUAL(NULL, response->message);
CHECK(response->headers);
LONGS_EQUAL(0, response->headers->items_count);
LONGS_EQUAL(0, response->content_length);
LONGS_EQUAL(0, response->body_size);
POINTERS_EQUAL(NULL, response->body);
relay_http_response_free (response);
response = relay_http_response_alloc ();
CHECK(response);
relay_http_parse_response_code (response, "HTTP/1.1 200");
LONGS_EQUAL(RELAY_HTTP_HEADERS, response->status);
STRCMP_EQUAL("HTTP/1.1", response->http_version);
LONGS_EQUAL(200, response->return_code);
POINTERS_EQUAL(NULL, response->message);
CHECK(response->headers);
LONGS_EQUAL(0, response->headers->items_count);
LONGS_EQUAL(0, response->content_length);
LONGS_EQUAL(0, response->body_size);
POINTERS_EQUAL(NULL, response->body);
relay_http_response_free (response);
response = relay_http_response_alloc ();
CHECK(response);
relay_http_parse_response_code (response, "HTTP/1.1 200 OK");
LONGS_EQUAL(RELAY_HTTP_HEADERS, response->status);
STRCMP_EQUAL("HTTP/1.1", response->http_version);
LONGS_EQUAL(200, response->return_code);
STRCMP_EQUAL("OK", response->message);
CHECK(response->headers);
LONGS_EQUAL(0, response->headers->items_count);
LONGS_EQUAL(0, response->content_length);
LONGS_EQUAL(0, response->body_size);
POINTERS_EQUAL(NULL, response->body);
relay_http_response_free (response);
response = relay_http_response_alloc ();
CHECK(response);
relay_http_parse_response_code (response, "HTTP/1.1 101 Switching Protocols");
LONGS_EQUAL(RELAY_HTTP_HEADERS, response->status);
STRCMP_EQUAL("HTTP/1.1", response->http_version);
LONGS_EQUAL(101, response->return_code);
STRCMP_EQUAL("Switching Protocols", response->message);
CHECK(response->headers);
LONGS_EQUAL(0, response->headers->items_count);
LONGS_EQUAL(0, response->content_length);
LONGS_EQUAL(0, response->body_size);
POINTERS_EQUAL(NULL, response->body);
relay_http_response_free (response);
response = relay_http_response_alloc ();
CHECK(response);
relay_http_parse_response_code (response, "HTTP/1.1 401 Unauthorized");
LONGS_EQUAL(RELAY_HTTP_HEADERS, response->status);
STRCMP_EQUAL("HTTP/1.1", response->http_version);
LONGS_EQUAL(401, response->return_code);
STRCMP_EQUAL("Unauthorized", response->message);
CHECK(response->headers);
LONGS_EQUAL(0, response->headers->items_count);
LONGS_EQUAL(0, response->content_length);
LONGS_EQUAL(0, response->body_size);
POINTERS_EQUAL(NULL, response->body);
relay_http_response_free (response);
}
/*
* Tests functions:
* relay_http_parse_response_header
*/
TEST(RelayHttp, ParseResponseHeader)
{
struct t_relay_http_response *response;
response = relay_http_response_alloc ();
CHECK(response);
relay_http_parse_response_code (response, "HTTP/1.1 200 OK");
relay_http_parse_response_header (response, NULL);
LONGS_EQUAL(RELAY_HTTP_END, response->status);
STRCMP_EQUAL("HTTP/1.1", response->http_version);
LONGS_EQUAL(200, response->return_code);
STRCMP_EQUAL("OK", response->message);
LONGS_EQUAL(0, response->headers->items_count);
relay_http_response_free (response);
response = relay_http_response_alloc ();
CHECK(response);
relay_http_parse_response_code (response, "HTTP/1.1 200 OK");
relay_http_parse_response_header (response, "");
LONGS_EQUAL(RELAY_HTTP_END, response->status);
STRCMP_EQUAL("HTTP/1.1", response->http_version);
LONGS_EQUAL(200, response->return_code);
STRCMP_EQUAL("OK", response->message);
LONGS_EQUAL(0, response->headers->items_count);
relay_http_response_free (response);
response = relay_http_response_alloc ();
CHECK(response);
relay_http_parse_response_code (response, "HTTP/1.1 200 OK");
relay_http_parse_response_header (response, "Test");
LONGS_EQUAL(RELAY_HTTP_HEADERS, response->status);
STRCMP_EQUAL("HTTP/1.1", response->http_version);
LONGS_EQUAL(200, response->return_code);
STRCMP_EQUAL("OK", response->message);
LONGS_EQUAL(0, response->headers->items_count);
relay_http_response_free (response);
response = relay_http_response_alloc ();
CHECK(response);
relay_http_parse_response_code (response, "HTTP/1.1 200 OK");
relay_http_parse_response_header (response, "X-Test: value");
LONGS_EQUAL(RELAY_HTTP_HEADERS, response->status);
STRCMP_EQUAL("HTTP/1.1", response->http_version);
LONGS_EQUAL(200, response->return_code);
STRCMP_EQUAL("OK", response->message);
LONGS_EQUAL(1, response->headers->items_count);
STRCMP_EQUAL("value", (const char *)hashtable_get (response->headers, "x-test"));
relay_http_response_free (response);
response = relay_http_response_alloc ();
CHECK(response);
relay_http_parse_response_code (response, "HTTP/1.1 200 OK");
relay_http_parse_response_header (response, "Content-Length: 123");
LONGS_EQUAL(RELAY_HTTP_HEADERS, response->status);
STRCMP_EQUAL("HTTP/1.1", response->http_version);
LONGS_EQUAL(200, response->return_code);
STRCMP_EQUAL("OK", response->message);
LONGS_EQUAL(1, response->headers->items_count);
LONGS_EQUAL(123, response->content_length);
relay_http_response_free (response);
}
/*
* Tests functions:
* relay_http_parse_response
*/
TEST(RelayHttp, ParseResponse)
{
struct t_relay_http_response *response;
POINTERS_EQUAL(NULL, relay_http_parse_response (NULL));
POINTERS_EQUAL(NULL, relay_http_parse_response (""));
response = relay_http_parse_response ("invalid");
CHECK(response);
LONGS_EQUAL(RELAY_HTTP_METHOD, response->status);
POINTERS_EQUAL(NULL, response->http_version);
LONGS_EQUAL(0, response->return_code);
POINTERS_EQUAL(NULL, response->message);
CHECK(response->headers);
LONGS_EQUAL(0, response->headers->items_count);
LONGS_EQUAL(0, response->content_length);
LONGS_EQUAL(0, response->body_size);
POINTERS_EQUAL(NULL, response->body);
relay_http_response_free (response);
response = relay_http_parse_response ("invalid\r\n");
CHECK(response);
LONGS_EQUAL(RELAY_HTTP_END, response->status);
POINTERS_EQUAL(NULL, response->http_version);
LONGS_EQUAL(0, response->return_code);
POINTERS_EQUAL(NULL, response->message);
CHECK(response->headers);
LONGS_EQUAL(0, response->headers->items_count);
LONGS_EQUAL(0, response->content_length);
LONGS_EQUAL(0, response->body_size);
POINTERS_EQUAL(NULL, response->body);
relay_http_response_free (response);
response = relay_http_parse_response ("HTTP/1.1 200 OK\r\n"
"Content-Type: application/json\r\n"
"Content-Length: 13\r\n"
"\r\n"
"{\"test\": 123}");
CHECK(response);
LONGS_EQUAL(RELAY_HTTP_END, response->status);
STRCMP_EQUAL("HTTP/1.1", response->http_version);
LONGS_EQUAL(200, response->return_code);
STRCMP_EQUAL("OK", response->message);
CHECK(response->headers);
LONGS_EQUAL(2, response->headers->items_count);
STRCMP_EQUAL("application/json", (const char *)hashtable_get (response->headers,
"content-type"));
STRCMP_EQUAL("13", (const char *)hashtable_get (response->headers,
"content-length"));
LONGS_EQUAL(13, response->content_length);
LONGS_EQUAL(13, response->body_size);
STRCMP_EQUAL("{\"test\": 123}", response->body);
relay_http_response_free (response);
}
/*
* Tests functions:
* relay_http_print_log_request
*/
TEST(RelayHttp, PrintLogRequest)
{
/* TODO: write tests */
}
/*
* Tests functions:
* relay_http_print_log_response
*/
TEST(RelayHttp, PrintLogResponse)
{
/* TODO: write tests */
}