1 /*
2 * This file and its contents are licensed under the Apache License 2.0.
3 * Please see the included NOTICE for copyright information and
4 * LICENSE-APACHE for a copy of the license.
5 */
6 #include <postgres.h>
7 #include <lib/stringinfo.h>
8 #include <utils/memutils.h>
9
10 #include "http.h"
11
12 #define SPACE ' '
13 #define COLON ':'
14 #define CARRIAGE '\r'
15 #define NEW_LINE '\n'
16
17 /* So that http_response.c can find this function */
18 HttpHeader *ts_http_header_create(const char *name, size_t name_len, const char *value,
19 size_t value_len, HttpHeader *next);
20
21 HttpHeader *
ts_http_header_create(const char * name,size_t name_len,const char * value,size_t value_len,HttpHeader * next)22 ts_http_header_create(const char *name, size_t name_len, const char *value, size_t value_len,
23 HttpHeader *next)
24 {
25 HttpHeader *new_header = palloc(sizeof(HttpHeader));
26
27 memset(new_header, 0, sizeof(*new_header));
28 new_header->name = palloc(name_len + 1);
29 if (name_len > 0)
30 memcpy(new_header->name, name, name_len);
31 new_header->name[name_len] = '\0';
32 new_header->name_len = name_len;
33
34 new_header->value = palloc(value_len + 1);
35 if (value_len > 0)
36 memcpy(new_header->value, value, value_len);
37 new_header->value[value_len] = '\0';
38 new_header->value_len = value_len;
39
40 new_header->next = next;
41 return new_header;
42 }
43
44 /* NOTE: The setter functions for HttpRequest should all */
45 /* ensure that every char * in this struct is null-terminated */
46 typedef struct HttpRequest
47 {
48 HttpRequestMethod method;
49 char *uri;
50 size_t uri_len;
51 HttpVersion version;
52 HttpHeader *headers;
53 char *body;
54 size_t body_len;
55 MemoryContext context;
56 } HttpRequest;
57
58 static const char *http_method_strings[] = { [HTTP_GET] = "GET", [HTTP_POST] = "POST" };
59
60 #define METHOD_STRING(x) http_method_strings[x]
61 #define VERSION_STRING(x) ts_http_version_string(x)
62
63 /* appendBinaryStringInfo is UB if data is NULL. This function wraps it in a check that datalen > 0
64 */
65 static void
appendOptionalBinaryStringInfo(StringInfo str,const char * data,int datalen)66 appendOptionalBinaryStringInfo(StringInfo str, const char *data, int datalen)
67 {
68 if (datalen <= 0)
69 return;
70
71 Assert(data != NULL);
72 appendBinaryStringInfo(str, data, datalen);
73 }
74
75 void
ts_http_request_init(HttpRequest * req,HttpRequestMethod method)76 ts_http_request_init(HttpRequest *req, HttpRequestMethod method)
77 {
78 req->method = method;
79 }
80
81 HttpRequest *
ts_http_request_create(HttpRequestMethod method)82 ts_http_request_create(HttpRequestMethod method)
83 {
84 MemoryContext request_context =
85 AllocSetContextCreate(CurrentMemoryContext, "Http Request", ALLOCSET_DEFAULT_SIZES);
86 MemoryContext old = MemoryContextSwitchTo(request_context);
87 HttpRequest *req = palloc0(sizeof(HttpRequest));
88
89 req->context = request_context;
90 ts_http_request_init(req, method);
91
92 MemoryContextSwitchTo(old);
93 return req;
94 }
95
96 void
ts_http_request_destroy(HttpRequest * req)97 ts_http_request_destroy(HttpRequest *req)
98 {
99 MemoryContextDelete(req->context);
100 }
101
102 void
ts_http_request_set_uri(HttpRequest * req,const char * uri)103 ts_http_request_set_uri(HttpRequest *req, const char *uri)
104 {
105 MemoryContext old = MemoryContextSwitchTo(req->context);
106 int uri_len = strlen(uri);
107
108 req->uri = palloc(uri_len + 1);
109 memcpy(req->uri, uri, uri_len);
110 req->uri[uri_len] = '\0';
111 req->uri_len = uri_len;
112 MemoryContextSwitchTo(old);
113 }
114
115 void
ts_http_request_set_version(HttpRequest * req,HttpVersion version)116 ts_http_request_set_version(HttpRequest *req, HttpVersion version)
117 {
118 req->version = version;
119 }
120
121 void
ts_http_request_set_header(HttpRequest * req,const char * name,const char * value)122 ts_http_request_set_header(HttpRequest *req, const char *name, const char *value)
123 {
124 MemoryContext old = MemoryContextSwitchTo(req->context);
125 int name_len = strlen(name);
126 int value_len = strlen(value);
127 HttpHeader *new_header = ts_http_header_create(name, name_len, value, value_len, req->headers);
128
129 req->headers = new_header;
130 MemoryContextSwitchTo(old);
131 }
132
133 void
ts_http_request_set_body(HttpRequest * req,const char * body,size_t body_len)134 ts_http_request_set_body(HttpRequest *req, const char *body, size_t body_len)
135 {
136 MemoryContext old = MemoryContextSwitchTo(req->context);
137
138 req->body = palloc(body_len + 1);
139 memcpy(req->body, body, body_len);
140 req->body[body_len] = '\0';
141 req->body_len = body_len;
142 MemoryContextSwitchTo(old);
143 }
144
145 static void
http_request_serialize_method(HttpRequest * req,StringInfo buf)146 http_request_serialize_method(HttpRequest *req, StringInfo buf)
147 {
148 const char *method = METHOD_STRING(req->method);
149
150 appendStringInfoString(buf, method);
151 }
152
153 static void
http_request_serialize_version(HttpRequest * req,StringInfo buf)154 http_request_serialize_version(HttpRequest *req, StringInfo buf)
155 {
156 const char *version = VERSION_STRING(req->version);
157
158 appendStringInfoString(buf, version);
159 }
160
161 static void
http_request_serialize_uri(HttpRequest * req,StringInfo buf)162 http_request_serialize_uri(HttpRequest *req, StringInfo buf)
163 {
164 appendOptionalBinaryStringInfo(buf, req->uri, req->uri_len);
165 }
166
167 static void
http_request_serialize_char(char to_serialize,StringInfo buf)168 http_request_serialize_char(char to_serialize, StringInfo buf)
169 {
170 appendStringInfoChar(buf, to_serialize);
171 }
172
173 static void
http_request_serialize_body(HttpRequest * req,StringInfo buf)174 http_request_serialize_body(HttpRequest *req, StringInfo buf)
175 {
176 appendOptionalBinaryStringInfo(buf, req->body, req->body_len);
177 }
178
179 static void
http_header_serialize(HttpHeader * header,StringInfo buf)180 http_header_serialize(HttpHeader *header, StringInfo buf)
181 {
182 appendOptionalBinaryStringInfo(buf, header->name, header->name_len);
183 http_request_serialize_char(COLON, buf);
184 http_request_serialize_char(SPACE, buf);
185 appendOptionalBinaryStringInfo(buf, header->value, header->value_len);
186 }
187
188 static int
http_header_get_content_length(HttpHeader * header)189 http_header_get_content_length(HttpHeader *header)
190 {
191 int content_length = -1;
192
193 if (!strncmp(header->name, HTTP_CONTENT_LENGTH, header->name_len))
194 sscanf(header->value, "%d", &content_length);
195 return content_length;
196 }
197
198 const char *
ts_http_request_build(HttpRequest * req,size_t * buf_size)199 ts_http_request_build(HttpRequest *req, size_t *buf_size)
200 {
201 /* serialize into this buf, which is allocated on caller's memory context */
202 StringInfoData buf;
203 HttpHeader *cur_header;
204 int content_length = 0;
205 bool verified_content_length = false;
206
207 initStringInfo(&buf);
208
209 http_request_serialize_method(req, &buf);
210 http_request_serialize_char(SPACE, &buf);
211
212 http_request_serialize_uri(req, &buf);
213 http_request_serialize_char(SPACE, &buf);
214
215 http_request_serialize_version(req, &buf);
216 http_request_serialize_char(CARRIAGE, &buf);
217 http_request_serialize_char(NEW_LINE, &buf);
218
219 cur_header = req->headers;
220
221 while (cur_header != NULL)
222 {
223 content_length = http_header_get_content_length(cur_header);
224 if (content_length != -1)
225 {
226 /* make sure it's equal to body_len */
227 if (content_length != req->body_len)
228 {
229 return NULL;
230 }
231 else
232 verified_content_length = true;
233 }
234 http_header_serialize(cur_header, &buf);
235 http_request_serialize_char(CARRIAGE, &buf);
236 http_request_serialize_char(NEW_LINE, &buf);
237
238 cur_header = cur_header->next;
239 }
240 http_request_serialize_char(CARRIAGE, &buf);
241 http_request_serialize_char(NEW_LINE, &buf);
242
243 if (!verified_content_length)
244 {
245 /* Then there was no header field for Content-Length */
246 if (req->body_len != 0)
247 {
248 return NULL;
249 }
250 }
251
252 http_request_serialize_body(req, &buf);
253 /* Now everything lives in buf.data */
254 if (buf_size != NULL)
255 *buf_size = buf.len;
256 return buf.data;
257 }
258