comparison seobeo/s_web.c @ 231:09a96dcb2b4c hg-web

[merge] Join existing hg-web branch head
author MrJuneJune <me@mrjunejune.com>
date Sun, 02 Aug 2026 16:50:48 -0700
parents 0e7b9464248d
children 543df0fe7168
comparison
equal deleted inserted replaced
217:7ef4c9d2a72d 231:09a96dcb2b4c
49 const char *status_text; 49 const char *status_text;
50 switch(status) 50 switch(status)
51 { 51 {
52 case HTTP_OK: status_text = "OK"; break; 52 case HTTP_OK: status_text = "OK"; break;
53 case HTTP_CREATED: status_text = "Created"; break; 53 case HTTP_CREATED: status_text = "Created"; break;
54 case HTTP_NO_CONTENT: status_text = "No Content"; break;
54 case HTTP_MOVED_PERMANENTLY: status_text = "Moved Permanently"; break; 55 case HTTP_MOVED_PERMANENTLY: status_text = "Moved Permanently"; break;
55 case HTTP_FOUND: status_text = "Found"; break; 56 case HTTP_FOUND: status_text = "Found"; break;
56 case HTTP_BAD_REQUEST: status_text = "Bad Request"; break; 57 case HTTP_BAD_REQUEST: status_text = "Bad Request"; break;
57 case HTTP_UNAUTHORIZED: status_text = "Unauthorized"; break; 58 case HTTP_UNAUTHORIZED: status_text = "Unauthorized"; break;
58 case HTTP_FORBIDDEN: status_text = "Forbidden"; break; 59 case HTTP_FORBIDDEN: status_text = "Forbidden"; break;
59 case HTTP_NOT_FOUND: status_text = "Not Found"; break; 60 case HTTP_NOT_FOUND: status_text = "Not Found"; break;
60 case HTTP_INTERNAL_ERROR: status_text = "Internal Server Error"; break; 61 case HTTP_INTERNAL_ERROR: status_text = "Internal Server Error"; break;
62 case 502: status_text = "Bad Gateway"; break;
63 case 504: status_text = "Gateway Timeout"; break;
61 default: status_text = "Unknown"; break; 64 default: status_text = "Unknown"; break;
62 } 65 }
63 66
64 sprintf( 67 snprintf(
65 buffer, 68 (char*)buffer, 1024,
66 "HTTP/1.1 %d %s\r\n" 69 "HTTP/1.1 %d %s\r\n"
67 "Content-Type: %s\r\n" 70 "Content-Type: %s\r\n"
68 "Content-Length: %d\r\n" 71 "Content-Length: %d\r\n"
69 "Connection: %s\r\n" 72 "Connection: %s\r\n"
70 "\r\n", 73 "\r\n",
772 Dowa_Arena *p_arena) 775 Dowa_Arena *p_arena)
773 { 776 {
774 Seobeo_Router_Send_Response_KeepAlive(p_handle, p_response_map, p_arena, FALSE); 777 Seobeo_Router_Send_Response_KeepAlive(p_handle, p_response_map, p_arena, FALSE);
775 } 778 }
776 779
780 static boolean Seobeo_Response_Header_Value_Is_Safe(const char *value)
781 {
782 return value && strchr(value, '\r') == NULL && strchr(value, '\n') == NULL;
783 }
784
777 void Seobeo_Router_Send_Response_KeepAlive( 785 void Seobeo_Router_Send_Response_KeepAlive(
778 Seobeo_Handle *p_handle, 786 Seobeo_Handle *p_handle,
779 Seobeo_Request_Entry *p_response_map, 787 Seobeo_Request_Entry *p_response_map,
780 Dowa_Arena *p_arena, 788 Dowa_Arena *p_arena,
781 boolean keep_alive) 789 boolean keep_alive)
817 body_length = atoi(content_length_str); 825 body_length = atoi(content_length_str);
818 } 826 }
819 else 827 else
820 body_length = strlen(body); 828 body_length = strlen(body);
821 829
822 char *header = Dowa_Arena_Allocate(p_arena, 4096); 830 size_t header_capacity = 1024;
823 Seobeo_Web_Header_Generate_KeepAlive(header, status, content_type, body_length, keep_alive);
824 for (int i = 0; i < Dowa_Array_Length(p_response_map); i++) 831 for (int i = 0; i < Dowa_Array_Length(p_response_map); i++)
825 { 832 {
833 const char *key = p_response_map[i].key;
834 const char *value = p_response_map[i].value;
826 if ( 835 if (
827 strstr(p_response_map[i].key, "status") || 836 strcasecmp(key, "status") == 0 ||
828 strstr(p_response_map[i].key, "body") || 837 strcasecmp(key, "body") == 0 ||
829 strstr(p_response_map[i].key, "content-type") || 838 strcasecmp(key, "content-type") == 0 ||
830 strstr(p_response_map[i].key, "content-length") 839 strcasecmp(key, "content-length") == 0
831 ) 840 )
832 continue; 841 continue;
833 842 if (Seobeo_Response_Header_Value_Is_Safe(key) &&
834 int32 current_header_len = strlen(header); 843 Seobeo_Response_Header_Value_Is_Safe(value))
835 char *temp = malloc(sizeof(char) * 1024); 844 header_capacity += strlen(key) + strlen(value) + 4;
836 sprintf(temp, "%s: %s\r\n\r\n", p_response_map[i].key, p_response_map[i].value); 845 }
837 memcpy(&header[current_header_len - 2 /* \r\n */], temp, strlen(temp)); 846
838 free(temp); 847 char *header = Dowa_Arena_Allocate(p_arena, header_capacity);
839 } 848 Seobeo_Web_Header_Generate_KeepAlive(header, status, content_type, body_length, keep_alive);
840 849 size_t header_length = strlen(header);
841 printf("hEADER %s\n", header); 850 if (header_length < 2)
851 return;
852 header_length -= 2;
853
854 for (int i = 0; i < Dowa_Array_Length(p_response_map); i++)
855 {
856 const char *key = p_response_map[i].key;
857 const char *value = p_response_map[i].value;
858 if (
859 strcasecmp(key, "status") == 0 ||
860 strcasecmp(key, "body") == 0 ||
861 strcasecmp(key, "content-type") == 0 ||
862 strcasecmp(key, "content-length") == 0
863 )
864 continue;
865
866 if (!Seobeo_Response_Header_Value_Is_Safe(key) ||
867 !Seobeo_Response_Header_Value_Is_Safe(value))
868 {
869 Seobeo_Log(SEOBEO_WARNING, "Skipping unsafe response header\n");
870 continue;
871 }
872
873 int written = snprintf(
874 header + header_length,
875 header_capacity - header_length,
876 "%s: %s\r\n",
877 key,
878 value);
879 if (written < 0 || (size_t)written >= header_capacity - header_length)
880 {
881 Seobeo_Log(SEOBEO_ERROR, "Response header exceeded allocated capacity\n");
882 return;
883 }
884 header_length += (size_t)written;
885 }
886 memcpy(header + header_length, "\r\n", 3);
842 887
843 Seobeo_Handle_Queue(p_handle, (uint8_t*)header, strlen(header)); 888 Seobeo_Handle_Queue(p_handle, (uint8_t*)header, strlen(header));
844 Seobeo_Handle_Queue(p_handle, (uint8_t*)body, body_length); 889 Seobeo_Handle_Queue(p_handle, (uint8_t*)body, body_length);
845 Seobeo_Handle_Flush(p_handle); 890 Seobeo_Handle_Flush(p_handle);
846 } 891 }