Mercurial
comparison seobeo/snapshot_creator.c @ 126:e7899c93da77
Remove playground.
| author | June Park <parkjune1995@gmail.com> |
|---|---|
| date | Thu, 08 Jan 2026 18:03:34 -0800 |
| parents | 6626ec933933 |
| children | 7eb79fd91c7e |
comparison
equal
deleted
inserted
replaced
| 125:f236c895604e | 126:e7899c93da77 |
|---|---|
| 1 #include "seobeo/snapshot_creator.h" | 1 #include "seobeo/snapshot_creator.h" |
| 2 #include "seobeo/seobeo_internal.h" | |
| 2 #include <stdio.h> | 3 #include <stdio.h> |
| 3 #include <stdlib.h> | 4 #include <stdlib.h> |
| 4 #include <string.h> | 5 #include <string.h> |
| 5 #include <sys/stat.h> | 6 #include <sys/stat.h> |
| 6 #include <unistd.h> | 7 #include <unistd.h> |
| 7 | 8 |
| 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) | 9 static void path_to_filename(const char *path, char *filename, size_t max_len) |
| 12 { | 10 { |
| 13 if (strcmp(path, "/") == 0) | 11 if (strcmp(path, "/") == 0) |
| 14 { | 12 { |
| 15 snprintf(filename, max_len, "root.snapshot"); | 13 snprintf(filename, max_len, "root.snapshot"); |
| 16 return; | 14 return; |
| 17 } | 15 } |
| 18 | 16 |
| 19 const char *p = path; | 17 const char *p = path; |
| 20 if (*p == '/') | 18 if (*p == '/') |
| 21 { | |
| 22 p++; | 19 p++; |
| 23 } | |
| 24 | 20 |
| 25 char *out = filename; | 21 char *out = filename; |
| 26 size_t remaining = max_len - 1; | 22 size_t remaining = max_len - 1; |
| 27 | 23 |
| 28 while (*p && remaining > 0) | 24 while (*p && remaining > 0) |
| 67 } | 63 } |
| 68 } | 64 } |
| 69 return mkdir(tmp, 0755); | 65 return mkdir(tmp, 0755); |
| 70 } | 66 } |
| 71 | 67 |
| 72 // Helper: Generate HTTP GET request | 68 // Helper: Build full HTTP response from Seobeo_Client_Response |
| 73 static int generate_http_get(char *buffer, size_t size, const char *path, const char *host) | 69 static char* build_full_response(Seobeo_Client_Response *p_resp, size_t *len_out) |
| 74 { | 70 { |
| 75 return snprintf( | 71 if (!p_resp) |
| 76 buffer, size, | 72 return NULL; |
| 77 "GET %s HTTP/1.1\r\n" | 73 |
| 78 "Host: %s\r\n" | 74 // Calculate total response size |
| 79 "Connection: close\r\n" | 75 size_t status_line_len = 100; // "HTTP/1.1 200 OK\r\n" |
| 80 "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" | 76 size_t headers_len = 0; |
| 81 "User-Agent: SeobeoSnapshotCreator/1.0\r\n" | 77 size_t body_len = p_resp->body_length; |
| 82 "\r\n", | 78 |
| 83 path, host | 79 // Estimate header size |
| 84 ); | 80 if (p_resp->headers) |
| 85 } | 81 { |
| 86 | 82 size_t header_count = Dowa_Array_Length(p_resp->headers); |
| 87 // Helper: Read full HTTP response | 83 for (size_t i = 0; i < header_count; i++) |
| 88 static char* read_full_response(Seobeo_Handle *client, size_t *len_out) | 84 { |
| 89 { | 85 headers_len += strlen(p_resp->headers[i].key) + strlen(p_resp->headers[i].value) + 4; // ": " + "\r\n" |
| 90 char *response = malloc(MAX_RESPONSE_SIZE); | 86 } |
| 87 } | |
| 88 | |
| 89 size_t total_size = status_line_len + headers_len + 2 + body_len + 1; // +2 for "\r\n", +1 for null terminator | |
| 90 char *response = malloc(total_size); | |
| 91 if (!response) | 91 if (!response) |
| 92 { | |
| 93 return NULL; | 92 return NULL; |
| 94 } | 93 |
| 95 | 94 // Build status line |
| 96 size_t total = 0; | 95 int offset = snprintf(response, total_size, "HTTP/1.1 %d %s\r\n", |
| 97 int attempts = 0; | 96 p_resp->status_code, |
| 98 const int max_attempts = 100; | 97 p_resp->status_text ? p_resp->status_text : "OK"); |
| 99 | 98 |
| 100 while (attempts++ < max_attempts && total < MAX_RESPONSE_SIZE - 1) | 99 // Add headers |
| 101 { | 100 if (p_resp->headers) |
| 102 int n = Seobeo_Handle_Read(client); | 101 { |
| 103 | 102 size_t header_count = Dowa_Array_Length(p_resp->headers); |
| 104 if (n > 0) | 103 for (size_t i = 0; i < header_count; i++) |
| 105 { | 104 { |
| 106 size_t to_copy = client->read_buffer_len; | 105 offset += snprintf(response + offset, total_size - offset, "%s: %s\r\n", |
| 107 if (total + to_copy > MAX_RESPONSE_SIZE - 1) | 106 p_resp->headers[i].key, |
| 108 { | 107 p_resp->headers[i].value); |
| 109 to_copy = MAX_RESPONSE_SIZE - 1 - total; | 108 } |
| 110 } | 109 } |
| 111 | 110 |
| 112 memcpy(response + total, client->read_buffer, to_copy); | 111 // End of headers |
| 113 total += to_copy; | 112 offset += snprintf(response + offset, total_size - offset, "\r\n"); |
| 114 Seobeo_Handle_Consume(client, (uint32)to_copy); | 113 |
| 115 } | 114 // Add body |
| 116 else if (n == -2) | 115 if (p_resp->body && body_len > 0) |
| 117 { | 116 { |
| 118 break; | 117 memcpy(response + offset, p_resp->body, body_len); |
| 119 } | 118 offset += body_len; |
| 120 else if (n == 0) | 119 } |
| 121 { | 120 |
| 122 usleep(10000); | 121 response[offset] = '\0'; |
| 123 continue; | 122 *len_out = offset; |
| 124 } | |
| 125 else | |
| 126 { | |
| 127 free(response); | |
| 128 return NULL; | |
| 129 } | |
| 130 } | |
| 131 | |
| 132 response[total] = '\0'; | |
| 133 *len_out = total; | |
| 134 return response; | 123 return response; |
| 135 } | 124 } |
| 136 | 125 |
| 137 // Helper: Write snapshot to file | 126 // Helper: Write snapshot to file |
| 138 static int write_snapshot(const char *filepath, const char *data, size_t size) | 127 static int write_snapshot(const char *filepath, const char *data, size_t size) |
| 169 return -1; | 158 return -1; |
| 170 } | 159 } |
| 171 | 160 |
| 172 printf("Creating snapshot: %s (expecting %d)\n", config->path, config->expected_status); | 161 printf("Creating snapshot: %s (expecting %d)\n", config->path, config->expected_status); |
| 173 | 162 |
| 174 // Connect to server | 163 // Build URL |
| 175 Seobeo_Handle *client = Seobeo_Stream_Handle_Client_Create(config->host, config->port, FALSE); | 164 char url[2048]; |
| 176 if (!client || client->socket < 0) | 165 snprintf(url, sizeof(url), "http://%s:%s%s", config->host, config->port, config->path); |
| 177 { | 166 |
| 178 fprintf(stderr, " ✗ Failed to connect to %s:%s\n", config->host, config->port); | 167 // Create HTTP request |
| 179 if (client) | 168 Seobeo_Client_Request *p_req = Seobeo_Client_Request_Create(url); |
| 180 { | 169 if (!p_req) |
| 181 Seobeo_Handle_Destroy(client); | 170 { |
| 182 } | 171 fprintf(stderr, " ✗ Failed to create request for %s\n", url); |
| 183 return -1; | 172 return -1; |
| 184 } | 173 } |
| 185 | 174 |
| 186 // Send GET request | 175 // Execute request |
| 187 char request[4096]; | 176 Seobeo_Client_Response *p_resp = Seobeo_Client_Request_Execute(p_req); |
| 188 int req_len = generate_http_get(request, sizeof(request), config->path, config->host); | 177 if (!p_resp) |
| 189 Seobeo_Handle_Queue(client, (uint8*)request, (uint32)req_len); | 178 { |
| 190 | 179 fprintf(stderr, " ✗ Failed to get response\n"); |
| 191 if (Seobeo_Handle_Flush(client) < 0) | 180 Seobeo_Client_Request_Destroy(p_req); |
| 192 { | 181 return -1; |
| 193 fprintf(stderr, " ✗ Failed to send request\n"); | 182 } |
| 194 Seobeo_Handle_Destroy(client); | 183 |
| 195 return -1; | 184 // Check status code |
| 196 } | 185 if (p_resp->status_code != config->expected_status) |
| 197 | 186 { |
| 198 // Read response | 187 fprintf(stderr, " ✗ Status mismatch: expected %d, got %d\n", |
| 188 config->expected_status, p_resp->status_code); | |
| 189 Seobeo_Client_Response_Destroy(p_resp); | |
| 190 Seobeo_Client_Request_Destroy(p_req); | |
| 191 return -1; | |
| 192 } | |
| 193 | |
| 194 printf(" ✓ Status: %d\n", p_resp->status_code); | |
| 195 | |
| 196 // Build full HTTP response | |
| 199 size_t response_len = 0; | 197 size_t response_len = 0; |
| 200 char *response = read_full_response(client, &response_len); | 198 char *response = build_full_response(p_resp, &response_len); |
| 201 Seobeo_Handle_Destroy(client); | 199 |
| 200 Seobeo_Client_Response_Destroy(p_resp); | |
| 201 Seobeo_Client_Request_Destroy(p_req); | |
| 202 | 202 |
| 203 if (!response || response_len == 0) | 203 if (!response || response_len == 0) |
| 204 { | 204 { |
| 205 fprintf(stderr, " ✗ Failed to read response\n"); | 205 fprintf(stderr, " ✗ Failed to build response\n"); |
| 206 if (response) | 206 if (response) |
| 207 { | |
| 208 free(response); | 207 free(response); |
| 209 } | 208 return -1; |
| 210 return -1; | 209 } |
| 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 | 210 |
| 235 // Generate snapshot filename | 211 // Generate snapshot filename |
| 236 char filename[256]; | 212 char filename[256]; |
| 237 path_to_filename(config->path, filename, sizeof(filename)); | 213 path_to_filename(config->path, filename, sizeof(filename)); |
| 238 | 214 |