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>