]> git.seodisparate.com - c_simple_http/commitdiff
backport: unesc. percent-encoded uri, string parts
authorStephen Seo <seo.disparate@gmail.com>
Tue, 24 Sep 2024 04:16:34 +0000 (13:16 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Tue, 24 Sep 2024 05:29:46 +0000 (14:29 +0900)
Resolves https://git.seodisparate.com/stephenseo/c_simple_http/issues/6

src/helpers.c
src/helpers.h
src/http.c
src/test.c

index c8e49d21bb60502aef34877533c7a2a28335d277..bf3a6fddf648237ce5c79d7a64fcc497715cfd2c 100644 (file)
 
 // Standard library includes.
 #include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+int c_simple_http_internal_get_string_part_full_size(void *data, void *ud) {
+  C_SIMPLE_HTTP_String_Part *part = data;
+  size_t *count = ud;
+
+  *count += part->size - 1;
+
+  return 0;
+}
+
+int c_simple_http_internal_combine_string_parts_from_list(void *data,
+                                                          void *ud) {
+  C_SIMPLE_HTTP_String_Part *part = data;
+  void **ptrs = ud;
+  char *buf = ptrs[0];
+  size_t *current_count = ptrs[1];
+  const size_t *total_count = ptrs[2];
+
+  if (!part->buf || part->size == 0) {
+    // Empty string part, just continue.
+    return 0;
+  }
+
+  if (*current_count + part->size - 1 > *total_count) {
+    fprintf(stderr, "ERROR Invalid state combining string parts!\n");
+    return 1;
+  }
+
+  memcpy(buf + *current_count, part->buf, part->size - 1);
+
+  *current_count += part->size - 1;
+
+  return 0;
+}
+
+void c_simple_http_cleanup_attr_string_part(C_SIMPLE_HTTP_String_Part **part) {
+  if (part && *part) {
+    if ((*part)->buf) {
+      free((*part)->buf);
+    }
+    free(*part);
+  }
+  *part = NULL;
+}
+
+void c_simple_http_cleanup_string_part(void *data) {
+  C_SIMPLE_HTTP_String_Part *part = data;
+  if (part) {
+    if (part->buf) {
+      free(part->buf);
+    }
+    free(part);
+  }
+}
+
+void c_simple_http_add_string_part(
+    SDArchiverLinkedList *list, const char *c_string, size_t extra) {
+  C_SIMPLE_HTTP_String_Part *string_part =
+    malloc(sizeof(C_SIMPLE_HTTP_String_Part));
+
+  string_part->size = strlen(c_string) + 1;
+  string_part->buf = malloc(string_part->size);
+  memcpy(string_part->buf, c_string, string_part->size);
+
+  string_part->extra = extra;
+
+  simple_archiver_list_add(
+    list, string_part, c_simple_http_cleanup_string_part);
+}
+
+char *c_simple_http_combine_string_parts(const SDArchiverLinkedList *list) {
+  if (!list || list->count == 0) {
+    return NULL;
+  }
+
+  size_t count = 0;
+
+  simple_archiver_list_get(
+    list, c_simple_http_internal_get_string_part_full_size, &count);
+
+  char *buf = malloc(count + 1);
+  size_t current_count = 0;
+
+  void **ptrs = malloc(sizeof(void*) * 3);
+  ptrs[0] = buf;
+  ptrs[1] = &current_count;
+  ptrs[2] = &count;
+
+  if (simple_archiver_list_get(
+      list, c_simple_http_internal_combine_string_parts_from_list, ptrs)) {
+    free(buf);
+    free(ptrs);
+    return NULL;
+  }
+
+  free(ptrs);
+
+  buf[count] = 0;
+
+  return buf;
+}
 
 void c_simple_http_helper_to_lowercase_in_place(char *buf, size_t size) {
   for (size_t idx = 0; idx < size; ++idx) {
@@ -41,4 +144,76 @@ char *c_simple_http_helper_to_lowercase(const char *buf, size_t size) {
   return ret_buf;
 }
 
+char c_simple_http_helper_hex_to_value(const char upper, const char lower) {
+  char result = 0;
+
+  if (upper >= '0' && upper <= '9') {
+    result |= (char)(upper - '0') << 4;
+  } else if (upper >= 'a' && upper <= 'f') {
+    result |= (char)(upper - 'a' + 10) << 4;
+  } else if (upper >= 'A' && upper <= 'F') {
+    result |= (char)(upper - 'A' + 10) << 4;
+  } else {
+    return 0;
+  }
+
+  if (lower >= '0' && lower <= '9') {
+    result |= lower - '0';
+  } else if (lower >= 'a' && lower <= 'f') {
+    result |= lower - 'a' + 10;
+  } else if (lower >= 'A' && lower <= 'F') {
+    result |= lower - 'A' + 10;
+  } else {
+    return 0;
+  }
+
+  return result;
+}
+
+char *c_simple_http_helper_unescape_uri(const char *uri) {
+  __attribute__((cleanup(simple_archiver_list_free)))
+  SDArchiverLinkedList *parts = simple_archiver_list_init();
+
+  const size_t size = strlen(uri);
+  size_t idx = 0;
+  size_t prev_idx = 0;
+  size_t buf_size;
+  char *buf;
+
+  for (; idx <= size; ++idx) {
+    if (uri[idx] == '%' && idx + 2 < size) {
+      if (idx > prev_idx) {
+        buf_size = idx - prev_idx + 1;
+        buf = malloc(buf_size);
+        memcpy(buf, uri + prev_idx, buf_size - 1);
+        buf[buf_size - 1] = 0;
+        c_simple_http_add_string_part(parts, buf, 0);
+        free(buf);
+      }
+      buf = malloc(2);
+      buf[0] = c_simple_http_helper_hex_to_value(uri[idx + 1], uri[idx + 2]);
+      buf[1] = 0;
+      if (buf[0] == 0) {
+        free(buf);
+        return NULL;
+      }
+      c_simple_http_add_string_part(parts, buf, 0);
+      free(buf);
+      prev_idx = idx + 3;
+      idx += 2;
+    }
+  }
+
+  if (idx > prev_idx) {
+    buf_size = idx - prev_idx + 1;
+    buf = malloc(buf_size);
+    memcpy(buf, uri + prev_idx, buf_size - 1);
+    buf[buf_size - 1] = 0;
+    c_simple_http_add_string_part(parts, buf, 0);
+    free(buf);
+  }
+
+  return c_simple_http_combine_string_parts(parts);
+}
+
 // vim: et ts=2 sts=2 sw=2
index c6743fbf7193f13d132fe67022699de605ea005a..4a638341915a0be2e7b8ad6555fa87a62d9178c2 100644 (file)
 // Standard library includes.
 #include <stddef.h>
 
+// Third-party includes.
+#include <SimpleArchiver/src/data_structures/linked_list.h>
+
+typedef struct C_SIMPLE_HTTP_String_Part {
+  char *buf;
+  size_t size;
+  size_t extra;
+} C_SIMPLE_HTTP_String_Part;
+
+void c_simple_http_cleanup_attr_string_part(C_SIMPLE_HTTP_String_Part **);
+
+/// Assumes "data" is a C_SIMPLE_HTTP_String_Part, "data" was malloced, and
+/// "data->buf" was malloced.
+void c_simple_http_cleanup_string_part(void *data);
+
+/// Puts a malloced instance of String_Part into the list.
+/// The given c_string will be copied into a newly malloced buffer.
+void c_simple_http_add_string_part(
+  SDArchiverLinkedList *list, const char *c_string, size_t extra);
+
+/// Combines all String_Parts in the list and returns it as a single buffer.
+char *c_simple_http_combine_string_parts(const SDArchiverLinkedList *list);
+
 /// Modifies "buf" in-place to change all uppercase to lowercase alpha chars.
 void c_simple_http_helper_to_lowercase_in_place(char *buf, size_t size);
 
@@ -27,6 +50,13 @@ void c_simple_http_helper_to_lowercase_in_place(char *buf, size_t size);
 /// uppercase to lowercase alpha chars.
 char *c_simple_http_helper_to_lowercase(const char *buf, size_t size);
 
+/// Converts two hexadecimal digits into its corresponding value.
+char c_simple_http_helper_hex_to_value(const char upper, const char lower);
+
+/// Unescapes percent-encoded parts in the given uri string. If this returns
+/// non-NULL, it must be free'd.
+char *c_simple_http_helper_unescape_uri(const char *uri);
+
 #endif
 
 // vim: et ts=2 sts=2 sw=2
index f79797d942e1012d91ccb647eb7b9f167fd7409f..710ea73f0711c407fbc0e190f6e2b1aa4d171136 100644 (file)
@@ -25,7 +25,6 @@
 #include <SimpleArchiver/src/helpers.h>
 
 // Local includes
-#include "constants.h"
 #include "http_template.h"
 #include "helpers.h"
 
@@ -111,6 +110,13 @@ char *c_simple_http_request_response(
   }
 #ifndef NDEBUG
   fprintf(stderr, "Parsing request: got path \"%s\"\n", request_path);
+#endif
+  __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
+  char *request_path_unescaped =
+    c_simple_http_helper_unescape_uri(request_path);
+#ifndef NDEBUG
+  fprintf(
+    stderr, "Parsing request: unescaped path \"%s\"\n", request_path_unescaped);
 #endif
   // skip whitespace until next part.
   for (; idx < size
@@ -158,9 +164,9 @@ char *c_simple_http_request_response(
   size_t generated_size = 0;
   __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
   char *stripped_path = c_simple_http_strip_path(
-    request_path, request_path_idx);
+    request_path_unescaped, strlen(request_path_unescaped));
   char *generated_buf = c_simple_http_path_to_generated(
-    stripped_path ? stripped_path : request_path,
+    stripped_path ? stripped_path : request_path_unescaped,
     templates,
     &generated_size);
 
@@ -171,8 +177,10 @@ char *c_simple_http_request_response(
       if (
           simple_archiver_hash_map_get(
             templates->hash_map,
-            stripped_path ? stripped_path : request_path,
-            stripped_path ? strlen(stripped_path) + 1 : request_path_idx + 1)
+            stripped_path ? stripped_path : request_path_unescaped,
+            stripped_path
+              ? strlen(stripped_path) + 1
+              : strlen(request_path_unescaped) + 1)
           == NULL) {
         *out_response_code = C_SIMPLE_HTTP_Response_404_Not_Found;
       } else {
index 66c4d0e0dc6e6d4392dc164122af8da379217216..4feb9c4d9e592499931dabd7e064e30f51a5df4f 100644 (file)
@@ -8,6 +8,7 @@
 #include "config.h"
 #include "http_template.h"
 #include "http.h"
+#include "helpers.h"
 
 // Third party includes.
 #include <SimpleArchiver/src/helpers.h>
@@ -503,6 +504,46 @@ int main(void) {
     free(stripped_path_buf);
   }
 
+  // Test helpers.
+  {
+    __attribute__((cleanup(simple_archiver_list_free)))
+    SDArchiverLinkedList *list = simple_archiver_list_init();
+
+    c_simple_http_add_string_part(list, "one\n", 0);
+    c_simple_http_add_string_part(list, "two\n", 0);
+    c_simple_http_add_string_part(list, "three\n", 0);
+
+    __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
+    char *buf = c_simple_http_combine_string_parts(list);
+    ASSERT_TRUE(buf);
+    ASSERT_TRUE(strcmp(buf, "one\ntwo\nthree\n") == 0);
+    free(buf);
+    buf = NULL;
+
+    char hex_result = c_simple_http_helper_hex_to_value('2', 'f');
+    CHECK_TRUE(hex_result == '/');
+    hex_result = c_simple_http_helper_hex_to_value('2', 'F');
+    CHECK_TRUE(hex_result == '/');
+
+    hex_result = c_simple_http_helper_hex_to_value('7', 'a');
+    CHECK_TRUE(hex_result == 'z');
+    hex_result = c_simple_http_helper_hex_to_value('7', 'A');
+    CHECK_TRUE(hex_result == 'z');
+
+    hex_result = c_simple_http_helper_hex_to_value('4', '1');
+    CHECK_TRUE(hex_result == 'A');
+
+    buf = c_simple_http_helper_unescape_uri("%2fderp%2Fdoop%21");
+    CHECK_TRUE(strcmp(buf, "/derp/doop!") == 0);
+    free(buf);
+    buf = NULL;
+
+    buf = c_simple_http_helper_unescape_uri("%41%42%43%25%5A%5a");
+    CHECK_TRUE(strcmp(buf, "ABC%ZZ") == 0);
+    free(buf);
+    buf = NULL;
+  }
+
   RETURN()
 }