HomePort
httpd_response.c
Go to the documentation of this file.
1 /*
2  * Copyright 2011 Aalborg University. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without modification, are
5  * permitted provided that the following conditions are met:
6  *
7  * 1. Redistributions of source code must retain the above copyright notice, this list of
8  * conditions and the following disclaimer.
9  *
10  * 2. Redistributions in binary form must reproduce the above copyright notice, this list
11  * of conditions and the following disclaimer in the documentation and/or other materials
12  * provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY Aalborg University ''AS IS'' AND ANY EXPRESS OR IMPLIED
15  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Aalborg University OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  *
24  * The views and conclusions contained in the software and documentation are those of the
25  * authors and should not be interpreted as representing official policies, either expressed
26  */
27 
28 #include "httpd_request.h"
29 #include "hpd/hpd_shared_api.h"
30 
31 #include <string.h>
32 #include <stdlib.h>
33 #include <stdio.h>
34 
35 #define HTTPD_HTTP_VERSION "HTTP/1.1 "
36 #define HTTPD_CRLF "\r\n"
37 
53 {
56  char *msg;
58 };
59 
71 {
72 #define XX(num, str) if(status == num) {return #str;}
74 #undef XX
75  return NULL;
76 }
77 
90 {
91  if (!res) return HPD_E_NULL;
92 
94  free(res->msg);
95  free(res);
96  return rc;
97 }
98 
116 {
117  if (!req) return HPD_E_NULL;
118  hpd_error_t rc, rc2;
119  const hpd_module_t *context;
120  if ((rc = http_request_get_context(req, &context))) return rc;
121  if (!response) HPD_LOG_RETURN_E_NULL(context);
122 
123  int len;
124 
125  // Get data
126  char *status_str = httpd_status_codes_to_str(status);
127 
128  // Calculate msg length
129  len = 1;
130  len += strlen(HTTPD_HTTP_VERSION);
131  // TODO Problem if this if is NULL !
132  if (status_str) len += strlen(status_str);
133  len += 4;
134  len += strlen(HTTPD_CRLF);
135 
136  // Allocate space
137  (*response) = malloc(sizeof(hpd_httpd_response_t));
138  if (!(*response)) HPD_LOG_RETURN_E_ALLOC(context);
139  (*response)->status = status;
140  (*response)->msg = malloc(len*sizeof(char));
141  if (!(*response)->msg) {
142  if ((rc = hpd_httpd_response_destroy((*response))))
143  HPD_LOG_ERROR(context, "Failed to destroy response (code: %d).", rc);
144  HPD_LOG_RETURN_E_ALLOC(context);
145  }
146 
147  // Init struct
148  (*response)->context = context;
149  if ((rc = http_request_get_connection(req, &(*response)->conn))) return rc;
150 
151  // Construct msg
152  strcpy((*response)->msg, HTTPD_HTTP_VERSION);
153  sprintf(&(*response)->msg[strlen(HTTPD_HTTP_VERSION)], "%i ", status);
154  if (status_str) strcat((*response)->msg, status_str);
155  strcat((*response)->msg, HTTPD_CRLF);
156 
157  // Real persistent connections is not supported, so tell client that we close connection after response has been sent
158  rc = hpd_httpd_response_add_header((*response), "Connection", "close");
159  if (rc && (rc2 = hpd_httpd_response_destroy(*response)))
160  HPD_LOG_ERROR(context, "Failed to destroy response (code: %d).", rc2);;
161  return rc;
162 }
163 
179 {
180  if (!res) return HPD_E_NULL;
181  if (!field || !value) HPD_LOG_RETURN_E_NULL(res->context);
182 
183  // Headers already sent
184  if (!res->msg)
185  HPD_LOG_RETURN(res->context, HPD_E_STATE, "Cannot add header, they have already been sent to client.");
186 
187  char *msg;
188  size_t msg_len = strlen(res->msg)+strlen(field)+2+strlen(value)+strlen(HTTPD_CRLF)+1;
189 
190  msg = realloc(res->msg, msg_len*sizeof(char));
191  if (!msg) HPD_LOG_RETURN_E_ALLOC(res->context);
192  res->msg = msg;
193 
194  strcat(res->msg, field);
195  strcat(res->msg, ": ");
196  strcat(res->msg, value);
197  strcat(res->msg, HTTPD_CRLF);
198 
199  return HPD_E_SUCCESS;
200 }
201 
223  const char *field, const char *value,
224  const char *expires, const char *max_age,
225  const char *domain, const char *path,
226  int secure, int http_only,
227  const char *extension)
228 {
229  if (!res) return HPD_E_NULL;
230  if (!field || !value) HPD_LOG_RETURN_E_NULL(res->context);
231 
232  // Headers already sent
233  if (!res->msg) return HPD_E_STATE;
234 
235  char *msg;
236  size_t msg_len = strlen(res->msg) + 12 + strlen(field) + 1 + strlen(value) + strlen(HTTPD_CRLF) + 1;
237 
238  // Calculate length
239  if (expires) msg_len += 10 + strlen(expires);
240  if (max_age) msg_len += 10 + strlen(max_age);
241  if (domain) msg_len += 9 + strlen(domain);
242  if (path) msg_len += 7 + strlen(path);
243  if (secure) msg_len += 8;
244  if (http_only) msg_len += 10;
245  if (extension) msg_len += 2 + strlen(extension);
246 
247  // Reallocate message
248  msg = realloc(res->msg, msg_len*sizeof(char));
249  if (!msg) HPD_LOG_RETURN_E_ALLOC(res->context);
250  res->msg = msg;
251 
252  // Apply header
253  strcat(res->msg, "Set-Cookie: ");
254  strcat(res->msg, field);
255  strcat(res->msg, "=");
256  strcat(res->msg, value);
257  strcat(res->msg, HTTPD_CRLF);
258  if (expires) {
259  strcat(res->msg, "; Expires=");
260  strcat(res->msg, expires);
261  }
262  if (max_age) {
263  strcat(res->msg, "; Max-Age=");
264  strcat(res->msg, max_age);
265  }
266  if (domain) {
267  strcat(res->msg, "; Domain=");
268  strcat(res->msg, domain);
269  }
270  if (path) {
271  strcat(res->msg, "; Domain=");
272  strcat(res->msg, domain);
273  }
274  if (secure) strcat(res->msg, "; Secure");
275  if (http_only) strcat(res->msg, "; HttpOnly");
276  if (extension) {
277  strcat(res->msg, "; ");
278  strcat(res->msg, extension);
279  }
280 
281  return HPD_E_SUCCESS;
282 }
283 
294 {
295  if (!res) return HPD_E_NULL;
296 
297  va_list arg;
298  va_start(arg, fmt);
299  hpd_error_t rc = hpd_httpd_response_vsendf(res, fmt, arg);
300  va_end(arg);
301  return rc;
302 }
303 
321 hpd_error_t hpd_httpd_response_vsendf(hpd_httpd_response_t *res, const char *fmt, va_list arg)
322 {
323  if (!res) return HPD_E_NULL;
324 
325  hpd_error_t rc;
326 
327  if (res->msg) {
328  const char *ip;
329  if ((rc = hpd_tcpd_conn_get_ip(res->conn, &ip))) {
330  HPD_LOG_WARN(res->context, "Failed to get ip [code: %i].", rc);
331  ip = "(unknown)";
332  }
333  HPD_LOG_VERBOSE(res->context, "Sending response to %s: %i %s.", ip, res->status, httpd_status_codes_to_str(res->status));
334  if ((rc = hpd_tcpd_conn_sendf(res->conn, "%s%s", res->msg, HTTPD_CRLF))) return rc;
335  free(res->msg);
336  res->msg = NULL;
337  }
338 
339  if (fmt) {
340  return hpd_tcpd_conn_vsendf(res->conn, fmt, arg);
341  }
342 
343  return HPD_E_SUCCESS;
344 }
345 
hpd_error_t hpd_httpd_response_create(hpd_httpd_response_t **response, hpd_httpd_request_t *req, hpd_status_t status)
Create the reponse and constructs the status line.
enum hpd_status hpd_status_t
Definition: hpd_types.h:168
#define HPD_LOG_RETURN(CONTEXT, E, FMT,...)
hpd_tcpd_conn_t * conn
Connection to client.
hpd_error_t hpd_httpd_response_sendf(hpd_httpd_response_t *res, const char *fmt,...)
Send response to client.
hpd_error_t hpd_httpd_response_add_cookie(hpd_httpd_response_t *res, const char *field, const char *value, const char *expires, const char *max_age, const char *domain, const char *path, int secure, int http_only, const char *extension)
Add cookie header to response.
data field
data value
hpd_error_t hpd_httpd_response_vsendf(hpd_httpd_response_t *res, const char *fmt, va_list arg)
Send response to client.
#define HTTPD_HTTP_VERSION
data path
free(data.url)
hpd_error_t http_request_get_context(hpd_httpd_request_t *req, const hpd_module_t **context)
hpd_error_t hpd_tcpd_conn_get_ip(hpd_tcpd_conn_t *conn, const char **ip)
Get the IP address of the client.
Definition: tcpd.c:459
An http request.
#define HPD_HTTP_STATUS_CODE_MAP(XX)
[hpd_method_t]
Definition: hpd_types.h:105
hpd_error_t http_request_get_connection(hpd_httpd_request_t *req, hpd_tcpd_conn_t **conn)
Get the connection of a request.
hpd_error_t hpd_httpd_response_destroy(hpd_httpd_response_t *res)
Destroy a hpd_httpd_response.
#define HPD_LOG_VERBOSE(CONTEXT, FMT,...)
hpd_tcpd_conn_t * conn
The connection to send on.
enum hpd_error hpd_error_t
Definition: hpd_types.h:167
#define HPD_LOG_RETURN_E_ALLOC(CONTEXT)
hpd_error_t hpd_httpd_response_add_header(hpd_httpd_response_t *res, const char *field, const char *value)
Add header to a response.
char * msg
Status/headers to send.
#define HTTPD_CRLF
#define HPD_LOG_RETURN_E_NULL(CONTEXT)
All data to represent a connection.
Definition: tcpd_intern.h:56
hpd_error_t hpd_tcpd_conn_sendf(hpd_tcpd_conn_t *conn, const char *fmt,...)
Send message on connection.
Definition: tcpd.c:503
hpd_error_t hpd_tcpd_conn_close(hpd_tcpd_conn_t *conn)
Close a connection, after the remaining data has been sent.
Definition: tcpd.c:581
const hpd_module_t * context
hpd_error_t hpd_tcpd_conn_vsendf(hpd_tcpd_conn_t *conn, const char *fmt, va_list vp)
Send message on connection.
Definition: tcpd.c:534
#define XX(num, str)
#define HPD_LOG_WARN(CONTEXT, FMT,...)
static char * httpd_status_codes_to_str(hpd_status_t status)
Convert a status code to string.
hpd_status_t status
#define HPD_LOG_ERROR(CONTEXT, FMT,...)
A http response.