DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
grpcweb.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file grpcweb.cpp
6 * @brief gRPC-Web message framing builder + parser (pure, host-tested).
7 */
8
10
11#if DETWS_ENABLE_GRPC_WEB
12
13#include <string.h>
14
15size_t grpcweb_frame(uint8_t *buf, size_t cap, uint8_t flags, const uint8_t *body, size_t body_len)
16{
17 if (!buf || (body_len && !body) || body_len > 0xFFFFFFFFu)
18 return 0;
19 size_t total = GRPCWEB_PREFIX_LEN + body_len;
20 if (total > cap)
21 return 0;
22 buf[0] = flags;
23 buf[1] = (uint8_t)(body_len >> 24);
24 buf[2] = (uint8_t)(body_len >> 16);
25 buf[3] = (uint8_t)(body_len >> 8);
26 buf[4] = (uint8_t)(body_len);
27 if (body_len)
28 memcpy(buf + GRPCWEB_PREFIX_LEN, body, body_len);
29 return total;
30}
31
32size_t grpcweb_frame_message(uint8_t *buf, size_t cap, const uint8_t *msg, size_t msg_len, bool compressed)
33{
34 return grpcweb_frame(buf, cap, compressed ? GRPCWEB_FLAG_COMPRESSED : 0, msg, msg_len);
35}
36
37// Append a NUL-terminated string at *pos with bounds check; advance *pos. False on overflow.
38static bool put_str(uint8_t *buf, size_t cap, size_t *pos, const char *s)
39{
40 size_t n = strnlen(s, cap + 1);
41 if (*pos + n > cap)
42 return false;
43 memcpy(buf + *pos, s, n);
44 *pos += n;
45 return true;
46}
47
48// Append a non-negative integer as decimal at *pos. False on overflow.
49static bool put_int(uint8_t *buf, size_t cap, size_t *pos, int v)
50{
51 if (v < 0)
52 return false;
53 char tmp[12];
54 size_t n = 0;
55 char rev[12];
56 size_t r = 0;
57 if (v == 0)
58 rev[r++] = '0';
59 while (v)
60 {
61 rev[r++] = (char)('0' + (v % 10));
62 v /= 10;
63 }
64 while (r)
65 tmp[n++] = rev[--r];
66 if (*pos + n > cap)
67 return false;
68 memcpy(buf + *pos, tmp, n);
69 *pos += n;
70 return true;
71}
72
73size_t grpcweb_frame_trailer(uint8_t *buf, size_t cap, int status, const char *message)
74{
75 if (!buf || cap < GRPCWEB_PREFIX_LEN)
76 return 0;
77 size_t pos = GRPCWEB_PREFIX_LEN; // reserve the prefix; patch the length after the body
78 if (!put_str(buf, cap, &pos, "grpc-status:") || !put_int(buf, cap, &pos, status) ||
79 !put_str(buf, cap, &pos, "\r\n"))
80 return 0;
81 if (message && *message)
82 {
83 if (!put_str(buf, cap, &pos, "grpc-message:") || !put_str(buf, cap, &pos, message) ||
84 !put_str(buf, cap, &pos, "\r\n"))
85 return 0;
86 }
87 size_t body_len = pos - GRPCWEB_PREFIX_LEN;
88 buf[0] = GRPCWEB_FLAG_TRAILER;
89 buf[1] = (uint8_t)(body_len >> 24);
90 buf[2] = (uint8_t)(body_len >> 16);
91 buf[3] = (uint8_t)(body_len >> 8);
92 buf[4] = (uint8_t)(body_len);
93 return pos;
94}
95
96bool grpcweb_parse(const uint8_t *buf, size_t len, GrpcWebFrame *out, size_t *consumed)
97{
98 if (!buf || !out || !consumed || len < GRPCWEB_PREFIX_LEN)
99 return false;
100 uint32_t body_len = ((uint32_t)buf[1] << 24) | ((uint32_t)buf[2] << 16) | ((uint32_t)buf[3] << 8) | buf[4];
101 if ((size_t)GRPCWEB_PREFIX_LEN + body_len > len)
102 return false; // frame not fully buffered
103 out->flags = buf[0];
104 out->compressed = (buf[0] & GRPCWEB_FLAG_COMPRESSED) != 0;
105 out->trailer = (buf[0] & GRPCWEB_FLAG_TRAILER) != 0;
106 out->body = buf + GRPCWEB_PREFIX_LEN;
107 out->body_len = body_len;
108 *consumed = GRPCWEB_PREFIX_LEN + body_len;
109 return true;
110}
111
112bool grpcweb_trailer_status(const uint8_t *body, size_t len, int *status)
113{
114 if (!body)
115 return false;
116 static const char key[] = "grpc-status:";
117 const size_t klen = sizeof(key) - 1;
118 for (size_t i = 0; i + klen <= len; i++)
119 {
120 // Match at the start of a line (i == 0 or preceded by '\n').
121 if ((i == 0 || body[i - 1] == '\n') && memcmp(body + i, key, klen) == 0)
122 {
123 size_t j = i + klen;
124 if (j >= len || body[j] < '0' || body[j] > '9')
125 return false;
126 int v = 0;
127 for (; j < len && body[j] >= '0' && body[j] <= '9'; j++)
128 v = v * 10 + (body[j] - '0');
129 if (status)
130 *status = v;
131 return true;
132 }
133 }
134 return false;
135}
136
137#endif // DETWS_ENABLE_GRPC_WEB
gRPC-Web message framing (DETWS_ENABLE_GRPC_WEB) - zero-heap length-prefixed frame builder + parser,...