Project
Loading...
Searching...
No Matches
HTTPParser.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
12#include "HTTPParser.h"
14#include <string_view>
15#include "Framework/SHA1.h"
16#include "Base64.h"
17#include <regex>
18#include <cassert>
19
20using namespace o2::framework::internal;
21namespace o2::framework
22{
23
24namespace
25{
27void memmask(char* dst, char const* src, size_t size, uint32_t mask)
28{
29 if (mask) {
30 char* m = (char*)&mask;
31 for (size_t len = 0; len < size; ++len) {
32 *dst++ = *src++ ^ m[len % 4];
33 }
34 } else {
35 memcpy(dst, src, size);
36 }
37}
38
39void memunmask(char* data, size_t size, uint32_t mask)
40{
41 char* m = (char*)&mask;
42 for (size_t len = 0; len < size; ++len) {
43 *data++ ^= m[len % 4];
44 }
45}
46} // namespace
47
48void encode_websocket_frames(std::vector<uv_buf_t>& outputs, char const* src, size_t size, WebSocketOpCode opcode, uint32_t mask)
49{
50 void* finalHeader;
51 size_t headerSize;
52 char* buffer = nullptr;
53 char* startPayload = nullptr;
54 int maskSize = mask ? 4 : 0;
55
56 if (size < 126) {
57 headerSize = sizeof(WebSocketFrameTiny);
58 // Allocate a new page if we do not fit in the current one
59 if (outputs.empty() || outputs.back().len > WebSocketConstants::MaxChunkSize || (size + maskSize + headerSize) > (WebSocketConstants::MaxChunkSize - outputs.back().len)) {
60 char* chunk = (char*)malloc(WebSocketConstants::MaxChunkSize);
61 outputs.push_back(uv_buf_init(chunk, 0));
62 }
63 auto& buf = outputs.back();
64 // Reposition the buffer to the end of the current page
65 buffer = buf.base + buf.len;
66 buf.len += headerSize + size + maskSize;
67 WebSocketFrameTiny* header = (WebSocketFrameTiny*)buffer;
68 memset(buffer, 0, headerSize);
69 header->len = size;
70 } else if (size < 1 << 16) {
71 headerSize = sizeof(WebSocketFrameShort);
72 // Allocate a new page if we do not fit in the current one
73 if (outputs.empty() || outputs.back().len > WebSocketConstants::MaxChunkSize || (size + maskSize + headerSize) > (WebSocketConstants::MaxChunkSize - outputs.back().len)) {
74 char* chunk = (char*)malloc(WebSocketConstants::MaxChunkSize);
75 outputs.push_back(uv_buf_init(chunk, 0));
76 }
77 auto& buf = outputs.back();
78 // Reposition the buffer to the end of the current page
79 buffer = buf.base + buf.len;
80 buf.len += headerSize + size + maskSize;
81 WebSocketFrameShort* header = (WebSocketFrameShort*)buffer;
82 memset(buffer, 0, headerSize);
83 header->len = 126;
84 header->len16 = htons(size);
85 } else {
86 // For larger messages we do standalone allocation
87 // so that the message does not need to be sent in multiple chunks
88 headerSize = sizeof(WebSocketFrameHuge);
89 buffer = (char*)malloc(headerSize + maskSize + size);
90 WebSocketFrameHuge* header = (WebSocketFrameHuge*)buffer;
91 memset(buffer, 0, headerSize);
92 header->len = 127;
93 header->len64 = htonll(size);
94 outputs.push_back(uv_buf_init(buffer, size + maskSize + headerSize));
95 }
96 size_t fullHeaderSize = maskSize + headerSize;
97 startPayload = buffer + fullHeaderSize;
98 WebSocketFrameTiny* header = (WebSocketFrameTiny*)buffer;
99 header->fin = 1;
100 header->opcode = (unsigned char)opcode; // binary or text for now
101 // Mask is right before payload.
102 if (mask) {
103 *((uint32_t*)(startPayload - 4)) = mask;
104 }
105 header->mask = mask ? 1 : 0;
106 memmask(startPayload, src, size, mask);
107}
108
109void decode_websocket(char* start, size_t size, WebSocketHandler& handler)
110{
111 // Handle the case in whiche the header is cut
112 if (handler.pendingHeaderSize) {
113 assert(handler.pendingHeader);
114 size_t pendingFullSize = handler.pendingHeaderSize + size;
115 char* pendingFull = new char[handler.pendingHeaderSize + size];
116 memcpy(pendingFull, handler.pendingHeader, handler.pendingHeaderSize);
117 memcpy(pendingFull + handler.pendingHeaderSize, start, size);
118 // We do not need the intermediate buffer anymore.
119 handler.pendingHeaderSize = 0;
120 delete[] handler.pendingHeader;
121 handler.pendingHeader = nullptr;
122 decode_websocket(pendingFull, pendingFullSize, handler);
123 delete[] pendingFull;
124 return;
125 }
126
127 // Handle the case the previous message was cut in half
128 // by the I/O stack.
129 char* cur = start + handler.remainingSize;
130 if (handler.remainingSize) {
131 assert(handler.pendingBuffer);
132 auto newChunkSize = std::min(handler.remainingSize, size);
133 memcpy(handler.pendingBuffer + handler.pendingSize, start, newChunkSize);
134 handler.pendingSize += newChunkSize;
135 handler.remainingSize -= newChunkSize;
136 if (handler.remainingSize == 0) {
137 // One recursion should be enough.
138 decode_websocket(handler.pendingBuffer, handler.pendingSize, handler);
139 delete[] handler.pendingBuffer;
140 handler.pendingBuffer = nullptr;
141 }
142 }
143 handler.beginChunk();
144 // The + 2 is there because we need at least 2 bytes.
145 while (cur - start < size) {
146 WebSocketFrameTiny* header = (WebSocketFrameTiny*)cur;
147 size_t payloadSize = 0;
148 size_t headerSize = 0;
149 if ((cur + 2 - start >= size) ||
150 ((cur + 2 + 2 - start >= size) && header->len >= 126) ||
151 ((cur + 2 + 8 - start >= size) && header->len == 127)) {
152 // We do not have enough bytes for a tiny header. We copy in the pending header
153 handler.pendingHeaderSize = size - (cur - start);
154 handler.pendingHeader = new char[handler.pendingHeaderSize];
155 memcpy(handler.pendingHeader, cur, handler.pendingHeaderSize);
156 break;
157 }
158
159 if (header->len < 126) {
160 payloadSize = header->len;
161 headerSize = 2 + (header->mask ? 4 : 0);
162 } else if (header->len == 126) {
163 WebSocketFrameShort* headerSmall = (WebSocketFrameShort*)cur;
164 payloadSize = ntohs(headerSmall->len16);
165 headerSize = 2 + 2 + (header->mask ? 4 : 0);
166 } else if (header->len == 127) {
167 WebSocketFrameHuge* headerSmall = (WebSocketFrameHuge*)cur;
168 payloadSize = ntohll(headerSmall->len64);
169 headerSize = 2 + 8 + (header->mask ? 4 : 0);
170 }
171 size_t availableSize = size - (cur - start);
172 if (availableSize < payloadSize + headerSize) {
173 handler.remainingSize = payloadSize + headerSize - availableSize;
174 handler.pendingSize = availableSize;
175 handler.pendingBuffer = new char[payloadSize + headerSize];
176 memcpy(handler.pendingBuffer, cur, availableSize);
177 break;
178 }
179 if (header->mask) {
180 int32_t mask = *(int32_t*)(cur + headerSize - 4);
181 memunmask(cur + headerSize, payloadSize, mask);
182 }
183 handler.frame(cur + headerSize, payloadSize);
184 cur += headerSize + payloadSize;
185 }
186 handler.endChunk();
187}
188
189std::string encode_websocket_handshake_request(const char* endpoint, const char* protocol, int version, char const* nonce,
190 std::vector<std::pair<std::string, std::string>> headers)
191{
192 constexpr auto res =
193 "GET {} HTTP/1.1\r\n"
194 "Upgrade: websocket\r\n"
195 "Connection: Upgrade\r\n"
196 "Sec-WebSocket-Key: {}\r\n"
197 "Sec-WebSocket-Protocol: {}\r\n"
198 "Sec-WebSocket-Version: {}\r\n"
199 "{}\r\n";
200 std::string encodedHeaders;
201 for (auto [k, v] : headers) {
202 encodedHeaders += std::string(fmt::format("{}: {}\r\n", k, v));
203 }
204 return fmt::format(res, endpoint, nonce, protocol, version, encodedHeaders);
205}
206
207std::string HTTPParserHelpers::calculateAccept(const char* nonce)
208{
209 std::string reply = std::string(nonce) + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
210 char sha[21];
211 SHA1(sha, reply.data(), reply.size());
212 char base[64];
213 base64_encode(base, 64, (unsigned char*)sha, 20);
214 return fmt::format("{}", base);
215}
216
217std::string encode_websocket_handshake_reply(char const* nonce, const char* protocol)
218{
219 constexpr auto res =
220 "HTTP/1.1 101 Switching Protocols\r\n"
221 "Upgrade: websocket\r\n"
222 "Connection: Upgrade\r\n"
223 "Access-Control-Allow-Origin: \"*\"\r\n"
224 "{}"
225 "Sec-WebSocket-Accept: {}\r\n\r\n";
226 return fmt::format(res, protocol && protocol[0] ? fmt::format("Sec-WebSocket-Protocol: {}\r\n", protocol) : "", HTTPParserHelpers::calculateAccept(nonce));
227}
228
229void parse_http_request(char* start, size_t size, HTTPParser* parser)
230{
232 // Too short, let's try again...
233 if (size < 2) {
234 parser->remaining += std::string_view(start, size);
235 }
236 char* cur = start;
237 char* next = cur;
238 std::string_view lastToken(cur, 0);
239 std::string_view lastKey;
240 std::string_view lastValue;
241 std::string lastError;
242 if (parser->states.empty()) {
243 parser->states.push_back(HTTPState::IN_START);
244 }
245 char const* delimiters = nullptr;
246 char const* skippable = nullptr;
247 char const* separator = nullptr;
248 char const* spaces = "\t \v";
249 char const* colon = ":";
250 char const* newline = "\r\n";
251 bool done = false;
252
253 while (!done) {
254 HTTPState state = parser->states.back();
255 parser->states.pop_back();
256 switch (state) {
258 parser->states.push_back(HTTPState::BEGIN_METHOD);
259 break;
261 parser->states.push_back(HTTPState::BEGIN_REPLY_VERSION);
262 break;
264 parser->states.push_back(HTTPState::END_REPLY_VERSION);
265 parser->states.push_back(HTTPState::IN_CAPTURE_DELIMITERS);
266 delimiters = spaces;
267 break;
269 parser->replyVersion(lastToken);
270 parser->states.push_back(HTTPState::BEGIN_REPLY_CODE);
271 parser->states.push_back(HTTPState::IN_SKIP_CHARS);
272 skippable = spaces;
273 break;
275 parser->states.push_back(HTTPState::END_REPLY_CODE);
276 parser->states.push_back(HTTPState::IN_CAPTURE_DELIMITERS);
277 delimiters = spaces;
278 break;
280 parser->replyCode(lastToken);
281 parser->states.push_back(HTTPState::BEGIN_REPLY_MESSAGE);
282 parser->states.push_back(HTTPState::IN_SKIP_CHARS);
283 skippable = spaces;
284 break;
286 parser->states.push_back(HTTPState::END_REPLY_MESSAGE);
287 parser->states.push_back(HTTPState::IN_CAPTURE_SEPARATOR);
288 separator = newline;
289 break;
291 parser->replyMessage(lastToken);
292 parser->states.push_back(HTTPState::BEGIN_HEADERS);
293 break;
295 parser->states.push_back(HTTPState::END_METHOD);
296 parser->states.push_back(HTTPState::IN_CAPTURE_DELIMITERS);
297 delimiters = spaces;
298 break;
300 parser->method(lastToken);
301 parser->states.push_back(HTTPState::BEGIN_TARGET);
302 parser->states.push_back(HTTPState::IN_SKIP_CHARS);
303 skippable = spaces;
304 break;
306 parser->states.push_back(HTTPState::END_TARGET);
307 parser->states.push_back(HTTPState::IN_CAPTURE_DELIMITERS);
308 delimiters = spaces;
309 break;
311 parser->target(lastToken);
312 parser->states.push_back(HTTPState::BEGIN_VERSION);
313 parser->states.push_back(HTTPState::IN_SKIP_CHARS);
314 skippable = spaces;
315 break;
317 parser->states.push_back(HTTPState::END_VERSION);
318 parser->states.push_back(HTTPState::IN_CAPTURE_SEPARATOR);
319 separator = newline;
320 break;
322 parser->version(lastToken);
323 parser->states.push_back(HTTPState::BEGIN_HEADERS);
324 break;
326 parser->states.push_back(HTTPState::BEGIN_HEADER);
327 break;
329 parser->states.push_back(HTTPState::BEGIN_HEADER_KEY);
330 break;
332 parser->states.push_back(HTTPState::END_HEADER_KEY);
333 parser->states.push_back(HTTPState::IN_CAPTURE_SEPARATOR);
334 separator = colon;
335 break;
337 lastKey = lastToken;
338 parser->states.push_back(HTTPState::BEGIN_HEADER_VALUE);
339 parser->states.push_back(HTTPState::IN_SKIP_CHARS);
340 skippable = spaces;
341 break;
343 parser->states.push_back(HTTPState::END_HEADER_VALUE);
344 parser->states.push_back(HTTPState::IN_CAPTURE_SEPARATOR);
345 separator = newline;
346 break;
348 lastValue = lastToken;
349 parser->states.push_back(HTTPState::END_HEADER);
350 break;
352 if (strncmp("\r\n", next, 2) == 0) {
353 parser->header(lastKey, lastValue);
354 parser->states.push_back(HTTPState::END_HEADERS);
355 next += 2;
356 cur = next;
357 } else {
358 parser->header(lastKey, lastValue);
359 parser->states.push_back(HTTPState::BEGIN_HEADER);
360 cur = next;
361 }
362 break;
364 parser->endHeaders();
365 parser->states.push_back(HTTPState::BEGIN_BODY);
366 break;
368 size_t bodySize = size - (cur - start);
369 parser->body(cur, bodySize);
370 next = cur + bodySize;
371 cur = next;
372 parser->states.push_back(HTTPState::BEGIN_BODY);
373 parser->states.push_back(HTTPState::IN_DONE);
374 } break;
376 while (true) {
377 if (next - start == size) {
378 parser->remaining += std::string_view(cur, next - cur);
379 }
380 if (strchr(skippable, *next)) {
381 next++;
382 continue;
383 }
384 cur = next;
385 break;
386 }
387 break;
389 if (memcmp(separator, cur, strlen(separator)) != 0) {
390 parser->states.push_back(HTTPState::IN_ERROR);
391 break;
392 }
393 next += strlen(separator);
394 cur = next;
395 break;
397 while (true) {
398 if (next - start == size) {
399 parser->remaining += std::string_view(cur, next - cur);
400 }
401 if (strchr(delimiters, *next) == nullptr) {
402 next++;
403 continue;
404 }
405 lastToken = std::string_view(cur, next - cur);
406 cur = next;
407 break;
408 }
409 break;
411 while (true) {
412 if (next + strlen(separator) - start == size) {
413 parser->remaining += std::string_view(cur, next - cur);
414 }
415 if (memcmp(separator, next, strlen(separator)) != 0) {
416 next++;
417 continue;
418 }
419 lastToken = std::string_view(cur, next - cur);
420 next += strlen(separator);
421 cur = next;
422 break;
423 }
424 break;
426 // The only case in which there can be a pending state when IN_DONE, is if
427 // we plan to resume processing.
428 if (parser->states.size() == 1 && parser->states.back() == HTTPState::BEGIN_BODY) {
429 done = true;
430 } else if (parser->states.empty()) {
431 done = true;
432 } else {
433 parser->states.push_back(HTTPState::IN_ERROR);
434 }
435 break;
437 parser->error = lastError;
438 parser->states.clear();
439 done = true;
440 break;
441 default:
442 parser->states.push_back(HTTPState::IN_ERROR);
443 break;
444 }
445 }
446}
447
448std::pair<std::string, unsigned short> parse_websocket_url(char const* url)
449{
450 std::string s = url;
451 if (s == "ws://") {
452 s = "ws://127.0.0.1:8080";
453 }
454 const std::regex urlMatcher("^ws://([0-9-_.]+)[:]([0-9]+)$");
455 std::smatch parts;
456 if (!std::regex_match(s, parts, urlMatcher)) {
457 throw runtime_error_f(
458 "Unable to parse driver client url: %s.\n"
459 "Format should be ws://[<driver ip>:<port>] e.g. ws://127.0.0.1:8080 or just ws://");
460 }
461 std::string ip = std::string{parts[1]};
462 auto portS = std::string(parts[2]);
463 unsigned short port = std::stoul(portS);
464 return {ip, port};
465}
466} // namespace o2::framework
benchmark::State & state
std::string url
#define ntohll
Definition Endian.h:28
#define htonll
Definition Endian.h:29
bool done
uint8_t endpoint
Definition RawData.h:0
uint32_t res
Definition RawData.h:0
uint32_t version
Definition RawData.h:8
const GLfloat * m
Definition glcorearb.h:4066
GLenum src
Definition glcorearb.h:1767
GLuint buffer
Definition glcorearb.h:655
GLsizeiptr size
Definition glcorearb.h:659
const GLdouble * v
Definition glcorearb.h:832
GLenum GLenum dst
Definition glcorearb.h:1767
GLboolean * data
Definition glcorearb.h:298
GLuint start
Definition glcorearb.h:469
GLenum GLenum GLsizei len
Definition glcorearb.h:4232
GLenum GLuint GLenum GLsizei const GLchar * buf
Definition glcorearb.h:2514
GLint GLuint mask
Definition glcorearb.h:291
int base64_encode(char *dest, int size, unsigned char *src, int slen)
Definition Base64.cxx:52
Defining PrimaryVertex explicitly as messageable.
@ IN_SEPARATOR
capture until a specific "separator"
@ IN_CAPTURE_SEPARATOR
capture until any or the "delimiters" characters
@ IN_CAPTURE_DELIMITERS
skip any "delimiters" char.
void encode_websocket_frames(std::vector< uv_buf_t > &outputs, char const *src, size_t size, WebSocketOpCode opcode, uint32_t mask)
std::pair< std::string, unsigned short > parse_websocket_url(char const *url)
void parse_http_request(char *start, size_t size, HTTPParser *parser)
std::string encode_websocket_handshake_request(const char *endpoint, const char *protocol, int version, char const *nonce, std::vector< std::pair< std::string, std::string > > headers)
void decode_websocket(char *start, size_t size, WebSocketHandler &handler)
RuntimeErrorRef runtime_error_f(const char *,...)
std::string encode_websocket_handshake_reply(char const *nonce, const char *protocol)
static std::string calculateAccept(const char *nonce)
Helper to calculate the reply to a nonce.
virtual void replyVersion(std::string_view const &s)
Definition HTTPParser.h:217
virtual void header(std::string_view const &k, std::string_view const &v)
Definition HTTPParser.h:211
virtual void method(std::string_view const &s)
Definition HTTPParser.h:208
virtual void replyCode(std::string_view const &s)
Definition HTTPParser.h:218
virtual void endHeaders()
Definition HTTPParser.h:212
virtual void body(char *data, size_t s)
Definition HTTPParser.h:216
virtual void target(std::string_view const &s)
Definition HTTPParser.h:209
std::vector< HTTPState > states
Definition HTTPParser.h:207
virtual void replyMessage(std::string_view const &s)
Definition HTTPParser.h:219
virtual void version(std::string_view const &s)
Definition HTTPParser.h:210
static constexpr size_t MaxChunkSize
Definition HTTPParser.h:114
An handler for a websocket message stream.
Definition HTTPParser.h:137
size_t pendingHeaderSize
Bytes from an incomplete header.
Definition HTTPParser.h:163
virtual void endChunk()
Invoked whenever we have no more input to process.
Definition HTTPParser.h:150
virtual void frame(char const *frame, size_t s)
Definition HTTPParser.h:146
char * pendingBuffer
A buffer large enough to contain the next frame to be processed.
Definition HTTPParser.h:161
size_t remainingSize
Bytes which are still to be received for the previous, half delivered frame.
Definition HTTPParser.h:157
size_t pendingSize
Bytes which are already there from the previous, half delivered frame.
Definition HTTPParser.h:159
virtual void beginChunk()
Invoked before processing the next round of input.
Definition HTTPParser.h:148