Annotation of embedaddon/lighttpd/src/http-header-glue.c, revision 1.1.1.1
1.1 misho 1: #include "base.h"
2: #include "array.h"
3: #include "buffer.h"
4: #include "log.h"
5: #include "etag.h"
6: #include "response.h"
7:
8: #include <string.h>
9: #include <errno.h>
10:
11: #include <time.h>
12:
13: /*
14: * This was 'borrowed' from tcpdump.
15: *
16: *
17: * This is fun.
18: *
19: * In older BSD systems, socket addresses were fixed-length, and
20: * "sizeof (struct sockaddr)" gave the size of the structure.
21: * All addresses fit within a "struct sockaddr".
22: *
23: * In newer BSD systems, the socket address is variable-length, and
24: * there's an "sa_len" field giving the length of the structure;
25: * this allows socket addresses to be longer than 2 bytes of family
26: * and 14 bytes of data.
27: *
28: * Some commercial UNIXes use the old BSD scheme, some use the RFC 2553
29: * variant of the old BSD scheme (with "struct sockaddr_storage" rather
30: * than "struct sockaddr"), and some use the new BSD scheme.
31: *
32: * Some versions of GNU libc use neither scheme, but has an "SA_LEN()"
33: * macro that determines the size based on the address family. Other
34: * versions don't have "SA_LEN()" (as it was in drafts of RFC 2553
35: * but not in the final version). On the latter systems, we explicitly
36: * check the AF_ type to determine the length; we assume that on
37: * all those systems we have "struct sockaddr_storage".
38: */
39:
40: #ifdef HAVE_IPV6
41: # ifndef SA_LEN
42: # ifdef HAVE_SOCKADDR_SA_LEN
43: # define SA_LEN(addr) ((addr)->sa_len)
44: # else /* HAVE_SOCKADDR_SA_LEN */
45: # ifdef HAVE_STRUCT_SOCKADDR_STORAGE
46: static size_t get_sa_len(const struct sockaddr *addr) {
47: switch (addr->sa_family) {
48:
49: # ifdef AF_INET
50: case AF_INET:
51: return (sizeof (struct sockaddr_in));
52: # endif
53:
54: # ifdef AF_INET6
55: case AF_INET6:
56: return (sizeof (struct sockaddr_in6));
57: # endif
58:
59: default:
60: return (sizeof (struct sockaddr));
61:
62: }
63: }
64: # define SA_LEN(addr) (get_sa_len(addr))
65: # else /* HAVE_SOCKADDR_STORAGE */
66: # define SA_LEN(addr) (sizeof (struct sockaddr))
67: # endif /* HAVE_SOCKADDR_STORAGE */
68: # endif /* HAVE_SOCKADDR_SA_LEN */
69: # endif /* SA_LEN */
70: #endif
71:
72:
73:
74:
75: int response_header_insert(server *srv, connection *con, const char *key, size_t keylen, const char *value, size_t vallen) {
76: data_string *ds;
77:
78: UNUSED(srv);
79:
80: if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) {
81: ds = data_response_init();
82: }
83: buffer_copy_string_len(ds->key, key, keylen);
84: buffer_copy_string_len(ds->value, value, vallen);
85:
86: array_insert_unique(con->response.headers, (data_unset *)ds);
87:
88: return 0;
89: }
90:
91: int response_header_overwrite(server *srv, connection *con, const char *key, size_t keylen, const char *value, size_t vallen) {
92: data_string *ds;
93:
94: UNUSED(srv);
95:
96: /* if there already is a key by this name overwrite the value */
97: if (NULL != (ds = (data_string *)array_get_element(con->response.headers, key))) {
98: buffer_copy_string(ds->value, value);
99:
100: return 0;
101: }
102:
103: return response_header_insert(srv, con, key, keylen, value, vallen);
104: }
105:
106: int response_header_append(server *srv, connection *con, const char *key, size_t keylen, const char *value, size_t vallen) {
107: data_string *ds;
108:
109: UNUSED(srv);
110:
111: /* if there already is a key by this name append the value */
112: if (NULL != (ds = (data_string *)array_get_element(con->response.headers, key))) {
113: buffer_append_string_len(ds->value, CONST_STR_LEN(", "));
114: buffer_append_string_len(ds->value, value, vallen);
115: return 0;
116: }
117:
118: return response_header_insert(srv, con, key, keylen, value, vallen);
119: }
120:
121: int http_response_redirect_to_directory(server *srv, connection *con) {
122: buffer *o;
123:
124: o = buffer_init();
125:
126: buffer_copy_string_buffer(o, con->uri.scheme);
127: buffer_append_string_len(o, CONST_STR_LEN("://"));
128: if (con->uri.authority->used) {
129: buffer_append_string_buffer(o, con->uri.authority);
130: } else {
131: /* get the name of the currently connected socket */
132: struct hostent *he;
133: #ifdef HAVE_IPV6
134: char hbuf[256];
135: #endif
136: sock_addr our_addr;
137: socklen_t our_addr_len;
138:
139: our_addr_len = sizeof(our_addr);
140:
141: if (-1 == getsockname(con->fd, &(our_addr.plain), &our_addr_len)) {
142: con->http_status = 500;
143:
144: log_error_write(srv, __FILE__, __LINE__, "ss",
145: "can't get sockname", strerror(errno));
146:
147: buffer_free(o);
148: return 0;
149: }
150:
151:
152: /* Lookup name: secondly try to get hostname for bind address */
153: switch(our_addr.plain.sa_family) {
154: #ifdef HAVE_IPV6
155: case AF_INET6:
156: if (0 != getnameinfo((const struct sockaddr *)(&our_addr.ipv6),
157: SA_LEN((const struct sockaddr *)&our_addr.ipv6),
158: hbuf, sizeof(hbuf), NULL, 0, 0)) {
159:
160: char dst[INET6_ADDRSTRLEN];
161:
162: log_error_write(srv, __FILE__, __LINE__,
163: "SSS", "NOTICE: getnameinfo failed: ",
164: strerror(errno), ", using ip-address instead");
165:
166: buffer_append_string(o,
167: inet_ntop(AF_INET6, (char *)&our_addr.ipv6.sin6_addr,
168: dst, sizeof(dst)));
169: } else {
170: buffer_append_string(o, hbuf);
171: }
172: break;
173: #endif
174: case AF_INET:
175: if (NULL == (he = gethostbyaddr((char *)&our_addr.ipv4.sin_addr, sizeof(struct in_addr), AF_INET))) {
176: log_error_write(srv, __FILE__, __LINE__,
177: "SdS", "NOTICE: gethostbyaddr failed: ",
178: h_errno, ", using ip-address instead");
179:
180: buffer_append_string(o, inet_ntoa(our_addr.ipv4.sin_addr));
181: } else {
182: buffer_append_string(o, he->h_name);
183: }
184: break;
185: default:
186: log_error_write(srv, __FILE__, __LINE__,
187: "S", "ERROR: unsupported address-type");
188:
189: buffer_free(o);
190: return -1;
191: }
192:
193: {
194: unsigned short default_port = 80;
195: if (buffer_is_equal_caseless_string(con->uri.scheme, CONST_STR_LEN("https"))) {
196: default_port = 443;
197: }
198: if (default_port != srv->srvconf.port) {
199: buffer_append_string_len(o, CONST_STR_LEN(":"));
200: buffer_append_long(o, srv->srvconf.port);
201: }
202: }
203: }
204: buffer_append_string_buffer(o, con->uri.path);
205: buffer_append_string_len(o, CONST_STR_LEN("/"));
206: if (!buffer_is_empty(con->uri.query)) {
207: buffer_append_string_len(o, CONST_STR_LEN("?"));
208: buffer_append_string_buffer(o, con->uri.query);
209: }
210:
211: response_header_insert(srv, con, CONST_STR_LEN("Location"), CONST_BUF_LEN(o));
212:
213: con->http_status = 301;
214: con->file_finished = 1;
215:
216: buffer_free(o);
217:
218: return 0;
219: }
220:
221: buffer * strftime_cache_get(server *srv, time_t last_mod) {
222: struct tm *tm;
223: size_t i;
224:
225: for (i = 0; i < FILE_CACHE_MAX; i++) {
226: /* found cache-entry */
227: if (srv->mtime_cache[i].mtime == last_mod) return srv->mtime_cache[i].str;
228:
229: /* found empty slot */
230: if (srv->mtime_cache[i].mtime == 0) break;
231: }
232:
233: if (i == FILE_CACHE_MAX) {
234: i = 0;
235: }
236:
237: srv->mtime_cache[i].mtime = last_mod;
238: buffer_prepare_copy(srv->mtime_cache[i].str, 1024);
239: tm = gmtime(&(srv->mtime_cache[i].mtime));
240: srv->mtime_cache[i].str->used = strftime(srv->mtime_cache[i].str->ptr,
241: srv->mtime_cache[i].str->size - 1,
242: "%a, %d %b %Y %H:%M:%S GMT", tm);
243: srv->mtime_cache[i].str->used++;
244:
245: return srv->mtime_cache[i].str;
246: }
247:
248:
249: int http_response_handle_cachable(server *srv, connection *con, buffer *mtime) {
250: UNUSED(srv);
251: /*
252: * 14.26 If-None-Match
253: * [...]
254: * If none of the entity tags match, then the server MAY perform the
255: * requested method as if the If-None-Match header field did not exist,
256: * but MUST also ignore any If-Modified-Since header field(s) in the
257: * request. That is, if no entity tags match, then the server MUST NOT
258: * return a 304 (Not Modified) response.
259: */
260:
261: /* last-modified handling */
262: if (con->request.http_if_none_match) {
263: if (etag_is_equal(con->physical.etag, con->request.http_if_none_match)) {
264: if (con->request.http_method == HTTP_METHOD_GET ||
265: con->request.http_method == HTTP_METHOD_HEAD) {
266:
267: con->http_status = 304;
268: return HANDLER_FINISHED;
269: } else {
270: con->http_status = 412;
271: con->mode = DIRECT;
272: return HANDLER_FINISHED;
273: }
274: }
275: } else if (con->request.http_if_modified_since &&
276: (con->request.http_method == HTTP_METHOD_GET ||
277: con->request.http_method == HTTP_METHOD_HEAD)) {
278: size_t used_len;
279: char *semicolon;
280:
281: if (NULL == (semicolon = strchr(con->request.http_if_modified_since, ';'))) {
282: used_len = strlen(con->request.http_if_modified_since);
283: } else {
284: used_len = semicolon - con->request.http_if_modified_since;
285: }
286:
287: if (0 == strncmp(con->request.http_if_modified_since, mtime->ptr, used_len)) {
288: if ('\0' == mtime->ptr[used_len]) con->http_status = 304;
289: return HANDLER_FINISHED;
290: } else {
291: char buf[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")];
292: time_t t_header, t_file;
293: struct tm tm;
294:
295: /* convert to timestamp */
296: if (used_len >= sizeof(buf)) return HANDLER_GO_ON;
297:
298: strncpy(buf, con->request.http_if_modified_since, used_len);
299: buf[used_len] = '\0';
300:
301: if (NULL == strptime(buf, "%a, %d %b %Y %H:%M:%S GMT", &tm)) {
302: /**
303: * parsing failed, let's get out of here
304: */
305: return HANDLER_GO_ON;
306: }
307: tm.tm_isdst = 0;
308: t_header = mktime(&tm);
309:
310: strptime(mtime->ptr, "%a, %d %b %Y %H:%M:%S GMT", &tm);
311: tm.tm_isdst = 0;
312: t_file = mktime(&tm);
313:
314: if (t_file > t_header) return HANDLER_GO_ON;
315:
316: con->http_status = 304;
317: return HANDLER_FINISHED;
318: }
319: }
320:
321: return HANDLER_GO_ON;
322: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>