TEST_END TEST_START(at_kv) { clax_kv_list_t list; clax_kv_list_item_t *item; clax_kv_list_init(&list); ASSERT(clax_kv_list_at(&list, 0) == NULL); ASSERT(clax_kv_list_at(&list, 10) == NULL); clax_kv_list_push(&list, "foo", "bar"); clax_kv_list_push(&list, "bar", "baz"); clax_kv_list_push(&list, "bar", "zab"); item = clax_kv_list_at(&list, 0); ASSERT_STR_EQ(item->key, "foo"); ASSERT_STR_EQ(item->val, "bar"); item = clax_kv_list_at(&list, 1); ASSERT_STR_EQ(item->key, "bar"); ASSERT_STR_EQ(item->val, "baz"); item = clax_kv_list_at(&list, 2); ASSERT_STR_EQ(item->key, "bar"); ASSERT_STR_EQ(item->val, "zab"); ASSERT(clax_kv_list_at(&list, 3) == NULL); clax_kv_list_free(&list); }
void clax_dispatch_not_authorized(clax_ctx_t *clax_ctx, clax_http_request_t *req, clax_http_response_t *res) { res->status_code = 401; clax_kv_list_push(&res->headers, "Content-Type", "text/plain"); clax_kv_list_push(&res->headers, "WWW-Authenticate", "Basic realm=\"clax\""); clax_big_buf_append_str(&res->body, "Authorization required"); }
TEST_END TEST_START(finds_all_kv) { size_t start = 0; clax_kv_list_t list; clax_kv_list_init(&list); ASSERT(clax_kv_list_find_all(&list, "foo", &start) == NULL); ASSERT_EQ(start, 0); start = 100; ASSERT(clax_kv_list_find_all(&list, "foo", &start) == NULL); ASSERT_EQ(start, 100); clax_kv_list_push(&list, "foo", "bar"); clax_kv_list_push(&list, "bar", "baz"); clax_kv_list_push(&list, "bar", "zab"); start = 0; ASSERT_STR_EQ(clax_kv_list_find_all(&list, "foo", &start), "bar"); ASSERT_EQ(start, 1); ASSERT(clax_kv_list_find_all(&list, "foo", &start) == NULL); ASSERT(clax_kv_list_find_all(&list, "foo", &start) == NULL); start = 0; ASSERT_STR_EQ(clax_kv_list_find_all(&list, "bar", &start), "baz"); ASSERT_STR_EQ(clax_kv_list_find_all(&list, "bar", &start), "zab"); ASSERT(clax_kv_list_find_all(&list, "bar", &start) == NULL); ASSERT(clax_kv_list_find_all(&list, "bar", &start) == NULL); clax_kv_list_free(&list); }
TEST_END TEST_START(pushes_kv) { clax_kv_list_t list; clax_kv_list_init(&list); clax_kv_list_push(&list, "foo", "bar"); ASSERT_EQ(list.size, 1); clax_kv_list_push(&list, "bar", "baz"); ASSERT_EQ(list.size, 2); clax_kv_list_push(&list, NULL, NULL); ASSERT_EQ(list.size, 3); clax_kv_list_free(&list); }
void clax_dispatch_bad_request(clax_ctx_t *clax_ctx, clax_http_request_t *req, clax_http_response_t *res, char *msg) { char *p = msg; if (p == NULL) { p = "Bad Request"; } res->status_code = 400; clax_kv_list_push(&res->headers, "Content-Type", "text/plain"); clax_big_buf_append_str(&res->body, p); }
void clax_dispatch_system_error(clax_ctx_t *clax_ctx, clax_http_request_t *req, clax_http_response_t *res, char *msg) { char *p = msg; if (p == NULL) { p = "System error"; } clax_kv_list_push(&res->headers, "Content-Type", "text/plain"); res->status_code = 500; clax_big_buf_append_str(&res->body, p); }
TEST_END TEST_START(finds_kv) { clax_kv_list_t list; clax_kv_list_init(&list); ASSERT(clax_kv_list_find(&list, "foo") == NULL); clax_kv_list_push(&list, "foo", "bar"); clax_kv_list_push(&list, "bar", "baz"); clax_kv_list_push(&list, "bar", "zab"); ASSERT_STR_EQ(clax_kv_list_find(&list, "foo"), "bar"); ASSERT_STR_EQ(clax_kv_list_find(&list, "bar"), "baz"); ASSERT_STR_EQ(clax_kv_list_find(&list, "bar"), "baz"); ASSERT(clax_kv_list_find(&list, "unknown") == NULL); clax_kv_list_free(&list); }
TEST_END TEST_START(iter_kv) { clax_kv_list_t list; clax_kv_list_item_t *item; size_t iter = 0; clax_kv_list_init(&list); ASSERT(clax_kv_list_next(&list, &iter) == NULL); ASSERT(clax_kv_list_next(&list, &iter) == NULL); clax_kv_list_push(&list, "foo", "bar"); clax_kv_list_push(&list, "bar", "baz"); clax_kv_list_push(&list, "bar", "zab"); item = clax_kv_list_next(&list, &iter); ASSERT_EQ(iter, 1); ASSERT_STR_EQ(item->key, "foo"); ASSERT_STR_EQ(item->val, "bar"); item = clax_kv_list_next(&list, &iter); ASSERT_EQ(iter, 2); ASSERT_STR_EQ(item->key, "bar"); ASSERT_STR_EQ(item->val, "baz"); item = clax_kv_list_next(&list, &iter); ASSERT_EQ(iter, 3); ASSERT_STR_EQ(item->key, "bar"); ASSERT_STR_EQ(item->val, "zab"); ASSERT(clax_kv_list_next(&list, &iter) == NULL); ASSERT(clax_kv_list_next(&list, &iter) == NULL); clax_kv_list_free(&list); }
void clax_dispatch_command(clax_ctx_t *clax_ctx, clax_http_request_t *req, clax_http_response_t *res) { memset(&command_ctx, 0, sizeof(command_ctx_t)); char *command = clax_kv_list_find(&req->body_params, "command"); if (!command || !strlen(command)) { clax_dispatch_bad_request(clax_ctx, req, res); return; } strncpy(command_ctx.command, command, sizeof_struct_member(command_ctx_t, command)); char *timeout = clax_kv_list_find(&req->body_params, "timeout"); if (timeout && strlen(timeout)) { command_ctx.timeout = atoi(timeout); } pid_t pid = clax_command_start(&command_ctx); if (pid <= 0) { clax_dispatch_system_error(clax_ctx, req, res); return; } res->status_code = 200; clax_kv_list_push(&res->headers, "Transfer-Encoding", "chunked"); char buf_pid[15]; snprintf(buf_pid, sizeof(buf_pid), "%d", pid); clax_kv_list_push(&res->headers, "Trailer", "X-Clax-Exit"); clax_kv_list_push(&res->headers, "X-Clax-PID", buf_pid); res->body_cb_ctx = &command_ctx; res->body_cb = clax_command_read_cb; }
void clax_dispatch_download(clax_ctx_t *clax_ctx, clax_http_request_t *req, clax_http_response_t *res) { char *file = req->path_info + strlen("/tree/"); char buf[255]; char base_buf[255]; char last_modified_buf[30]; struct stat st; struct tm last_modified_time; struct tm *last_modified_time_p; if (stat(file, &st) != 0 || !(st.st_mode & S_IFREG)) { clax_dispatch_not_found(clax_ctx, req, res); return; } unsigned long crc32 = clax_crc32_calc_file(file); char crc32_hex[9] = {0}; snprintf(crc32_hex, sizeof(crc32_hex), "%lx", crc32); FILE *fh = fopen(file, "rb"); if (fh == NULL) { clax_dispatch_not_found(clax_ctx, req, res); return; } snprintf(buf, sizeof(buf), "%d", (int)st.st_size); char *base = basename(file); strcpy(base_buf, "attachment; filename=\""); strcat(base_buf, base); strcat(base_buf, "\""); res->status_code = 200; clax_kv_list_push(&res->headers, "Content-Type", "application/octet-stream"); clax_kv_list_push(&res->headers, "Content-Disposition", base_buf); clax_kv_list_push(&res->headers, "Pragma", "no-cache"); clax_kv_list_push(&res->headers, "Content-Length", buf); last_modified_time_p = gmtime_r(&st.st_mtime, &last_modified_time); strftime(last_modified_buf, sizeof(last_modified_buf), "%a, %d %b %Y %H:%M:%S GMT", last_modified_time_p); clax_kv_list_push(&res->headers, "Last-Modified", last_modified_buf); clax_kv_list_push(&res->headers, "X-Clax-CRC32", crc32_hex); if (req->method == HTTP_GET) res->body_fh = fh; else fclose(fh); }
void clax_dispatch_delete(clax_ctx_t *clax_ctx, clax_http_request_t *req, clax_http_response_t *res) { char *file = req->path_info + strlen("/tree/"); struct stat st; if (stat(file, &st) != 0 || !(st.st_mode & S_IFREG)) { clax_dispatch_not_found(clax_ctx, req, res); return; } int ret = unlink(file); if (ret == 0) { res->status_code = 200; clax_kv_list_push(&res->headers, "Content-Type", "application/json"); clax_big_buf_append_str(&res->body, "{\"message\":\"ok\"}"); } else { clax_dispatch_system_error(clax_ctx, req, res); return; } }
void clax_dispatch_not_found(clax_ctx_t *clax_ctx, clax_http_request_t *req, clax_http_response_t *res) { clax_kv_list_push(&res->headers, "Content-Type", "text/plain"); res->status_code = 404; clax_big_buf_append_str(&res->body, "Not found"); }
void clax_dispatch_upload(clax_ctx_t *clax_ctx, clax_http_request_t *req, clax_http_response_t *res) { char *subdir = req->path_info + strlen("/tree/"); if (strlen(subdir)) { struct stat info; if (stat(subdir, &info) == 0 && info.st_mode & S_IFDIR) { } else { clax_log("Output directory '%s' does not exist", subdir); clax_dispatch_bad_request(clax_ctx, req, res); return; } } if (req->continue_expected) { return; } if (strlen(req->multipart_boundary)) { int i; for (i = 0; i < req->multiparts.size; i++) { clax_http_multipart_t *multipart = clax_http_multipart_list_at(&req->multiparts, i); const char *content_disposition = clax_kv_list_find(&multipart->headers, "Content-Disposition"); if (!content_disposition) continue; char prefix[] = "form-data; "; if (strncmp(content_disposition, prefix, strlen(prefix)) != 0) continue; const char *kv = content_disposition + strlen(prefix); size_t name_len; size_t filename_len; const char *name = clax_http_extract_kv(kv, "name", &name_len); const char *filename = clax_http_extract_kv(kv, "filename", &filename_len); if (!name || !filename || (strncmp(name, "file", name_len) != 0)) continue; char *new_name = clax_kv_list_find(&req->query_params, "name"); char *crc32 = clax_kv_list_find(&req->query_params, "crc"); char *time = clax_kv_list_find(&req->query_params, "time"); if (crc32 && strlen(crc32) != 8) { clax_dispatch_bad_request(clax_ctx, req, res); return; } char *fpath; if (new_name && strlen(new_name)) { fpath = clax_strjoin("/", subdir, new_name, NULL); } else { char *p = clax_strndup(filename, filename_len); fpath = clax_strjoin("/", subdir, p, NULL); free(p); } clax_san_path(fpath); int ret = clax_big_buf_write_file(&multipart->bbuf, fpath); if (ret < 0) { clax_log("Saving file failed: %s\n", fpath); clax_dispatch_system_error(clax_ctx, req, res); } else { if (crc32 && strlen(crc32)) { unsigned long got_crc32 = clax_htol(crc32); int fd = open(fpath, O_RDONLY); unsigned long exp_crc32 = clax_crc32_calc_fd(fd); close(fd); if (got_crc32 != exp_crc32) { clax_log("CRC mismatch %u != %u (%s)", exp_crc32, got_crc32, crc32); clax_dispatch_bad_request(clax_ctx, req, res); remove(fpath); free(fpath); return; } else { clax_log("CRC ok %u != %u (%s)", exp_crc32, got_crc32, crc32); } } if (time && strlen(time)) { int mtime = atol(time); struct utimbuf t; t.actime = mtime; t.modtime = mtime; int ok = utime(fpath, &t); if (ok < 0) clax_log("utime on file '%s' failed: %s", fpath, strerror(errno)); } res->status_code = 200; clax_kv_list_push(&res->headers, "Content-Type", "application/json"); clax_big_buf_append_str(&res->body, "{\"message\":\"ok\"}"); } free(fpath); break; } } if (!res->status_code) { clax_dispatch_bad_request(clax_ctx, req, res); } }
void clax_dispatch_system_error(clax_ctx_t *clax_ctx, clax_http_request_t *req, clax_http_response_t *res) { clax_kv_list_push(&res->headers, "Content-Type", "text/plain"); res->status_code = 500; clax_big_buf_append_str(&res->body, "System error"); }
void clax_dispatch_success(clax_ctx_t *clax_ctx, clax_http_request_t *req, clax_http_response_t *res) { clax_kv_list_push(&res->headers, "Content-Type", "application/json"); res->status_code = 200; clax_big_buf_append_str(&res->body, "{\"message\":\"ok\"}"); }
void clax_dispatch_method_not_allowed(clax_ctx_t *clax_ctx, clax_http_request_t *req, clax_http_response_t *res) { clax_kv_list_push(&res->headers, "Content-Type", "text/plain"); res->status_code = 405; clax_big_buf_append_str(&res->body, "Method not allowed"); }
void clax_dispatch_bad_gateway(clax_ctx_t *clax_ctx, clax_http_request_t *req, clax_http_response_t *res) { clax_kv_list_push(&res->headers, "Content-Type", "text/plain"); res->status_code = 502; clax_big_buf_append_str(&res->body, "Bad Gateway"); }