1 // This module handles HTTP transactions.
3 // Copyright (c) 1996-2023 by the citadel.org team
5 // This program is open source software. Use, duplication, or
6 // disclosure is subject to the GNU General Public License v3.
8 // uncomment one or more of these to see raw http transactions
10 //#define REQUEST_BODY_TO_STDERR
11 //#define RESPONSE_BODY_TO_STDERR
15 // Write data to the HTTP client. Encrypt if necessary.
16 int client_write(struct client_handle *ch, char *buf, int nbytes) {
18 return client_write_ssl(ch, buf, nbytes);
21 return write(ch->sock, buf, nbytes);
26 // Read data from the HTTP client. Decrypt if necessary.
27 // Returns number of bytes read, or -1 to indicate an error.
28 int client_read(struct client_handle *ch, char *buf, int nbytes) {
30 return client_read_ssl(ch, buf, nbytes);
33 int bytes_received = 0;
34 int bytes_this_block = 0;
35 while (bytes_received < nbytes) {
36 bytes_this_block = read(ch->sock, &buf[bytes_received], nbytes - bytes_received);
37 if (bytes_this_block < 1) {
40 bytes_received += bytes_this_block;
47 // Read a newline-terminated line of text from the client.
48 // Implemented in terms of client_read() and is therefore transparent...
49 // Returns the string length or -1 for error.
50 int client_readline(struct client_handle *ch, char *buf, int maxbytes) {
58 while (len < maxbytes) {
59 c = client_read(ch, &buf[len], 1);
61 syslog(LOG_DEBUG, "Socket error or zero-length read");
64 if (buf[len] == '\n') {
65 if ((len > 0) && (buf[len - 1] == '\r')) {
77 // printf() type function to send data to the web client.
78 void client_printf(struct client_handle *ch, const char *format, ...) {
80 StrBuf *Buf = NewStrBuf();
82 va_start(arg_ptr, format);
83 StrBufVAppendPrintf(Buf, format, arg_ptr);
86 client_write(ch, (char *) ChrPtr(Buf), StrLength(Buf));
91 // Push one new header into the response of an HTTP request.
92 // When completed, the HTTP transaction now owns the memory allocated for key and val.
93 void add_response_header(struct http_transaction *h, char *key, char *val) {
94 struct keyval new_response_header;
95 new_response_header.key = key;
96 new_response_header.val = val;
97 array_append(h->response_headers, &new_response_header);
101 // Entry point for this layer. Socket is connected. If running as an HTTPS
102 // server, SSL has already been negotiated. Now perform one transaction.
103 void perform_one_http_transaction(struct client_handle *ch) {
107 struct http_transaction h;
109 struct sockaddr_in sin;
110 int i; // general purpose iterator variable
112 memset(&h, 0, sizeof h);
113 h.request_headers = array_new(sizeof(struct keyval));
114 h.request_parms = array_new(sizeof(struct keyval));
115 h.response_headers = array_new(sizeof(struct keyval));
117 while (len = client_readline(ch, buf, sizeof buf), (len > 0)) {
120 if (lines_read == 1) { // First line is method and URL.
121 c = strchr(buf, ' '); // IGnore the HTTP-version.
123 h.method = strdup("NULL");
128 h.method = strdup(buf);
139 else { // Subsequent lines are headers.
140 c = strchr(buf, ':'); // Header line folding is obsolete so we don't support it.
143 struct keyval new_request_header;
145 new_request_header.key = strdup(buf);
147 new_request_header.val = strdup(c);
148 string_trim(new_request_header.key);
149 string_trim(new_request_header.val);
150 array_append(h.request_headers, &new_request_header);
152 syslog(LOG_DEBUG, "{ %s: %s", new_request_header.key, new_request_header.val);
159 // If the URL had any query parameters in it, parse them out now.
160 char *p = (h.url ? strchr(h.url, '?') : NULL);
162 *p++ = 0; // insert a null to remove parameters from the URL
163 char *tok, *saveptr = NULL;
164 for (tok = strtok_r(p, "&", &saveptr); tok!=NULL; tok = strtok_r(NULL, "&", &saveptr)) {
165 char *eq = strchr(tok, '=');
170 kv.key = strdup(tok);
172 array_append(h.request_parms, &kv);
174 syslog(LOG_DEBUG, "| %s = %s", kv.key, kv.val);
180 // build up the site prefix, such as https://foo.bar.com:4343
181 h.site_prefix = malloc(256);
182 strcpy(h.site_prefix, (is_https ? "https://" : "http://"));
183 char *hostheader = header_val(&h, "Host");
185 strcat(h.site_prefix, hostheader);
188 strcat(h.site_prefix, "127.0.0.1");
190 socklen_t llen = sizeof(sin);
191 if (getsockname(ch->sock, (struct sockaddr *) &sin, &llen) >= 0) {
192 sprintf(&h.site_prefix[strlen(h.site_prefix)], ":%d", ntohs(sin.sin_port));
195 // Now try to read in the request body (if one is present)
197 syslog(LOG_DEBUG, "Client disconnected");
201 syslog(LOG_DEBUG, "< %s %s", h.method, h.url);
204 // If there is a request body, read it now.
205 char *ccl = header_val(&h, "Content-Length");
207 h.request_body_length = atol(ccl);
209 if (h.request_body_length > 0) {
210 syslog(LOG_DEBUG, "Reading request body (%ld bytes)", h.request_body_length);
212 // Sometimes we need the request body by itself, sometimes we need it with headers.
213 // So we save it with synthetic headers, and also provide a pointer into the place where the body begins.
215 char *cct = header_val(&h, "Content-Type");
217 h.request_body_with_synth_headers = malloc(h.request_body_length + 1024);
218 memset(h.request_body_with_synth_headers, h.request_body_length + 1024, 0);
219 sprintf(h.request_body_with_synth_headers, "Content-Type: %s\r\n\r\n", cct);
220 h.request_body = h.request_body_with_synth_headers + strlen(h.request_body_with_synth_headers);
222 else { // a request body absent a Content-Type: header is invalid, but handle it anyway.
223 h.request_body_with_synth_headers = malloc(h.request_body_length);
224 memset(h.request_body_with_synth_headers, h.request_body_length, 0);
225 h.request_body = h.request_body_with_synth_headers;
228 client_read(ch, h.request_body, h.request_body_length);
230 // Write the entire request body to stderr -- not what you want during normal operation.
231 #ifdef REQUEST_BODY_TO_STDERR
232 write(2, HKEY("--- REQUEST BODY BEGIN ---\n"));
233 write(2, h.request_body, h.request_body_length);
234 write(2, HKEY("--- REQUEST BODY END ---\n"));
239 // Now pass control up to the next layer to perform the request.
242 // Write the entire response body to stderr -- not what you want during normal operation.
243 #ifdef RESPONSE_BODY_TO_STDERR
244 write(2, HKEY("--- RESPONSE BODY BEGIN ---\n"));
245 write(2, h.response_body, h.response_body_length);
246 write(2, HKEY("--- RESPONSE BODY END ---\n"));
249 // Output the results back to the client.
251 syslog(LOG_DEBUG, "> %03d %s", h.response_code, h.response_string);
253 client_printf(ch, "HTTP/1.1 %03d %s\r\n", h.response_code, h.response_string);
254 client_printf(ch, "Connection: close\r\n");
255 client_printf(ch, "Content-Length: %ld\r\n", h.response_body_length);
256 char *datestring = http_datestring(time(NULL));
258 client_printf(ch, "Date: %s\r\n", datestring);
262 client_printf(ch, "Content-encoding: identity\r\n"); // change if we gzip/deflate
263 int number_of_response_headers = array_len(h.response_headers);
264 for (i=0; i<number_of_response_headers; ++i) {
265 struct keyval *kv = array_get_element_at(h.response_headers, i);
267 syslog(LOG_DEBUG, "} %s: %s", kv->key, kv->val);
269 client_printf(ch, "%s: %s\r\n", kv->key, kv->val);
271 client_printf(ch, "\r\n");
272 if ((h.response_body_length > 0) && (h.response_body != NULL)) {
273 client_write(ch, h.response_body, h.response_body_length);
277 // free the transaction memory
278 while (array_len(h.request_headers) > 0) {
279 struct keyval *kv = array_get_element_at(h.request_headers, 0);
280 if (kv->key) free(kv->key);
281 if (kv->val) free(kv->val);
282 array_delete_element_at(h.request_headers, 0);
284 array_free(h.request_headers);
285 while (array_len(h.request_parms) > 0) {
286 struct keyval *kv = array_get_element_at(h.request_parms, 0);
287 if (kv->key) free(kv->key);
288 if (kv->val) free(kv->val);
289 array_delete_element_at(h.request_parms, 0);
291 array_free(h.request_parms);
292 while (array_len(h.response_headers) > 0) {
293 struct keyval *kv = array_get_element_at(h.response_headers, 0);
294 if (kv->key) free(kv->key);
295 if (kv->val) free(kv->val);
296 array_delete_element_at(h.response_headers, 0);
298 array_free(h.response_headers);
305 if (h.request_body_with_synth_headers) {
306 free(h.request_body_with_synth_headers);
308 if (h.response_string) {
309 free(h.response_string);
317 // Utility function to fetch a specific header from a completely read-in request.
318 // Returns the value of the requested header, or NULL if the specified header was not sent.
319 // The caller does NOT own the memory of the returned pointer, but can count on the pointer
320 // to still be valid through the end of the transaction.
321 char *header_val(struct http_transaction *h, char *requested_header) {
324 for (i=0; i<array_len(h->request_headers); ++i) {
325 kv = array_get_element_at(h->request_headers, i);
326 if (!strcasecmp(kv->key, requested_header)) {
334 // Utility function to fetch a specific URL parameter from a completely read-in request.
335 // Returns the value of the requested parameter, or NULL if the specified parameter was not sent.
336 // The caller does NOT own the memory of the returned pointer, but can count on the pointer
337 // to still be valid through the end of the transaction.
338 char *get_url_param(struct http_transaction *h, char *requested_param) {
341 for (i=0; i<array_len(h->request_parms); ++i) {
342 kv = array_get_element_at(h->request_parms, i);
343 if (!strcasecmp(kv->key, requested_param)) {