228 lines
11 KiB
C
228 lines
11 KiB
C
#include "core/research_transport.h"
|
|
#include "core/research_http_transport_curl.h"
|
|
|
|
#include <arpa/inet.h>
|
|
#include <sys/socket.h>
|
|
#include <unistd.h>
|
|
|
|
static const char *selected[] = {"11111111-1111-4111-8111-111111111111"};
|
|
static const guint16 ports[] = {443};
|
|
static const char *endpoints[] = {"https://example.org/specimen"};
|
|
static ResearchNetworkProfile profile = {ports, 1, endpoints, 1};
|
|
static ResearchAction action = {"11111111-1111-4111-8111-111111111111",
|
|
"page.fetch","specimen","https://example.org/specimen","example.org",
|
|
RESEARCH_CONTACT_TARGET,"host",1,4096,1000};
|
|
static ScopeGrant grant = {RESEARCH_GRANT_CONTRACT,
|
|
"22222222-2222-4222-8222-222222222222","33333333-3333-4333-8333-333333333333",
|
|
"key","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
|
"2026-01-01T00:00:00Z","2027-01-01T00:00:00Z",selected,1,NULL,0,1,4096,1000};
|
|
|
|
static ResearchTransportRequest request(void) {
|
|
return (ResearchTransportRequest){&action,&grant,&profile,"GET",NULL,0,
|
|
2048,4096,512,NULL};
|
|
}
|
|
|
|
static void test_production_unavailable(void) {
|
|
ResearchTransportRequest req=request(); ResearchTransportReply reply={0};
|
|
g_assert_true(research_transport_execute(&req,&reply,NULL));
|
|
g_assert_cmpint(reply.state,==,RESEARCH_TRANSPORT_UNAVAILABLE);
|
|
research_transport_reply_clear(&reply);
|
|
}
|
|
|
|
static void test_headers_and_fixture_states(void) {
|
|
ResearchHeader forbidden={"Cookie","secret=forbidden"};
|
|
ResearchTransportRequest req=request();req.headers=&forbidden;req.header_count=1;
|
|
ResearchHttpTarget target={0};
|
|
g_assert_false(research_transport_request_validate(&req,&target,NULL));
|
|
req=request();
|
|
ResearchHeader headers[]={{"Retry-After","17"}};
|
|
ResearchTransportFixture fixture={429,headers,1,(const guint8 *)"busy",4,
|
|
FALSE,4,"127.0.0.1",NULL,FALSE,FALSE};
|
|
const char *authorities[]={"example.org"};ResearchTransportReply reply={0};
|
|
g_assert_true(research_transport_execute_fixture(&req,&fixture,authorities,1,&reply,NULL));
|
|
g_assert_cmpint(reply.state,==,RESEARCH_TRANSPORT_RATE_LIMITED);
|
|
g_assert_true(reply.retry_after_present);g_assert_cmpuint(reply.retry_after_seconds,==,17);
|
|
research_transport_reply_clear(&reply);
|
|
fixture.status_code=403;fixture.headers=NULL;fixture.header_count=0;
|
|
g_assert_true(research_transport_execute_fixture(&req,&fixture,authorities,1,&reply,NULL));
|
|
g_assert_cmpint(reply.state,==,RESEARCH_TRANSPORT_FORBIDDEN);
|
|
research_transport_reply_clear(&reply);
|
|
}
|
|
|
|
static void test_bounds_cancel_redirect(void) {
|
|
ResearchTransportRequest req=request();ResearchTransportReply reply={0};
|
|
ResearchTransportFixture fixture={200,NULL,0,(const guint8 *)"x",1,FALSE,
|
|
5000,"127.0.0.1",NULL,FALSE,FALSE};const char *authorities[]={"example.org"};
|
|
g_assert_true(research_transport_execute_fixture(&req,&fixture,authorities,1,&reply,NULL));
|
|
g_assert_cmpint(reply.state,==,RESEARCH_TRANSPORT_TOO_LARGE);research_transport_reply_clear(&reply);
|
|
fixture.decompressed_size=1;fixture.status_code=302;fixture.redirect_location="https://example.net/next";
|
|
g_assert_true(research_transport_execute_fixture(&req,&fixture,authorities,1,&reply,NULL));
|
|
g_assert_cmpint(reply.state,==,RESEARCH_TRANSPORT_REDIRECT);research_transport_reply_clear(&reply);
|
|
GCancellable *cancel=g_cancellable_new();g_cancellable_cancel(cancel);req.cancellable=cancel;
|
|
fixture.status_code=200;fixture.redirect_location=NULL;
|
|
g_assert_true(research_transport_execute_fixture(&req,&fixture,authorities,1,&reply,NULL));
|
|
g_assert_cmpint(reply.state,==,RESEARCH_TRANSPORT_CANCELLED);research_transport_reply_clear(&reply);g_object_unref(cancel);
|
|
}
|
|
|
|
typedef struct {
|
|
int listener;
|
|
const guint8 *response;
|
|
gsize response_size;
|
|
char *request_log;
|
|
} LocalServer;
|
|
|
|
static gpointer serve_once(gpointer data) {
|
|
LocalServer *server = data;
|
|
int client = accept(server->listener, NULL, NULL);
|
|
g_assert_cmpint(client, >=, 0);
|
|
GString *log = g_string_new(NULL);
|
|
char buffer[512];
|
|
while (strstr(log->str, "\r\n\r\n") == NULL) {
|
|
ssize_t count = recv(client, buffer, sizeof buffer, 0);
|
|
if (count <= 0)
|
|
break;
|
|
g_string_append_len(log, buffer, count);
|
|
}
|
|
server->request_log = g_string_free(log, FALSE);
|
|
gsize remaining = server->response_size;
|
|
const guint8 *cursor = server->response;
|
|
while (remaining > 0) {
|
|
ssize_t count = send(client, cursor, remaining, 0);
|
|
if (count <= 0)
|
|
break;
|
|
cursor += count;
|
|
remaining -= (gsize)count;
|
|
}
|
|
close(client);
|
|
close(server->listener);
|
|
return NULL;
|
|
}
|
|
|
|
static guint16 local_server_start_bytes(LocalServer *server,
|
|
const guint8 *response,
|
|
gsize response_size,
|
|
GThread **thread) {
|
|
server->listener = socket(AF_INET, SOCK_STREAM, 0);
|
|
g_assert_cmpint(server->listener, >=, 0);
|
|
struct sockaddr_in address = {.sin_family = AF_INET,
|
|
.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
|
|
.sin_port = 0};
|
|
g_assert_cmpint(bind(server->listener, (struct sockaddr *)&address,
|
|
sizeof address), ==, 0);
|
|
g_assert_cmpint(listen(server->listener, 1), ==, 0);
|
|
socklen_t length = sizeof address;
|
|
g_assert_cmpint(getsockname(server->listener, (struct sockaddr *)&address,
|
|
&length), ==, 0);
|
|
server->response = response;
|
|
server->response_size = response_size;
|
|
*thread = g_thread_new("research-http-fixture", serve_once, server);
|
|
return ntohs(address.sin_port);
|
|
}
|
|
|
|
static guint16 local_server_start(LocalServer *server, const char *response,
|
|
GThread **thread) {
|
|
return local_server_start_bytes(server, (const guint8 *)response,
|
|
strlen(response), thread);
|
|
}
|
|
|
|
static void test_curl_local_fixture(void) {
|
|
LocalServer server = {0};
|
|
GThread *thread = NULL;
|
|
guint16 port = local_server_start(
|
|
&server, "HTTP/1.1 200 OK\r\nContent-Length: 7\r\n\r\nspecimen", &thread);
|
|
char *endpoint = g_strdup_printf("http://fixture.test:%u/specimen", port);
|
|
const char *allowed_endpoints[] = {endpoint};
|
|
guint16 allowed_ports[] = {port};
|
|
ResearchNetworkProfile local_profile = {allowed_ports, 1,
|
|
allowed_endpoints, 1};
|
|
ResearchAction local_action = action;
|
|
local_action.endpoint = endpoint;
|
|
local_action.subject = "fixture.test";
|
|
ResearchHeader headers[] = {{"Accept", "text/plain"}};
|
|
ResearchTransportRequest req = {&local_action, &grant, &local_profile, "GET",
|
|
headers, 1, 64, 64, 256, NULL};
|
|
ResearchTransportReply reply = {0};
|
|
g_assert_true(research_http_transport_curl_execute_fixture(
|
|
&req, "fixture.test", "127.0.0.1", &reply, NULL));
|
|
g_assert_cmpint(reply.state, ==, RESEARCH_TRANSPORT_OK);
|
|
g_assert_cmpuint(g_bytes_get_size(reply.body), ==, 7);
|
|
g_thread_join(thread);
|
|
g_assert_nonnull(strstr(server.request_log, "GET /specimen HTTP/1.1"));
|
|
g_assert_null(strstr(server.request_log, "Authorization:"));
|
|
g_assert_null(strstr(server.request_log, "Cookie:"));
|
|
g_assert_null(strstr(server.request_log, "Proxy-Authorization:"));
|
|
g_free(server.request_log);
|
|
research_transport_reply_clear(&reply);
|
|
g_free(endpoint);
|
|
}
|
|
|
|
static void test_curl_body_limit(void) {
|
|
LocalServer server = {0};
|
|
GThread *thread = NULL;
|
|
guint16 port = local_server_start(&server,
|
|
"HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\nabcdefghijkl", &thread);
|
|
char *endpoint = g_strdup_printf("http://fixture.test:%u/large", port);
|
|
const char *allowed_endpoints[] = {endpoint};
|
|
guint16 allowed_ports[] = {port};
|
|
ResearchNetworkProfile local_profile = {allowed_ports, 1, allowed_endpoints, 1};
|
|
ResearchAction local_action = action;
|
|
local_action.endpoint = endpoint;
|
|
local_action.subject = "fixture.test";
|
|
ResearchTransportRequest req = {&local_action, &grant, &local_profile, "GET",
|
|
NULL, 0, 8, 8, 256, NULL};
|
|
ResearchTransportReply reply = {0};
|
|
g_assert_true(research_http_transport_curl_execute_fixture(
|
|
&req, "fixture.test", "127.0.0.1", &reply, NULL));
|
|
g_assert_cmpint(reply.state, ==, RESEARCH_TRANSPORT_TOO_LARGE);
|
|
g_thread_join(thread);
|
|
g_free(server.request_log);
|
|
research_transport_reply_clear(&reply);
|
|
g_free(endpoint);
|
|
}
|
|
|
|
static void test_curl_chunked_compressed_limit(void) {
|
|
static const guint8 response[] = {
|
|
'H','T','T','P','/','1','.','1',' ','2','0','0',' ','O','K','\r','\n',
|
|
'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',':',' ',
|
|
'g','z','i','p','\r','\n','T','r','a','n','s','f','e','r','-','E','n',
|
|
'c','o','d','i','n','g',':',' ','c','h','u','n','k','e','d','\r','\n',
|
|
'\r','\n','2','8','\r','\n',
|
|
0x1f,0x8b,0x08,0x00,0x00,0x00,0x00,0x00,0x02,0xff,
|
|
0xed,0xc1,0x01,0x0d,0x00,0x00,0x00,0xc2,0xa0,0x6c,
|
|
0xef,0x5f,0xca,0x1e,0x0e,0x28,0x00,0x00,0x00,0xe0,
|
|
0xdd,0x00,0x40,0x34,0xa6,0xfe,0x00,0x10,0x00,0x00,
|
|
'\r','\n','0','\r','\n','\r','\n'};
|
|
LocalServer server = {0};
|
|
GThread *thread = NULL;
|
|
guint16 port = local_server_start_bytes(&server, response, sizeof response,
|
|
&thread);
|
|
char *endpoint = g_strdup_printf("http://fixture.test:%u/compressed", port);
|
|
const char *allowed_endpoints[] = {endpoint};
|
|
guint16 allowed_ports[] = {port};
|
|
ResearchNetworkProfile local_profile = {allowed_ports, 1,
|
|
allowed_endpoints, 1};
|
|
ResearchAction local_action = action;
|
|
local_action.endpoint = endpoint;
|
|
local_action.subject = "fixture.test";
|
|
ResearchTransportRequest req = {&local_action, &grant, &local_profile, "GET",
|
|
NULL, 0, 32, 4096, 256, NULL};
|
|
ResearchTransportReply reply = {0};
|
|
g_assert_true(research_http_transport_curl_execute_fixture(
|
|
&req, "fixture.test", "127.0.0.1", &reply, NULL));
|
|
g_assert_cmpint(reply.state, ==, RESEARCH_TRANSPORT_TOO_LARGE);
|
|
g_thread_join(thread);
|
|
g_free(server.request_log);
|
|
research_transport_reply_clear(&reply);
|
|
g_free(endpoint);
|
|
}
|
|
|
|
int main(int argc,char **argv){g_test_init(&argc,&argv,NULL);
|
|
g_test_add_func("/research/transport/unavailable",test_production_unavailable);
|
|
g_test_add_func("/research/transport/states",test_headers_and_fixture_states);
|
|
g_test_add_func("/research/transport/bounds",test_bounds_cancel_redirect);
|
|
g_test_add_func("/research/transport/curl-local", test_curl_local_fixture);
|
|
g_test_add_func("/research/transport/curl-body-limit", test_curl_body_limit);
|
|
g_test_add_func("/research/transport/curl-compressed-limit",
|
|
test_curl_chunked_compressed_limit);
|
|
return g_test_run();}
|