diff --git a/Slon/Api/V1/Statuses.HC b/Slon/Api/V1/Statuses.HC index 207832d..96831ed 100644 --- a/Slon/Api/V1/Statuses.HC +++ b/Slon/Api/V1/Statuses.HC @@ -57,7 +57,7 @@ U0 @slon_api_v1_statuses_post(SlonHttpSession* session) SLON_AUTH_ACCOUNT_ID Bool idempotency_key_already_seen = FALSE; - U8* idempotency_key = @slon_http_request_header(session, "idempotency-key"); + U8* idempotency_key = session->header("idempotency-key"); if (StrLen(idempotency_key) > 0 && db->o("idempotency_keys")->@(idempotency_key)) { idempotency_key_already_seen = TRUE; } diff --git a/Slon/Endpoints/Get/ActivityPub.HC b/Slon/Endpoints/Get/ActivityPub.HC index 974f2a2..110c7bf 100644 --- a/Slon/Endpoints/Get/ActivityPub.HC +++ b/Slon/Endpoints/Get/ActivityPub.HC @@ -1,4 +1,4 @@ -if (String.BeginsWith("/users/", @slon_http_request_path(session)) && String.EndsWith("json", @slon_http_request_header(session, "accept"))) { +if (String.BeginsWith("/users/", @slon_http_request_path(session)) && String.EndsWith("json", session->header("accept"))) { @slon_activitypub_users_get(session); return; } diff --git a/Slon/Http/AdminServer.HC b/Slon/Http/AdminServer.HC index 1c2c0d2..0cb5b57 100644 --- a/Slon/Http/AdminServer.HC +++ b/Slon/Http/AdminServer.HC @@ -285,7 +285,7 @@ U0 @slon_admin_server_get(SlonHttpSession* session) if (!db->@("setup")) { if (StrICmp("/", @slon_http_request_path(session))) { session->status(302); - @slon_http_set_header(session, "Location", "/"); + session->header("Location", "/"); } else { @slon_http_send_html_file(session, "M:/Slon/Static/html/admin/setup_instance.html"); } @@ -335,7 +335,7 @@ U0 @slon_admin_setup_instance(SlonHttpSession* session) U0 @slon_admin_server_post(SlonHttpSession* session) { - if (StrFind("json", @slon_http_request_header(session, "content-type")) > 0) { + if (StrFind("json", session->header("content-type")) > 0) { @slon_http_parse_request_as_json(session); } @@ -402,8 +402,8 @@ U0 @slon_admin_http_task(TcpSocket* s) } // If we have a content-length header, consume until we receive all the data, then set request->data pointer and size - if (StrLen(@slon_http_request_header(session, "content-length"))) { - I64 content_length = Str2I64(@slon_http_request_header(session, "content-length")); + if (StrLen(session->header("content-length"))) { + I64 content_length = Str2I64(session->header("content-length")); while (session->request->buffer->data + session->request->buffer->size - session->request->data < content_length) @slon_http_receive(session); } diff --git a/Slon/Http/LocalServer.HC b/Slon/Http/LocalServer.HC index 371c9cf..06ccece 100644 --- a/Slon/Http/LocalServer.HC +++ b/Slon/Http/LocalServer.HC @@ -164,7 +164,7 @@ U0 @slon_local_server_get(SlonHttpSession* session) if (IsDir(scratch_buffer)) { session->status(301); StrPrint(scratch_buffer, "%s/", path); - @slon_http_set_header(session, "Location", scratch_buffer); + session->header("Location", scratch_buffer); } else { @slon_local_server_send_file(session, scratch_buffer); } @@ -214,8 +214,8 @@ U0 @slon_local_http_task(TcpSocket* s) } // If we have a content-length header, consume until we receive all the data, then set request->data pointer and size - if (StrLen(@slon_http_request_header(session, "content-length"))) { - I64 content_length = Str2I64(@slon_http_request_header(session, "content-length")); + if (StrLen(session->header("content-length"))) { + I64 content_length = Str2I64(session->header("content-length")); while (session->request->buffer->data + session->request->buffer->size - session->request->data < content_length) @slon_http_receive(session); } diff --git a/Slon/Http/Server.HC b/Slon/Http/Server.HC index 0462483..0a76689 100644 --- a/Slon/Http/Server.HC +++ b/Slon/Http/Server.HC @@ -43,6 +43,7 @@ U0 @slon_http_free_session(SlonHttpSession* session) @slon_http_free_response(session, session->response); @slon_http_free_request(session, session->request); I64 bytes_used = session->bytes_used - MSize2(session); + Free(session->header); Free(session->status); Free(session); if (bytes_used) { @@ -84,6 +85,23 @@ SlonHttpSession* @slon_http_init_session(TcpSocket* s) a += 0x10; MemSetI64(a, session, 1); + // Create a copy of function and patch header + code_size = MSize(&@slon_session_header_wrapper_function); + session->header = CAlloc(code_size, adam_task->code_heap); + MemCpy(session->header, &@slon_session_header_wrapper_function, code_size); + + a = session->header; + a += 0x16; + MemSetI64(a, session, 1); + + a = session->header; + a += 0x26; + @patch_call_rel32(a, &@slon_http_request_header); + + a = session->header; + a += 0x31; + @patch_call_rel32(a, &@slon_http_set_header); + return session; } @@ -215,8 +233,8 @@ slon_http_parse_request_headers: U0 @slon_http_authorize(SlonHttpSession* session) { - if (StrLen(@slon_http_request_header(session, "authorization"))) { - U8* access_token = StrFind(" ", @slon_http_request_header(session, "authorization")) + 1; + if (StrLen(session->header("authorization"))) { + U8* access_token = StrFind(" ", session->header("authorization")) + 1; session->auth = db->o("oauth")->o("tokens")->@(access_token); } } @@ -283,7 +301,7 @@ U8* @slon_http_json_string_from_multipart_form_data(SlonHttpSession* session, U8 String.Append(json_string, "{"); U8* multipart_form_data_copy = @slon_strnew(session, multipart_form_data); - U8* boundary = StrFind("boundary=", @slon_http_request_header(session, "content-type")) + 9; + U8* boundary = StrFind("boundary=", session->header("content-type")) + 9; // Strip begin double-quotes and ending CRLF, double-quotes while (boundary[0] == '"') boundary++; @@ -430,13 +448,13 @@ U0 @slon_http_handle_get_request(SlonHttpSession* session) U0 @slon_http_handle_patch_request(SlonHttpSession* session) { - if (StrFind("json", @slon_http_request_header(session, "content-type")) > 0) { + if (StrFind("json", session->header("content-type")) > 0) { @slon_http_parse_request_as_json(session); } - if (String.BeginsWith("application/x-www-form-urlencoded", @slon_http_request_header(session, "content-type"))) { + if (String.BeginsWith("application/x-www-form-urlencoded", session->header("content-type"))) { @slon_http_parse_request_as_form_urlencoded(session); } - if (String.BeginsWith("multipart/form-data", @slon_http_request_header(session, "content-type"))) { + if (String.BeginsWith("multipart/form-data", session->header("content-type"))) { @slon_http_parse_request_as_multipart_form_data(session); } @@ -453,17 +471,17 @@ U0 @slon_http_handle_patch_request(SlonHttpSession* session) U0 @slon_http_handle_post_request(SlonHttpSession* session) { - if (StrFind("json", @slon_http_request_header(session, "content-type")) > 0) { + if (StrFind("json", session->header("content-type")) > 0) { @slon_http_parse_request_as_json(session); } - if (String.BeginsWith("application/x-www-form-urlencoded", @slon_http_request_header(session, "content-type"))) { + if (String.BeginsWith("application/x-www-form-urlencoded", session->header("content-type"))) { @slon_http_parse_request_as_form_urlencoded(session); } - if (String.BeginsWith("multipart/form-data", @slon_http_request_header(session, "content-type"))) { + if (String.BeginsWith("multipart/form-data", session->header("content-type"))) { @slon_http_parse_request_as_multipart_form_data(session); } // Workaround for IceCubesApp: https://github.com/Dimillian/IceCubesApp/issues/2235 - if (!StrLen(@slon_http_request_header(session, "content-type")) && @slon_http_request_has_query_string(session)) { + if (!StrLen(session->header("content-type")) && @slon_http_request_has_query_string(session)) { @slon_http_parse_query_string(session); } @@ -533,8 +551,8 @@ U0 @slon_http_task(TcpSocket* s) //@slon_http_debug_print_request(session, FALSE); // If we have a content-length header, consume until we receive all the data, then set request->data pointer and size - if (StrLen(@slon_http_request_header(session, "content-length"))) { - I64 content_length = Str2I64(@slon_http_request_header(session, "content-length")); + if (StrLen(session->header("content-length"))) { + I64 content_length = Str2I64(session->header("content-length")); while (session->request->buffer->data + session->request->buffer->size - session->request->data < content_length) @slon_http_receive(session); } diff --git a/Slon/Modules/ActivityPub.HC b/Slon/Modules/ActivityPub.HC index fb31b22..70fe831 100644 --- a/Slon/Modules/ActivityPub.HC +++ b/Slon/Modules/ActivityPub.HC @@ -13,19 +13,19 @@ Bool @slon_activitypub_http_signature_is_valid(SlonHttpSession* session) no_warn scratch_buffer; // 1. Check that we have a signature and digest - if (!StrLen(@slon_http_request_header(session, "signature")) || !StrLen(@slon_http_request_header(session, "digest"))) { + if (!StrLen(session->header("signature")) || !StrLen(session->header("digest"))) { AdamLog("[verify_signature] no signature or digest header present\n"); return FALSE; } // 2. Check that digest 1) is SHA-256 and 2) matches content - U8* request_digest = @slon_http_request_header(session, "digest"); + U8* request_digest = session->header("digest"); if (!(String.BeginsWith("SHA-256", request_digest) || String.BeginsWith("sha-256", request_digest))) { AdamLog("[verify_signature] digest is not SHA-256\n"); return FALSE; } request_digest = StrFind("=", request_digest) + 1; - I64 content_length = Str2I64(@slon_http_request_header(session, "content-length")); + I64 content_length = Str2I64(session->header("content-length")); if (!content_length) { AdamLog("[verify_signature] content-length is 0\n"); return FALSE; @@ -44,7 +44,7 @@ Bool @slon_activitypub_http_signature_is_valid(SlonHttpSession* session) } // Parse values from Signature header - U8* signature_header = @slon_http_request_header(session, "signature"); + U8* signature_header = session->header("signature"); I64 signature_fragment_count = 0; U8** signature_fragments = String.Split(signature_header, ',', &signature_fragment_count); @@ -192,7 +192,7 @@ Bool @slon_activitypub_http_signature_is_valid(SlonHttpSession* session) U8** headers_split = String.Split(headers, ' ', &headers_split_count); i = 0; while (i < headers_split_count) { - sig_string_alloc_length += StrLen(@slon_http_request_header(session, headers_split[i])); + sig_string_alloc_length += StrLen(session->header(headers_split[i])); ++i; } sig_string_alloc_length += StrLen(@slon_http_request_verb(session)); @@ -209,7 +209,7 @@ Bool @slon_activitypub_http_signature_is_valid(SlonHttpSession* session) if (!StrCmp("(request-target)", headers_split[i])) { String.Append(sig_string, "(request-target): %s %s", "post", @slon_http_request_path(session)); } else { - String.Append(sig_string, "%s: %s", headers_split[i], @slon_http_request_header(session, headers_split[i])); + String.Append(sig_string, "%s: %s", headers_split[i], session->header(headers_split[i])); } ++i; if (i < headers_split_count) { diff --git a/Slon/Modules/Http.HC b/Slon/Modules/Http.HC index 4767f47..73e8d0b 100644 --- a/Slon/Modules/Http.HC +++ b/Slon/Modules/Http.HC @@ -63,6 +63,7 @@ class SlonHttpSession { JsonObject* auth; U8* actor_for_key_id; + U8* (*header)(U8* key, U8* value = NULL); I64 (*status)(I64 code = NULL); }; @@ -148,12 +149,16 @@ JsonObject* @slon_http_request_json(SlonHttpSession* session) U0 @slon_http_set_header(SlonHttpSession* session, U8* key, U8* value) { - Json.Set(session->response->headers, key, value, JSON_STRING); + if (!StrICmp(value, "")) { + Json.Unset(session->response->headers, key); + } else { + Json.Set(session->response->headers, key, value, JSON_STRING); + } } U0 @slon_http_set_content_type(SlonHttpSession* session, U8* value) { - @slon_http_set_header(session, "content-type", value); + session->header("content-type", value); } U0 @slon_http_send_ap_json(SlonHttpSession* session, U64 json) @@ -263,3 +268,13 @@ I64 @slon_session_status_wrapper_function(I64 code) } return session->response->status_code; } + +U8* @slon_session_header_wrapper_function(U8* key, U8* value = NULL) +{ + SlonHttpSession* session = SLON_WRAPPER_MAGIC_NUMBER; + if (!value) { + return @slon_http_request_header(session, key); + } + @slon_http_set_header(session, key, value); + return value; +}