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;
+}