Annotation of embedaddon/lighttpd/src/http-header-glue.c, revision 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>