[core] set REDIRECT_STATUS to error_handler_saved_status (fixes #1828)
[lighttpd.git] / src / http-header-glue.c
blob5cbb809705310da95e8594bb644b70b7d5d14d5c
1 #include "first.h"
3 #include "base.h"
4 #include "array.h"
5 #include "buffer.h"
6 #include "log.h"
7 #include "etag.h"
8 #include "response.h"
10 #include <string.h>
11 #include <errno.h>
13 #include <time.h>
16 * This was 'borrowed' from tcpdump.
19 * This is fun.
21 * In older BSD systems, socket addresses were fixed-length, and
22 * "sizeof (struct sockaddr)" gave the size of the structure.
23 * All addresses fit within a "struct sockaddr".
25 * In newer BSD systems, the socket address is variable-length, and
26 * there's an "sa_len" field giving the length of the structure;
27 * this allows socket addresses to be longer than 2 bytes of family
28 * and 14 bytes of data.
30 * Some commercial UNIXes use the old BSD scheme, some use the RFC 2553
31 * variant of the old BSD scheme (with "struct sockaddr_storage" rather
32 * than "struct sockaddr"), and some use the new BSD scheme.
34 * Some versions of GNU libc use neither scheme, but has an "SA_LEN()"
35 * macro that determines the size based on the address family. Other
36 * versions don't have "SA_LEN()" (as it was in drafts of RFC 2553
37 * but not in the final version). On the latter systems, we explicitly
38 * check the AF_ type to determine the length; we assume that on
39 * all those systems we have "struct sockaddr_storage".
42 #ifdef HAVE_IPV6
43 # ifndef SA_LEN
44 # ifdef HAVE_SOCKADDR_SA_LEN
45 # define SA_LEN(addr) ((addr)->sa_len)
46 # else /* HAVE_SOCKADDR_SA_LEN */
47 # ifdef HAVE_STRUCT_SOCKADDR_STORAGE
48 static size_t get_sa_len(const struct sockaddr *addr) {
49 switch (addr->sa_family) {
51 # ifdef AF_INET
52 case AF_INET:
53 return (sizeof (struct sockaddr_in));
54 # endif
56 # ifdef AF_INET6
57 case AF_INET6:
58 return (sizeof (struct sockaddr_in6));
59 # endif
61 default:
62 return (sizeof (struct sockaddr));
66 # define SA_LEN(addr) (get_sa_len(addr))
67 # else /* HAVE_SOCKADDR_STORAGE */
68 # define SA_LEN(addr) (sizeof (struct sockaddr))
69 # endif /* HAVE_SOCKADDR_STORAGE */
70 # endif /* HAVE_SOCKADDR_SA_LEN */
71 # endif /* SA_LEN */
72 #endif
77 int response_header_insert(server *srv, connection *con, const char *key, size_t keylen, const char *value, size_t vallen) {
78 data_string *ds;
80 UNUSED(srv);
82 if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) {
83 ds = data_response_init();
85 buffer_copy_string_len(ds->key, key, keylen);
86 buffer_copy_string_len(ds->value, value, vallen);
88 array_insert_unique(con->response.headers, (data_unset *)ds);
90 return 0;
93 int response_header_overwrite(server *srv, connection *con, const char *key, size_t keylen, const char *value, size_t vallen) {
94 data_string *ds;
96 UNUSED(srv);
98 /* if there already is a key by this name overwrite the value */
99 if (NULL != (ds = (data_string *)array_get_element(con->response.headers, key))) {
100 buffer_copy_string(ds->value, value);
102 return 0;
105 return response_header_insert(srv, con, key, keylen, value, vallen);
108 int response_header_append(server *srv, connection *con, const char *key, size_t keylen, const char *value, size_t vallen) {
109 data_string *ds;
111 UNUSED(srv);
113 /* if there already is a key by this name append the value */
114 if (NULL != (ds = (data_string *)array_get_element(con->response.headers, key))) {
115 buffer_append_string_len(ds->value, CONST_STR_LEN(", "));
116 buffer_append_string_len(ds->value, value, vallen);
117 return 0;
120 return response_header_insert(srv, con, key, keylen, value, vallen);
123 int http_response_redirect_to_directory(server *srv, connection *con) {
124 buffer *o;
126 o = buffer_init();
128 buffer_copy_buffer(o, con->uri.scheme);
129 buffer_append_string_len(o, CONST_STR_LEN("://"));
130 if (!buffer_is_empty(con->uri.authority)) {
131 buffer_append_string_buffer(o, con->uri.authority);
132 } else {
133 /* get the name of the currently connected socket */
134 struct hostent *he;
135 #ifdef HAVE_IPV6
136 char hbuf[256];
137 #endif
138 sock_addr our_addr;
139 socklen_t our_addr_len;
141 our_addr_len = sizeof(our_addr);
143 if (-1 == getsockname(con->fd, (struct sockaddr *)&our_addr, &our_addr_len)
144 || our_addr_len > sizeof(our_addr)) {
145 con->http_status = 500;
147 log_error_write(srv, __FILE__, __LINE__, "ss",
148 "can't get sockname", strerror(errno));
150 buffer_free(o);
151 return 0;
155 /* Lookup name: secondly try to get hostname for bind address */
156 switch(our_addr.plain.sa_family) {
157 #ifdef HAVE_IPV6
158 case AF_INET6:
159 if (0 != getnameinfo((const struct sockaddr *)(&our_addr.ipv6),
160 SA_LEN((const struct sockaddr *)&our_addr.ipv6),
161 hbuf, sizeof(hbuf), NULL, 0, 0)) {
163 char dst[INET6_ADDRSTRLEN];
165 log_error_write(srv, __FILE__, __LINE__,
166 "SSS", "NOTICE: getnameinfo failed: ",
167 strerror(errno), ", using ip-address instead");
169 buffer_append_string(o,
170 inet_ntop(AF_INET6, (char *)&our_addr.ipv6.sin6_addr,
171 dst, sizeof(dst)));
172 } else {
173 buffer_append_string(o, hbuf);
175 break;
176 #endif
177 case AF_INET:
178 if (NULL == (he = gethostbyaddr((char *)&our_addr.ipv4.sin_addr, sizeof(struct in_addr), AF_INET))) {
179 log_error_write(srv, __FILE__, __LINE__,
180 "SdS", "NOTICE: gethostbyaddr failed: ",
181 h_errno, ", using ip-address instead");
183 buffer_append_string(o, inet_ntoa(our_addr.ipv4.sin_addr));
184 } else {
185 buffer_append_string(o, he->h_name);
187 break;
188 default:
189 log_error_write(srv, __FILE__, __LINE__,
190 "S", "ERROR: unsupported address-type");
192 buffer_free(o);
193 return -1;
197 unsigned short default_port = 80;
198 if (buffer_is_equal_caseless_string(con->uri.scheme, CONST_STR_LEN("https"))) {
199 default_port = 443;
201 if (default_port != srv->srvconf.port) {
202 buffer_append_string_len(o, CONST_STR_LEN(":"));
203 buffer_append_int(o, srv->srvconf.port);
207 buffer_append_string_encoded(o, CONST_BUF_LEN(con->uri.path), ENCODING_REL_URI);
208 buffer_append_string_len(o, CONST_STR_LEN("/"));
209 if (!buffer_string_is_empty(con->uri.query)) {
210 buffer_append_string_len(o, CONST_STR_LEN("?"));
211 buffer_append_string_buffer(o, con->uri.query);
214 response_header_insert(srv, con, CONST_STR_LEN("Location"), CONST_BUF_LEN(o));
216 con->http_status = 301;
217 con->file_finished = 1;
219 buffer_free(o);
221 return 0;
224 buffer * strftime_cache_get(server *srv, time_t last_mod) {
225 struct tm *tm;
226 size_t i;
228 for (i = 0; i < FILE_CACHE_MAX; i++) {
229 /* found cache-entry */
230 if (srv->mtime_cache[i].mtime == last_mod) return srv->mtime_cache[i].str;
232 /* found empty slot */
233 if (srv->mtime_cache[i].mtime == 0) break;
236 if (i == FILE_CACHE_MAX) {
237 i = 0;
240 srv->mtime_cache[i].mtime = last_mod;
241 buffer_string_prepare_copy(srv->mtime_cache[i].str, 1023);
242 tm = gmtime(&(srv->mtime_cache[i].mtime));
243 buffer_append_strftime(srv->mtime_cache[i].str, "%a, %d %b %Y %H:%M:%S GMT", tm);
245 return srv->mtime_cache[i].str;
249 int http_response_handle_cachable(server *srv, connection *con, buffer *mtime) {
250 int head_or_get =
251 ( HTTP_METHOD_GET == con->request.http_method
252 || HTTP_METHOD_HEAD == con->request.http_method);
253 UNUSED(srv);
256 * 14.26 If-None-Match
257 * [...]
258 * If none of the entity tags match, then the server MAY perform the
259 * requested method as if the If-None-Match header field did not exist,
260 * but MUST also ignore any If-Modified-Since header field(s) in the
261 * request. That is, if no entity tags match, then the server MUST NOT
262 * return a 304 (Not Modified) response.
265 if (con->request.http_if_none_match) {
266 /* use strong etag checking for now: weak comparison must not be used
267 * for ranged requests
269 if (etag_is_equal(con->physical.etag, con->request.http_if_none_match, 0)) {
270 if (head_or_get) {
271 con->http_status = 304;
272 return HANDLER_FINISHED;
273 } else {
274 con->http_status = 412;
275 con->mode = DIRECT;
276 return HANDLER_FINISHED;
279 } else if (con->request.http_if_modified_since && head_or_get) {
280 /* last-modified handling */
281 size_t used_len;
282 char *semicolon;
284 if (NULL == (semicolon = strchr(con->request.http_if_modified_since, ';'))) {
285 used_len = strlen(con->request.http_if_modified_since);
286 } else {
287 used_len = semicolon - con->request.http_if_modified_since;
290 if (0 == strncmp(con->request.http_if_modified_since, mtime->ptr, used_len)) {
291 if ('\0' == mtime->ptr[used_len]) con->http_status = 304;
292 return HANDLER_FINISHED;
293 } else {
294 char buf[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")];
295 time_t t_header, t_file;
296 struct tm tm;
298 /* convert to timestamp */
299 if (used_len >= sizeof(buf)) return HANDLER_GO_ON;
301 strncpy(buf, con->request.http_if_modified_since, used_len);
302 buf[used_len] = '\0';
304 if (NULL == strptime(buf, "%a, %d %b %Y %H:%M:%S GMT", &tm)) {
306 * parsing failed, let's get out of here
308 return HANDLER_GO_ON;
310 tm.tm_isdst = 0;
311 t_header = mktime(&tm);
313 strptime(mtime->ptr, "%a, %d %b %Y %H:%M:%S GMT", &tm);
314 tm.tm_isdst = 0;
315 t_file = mktime(&tm);
317 if (t_file > t_header) return HANDLER_GO_ON;
319 con->http_status = 304;
320 return HANDLER_FINISHED;
324 return HANDLER_GO_ON;