[mod_status] page refresh option (fixes #2170)
[lighttpd.git] / src / http-header-glue.c
blob992bef0f35d3ba2c1f0ad4c98ae1aa4ea5763112
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, &(our_addr.plain), &our_addr_len)) {
144 con->http_status = 500;
146 log_error_write(srv, __FILE__, __LINE__, "ss",
147 "can't get sockname", strerror(errno));
149 buffer_free(o);
150 return 0;
154 /* Lookup name: secondly try to get hostname for bind address */
155 switch(our_addr.plain.sa_family) {
156 #ifdef HAVE_IPV6
157 case AF_INET6:
158 if (0 != getnameinfo((const struct sockaddr *)(&our_addr.ipv6),
159 SA_LEN((const struct sockaddr *)&our_addr.ipv6),
160 hbuf, sizeof(hbuf), NULL, 0, 0)) {
162 char dst[INET6_ADDRSTRLEN];
164 log_error_write(srv, __FILE__, __LINE__,
165 "SSS", "NOTICE: getnameinfo failed: ",
166 strerror(errno), ", using ip-address instead");
168 buffer_append_string(o,
169 inet_ntop(AF_INET6, (char *)&our_addr.ipv6.sin6_addr,
170 dst, sizeof(dst)));
171 } else {
172 buffer_append_string(o, hbuf);
174 break;
175 #endif
176 case AF_INET:
177 if (NULL == (he = gethostbyaddr((char *)&our_addr.ipv4.sin_addr, sizeof(struct in_addr), AF_INET))) {
178 log_error_write(srv, __FILE__, __LINE__,
179 "SdS", "NOTICE: gethostbyaddr failed: ",
180 h_errno, ", using ip-address instead");
182 buffer_append_string(o, inet_ntoa(our_addr.ipv4.sin_addr));
183 } else {
184 buffer_append_string(o, he->h_name);
186 break;
187 default:
188 log_error_write(srv, __FILE__, __LINE__,
189 "S", "ERROR: unsupported address-type");
191 buffer_free(o);
192 return -1;
196 unsigned short default_port = 80;
197 if (buffer_is_equal_caseless_string(con->uri.scheme, CONST_STR_LEN("https"))) {
198 default_port = 443;
200 if (default_port != srv->srvconf.port) {
201 buffer_append_string_len(o, CONST_STR_LEN(":"));
202 buffer_append_int(o, srv->srvconf.port);
206 buffer_append_string_encoded(o, CONST_BUF_LEN(con->uri.path), ENCODING_REL_URI);
207 buffer_append_string_len(o, CONST_STR_LEN("/"));
208 if (!buffer_string_is_empty(con->uri.query)) {
209 buffer_append_string_len(o, CONST_STR_LEN("?"));
210 buffer_append_string_buffer(o, con->uri.query);
213 response_header_insert(srv, con, CONST_STR_LEN("Location"), CONST_BUF_LEN(o));
215 con->http_status = 301;
216 con->file_finished = 1;
218 buffer_free(o);
220 return 0;
223 buffer * strftime_cache_get(server *srv, time_t last_mod) {
224 struct tm *tm;
225 size_t i;
227 for (i = 0; i < FILE_CACHE_MAX; i++) {
228 /* found cache-entry */
229 if (srv->mtime_cache[i].mtime == last_mod) return srv->mtime_cache[i].str;
231 /* found empty slot */
232 if (srv->mtime_cache[i].mtime == 0) break;
235 if (i == FILE_CACHE_MAX) {
236 i = 0;
239 srv->mtime_cache[i].mtime = last_mod;
240 buffer_string_prepare_copy(srv->mtime_cache[i].str, 1023);
241 tm = gmtime(&(srv->mtime_cache[i].mtime));
242 buffer_append_strftime(srv->mtime_cache[i].str, "%a, %d %b %Y %H:%M:%S GMT", tm);
244 return srv->mtime_cache[i].str;
248 int http_response_handle_cachable(server *srv, connection *con, buffer *mtime) {
249 int head_or_get =
250 ( HTTP_METHOD_GET == con->request.http_method
251 || HTTP_METHOD_HEAD == con->request.http_method);
252 UNUSED(srv);
255 * 14.26 If-None-Match
256 * [...]
257 * If none of the entity tags match, then the server MAY perform the
258 * requested method as if the If-None-Match header field did not exist,
259 * but MUST also ignore any If-Modified-Since header field(s) in the
260 * request. That is, if no entity tags match, then the server MUST NOT
261 * return a 304 (Not Modified) response.
264 if (con->request.http_if_none_match) {
265 /* use strong etag checking for now: weak comparison must not be used
266 * for ranged requests
268 if (etag_is_equal(con->physical.etag, con->request.http_if_none_match, 0)) {
269 if (head_or_get) {
270 con->http_status = 304;
271 return HANDLER_FINISHED;
272 } else {
273 con->http_status = 412;
274 con->mode = DIRECT;
275 return HANDLER_FINISHED;
278 } else if (con->request.http_if_modified_since && head_or_get) {
279 /* last-modified handling */
280 size_t used_len;
281 char *semicolon;
283 if (NULL == (semicolon = strchr(con->request.http_if_modified_since, ';'))) {
284 used_len = strlen(con->request.http_if_modified_since);
285 } else {
286 used_len = semicolon - con->request.http_if_modified_since;
289 if (0 == strncmp(con->request.http_if_modified_since, mtime->ptr, used_len)) {
290 if ('\0' == mtime->ptr[used_len]) con->http_status = 304;
291 return HANDLER_FINISHED;
292 } else {
293 char buf[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")];
294 time_t t_header, t_file;
295 struct tm tm;
297 /* convert to timestamp */
298 if (used_len >= sizeof(buf)) return HANDLER_GO_ON;
300 strncpy(buf, con->request.http_if_modified_since, used_len);
301 buf[used_len] = '\0';
303 if (NULL == strptime(buf, "%a, %d %b %Y %H:%M:%S GMT", &tm)) {
305 * parsing failed, let's get out of here
307 return HANDLER_GO_ON;
309 tm.tm_isdst = 0;
310 t_header = mktime(&tm);
312 strptime(mtime->ptr, "%a, %d %b %Y %H:%M:%S GMT", &tm);
313 tm.tm_isdst = 0;
314 t_file = mktime(&tm);
316 if (t_file > t_header) return HANDLER_GO_ON;
318 con->http_status = 304;
319 return HANDLER_FINISHED;
323 return HANDLER_GO_ON;