// HTTP Implementation Test Suite
// Copyright (c) 2023 retoor
// Licensed under MIT License
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include "http.h"
double get_current_time_in_milliseconds() {
struct timeval time_value;
gettimeofday(&time_value, NULL);
return (time_value.tv_sec * 1000.0) + (time_value.tv_usec / 1000.0);
}
void print_response_preview(const char *response, size_t maximum_length) {
if (!response) {
printf("Response: NULL\n");
return;
}
size_t response_length = strlen(response);
printf("Response length: %zu bytes\n", response_length);
printf("First %zu chars: %.100s%s\n",
maximum_length, response, response_length > maximum_length ? "..." : "");
}
void test_https_get() {
printf("\n=== Testing HTTPS GET ===\n");
const char * const test_urls[] = {
"https://httpbin.org/get",
"https://jsonplaceholder.typicode.com/posts/1",
"https://api.github.com/repos/torvalds/linux",
"https://www.example.com/",
"https://httpbin.org/bytes/1024"
};
size_t number_of_tests = sizeof(test_urls) / sizeof(test_urls[0]);
for (size_t index = 0; index < number_of_tests; index++) {
printf("\nTest %zu: GET %s\n", index + 1, test_urls[index]);
double start_time = get_current_time_in_milliseconds();
char *response = https_get((char *)test_urls[index]);
double end_time = get_current_time_in_milliseconds();
printf("Time taken: %.2f ms\n", end_time - start_time);
print_response_preview(response, 100);
if (response) {
free(response);
printf("Status: SUCCESS\n");
} else {
printf("Status: FAILED\n");
}
}
}
void test_https_post() {
printf("\n=== Testing HTTPS POST ===\n");
const char * const test_urls[] = {
"https://httpbin.org/post",
"https://jsonplaceholder.typicode.com/posts",
"https://httpbin.org/anything"
};
const char * const test_payloads[] = {
"{\"test\": \"data\", \"number\": 123}",
"{\"title\": \"foo\", \"body\": \"bar\", \"userId\": 1}",
"{\"key1\": \"value1\", \"key2\": [1, 2, 3], \"key3\": {\"nested\": true}}"
};
size_t number_of_tests = sizeof(test_urls) / sizeof(test_urls[0]);
for (size_t index = 0; index < number_of_tests; index++) {
printf("\nTest %zu: POST %s\n", index + 1, test_urls[index]);
printf("Payload: %s\n", test_payloads[index]);
double start_time = get_current_time_in_milliseconds();
char *response = https_post((char *)test_urls[index], (char *)test_payloads[index]);
double end_time = get_current_time_in_milliseconds();
printf("Time taken: %.2f ms\n", end_time - start_time);
print_response_preview(response, 100);
if (response) {
free(response);
printf("Status: SUCCESS\n");
} else {
printf("Status: FAILED\n");
}
}
}
void test_http_get() {
printf("\n=== Testing HTTP GET ===\n");
const char * const test_urls[] = {
"http://httpbin.org/get",
"http://httpbin.org/headers",
"http://httpbin.org/user-agent"
};
size_t number_of_tests = sizeof(test_urls) / sizeof(test_urls[0]);
for (size_t index = 0; index < number_of_tests; index++) {
printf("\nTest %zu: GET %s\n", index + 1, test_urls[index]);
double start_time = get_current_time_in_milliseconds();
char *response = http_get((char *)test_urls[index]);
double end_time = get_current_time_in_milliseconds();
printf("Time taken: %.2f ms\n", end_time - start_time);
print_response_preview(response, 100);
if (response) {
free(response);
printf("Status: SUCCESS\n");
} else {
printf("Status: FAILED\n");
}
}
}
void test_http_post() {
printf("\n=== Testing HTTP POST ===\n");
const char * const test_urls[] = {
"http://httpbin.org/post",
"http://httpbin.org/anything"
};
const char * const test_payloads[] = {
"{\"message\": \"Hello World\"}",
"{\"test\": true, \"count\": 42}"
};
size_t number_of_tests = sizeof(test_urls) / sizeof(test_urls[0]);
for (size_t index = 0; index < number_of_tests; index++) {
printf("\nTest %zu: POST %s\n", index + 1, test_urls[index]);
printf("Payload: %s\n", test_payloads[index]);
double start_time = get_current_time_in_milliseconds();
char *response = http_post((char *)test_urls[index], (char *)test_payloads[index]);
double end_time = get_current_time_in_milliseconds();
printf("Time taken: %.2f ms\n", end_time - start_time);
print_response_preview(response, 100);
if (response) {
free(response);
printf("Status: SUCCESS\n");
} else {
printf("Status: FAILED\n");
}
}
}
void test_chunked_response() {
printf("\n=== Testing Chunked Transfer Encoding ===\n");
const char * const test_urls[] = {
"https://httpbin.org/stream/5",
"https://httpbin.org/drip?duration=2&numbytes=10&code=200",
"http://httpbin.org/stream-bytes/1024"
};
size_t number_of_tests = sizeof(test_urls) / sizeof(test_urls[0]);
for (size_t index = 0; index < number_of_tests; index++) {
printf("\nTest %zu: GET %s\n", index + 1, test_urls[index]);
double start_time = get_current_time_in_milliseconds();
char *response = NULL;
if (strncmp(test_urls[index], "https://", 8) == 0) {
response = https_get((char *)test_urls[index]);
} else {
response = http_get((char *)test_urls[index]);
}
double end_time = get_current_time_in_milliseconds();
printf("Time taken: %.2f ms\n", end_time - start_time);
print_response_preview(response, 100);
if (response) {
free(response);
printf("Status: SUCCESS\n");
} else {
printf("Status: FAILED\n");
}
}
}
void benchmark_requests() {
printf("\n=== Benchmarking Performance ===\n");
const char * const benchmark_url = "https://httpbin.org/get";
size_t number_of_requests = 10;
double total_time = 0.0;
size_t successful_requests = 0;
printf("Benchmarking %zu requests to %s\n", number_of_requests, benchmark_url);
for (size_t index = 0; index < number_of_requests; index++) {
double start_time = get_current_time_in_milliseconds();
char *response = https_get((char *)benchmark_url);
double end_time = get_current_time_in_milliseconds();
double request_time = end_time - start_time;
total_time += request_time;
if (response) {
successful_requests++;
free(response);
printf("Request %2zu: %.2f ms\n", index + 1, request_time);
} else {
printf("Request %2zu: FAILED\n", index + 1);
}
}
printf("\nBenchmark Results:\n");
printf("Total requests: %zu\n", number_of_requests);
printf("Successful: %zu\n", successful_requests);
printf("Failed: %zu\n", number_of_requests - successful_requests);
printf("Total time: %.2f ms\n", total_time);
printf("Average time per request: %.2f ms\n", total_time / number_of_requests);
printf("Success rate: %.1f%%\n", (successful_requests * 100.0) / number_of_requests);
}
void test_large_response() {
printf("\n=== Testing Large Response Handling ===\n");
const char * const test_urls[] = {
"https://httpbin.org/bytes/10240", // 10 KB
"https://httpbin.org/bytes/102400", // 100 KB
"https://httpbin.org/base64/SFRUUEJJTiBpcyBhd2Vzb21l" // Base64 response
};
const char * const size_descriptions[] = {"10 KB", "100 KB", "Base64"};
size_t number_of_tests = sizeof(test_urls) / sizeof(test_urls[0]);
for (size_t index = 0; index < number_of_tests; index++) {
printf("\nTest %zu: Large response (%s) from %s\n", index + 1, size_descriptions[index], test_urls[index]);
double start_time = get_current_time_in_milliseconds();
char *response = https_get((char *)test_urls[index]);
double end_time = get_current_time_in_milliseconds();
if (response) {
size_t response_length = strlen(response);
printf("Response size: %zu bytes\n", response_length);
printf("Time taken: %.2f ms\n", end_time - start_time);
printf("Transfer rate: %.2f KB/s\n", (response_length / 1024.0) / ((end_time - start_time) / 1000.0));
free(response);
printf("Status: SUCCESS\n");
} else {
printf("Status: FAILED\n");
}
}
}
int main(int argument_count, char *argument_values[]) {
printf("HTTP Implementation Test Suite\n");
printf("==============================\n");
test_https_get();
test_https_post();
test_http_get();
test_http_post();
test_chunked_response();
test_large_response();
benchmark_requests();
printf("\n\nAll tests completed!\n");
return 0;
}