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