Mercurial
diff seobeo/snapshot_creator.c @ 67:6626ec933933
[Seobeo] Separated out Client Server logic. Created test tools.
| author | June Park <parkjune1995@gmail.com> |
|---|---|
| date | Wed, 24 Dec 2025 09:15:55 -0800 |
| parents | |
| children | e7899c93da77 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/seobeo/snapshot_creator.c Wed Dec 24 09:15:55 2025 -0800 @@ -0,0 +1,280 @@ +#include "seobeo/snapshot_creator.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> + +#define MAX_RESPONSE_SIZE (1024 * 1024) + +// Helper: Convert URL path to filename +static void path_to_filename(const char *path, char *filename, size_t max_len) +{ + if (strcmp(path, "/") == 0) + { + snprintf(filename, max_len, "root.snapshot"); + return; + } + + const char *p = path; + if (*p == '/') + { + p++; + } + + char *out = filename; + size_t remaining = max_len - 1; + + while (*p && remaining > 0) + { + if (*p == '/') + { + *out++ = '_'; + remaining--; + } + else + { + *out++ = *p; + remaining--; + } + p++; + } + + snprintf(out, remaining, ".snapshot"); +} + +// Helper: Create directory recursively +static int create_directory(const char *path) +{ + char tmp[1024]; + char *p = NULL; + size_t len; + + snprintf(tmp, sizeof(tmp), "%s", path); + len = strlen(tmp); + if (tmp[len - 1] == '/') + { + tmp[len - 1] = 0; + } + + for (p = tmp + 1; *p; p++) + { + if (*p == '/') + { + *p = 0; + mkdir(tmp, 0755); + *p = '/'; + } + } + return mkdir(tmp, 0755); +} + +// Helper: Generate HTTP GET request +static int generate_http_get(char *buffer, size_t size, const char *path, const char *host) +{ + return snprintf( + buffer, size, + "GET %s HTTP/1.1\r\n" + "Host: %s\r\n" + "Connection: close\r\n" + "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" + "User-Agent: SeobeoSnapshotCreator/1.0\r\n" + "\r\n", + path, host + ); +} + +// Helper: Read full HTTP response +static char* read_full_response(Seobeo_Handle *client, size_t *len_out) +{ + char *response = malloc(MAX_RESPONSE_SIZE); + if (!response) + { + return NULL; + } + + size_t total = 0; + int attempts = 0; + const int max_attempts = 100; + + while (attempts++ < max_attempts && total < MAX_RESPONSE_SIZE - 1) + { + int n = Seobeo_Handle_Read(client); + + if (n > 0) + { + size_t to_copy = client->read_buffer_len; + if (total + to_copy > MAX_RESPONSE_SIZE - 1) + { + to_copy = MAX_RESPONSE_SIZE - 1 - total; + } + + memcpy(response + total, client->read_buffer, to_copy); + total += to_copy; + Seobeo_Handle_Consume(client, (uint32)to_copy); + } + else if (n == -2) + { + break; + } + else if (n == 0) + { + usleep(10000); + continue; + } + else + { + free(response); + return NULL; + } + } + + response[total] = '\0'; + *len_out = total; + return response; +} + +// Helper: Write snapshot to file +static int write_snapshot(const char *filepath, const char *data, size_t size) +{ + // Create directory if needed + char dir_copy[1024]; + snprintf(dir_copy, sizeof(dir_copy), "%s", filepath); + + char *last_slash = strrchr(dir_copy, '/'); + if (last_slash) + { + *last_slash = '\0'; + create_directory(dir_copy); + } + + FILE *f = fopen(filepath, "wb"); + if (!f) + { + perror("fopen"); + return -1; + } + + size_t written = fwrite(data, 1, size, f); + fclose(f); + + return (written == size) ? 0 : -1; +} + +int Seobeo_Snapshot_Create(const SnapshotConfig *config) +{ + if (!config || !config->path || !config->snapshot_dir || !config->host || !config->port) + { + fprintf(stderr, "Invalid snapshot config\n"); + return -1; + } + + printf("Creating snapshot: %s (expecting %d)\n", config->path, config->expected_status); + + // Connect to server + Seobeo_Handle *client = Seobeo_Stream_Handle_Client_Create(config->host, config->port, FALSE); + if (!client || client->socket < 0) + { + fprintf(stderr, " ✗ Failed to connect to %s:%s\n", config->host, config->port); + if (client) + { + Seobeo_Handle_Destroy(client); + } + return -1; + } + + // Send GET request + char request[4096]; + int req_len = generate_http_get(request, sizeof(request), config->path, config->host); + Seobeo_Handle_Queue(client, (uint8*)request, (uint32)req_len); + + if (Seobeo_Handle_Flush(client) < 0) + { + fprintf(stderr, " ✗ Failed to send request\n"); + Seobeo_Handle_Destroy(client); + return -1; + } + + // Read response + size_t response_len = 0; + char *response = read_full_response(client, &response_len); + Seobeo_Handle_Destroy(client); + + if (!response || response_len == 0) + { + fprintf(stderr, " ✗ Failed to read response\n"); + if (response) + { + free(response); + } + return -1; + } + + // Parse status code + int status = -1; + const char *status_line = strstr(response, "HTTP/1.1 "); + if (!status_line) + { + status_line = strstr(response, "HTTP/1.0 "); + } + if (status_line) + { + sscanf(status_line + 9, "%d", &status); + } + + if (status != config->expected_status) + { + fprintf(stderr, " ✗ Status mismatch: expected %d, got %d\n", + config->expected_status, status); + free(response); + return -1; + } + + printf(" ✓ Status: %d\n", status); + + // Generate snapshot filename + char filename[256]; + path_to_filename(config->path, filename, sizeof(filename)); + + char filepath[1024]; + snprintf(filepath, sizeof(filepath), "%s/%s", config->snapshot_dir, filename); + + // Write snapshot + if (write_snapshot(filepath, response, response_len) == 0) + { + printf(" ✓ Snapshot saved: %s (%zu bytes)\n", filepath, response_len); + free(response); + return 0; + } + else + { + fprintf(stderr, " ✗ Failed to write snapshot: %s\n", filepath); + free(response); + return -1; + } +} + +int Seobeo_Snapshots_Create_Batch(const SnapshotConfig configs[], int count) +{ + int failed = 0; + int passed = 0; + + for (int i = 0; i < count; i++) + { + if (Seobeo_Snapshot_Create(&configs[i]) == 0) + { + passed++; + } + else + { + failed++; + } + printf("\n"); + } + + printf("=== Summary ===\n"); + printf("Created: %d\n", passed); + printf("Failed: %d\n", failed); + + return (failed == 0) ? 0 : -1; +}