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)
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 "Sec-WebSocket-Accept: {}\r\n\r\n";
225 return fmt::format(res, HTTPParserHelpers::calculateAccept(nonce));
226}
227
228void parse_http_request(char* start, size_t size, HTTPParser* parser)
229{
231 // Too short, let's try again...
232 if (size < 2) {
233 parser->remaining += std::string_view(start, size);
234 }
235 char* cur = start;
236 char* next = cur;
237 std::string_view lastToken(cur, 0);
238 std::string_view lastKey;
239 std::string_view lastValue;
240 std::string lastError;
241 if (parser->states.empty()) {
242 parser->states.push_back(HTTPState::IN_START);
243 }
244 char const* delimiters = nullptr;
245 char const* skippable = nullptr;
246 char const* separator = nullptr;
247 char const* spaces = "\t \v";
248 char const* colon = ":";
249 char const* newline = "\r\n";
250 bool done = false;
251
252 while (!done) {
253 HTTPState state = parser->states.back();
254 parser->states.pop_back();
255 switch (state) {
257 parser->states.push_back(HTTPState::BEGIN_METHOD);
258 break;
260 parser->states.push_back(HTTPState::BEGIN_REPLY_VERSION);
261 break;
263 parser->states.push_back(HTTPState::END_REPLY_VERSION);
264 parser->states.push_back(HTTPState::IN_CAPTURE_DELIMITERS);
265 delimiters = spaces;
266 break;
268 parser->replyVersion(lastToken);
269 parser->states.push_back(HTTPState::BEGIN_REPLY_CODE);
270 parser->states.push_back(HTTPState::IN_SKIP_CHARS);
271 skippable = spaces;
272 break;
274 parser->states.push_back(HTTPState::END_REPLY_CODE);
275 parser->states.push_back(HTTPState::IN_CAPTURE_DELIMITERS);
276 delimiters = spaces;
277 break;
279 parser->replyCode(lastToken);
280 parser->states.push_back(HTTPState::BEGIN_REPLY_MESSAGE);
281 parser->states.push_back(HTTPState::IN_SKIP_CHARS);
282 skippable = spaces;
283 break;
285 parser->states.push_back(HTTPState::END_REPLY_MESSAGE);
286 parser->states.push_back(HTTPState::IN_CAPTURE_SEPARATOR);
287 separator = newline;
288 break;
290 parser->replyMessage(lastToken);
291 parser->states.push_back(HTTPState::BEGIN_HEADERS);
292 break;
294 parser->states.push_back(HTTPState::END_METHOD);
295 parser->states.push_back(HTTPState::IN_CAPTURE_DELIMITERS);
296 delimiters = spaces;
297 break;
299 parser->method(lastToken);
300 parser->states.push_back(HTTPState::BEGIN_TARGET);
301 parser->states.push_back(HTTPState::IN_SKIP_CHARS);
302 skippable = spaces;
303 break;
305 parser->states.push_back(HTTPState::END_TARGET);
306 parser->states.push_back(HTTPState::IN_CAPTURE_DELIMITERS);
307 delimiters = spaces;
308 break;
310 parser->target(lastToken);
311 parser->states.push_back(HTTPState::BEGIN_VERSION);
312 parser->states.push_back(HTTPState::IN_SKIP_CHARS);
313 skippable = spaces;
314 break;
316 parser->states.push_back(HTTPState::END_VERSION);
317 parser->states.push_back(HTTPState::IN_CAPTURE_SEPARATOR);
318 separator = newline;
319 break;
321 parser->version(lastToken);
322 parser->states.push_back(HTTPState::BEGIN_HEADERS);
323 break;
325 parser->states.push_back(HTTPState::BEGIN_HEADER);
326 break;
328 parser->states.push_back(HTTPState::BEGIN_HEADER_KEY);
329 break;
331 parser->states.push_back(HTTPState::END_HEADER_KEY);
332 parser->states.push_back(HTTPState::IN_CAPTURE_SEPARATOR);
333 separator = colon;
334 break;
336 lastKey = lastToken;
337 parser->states.push_back(HTTPState::BEGIN_HEADER_VALUE);
338 parser->states.push_back(HTTPState::IN_SKIP_CHARS);
339 skippable = spaces;
340 break;
342 parser->states.push_back(HTTPState::END_HEADER_VALUE);
343 parser->states.push_back(HTTPState::IN_CAPTURE_SEPARATOR);
344 separator = newline;
345 break;
347 lastValue = lastToken;
348 parser->states.push_back(HTTPState::END_HEADER);
349 break;
351 if (strncmp("\r\n", next, 2) == 0) {
352 parser->header(lastKey, lastValue);
353 parser->states.push_back(HTTPState::END_HEADERS);
354 next += 2;
355 cur = next;
356 } else {
357 parser->header(lastKey, lastValue);
358 parser->states.push_back(HTTPState::BEGIN_HEADER);
359 cur = next;
360 }
361 break;
363 parser->endHeaders();
364 parser->states.push_back(HTTPState::BEGIN_BODY);
365 break;
367 size_t bodySize = size - (cur - start);
368 parser->body(cur, bodySize);
369 next = cur + bodySize;
370 cur = next;
371 parser->states.push_back(HTTPState::BEGIN_BODY);
372 parser->states.push_back(HTTPState::IN_DONE);
373 } break;
375 while (true) {
376 if (next - start == size) {
377 parser->remaining += std::string_view(cur, next - cur);
378 }
379 if (strchr(skippable, *next)) {
380 next++;
381 continue;
382 }
383 cur = next;
384 break;
385 }
386 break;
388 if (memcmp(separator, cur, strlen(separator)) != 0) {
389 parser->states.push_back(HTTPState::IN_ERROR);
390 break;
391 }
392 next += strlen(separator);
393 cur = next;
394 break;
396 while (true) {
397 if (next - start == size) {
398 parser->remaining += std::string_view(cur, next - cur);
399 }
400 if (strchr(delimiters, *next) == nullptr) {
401 next++;
402 continue;
403 }
404 lastToken = std::string_view(cur, next - cur);
405 cur = next;
406 break;
407 }
408 break;
410 while (true) {
411 if (next + strlen(separator) - start == size) {
412 parser->remaining += std::string_view(cur, next - cur);
413 }
414 if (memcmp(separator, next, strlen(separator)) != 0) {
415 next++;
416 continue;
417 }
418 lastToken = std::string_view(cur, next - cur);
419 next += strlen(separator);
420 cur = next;
421 break;
422 }
423 break;
425 // The only case in which there can be a pending state when IN_DONE, is if
426 // we plan to resume processing.
427 if (parser->states.size() == 1 && parser->states.back() == HTTPState::BEGIN_BODY) {
428 done = true;
429 } else if (parser->states.empty()) {
430 done = true;
431 } else {
432 parser->states.push_back(HTTPState::IN_ERROR);
433 }
434 break;
436 parser->error = lastError;
437 parser->states.clear();
438 done = true;
439 break;
440 default:
441 parser->states.push_back(HTTPState::IN_ERROR);
442 break;
443 }
444 }
445}
446
447std::pair<std::string, unsigned short> parse_websocket_url(char const* url)
448{
449 std::string s = url;
450 if (s == "ws://") {
451 s = "ws://127.0.0.1:8080";
452 }
453 const std::regex urlMatcher("^ws://([0-9-_.]+)[:]([0-9]+)$");
454 std::smatch parts;
455 if (!std::regex_match(s, parts, urlMatcher)) {
456 throw runtime_error_f(
457 "Unable to parse driver client url: %s.\n"
458 "Format should be ws://[<driver ip>:<port>] e.g. ws://127.0.0.1:8080 or just ws://");
459 }
460 std::string ip = std::string{parts[1]};
461 auto portS = std::string(parts[2]);
462 unsigned short port = std::stoul(portS);
463 return {ip, port};
464}
465} // namespace o2::framework
benchmark::State & state
#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.
Definition TFIDInfo.h:20
@ 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)
std::string encode_websocket_handshake_reply(char const *nonce)
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 *,...)
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:216
virtual void header(std::string_view const &k, std::string_view const &v)
Definition HTTPParser.h:210
virtual void method(std::string_view const &s)
Definition HTTPParser.h:207
virtual void replyCode(std::string_view const &s)
Definition HTTPParser.h:217
virtual void endHeaders()
Definition HTTPParser.h:211
virtual void body(char *data, size_t s)
Definition HTTPParser.h:215
virtual void target(std::string_view const &s)
Definition HTTPParser.h:208
std::vector< HTTPState > states
Definition HTTPParser.h:206
virtual void replyMessage(std::string_view const &s)
Definition HTTPParser.h:218
virtual void version(std::string_view const &s)
Definition HTTPParser.h:209
static constexpr size_t MaxChunkSize
Definition HTTPParser.h:114
An handler for a websocket message stream.
Definition HTTPParser.h:136
size_t pendingHeaderSize
Bytes from an incomplete header.
Definition HTTPParser.h:162
virtual void endChunk()
Invoked whenever we have no more input to process.
Definition HTTPParser.h:149
virtual void frame(char const *frame, size_t s)
Definition HTTPParser.h:145
char * pendingBuffer
A buffer large enough to contain the next frame to be processed.
Definition HTTPParser.h:160
size_t remainingSize
Bytes which are still to be received for the previous, half delivered frame.
Definition HTTPParser.h:156
size_t pendingSize
Bytes which are already there from the previous, half delivered frame.
Definition HTTPParser.h:158
virtual void beginChunk()
Invoked before processing the next round of input.
Definition HTTPParser.h:147