Mercurial
comparison 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 |
comparison
equal
deleted
inserted
replaced
| 66:a0f0ad5e42eb | 67:6626ec933933 |
|---|---|
| 1 #include "seobeo/snapshot_creator.h" | |
| 2 #include <stdio.h> | |
| 3 #include <stdlib.h> | |
| 4 #include <string.h> | |
| 5 #include <sys/stat.h> | |
| 6 #include <unistd.h> | |
| 7 | |
| 8 #define MAX_RESPONSE_SIZE (1024 * 1024) | |
| 9 | |
| 10 // Helper: Convert URL path to filename | |
| 11 static void path_to_filename(const char *path, char *filename, size_t max_len) | |
| 12 { | |
| 13 if (strcmp(path, "/") == 0) | |
| 14 { | |
| 15 snprintf(filename, max_len, "root.snapshot"); | |
| 16 return; | |
| 17 } | |
| 18 | |
| 19 const char *p = path; | |
| 20 if (*p == '/') | |
| 21 { | |
| 22 p++; | |
| 23 } | |
| 24 | |
| 25 char *out = filename; | |
| 26 size_t remaining = max_len - 1; | |
| 27 | |
| 28 while (*p && remaining > 0) | |
| 29 { | |
| 30 if (*p == '/') | |
| 31 { | |
| 32 *out++ = '_'; | |
| 33 remaining--; | |
| 34 } | |
| 35 else | |
| 36 { | |
| 37 *out++ = *p; | |
| 38 remaining--; | |
| 39 } | |
| 40 p++; | |
| 41 } | |
| 42 | |
| 43 snprintf(out, remaining, ".snapshot"); | |
| 44 } | |
| 45 | |
| 46 // Helper: Create directory recursively | |
| 47 static int create_directory(const char *path) | |
| 48 { | |
| 49 char tmp[1024]; | |
| 50 char *p = NULL; | |
| 51 size_t len; | |
| 52 | |
| 53 snprintf(tmp, sizeof(tmp), "%s", path); | |
| 54 len = strlen(tmp); | |
| 55 if (tmp[len - 1] == '/') | |
| 56 { | |
| 57 tmp[len - 1] = 0; | |
| 58 } | |
| 59 | |
| 60 for (p = tmp + 1; *p; p++) | |
| 61 { | |
| 62 if (*p == '/') | |
| 63 { | |
| 64 *p = 0; | |
| 65 mkdir(tmp, 0755); | |
| 66 *p = '/'; | |
| 67 } | |
| 68 } | |
| 69 return mkdir(tmp, 0755); | |
| 70 } | |
| 71 | |
| 72 // Helper: Generate HTTP GET request | |
| 73 static int generate_http_get(char *buffer, size_t size, const char *path, const char *host) | |
| 74 { | |
| 75 return snprintf( | |
| 76 buffer, size, | |
| 77 "GET %s HTTP/1.1\r\n" | |
| 78 "Host: %s\r\n" | |
| 79 "Connection: close\r\n" | |
| 80 "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" | |
| 81 "User-Agent: SeobeoSnapshotCreator/1.0\r\n" | |
| 82 "\r\n", | |
| 83 path, host | |
| 84 ); | |
| 85 } | |
| 86 | |
| 87 // Helper: Read full HTTP response | |
| 88 static char* read_full_response(Seobeo_Handle *client, size_t *len_out) | |
| 89 { | |
| 90 char *response = malloc(MAX_RESPONSE_SIZE); | |
| 91 if (!response) | |
| 92 { | |
| 93 return NULL; | |
| 94 } | |
| 95 | |
| 96 size_t total = 0; | |
| 97 int attempts = 0; | |
| 98 const int max_attempts = 100; | |
| 99 | |
| 100 while (attempts++ < max_attempts && total < MAX_RESPONSE_SIZE - 1) | |
| 101 { | |
| 102 int n = Seobeo_Handle_Read(client); | |
| 103 | |
| 104 if (n > 0) | |
| 105 { | |
| 106 size_t to_copy = client->read_buffer_len; | |
| 107 if (total + to_copy > MAX_RESPONSE_SIZE - 1) | |
| 108 { | |
| 109 to_copy = MAX_RESPONSE_SIZE - 1 - total; | |
| 110 } | |
| 111 | |
| 112 memcpy(response + total, client->read_buffer, to_copy); | |
| 113 total += to_copy; | |
| 114 Seobeo_Handle_Consume(client, (uint32)to_copy); | |
| 115 } | |
| 116 else if (n == -2) | |
| 117 { | |
| 118 break; | |
| 119 } | |
| 120 else if (n == 0) | |
| 121 { | |
| 122 usleep(10000); | |
| 123 continue; | |
| 124 } | |
| 125 else | |
| 126 { | |
| 127 free(response); | |
| 128 return NULL; | |
| 129 } | |
| 130 } | |
| 131 | |
| 132 response[total] = '\0'; | |
| 133 *len_out = total; | |
| 134 return response; | |
| 135 } | |
| 136 | |
| 137 // Helper: Write snapshot to file | |
| 138 static int write_snapshot(const char *filepath, const char *data, size_t size) | |
| 139 { | |
| 140 // Create directory if needed | |
| 141 char dir_copy[1024]; | |
| 142 snprintf(dir_copy, sizeof(dir_copy), "%s", filepath); | |
| 143 | |
| 144 char *last_slash = strrchr(dir_copy, '/'); | |
| 145 if (last_slash) | |
| 146 { | |
| 147 *last_slash = '\0'; | |
| 148 create_directory(dir_copy); | |
| 149 } | |
| 150 | |
| 151 FILE *f = fopen(filepath, "wb"); | |
| 152 if (!f) | |
| 153 { | |
| 154 perror("fopen"); | |
| 155 return -1; | |
| 156 } | |
| 157 | |
| 158 size_t written = fwrite(data, 1, size, f); | |
| 159 fclose(f); | |
| 160 | |
| 161 return (written == size) ? 0 : -1; | |
| 162 } | |
| 163 | |
| 164 int Seobeo_Snapshot_Create(const SnapshotConfig *config) | |
| 165 { | |
| 166 if (!config || !config->path || !config->snapshot_dir || !config->host || !config->port) | |
| 167 { | |
| 168 fprintf(stderr, "Invalid snapshot config\n"); | |
| 169 return -1; | |
| 170 } | |
| 171 | |
| 172 printf("Creating snapshot: %s (expecting %d)\n", config->path, config->expected_status); | |
| 173 | |
| 174 // Connect to server | |
| 175 Seobeo_Handle *client = Seobeo_Stream_Handle_Client_Create(config->host, config->port, FALSE); | |
| 176 if (!client || client->socket < 0) | |
| 177 { | |
| 178 fprintf(stderr, " ✗ Failed to connect to %s:%s\n", config->host, config->port); | |
| 179 if (client) | |
| 180 { | |
| 181 Seobeo_Handle_Destroy(client); | |
| 182 } | |
| 183 return -1; | |
| 184 } | |
| 185 | |
| 186 // Send GET request | |
| 187 char request[4096]; | |
| 188 int req_len = generate_http_get(request, sizeof(request), config->path, config->host); | |
| 189 Seobeo_Handle_Queue(client, (uint8*)request, (uint32)req_len); | |
| 190 | |
| 191 if (Seobeo_Handle_Flush(client) < 0) | |
| 192 { | |
| 193 fprintf(stderr, " ✗ Failed to send request\n"); | |
| 194 Seobeo_Handle_Destroy(client); | |
| 195 return -1; | |
| 196 } | |
| 197 | |
| 198 // Read response | |
| 199 size_t response_len = 0; | |
| 200 char *response = read_full_response(client, &response_len); | |
| 201 Seobeo_Handle_Destroy(client); | |
| 202 | |
| 203 if (!response || response_len == 0) | |
| 204 { | |
| 205 fprintf(stderr, " ✗ Failed to read response\n"); | |
| 206 if (response) | |
| 207 { | |
| 208 free(response); | |
| 209 } | |
| 210 return -1; | |
| 211 } | |
| 212 | |
| 213 // Parse status code | |
| 214 int status = -1; | |
| 215 const char *status_line = strstr(response, "HTTP/1.1 "); | |
| 216 if (!status_line) | |
| 217 { | |
| 218 status_line = strstr(response, "HTTP/1.0 "); | |
| 219 } | |
| 220 if (status_line) | |
| 221 { | |
| 222 sscanf(status_line + 9, "%d", &status); | |
| 223 } | |
| 224 | |
| 225 if (status != config->expected_status) | |
| 226 { | |
| 227 fprintf(stderr, " ✗ Status mismatch: expected %d, got %d\n", | |
| 228 config->expected_status, status); | |
| 229 free(response); | |
| 230 return -1; | |
| 231 } | |
| 232 | |
| 233 printf(" ✓ Status: %d\n", status); | |
| 234 | |
| 235 // Generate snapshot filename | |
| 236 char filename[256]; | |
| 237 path_to_filename(config->path, filename, sizeof(filename)); | |
| 238 | |
| 239 char filepath[1024]; | |
| 240 snprintf(filepath, sizeof(filepath), "%s/%s", config->snapshot_dir, filename); | |
| 241 | |
| 242 // Write snapshot | |
| 243 if (write_snapshot(filepath, response, response_len) == 0) | |
| 244 { | |
| 245 printf(" ✓ Snapshot saved: %s (%zu bytes)\n", filepath, response_len); | |
| 246 free(response); | |
| 247 return 0; | |
| 248 } | |
| 249 else | |
| 250 { | |
| 251 fprintf(stderr, " ✗ Failed to write snapshot: %s\n", filepath); | |
| 252 free(response); | |
| 253 return -1; | |
| 254 } | |
| 255 } | |
| 256 | |
| 257 int Seobeo_Snapshots_Create_Batch(const SnapshotConfig configs[], int count) | |
| 258 { | |
| 259 int failed = 0; | |
| 260 int passed = 0; | |
| 261 | |
| 262 for (int i = 0; i < count; i++) | |
| 263 { | |
| 264 if (Seobeo_Snapshot_Create(&configs[i]) == 0) | |
| 265 { | |
| 266 passed++; | |
| 267 } | |
| 268 else | |
| 269 { | |
| 270 failed++; | |
| 271 } | |
| 272 printf("\n"); | |
| 273 } | |
| 274 | |
| 275 printf("=== Summary ===\n"); | |
| 276 printf("Created: %d\n", passed); | |
| 277 printf("Failed: %d\n", failed); | |
| 278 | |
| 279 return (failed == 0) ? 0 : -1; | |
| 280 } |