1 /** @file
2  * @brief HTTP compression handling functions
3  *
4  * Helper functions to handle compression formats
5  */
6 
7 /*
8  * Copyright (c) 2025 Endress+Hauser
9  *
10  * SPDX-License-Identifier: Apache-2.0
11  */
12 #include <stdbool.h>
13 #include <string.h>
14 #include <strings.h>
15 
16 #include <zephyr/net/http/server.h>
17 #include <zephyr/sys/__assert.h>
18 #include <zephyr/sys/util.h>
19 
20 #include "headers/server_internal.h"
21 
22 static int http_compression_match(enum http_compression *compression, const char *text,
23 				  enum http_compression expected);
24 
http_compression_parse_accept_encoding(const char * accept_encoding,size_t len,uint8_t * supported_compression)25 void http_compression_parse_accept_encoding(const char *accept_encoding, size_t len,
26 					    uint8_t *supported_compression)
27 {
28 	enum http_compression detected_compression;
29 	char strbuf[HTTP_COMPRESSION_MAX_STRING_LEN + 1] = {0};
30 	const char *start = accept_encoding;
31 	const char *end = NULL;
32 	bool priority_string = false;
33 
34 	*supported_compression = HTTP_NONE;
35 	for (size_t i = 0; i < len; i++) {
36 		if (accept_encoding[i] == 0) {
37 			break;
38 		} else if (accept_encoding[i] == ' ') {
39 			start = &accept_encoding[i + 1];
40 			continue;
41 		} else if (accept_encoding[i] == ';') {
42 			end = &accept_encoding[i];
43 			priority_string = true;
44 		} else if (accept_encoding[i] == ',') {
45 			if (!priority_string) {
46 				end = &accept_encoding[i];
47 			}
48 			priority_string = false;
49 		} else if (i + 1 == len) {
50 			end = &accept_encoding[i + 1];
51 		}
52 
53 		if (end == NULL) {
54 			continue;
55 		}
56 
57 		if (end - start > HTTP_COMPRESSION_MAX_STRING_LEN) {
58 			end = NULL;
59 			start = end + 1;
60 			continue;
61 		}
62 
63 		memcpy(strbuf, start, end - start);
64 		strbuf[end - start] = 0;
65 
66 		if (http_compression_from_text(&detected_compression, strbuf) == 0) {
67 			WRITE_BIT(*supported_compression, detected_compression, true);
68 		}
69 
70 		end = NULL;
71 		start = end + 1;
72 	}
73 }
74 
http_compression_text(enum http_compression compression)75 const char *http_compression_text(enum http_compression compression)
76 {
77 	switch (compression) {
78 	case HTTP_NONE:
79 		return "";
80 	case HTTP_GZIP:
81 		return "gzip";
82 	case HTTP_COMPRESS:
83 		return "compress";
84 	case HTTP_DEFLATE:
85 		return "deflate";
86 	case HTTP_BR:
87 		return "br";
88 	case HTTP_ZSTD:
89 		return "zstd";
90 	}
91 	return "";
92 }
93 
http_compression_from_text(enum http_compression * compression,const char * text)94 int http_compression_from_text(enum http_compression *compression, const char *text)
95 {
96 	__ASSERT_NO_MSG(compression);
97 	__ASSERT_NO_MSG(text);
98 
99 	for (enum http_compression i = 0; compression_value_is_valid(i); ++i) {
100 		if (http_compression_match(compression, text, i) == 0) {
101 			return 0;
102 		}
103 	}
104 	return 1;
105 }
106 
compression_value_is_valid(enum http_compression compression)107 bool compression_value_is_valid(enum http_compression compression)
108 {
109 	switch (compression) {
110 	case HTTP_NONE:
111 	case HTTP_GZIP:
112 	case HTTP_COMPRESS:
113 	case HTTP_DEFLATE:
114 	case HTTP_BR:
115 	case HTTP_ZSTD:
116 		return true;
117 	}
118 	return false;
119 }
120 
http_compression_match(enum http_compression * compression,const char * text,enum http_compression expected)121 static int http_compression_match(enum http_compression *compression, const char *text,
122 				  enum http_compression expected)
123 {
124 	__ASSERT_NO_MSG(compression);
125 	__ASSERT_NO_MSG(text);
126 
127 	if (strcasecmp(http_compression_text(expected), text) == 0) {
128 		*compression = expected;
129 		return 0;
130 	} else {
131 		return 1;
132 	}
133 }
134