Mercurial
view seobeo/snapshot_creator.c @ 71:75de5903355c
Giagantic changes that update Dowa library to be more align with stb style array and hashmap. Updated Seobeo to be caching on server side instead of file level caching. Deleted bunch of things I don't really use.
| author | June Park <parkjune1995@gmail.com> |
|---|---|
| date | Sun, 28 Dec 2025 20:34:22 -0800 |
| parents | 6626ec933933 |
| children | e7899c93da77 |
line wrap: on
line source
#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; }