master
h 221 lines 5.86 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #ifndef NETDATA_WEBSOCKET_BUFFER_H
4 #define NETDATA_WEBSOCKET_BUFFER_H
5
6 #include "websocket-internal.h"
7
8 ALWAYS_INLINE
9 static void websocket_unmask(char *dst, const char *src, size_t length, const unsigned char *mask_key) {
10 for (size_t i = 0; i < length; i++)
11 dst[i] = (char)((unsigned char)src[i] ^ mask_key[i % 4]);
12 }
13
14 // Initialize an already allocated buffer structure
15 ALWAYS_INLINE
16 static void wsb_init(WS_BUF *wsb, size_t initial_size) {
17 if (!wsb) return;
18 wsb->data = mallocz(initial_size);
19 wsb->size = initial_size;
20 wsb->length = 0;
21 }
22
23 // Clean up an embedded buffer (free data but not the buffer structure itself)
24 ALWAYS_INLINE
25 static void wsb_cleanup(WS_BUF *wsb) {
26 if (!wsb) return;
27 freez(wsb->data);
28 wsb->data = NULL;
29 wsb->size = 0;
30 wsb->length = 0;
31 }
32
33 // Initialize buffer structure
34 ALWAYS_INLINE
35 static WS_BUF *wsb_create(size_t initial_size) {
36 WS_BUF *buffer = mallocz(sizeof(WS_BUF));
37 wsb_init(buffer, MAX(initial_size, 1024));
38 return buffer;
39 }
40
41 // Free buffer structure
42 ALWAYS_INLINE
43 static void wsb_free(WS_BUF *wsb) {
44 if (!wsb) return;
45 wsb_cleanup(wsb);
46 freez(wsb);
47 }
48
49 // Resize buffer to a new size
50 ALWAYS_INLINE
51 static void wsb_resize(WS_BUF *wsb, size_t new_size) {
52 if (new_size <= wsb->size) return;
53 wsb->data = reallocz(wsb->data, new_size);
54 wsb->size = new_size;
55 }
56
57 // Append data to buffer
58 ALWAYS_INLINE
59 static void wsb_need_bytes(WS_BUF *wsb, size_t bytes) {
60 if (!wsb) return;
61
62 // 1 for null + 4 for the final decompression padding
63 size_t wanted_size = wsb->length + bytes + 1 + 4;
64 if (wanted_size < wsb->size)
65 return;
66
67 size_t new_size = wsb->size * 2;
68 if (new_size < wanted_size)
69 new_size = wanted_size;
70
71 wsb_resize(wsb, new_size);
72 }
73
74 // Reset buffer
75 ALWAYS_INLINE
76 static void wsb_reset(WS_BUF *wsb) {
77 if (!wsb) return;
78 wsb->length = 0;
79 }
80
81 // Ensure buffer has null termination for text data
82 ALWAYS_INLINE
83 static void wsb_null_terminate(WS_BUF *wsb) {
84 if (!wsb) return;
85 wsb_need_bytes(wsb, 1);
86 wsb->data[wsb->length] = '\0';
87 }
88
89 // Check if buffer is empty
90 ALWAYS_INLINE
91 static bool wsb_is_empty(const WS_BUF *wsb) {
92 return (!wsb || wsb->length == 0);
93 }
94
95 // Check if buffer has data
96 ALWAYS_INLINE
97 static bool wsb_has_data(const WS_BUF *wsb) {
98 return (wsb && wsb->data && wsb->length > 0);
99 }
100
101 // Get pointer to buffer data
102 ALWAYS_INLINE
103 static char *wsb_data(WS_BUF *wsb) {
104 return wsb ? wsb->data : NULL;
105 }
106
107 // Get current buffer length
108 ALWAYS_INLINE
109 static size_t wsb_length(const WS_BUF *wsb) {
110 return wsb ? wsb->length : 0;
111 }
112
113 // Get allocated buffer size
114 ALWAYS_INLINE
115 static size_t wsb_size(const WS_BUF *wsb) {
116 return wsb ? wsb->size : 0;
117 }
118
119 // Set buffer length (must be <= buffer size)
120 ALWAYS_INLINE
121 static void wsb_set_length(WS_BUF *wsb, size_t length) {
122 if (!wsb) return;
123
124 if (length > wsb->size)
125 fatal("WEBSOCKET: trying to set length to %zu, but buffer size is %zu", length, wsb->size);
126
127 wsb->length = length;
128 }
129
130 // Append data to a buffer
131 ALWAYS_INLINE
132 static char *wsb_append(WS_BUF *wsb, const void *data, size_t length) {
133 if (!wsb || !data || !length)
134 return NULL;
135
136 // Ensure buffer is large enough
137 wsb_need_bytes(wsb, length);
138
139 char *dst = wsb->data + wsb->length;
140
141 // Copy data to end of buffer
142 memcpy(dst, data, length);
143
144 // Update length
145 wsb->length += length;
146
147 return dst;
148 }
149
150 // Unmask and append binary data to a buffer, returns pointer to beginning of the unmasked data
151 ALWAYS_INLINE
152 static char *wsb_unmask_and_append(WS_BUF *wsb, const void *masked_data,
153 size_t length, const unsigned char *mask_key) {
154 if (!wsb || !masked_data || !length || !mask_key)
155 return NULL;
156
157 // Ensure buffer is large enough for the new data
158 wsb_need_bytes(wsb, length);
159
160 // Get a pointer to the destination in the expanded buffer
161 char *dst = wsb->data + wsb->length;
162
163 // Unmask the data directly into the buffer by calling websocket_unmask
164 websocket_unmask(dst, (const char *)masked_data, length, mask_key);
165
166 // Update buffer length
167 wsb->length += length;
168
169 return dst;
170 }
171
172 // Append data to a buffer but don't change the length (use for padding)
173 // Returns pointer to beginning of the appended data area
174 ALWAYS_INLINE
175 static char *wsb_append_padding(WS_BUF *wsb, const void *data, size_t length) {
176 if (!wsb || !data || !length)
177 return NULL;
178
179 // Ensure buffer is large enough for the new data
180 wsb_need_bytes(wsb, length);
181
182 // Get pointer to where the data will be stored
183 char *dst = wsb->data + wsb->length;
184
185 // Copy data to end of buffer
186 memcpy(dst, data, length);
187
188 // Don't update length - this is the difference from wsb_append()
189 // This allows adding "padding" data after the logical end of the buffer
190
191 return dst;
192 }
193
194 // Remove bytes from the front of the buffer, shifting remaining content forward
195 // Returns the number of bytes actually trimmed (may be less than requested if buffer is smaller)
196 ALWAYS_INLINE
197 static size_t wsb_trim_front(WS_BUF *wsb, size_t bytes_to_trim) {
198 if (!wsb || !wsb->data || bytes_to_trim == 0 || wsb->length == 0)
199 return 0;
200
201 // Cap the trim size to the actual buffer length
202 size_t actual_trim = (bytes_to_trim > wsb->length) ? wsb->length : bytes_to_trim;
203
204 if (actual_trim < wsb->length) {
205 // More data in buffer - shift remaining data to beginning
206 size_t remaining = wsb->length - actual_trim;
207
208 // Shift the remaining data to the beginning of the buffer
209 memmove(wsb->data, wsb->data + actual_trim, remaining);
210
211 // Update buffer length to reflect the shift
212 wsb->length = remaining;
213 } else {
214 // All data was trimmed or the buffer is empty - reset length
215 wsb->length = 0;
216 }
217
218 return actual_trim;
219 }
220
221 #endif //NETDATA_WEBSOCKET_BUFFER_H