Annotation of embedaddon/lighttpd/src/http-header-glue.c, revision 1.1.1.2

1.1.1.2 ! misho       1: #include "first.h"
        !             2: 
1.1       misho       3: #include "base.h"
                      4: #include "array.h"
                      5: #include "buffer.h"
                      6: #include "log.h"
                      7: #include "etag.h"
1.1.1.2 ! misho       8: #include "http_chunk.h"
1.1       misho       9: #include "response.h"
1.1.1.2 ! misho      10: #include "stat_cache.h"
1.1       misho      11: 
                     12: #include <string.h>
                     13: #include <errno.h>
                     14: 
                     15: #include <time.h>
                     16: 
                     17: /*
                     18:  * This was 'borrowed' from tcpdump.
                     19:  *
                     20:  *
                     21:  * This is fun.
                     22:  *
                     23:  * In older BSD systems, socket addresses were fixed-length, and
                     24:  * "sizeof (struct sockaddr)" gave the size of the structure.
                     25:  * All addresses fit within a "struct sockaddr".
                     26:  *
                     27:  * In newer BSD systems, the socket address is variable-length, and
                     28:  * there's an "sa_len" field giving the length of the structure;
                     29:  * this allows socket addresses to be longer than 2 bytes of family
                     30:  * and 14 bytes of data.
                     31:  *
                     32:  * Some commercial UNIXes use the old BSD scheme, some use the RFC 2553
                     33:  * variant of the old BSD scheme (with "struct sockaddr_storage" rather
                     34:  * than "struct sockaddr"), and some use the new BSD scheme.
                     35:  *
                     36:  * Some versions of GNU libc use neither scheme, but has an "SA_LEN()"
                     37:  * macro that determines the size based on the address family.  Other
                     38:  * versions don't have "SA_LEN()" (as it was in drafts of RFC 2553
                     39:  * but not in the final version).  On the latter systems, we explicitly
                     40:  * check the AF_ type to determine the length; we assume that on
                     41:  * all those systems we have "struct sockaddr_storage".
                     42:  */
                     43: 
                     44: #ifdef HAVE_IPV6
                     45: # ifndef SA_LEN
                     46: #  ifdef HAVE_SOCKADDR_SA_LEN
                     47: #   define SA_LEN(addr)   ((addr)->sa_len)
                     48: #  else /* HAVE_SOCKADDR_SA_LEN */
                     49: #   ifdef HAVE_STRUCT_SOCKADDR_STORAGE
                     50: static size_t get_sa_len(const struct sockaddr *addr) {
                     51:        switch (addr->sa_family) {
                     52: 
                     53: #    ifdef AF_INET
                     54:        case AF_INET:
                     55:                return (sizeof (struct sockaddr_in));
                     56: #    endif
                     57: 
                     58: #    ifdef AF_INET6
                     59:        case AF_INET6:
                     60:                return (sizeof (struct sockaddr_in6));
                     61: #    endif
                     62: 
                     63:        default:
                     64:                return (sizeof (struct sockaddr));
                     65: 
                     66:        }
                     67: }
                     68: #    define SA_LEN(addr)   (get_sa_len(addr))
                     69: #   else /* HAVE_SOCKADDR_STORAGE */
                     70: #    define SA_LEN(addr)   (sizeof (struct sockaddr))
                     71: #   endif /* HAVE_SOCKADDR_STORAGE */
                     72: #  endif /* HAVE_SOCKADDR_SA_LEN */
                     73: # endif /* SA_LEN */
                     74: #endif
                     75: 
                     76: 
                     77: 
                     78: 
                     79: int response_header_insert(server *srv, connection *con, const char *key, size_t keylen, const char *value, size_t vallen) {
                     80:        data_string *ds;
                     81: 
                     82:        UNUSED(srv);
                     83: 
                     84:        if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) {
                     85:                ds = data_response_init();
                     86:        }
                     87:        buffer_copy_string_len(ds->key, key, keylen);
                     88:        buffer_copy_string_len(ds->value, value, vallen);
                     89: 
                     90:        array_insert_unique(con->response.headers, (data_unset *)ds);
                     91: 
                     92:        return 0;
                     93: }
                     94: 
                     95: int response_header_overwrite(server *srv, connection *con, const char *key, size_t keylen, const char *value, size_t vallen) {
                     96:        data_string *ds;
                     97: 
                     98:        UNUSED(srv);
                     99: 
                    100:        /* if there already is a key by this name overwrite the value */
                    101:        if (NULL != (ds = (data_string *)array_get_element(con->response.headers, key))) {
                    102:                buffer_copy_string(ds->value, value);
                    103: 
                    104:                return 0;
                    105:        }
                    106: 
                    107:        return response_header_insert(srv, con, key, keylen, value, vallen);
                    108: }
                    109: 
                    110: int response_header_append(server *srv, connection *con, const char *key, size_t keylen, const char *value, size_t vallen) {
                    111:        data_string *ds;
                    112: 
                    113:        UNUSED(srv);
                    114: 
                    115:        /* if there already is a key by this name append the value */
                    116:        if (NULL != (ds = (data_string *)array_get_element(con->response.headers, key))) {
                    117:                buffer_append_string_len(ds->value, CONST_STR_LEN(", "));
                    118:                buffer_append_string_len(ds->value, value, vallen);
                    119:                return 0;
                    120:        }
                    121: 
                    122:        return response_header_insert(srv, con, key, keylen, value, vallen);
                    123: }
                    124: 
                    125: int http_response_redirect_to_directory(server *srv, connection *con) {
                    126:        buffer *o;
                    127: 
                    128:        o = buffer_init();
                    129: 
1.1.1.2 ! misho     130:        buffer_copy_buffer(o, con->uri.scheme);
1.1       misho     131:        buffer_append_string_len(o, CONST_STR_LEN("://"));
1.1.1.2 ! misho     132:        if (!buffer_is_empty(con->uri.authority)) {
1.1       misho     133:                buffer_append_string_buffer(o, con->uri.authority);
                    134:        } else {
                    135:                /* get the name of the currently connected socket */
                    136:                struct hostent *he;
                    137: #ifdef HAVE_IPV6
                    138:                char hbuf[256];
                    139: #endif
                    140:                sock_addr our_addr;
                    141:                socklen_t our_addr_len;
                    142: 
                    143:                our_addr_len = sizeof(our_addr);
                    144: 
1.1.1.2 ! misho     145:                if (-1 == getsockname(con->fd, (struct sockaddr *)&our_addr, &our_addr_len)
        !           146:                    || our_addr_len > (socklen_t)sizeof(our_addr)) {
1.1       misho     147:                        con->http_status = 500;
                    148: 
                    149:                        log_error_write(srv, __FILE__, __LINE__, "ss",
                    150:                                        "can't get sockname", strerror(errno));
                    151: 
                    152:                        buffer_free(o);
                    153:                        return 0;
                    154:                }
                    155: 
                    156: 
                    157:                /* Lookup name: secondly try to get hostname for bind address */
                    158:                switch(our_addr.plain.sa_family) {
                    159: #ifdef HAVE_IPV6
                    160:                case AF_INET6:
                    161:                        if (0 != getnameinfo((const struct sockaddr *)(&our_addr.ipv6),
                    162:                                             SA_LEN((const struct sockaddr *)&our_addr.ipv6),
                    163:                                             hbuf, sizeof(hbuf), NULL, 0, 0)) {
                    164: 
                    165:                                char dst[INET6_ADDRSTRLEN];
                    166: 
                    167:                                log_error_write(srv, __FILE__, __LINE__,
                    168:                                                "SSS", "NOTICE: getnameinfo failed: ",
                    169:                                                strerror(errno), ", using ip-address instead");
                    170: 
1.1.1.2 ! misho     171:                                buffer_append_string_len(o, CONST_STR_LEN("["));
1.1       misho     172:                                buffer_append_string(o,
                    173:                                                     inet_ntop(AF_INET6, (char *)&our_addr.ipv6.sin6_addr,
                    174:                                                               dst, sizeof(dst)));
1.1.1.2 ! misho     175:                                buffer_append_string_len(o, CONST_STR_LEN("]"));
1.1       misho     176:                        } else {
                    177:                                buffer_append_string(o, hbuf);
                    178:                        }
                    179:                        break;
                    180: #endif
                    181:                case AF_INET:
                    182:                        if (NULL == (he = gethostbyaddr((char *)&our_addr.ipv4.sin_addr, sizeof(struct in_addr), AF_INET))) {
                    183:                                log_error_write(srv, __FILE__, __LINE__,
                    184:                                                "SdS", "NOTICE: gethostbyaddr failed: ",
                    185:                                                h_errno, ", using ip-address instead");
                    186: 
                    187:                                buffer_append_string(o, inet_ntoa(our_addr.ipv4.sin_addr));
                    188:                        } else {
                    189:                                buffer_append_string(o, he->h_name);
                    190:                        }
                    191:                        break;
                    192:                default:
                    193:                        log_error_write(srv, __FILE__, __LINE__,
                    194:                                        "S", "ERROR: unsupported address-type");
                    195: 
                    196:                        buffer_free(o);
                    197:                        return -1;
                    198:                }
                    199: 
                    200:                {
                    201:                        unsigned short default_port = 80;
                    202:                        if (buffer_is_equal_caseless_string(con->uri.scheme, CONST_STR_LEN("https"))) {
                    203:                                default_port = 443;
                    204:                        }
                    205:                        if (default_port != srv->srvconf.port) {
                    206:                                buffer_append_string_len(o, CONST_STR_LEN(":"));
1.1.1.2 ! misho     207:                                buffer_append_int(o, srv->srvconf.port);
1.1       misho     208:                        }
                    209:                }
                    210:        }
1.1.1.2 ! misho     211:        buffer_append_string_encoded(o, CONST_BUF_LEN(con->uri.path), ENCODING_REL_URI);
1.1       misho     212:        buffer_append_string_len(o, CONST_STR_LEN("/"));
1.1.1.2 ! misho     213:        if (!buffer_string_is_empty(con->uri.query)) {
1.1       misho     214:                buffer_append_string_len(o, CONST_STR_LEN("?"));
                    215:                buffer_append_string_buffer(o, con->uri.query);
                    216:        }
                    217: 
                    218:        response_header_insert(srv, con, CONST_STR_LEN("Location"), CONST_BUF_LEN(o));
                    219: 
                    220:        con->http_status = 301;
                    221:        con->file_finished = 1;
                    222: 
                    223:        buffer_free(o);
                    224: 
                    225:        return 0;
                    226: }
                    227: 
                    228: buffer * strftime_cache_get(server *srv, time_t last_mod) {
                    229:        struct tm *tm;
                    230:        size_t i;
                    231: 
                    232:        for (i = 0; i < FILE_CACHE_MAX; i++) {
                    233:                /* found cache-entry */
                    234:                if (srv->mtime_cache[i].mtime == last_mod) return srv->mtime_cache[i].str;
                    235: 
                    236:                /* found empty slot */
                    237:                if (srv->mtime_cache[i].mtime == 0) break;
                    238:        }
                    239: 
                    240:        if (i == FILE_CACHE_MAX) {
                    241:                i = 0;
                    242:        }
                    243: 
                    244:        srv->mtime_cache[i].mtime = last_mod;
1.1.1.2 ! misho     245:        buffer_string_prepare_copy(srv->mtime_cache[i].str, 1023);
1.1       misho     246:        tm = gmtime(&(srv->mtime_cache[i].mtime));
1.1.1.2 ! misho     247:        buffer_append_strftime(srv->mtime_cache[i].str, "%a, %d %b %Y %H:%M:%S GMT", tm);
1.1       misho     248: 
                    249:        return srv->mtime_cache[i].str;
                    250: }
                    251: 
                    252: 
                    253: int http_response_handle_cachable(server *srv, connection *con, buffer *mtime) {
1.1.1.2 ! misho     254:        int head_or_get =
        !           255:                (  HTTP_METHOD_GET  == con->request.http_method
        !           256:                || HTTP_METHOD_HEAD == con->request.http_method);
1.1       misho     257:        UNUSED(srv);
1.1.1.2 ! misho     258: 
1.1       misho     259:        /*
                    260:         * 14.26 If-None-Match
                    261:         *    [...]
                    262:         *    If none of the entity tags match, then the server MAY perform the
                    263:         *    requested method as if the If-None-Match header field did not exist,
                    264:         *    but MUST also ignore any If-Modified-Since header field(s) in the
                    265:         *    request. That is, if no entity tags match, then the server MUST NOT
                    266:         *    return a 304 (Not Modified) response.
                    267:         */
                    268: 
                    269:        if (con->request.http_if_none_match) {
1.1.1.2 ! misho     270:                /* use strong etag checking for now: weak comparison must not be used
        !           271:                 * for ranged requests
        !           272:                 */
        !           273:                if (etag_is_equal(con->physical.etag, con->request.http_if_none_match, 0)) {
        !           274:                        if (head_or_get) {
1.1       misho     275:                                con->http_status = 304;
                    276:                                return HANDLER_FINISHED;
                    277:                        } else {
                    278:                                con->http_status = 412;
                    279:                                con->mode = DIRECT;
                    280:                                return HANDLER_FINISHED;
                    281:                        }
                    282:                }
1.1.1.2 ! misho     283:        } else if (con->request.http_if_modified_since && head_or_get) {
        !           284:                /* last-modified handling */
1.1       misho     285:                size_t used_len;
                    286:                char *semicolon;
                    287: 
                    288:                if (NULL == (semicolon = strchr(con->request.http_if_modified_since, ';'))) {
                    289:                        used_len = strlen(con->request.http_if_modified_since);
                    290:                } else {
                    291:                        used_len = semicolon - con->request.http_if_modified_since;
                    292:                }
                    293: 
                    294:                if (0 == strncmp(con->request.http_if_modified_since, mtime->ptr, used_len)) {
                    295:                        if ('\0' == mtime->ptr[used_len]) con->http_status = 304;
                    296:                        return HANDLER_FINISHED;
                    297:                } else {
                    298:                        char buf[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")];
                    299:                        time_t t_header, t_file;
                    300:                        struct tm tm;
                    301: 
                    302:                        /* convert to timestamp */
                    303:                        if (used_len >= sizeof(buf)) return HANDLER_GO_ON;
                    304: 
                    305:                        strncpy(buf, con->request.http_if_modified_since, used_len);
                    306:                        buf[used_len] = '\0';
                    307: 
                    308:                        if (NULL == strptime(buf, "%a, %d %b %Y %H:%M:%S GMT", &tm)) {
                    309:                                /**
                    310:                                 * parsing failed, let's get out of here 
                    311:                                 */
                    312:                                return HANDLER_GO_ON;
                    313:                        }
                    314:                        tm.tm_isdst = 0;
                    315:                        t_header = mktime(&tm);
                    316: 
                    317:                        strptime(mtime->ptr, "%a, %d %b %Y %H:%M:%S GMT", &tm);
                    318:                        tm.tm_isdst = 0;
                    319:                        t_file = mktime(&tm);
                    320: 
                    321:                        if (t_file > t_header) return HANDLER_GO_ON;
                    322: 
                    323:                        con->http_status = 304;
                    324:                        return HANDLER_FINISHED;
                    325:                }
                    326:        }
                    327: 
                    328:        return HANDLER_GO_ON;
1.1.1.2 ! misho     329: }
        !           330: 
        !           331: 
        !           332: static int http_response_parse_range(server *srv, connection *con, buffer *path, stat_cache_entry *sce) {
        !           333:        int multipart = 0;
        !           334:        int error;
        !           335:        off_t start, end;
        !           336:        const char *s, *minus;
        !           337:        char *boundary = "fkj49sn38dcn3";
        !           338:        data_string *ds;
        !           339:        buffer *content_type = NULL;
        !           340: 
        !           341:        start = 0;
        !           342:        end = sce->st.st_size - 1;
        !           343: 
        !           344:        con->response.content_length = 0;
        !           345: 
        !           346:        if (NULL != (ds = (data_string *)array_get_element(con->response.headers, "Content-Type"))) {
        !           347:                content_type = ds->value;
        !           348:        }
        !           349: 
        !           350:        for (s = con->request.http_range, error = 0;
        !           351:             !error && *s && NULL != (minus = strchr(s, '-')); ) {
        !           352:                char *err;
        !           353:                off_t la, le;
        !           354: 
        !           355:                if (s == minus) {
        !           356:                        /* -<stop> */
        !           357: 
        !           358:                        le = strtoll(s, &err, 10);
        !           359: 
        !           360:                        if (le == 0) {
        !           361:                                /* RFC 2616 - 14.35.1 */
        !           362: 
        !           363:                                con->http_status = 416;
        !           364:                                error = 1;
        !           365:                        } else if (*err == '\0') {
        !           366:                                /* end */
        !           367:                                s = err;
        !           368: 
        !           369:                                end = sce->st.st_size - 1;
        !           370:                                start = sce->st.st_size + le;
        !           371:                        } else if (*err == ',') {
        !           372:                                multipart = 1;
        !           373:                                s = err + 1;
        !           374: 
        !           375:                                end = sce->st.st_size - 1;
        !           376:                                start = sce->st.st_size + le;
        !           377:                        } else {
        !           378:                                error = 1;
        !           379:                        }
        !           380: 
        !           381:                } else if (*(minus+1) == '\0' || *(minus+1) == ',') {
        !           382:                        /* <start>- */
        !           383: 
        !           384:                        la = strtoll(s, &err, 10);
        !           385: 
        !           386:                        if (err == minus) {
        !           387:                                /* ok */
        !           388: 
        !           389:                                if (*(err + 1) == '\0') {
        !           390:                                        s = err + 1;
        !           391: 
        !           392:                                        end = sce->st.st_size - 1;
        !           393:                                        start = la;
        !           394: 
        !           395:                                } else if (*(err + 1) == ',') {
        !           396:                                        multipart = 1;
        !           397:                                        s = err + 2;
        !           398: 
        !           399:                                        end = sce->st.st_size - 1;
        !           400:                                        start = la;
        !           401:                                } else {
        !           402:                                        error = 1;
        !           403:                                }
        !           404:                        } else {
        !           405:                                /* error */
        !           406:                                error = 1;
        !           407:                        }
        !           408:                } else {
        !           409:                        /* <start>-<stop> */
        !           410: 
        !           411:                        la = strtoll(s, &err, 10);
        !           412: 
        !           413:                        if (err == minus) {
        !           414:                                le = strtoll(minus+1, &err, 10);
        !           415: 
        !           416:                                /* RFC 2616 - 14.35.1 */
        !           417:                                if (la > le) {
        !           418:                                        error = 1;
        !           419:                                }
        !           420: 
        !           421:                                if (*err == '\0') {
        !           422:                                        /* ok, end*/
        !           423:                                        s = err;
        !           424: 
        !           425:                                        end = le;
        !           426:                                        start = la;
        !           427:                                } else if (*err == ',') {
        !           428:                                        multipart = 1;
        !           429:                                        s = err + 1;
        !           430: 
        !           431:                                        end = le;
        !           432:                                        start = la;
        !           433:                                } else {
        !           434:                                        /* error */
        !           435: 
        !           436:                                        error = 1;
        !           437:                                }
        !           438:                        } else {
        !           439:                                /* error */
        !           440: 
        !           441:                                error = 1;
        !           442:                        }
        !           443:                }
        !           444: 
        !           445:                if (!error) {
        !           446:                        if (start < 0) start = 0;
        !           447: 
        !           448:                        /* RFC 2616 - 14.35.1 */
        !           449:                        if (end > sce->st.st_size - 1) end = sce->st.st_size - 1;
        !           450: 
        !           451:                        if (start > sce->st.st_size - 1) {
        !           452:                                error = 1;
        !           453: 
        !           454:                                con->http_status = 416;
        !           455:                        }
        !           456:                }
        !           457: 
        !           458:                if (!error) {
        !           459:                        if (multipart) {
        !           460:                                /* write boundary-header */
        !           461:                                buffer *b = buffer_init();
        !           462: 
        !           463:                                buffer_copy_string_len(b, CONST_STR_LEN("\r\n--"));
        !           464:                                buffer_append_string(b, boundary);
        !           465: 
        !           466:                                /* write Content-Range */
        !           467:                                buffer_append_string_len(b, CONST_STR_LEN("\r\nContent-Range: bytes "));
        !           468:                                buffer_append_int(b, start);
        !           469:                                buffer_append_string_len(b, CONST_STR_LEN("-"));
        !           470:                                buffer_append_int(b, end);
        !           471:                                buffer_append_string_len(b, CONST_STR_LEN("/"));
        !           472:                                buffer_append_int(b, sce->st.st_size);
        !           473: 
        !           474:                                buffer_append_string_len(b, CONST_STR_LEN("\r\nContent-Type: "));
        !           475:                                buffer_append_string_buffer(b, content_type);
        !           476: 
        !           477:                                /* write END-OF-HEADER */
        !           478:                                buffer_append_string_len(b, CONST_STR_LEN("\r\n\r\n"));
        !           479: 
        !           480:                                con->response.content_length += buffer_string_length(b);
        !           481:                                chunkqueue_append_buffer(con->write_queue, b);
        !           482:                                buffer_free(b);
        !           483:                        }
        !           484: 
        !           485:                        chunkqueue_append_file(con->write_queue, path, start, end - start + 1);
        !           486:                        con->response.content_length += end - start + 1;
        !           487:                }
        !           488:        }
        !           489: 
        !           490:        /* something went wrong */
        !           491:        if (error) return -1;
        !           492: 
        !           493:        if (multipart) {
        !           494:                /* add boundary end */
        !           495:                buffer *b = buffer_init();
        !           496: 
        !           497:                buffer_copy_string_len(b, "\r\n--", 4);
        !           498:                buffer_append_string(b, boundary);
        !           499:                buffer_append_string_len(b, "--\r\n", 4);
        !           500: 
        !           501:                con->response.content_length += buffer_string_length(b);
        !           502:                chunkqueue_append_buffer(con->write_queue, b);
        !           503:                buffer_free(b);
        !           504: 
        !           505:                /* set header-fields */
        !           506: 
        !           507:                buffer_copy_string_len(srv->tmp_buf, CONST_STR_LEN("multipart/byteranges; boundary="));
        !           508:                buffer_append_string(srv->tmp_buf, boundary);
        !           509: 
        !           510:                /* overwrite content-type */
        !           511:                response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(srv->tmp_buf));
        !           512:        } else {
        !           513:                /* add Content-Range-header */
        !           514: 
        !           515:                buffer_copy_string_len(srv->tmp_buf, CONST_STR_LEN("bytes "));
        !           516:                buffer_append_int(srv->tmp_buf, start);
        !           517:                buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("-"));
        !           518:                buffer_append_int(srv->tmp_buf, end);
        !           519:                buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("/"));
        !           520:                buffer_append_int(srv->tmp_buf, sce->st.st_size);
        !           521: 
        !           522:                response_header_insert(srv, con, CONST_STR_LEN("Content-Range"), CONST_BUF_LEN(srv->tmp_buf));
        !           523:        }
        !           524: 
        !           525:        /* ok, the file is set-up */
        !           526:        return 0;
        !           527: }
        !           528: 
        !           529: 
        !           530: void http_response_send_file (server *srv, connection *con, buffer *path) {
        !           531:        stat_cache_entry *sce = NULL;
        !           532:        buffer *mtime = NULL;
        !           533:        data_string *ds;
        !           534:        int allow_caching = (0 == con->http_status || 200 == con->http_status);
        !           535: 
        !           536:        if (HANDLER_ERROR == stat_cache_get_entry(srv, con, path, &sce)) {
        !           537:                con->http_status = (errno == ENOENT) ? 404 : 403;
        !           538: 
        !           539:                log_error_write(srv, __FILE__, __LINE__, "sbsb",
        !           540:                                "not a regular file:", con->uri.path,
        !           541:                                "->", path);
        !           542: 
        !           543:                return;
        !           544:        }
        !           545: 
        !           546:        /* we only handline regular files */
        !           547: #ifdef HAVE_LSTAT
        !           548:        if ((sce->is_symlink == 1) && !con->conf.follow_symlink) {
        !           549:                con->http_status = 403;
        !           550: 
        !           551:                if (con->conf.log_request_handling) {
        !           552:                        log_error_write(srv, __FILE__, __LINE__,  "s",  "-- access denied due symlink restriction");
        !           553:                        log_error_write(srv, __FILE__, __LINE__,  "sb", "Path         :", path);
        !           554:                }
        !           555: 
        !           556:                return;
        !           557:        }
        !           558: #endif
        !           559:        if (!S_ISREG(sce->st.st_mode)) {
        !           560:                con->http_status = 403;
        !           561: 
        !           562:                if (con->conf.log_file_not_found) {
        !           563:                        log_error_write(srv, __FILE__, __LINE__, "sbsb",
        !           564:                                        "not a regular file:", con->uri.path,
        !           565:                                        "->", sce->name);
        !           566:                }
        !           567: 
        !           568:                return;
        !           569:        }
        !           570: 
        !           571:        /* mod_compress might set several data directly, don't overwrite them */
        !           572: 
        !           573:        /* set response content-type, if not set already */
        !           574: 
        !           575:        if (NULL == array_get_element(con->response.headers, "Content-Type")) {
        !           576:                if (buffer_string_is_empty(sce->content_type)) {
        !           577:                        /* we are setting application/octet-stream, but also announce that
        !           578:                         * this header field might change in the seconds few requests
        !           579:                         *
        !           580:                         * This should fix the aggressive caching of FF and the script download
        !           581:                         * seen by the first installations
        !           582:                         */
        !           583:                        response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("application/octet-stream"));
        !           584: 
        !           585:                        allow_caching = 0;
        !           586:                } else {
        !           587:                        response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type));
        !           588:                }
        !           589:        }
        !           590: 
        !           591:        if (con->conf.range_requests) {
        !           592:                response_header_overwrite(srv, con, CONST_STR_LEN("Accept-Ranges"), CONST_STR_LEN("bytes"));
        !           593:        }
        !           594: 
        !           595:        if (allow_caching) {
        !           596:                if (con->etag_flags != 0 && !buffer_string_is_empty(sce->etag)) {
        !           597:                        if (NULL == array_get_element(con->response.headers, "ETag")) {
        !           598:                                /* generate e-tag */
        !           599:                                etag_mutate(con->physical.etag, sce->etag);
        !           600: 
        !           601:                                response_header_overwrite(srv, con, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con->physical.etag));
        !           602:                        }
        !           603:                }
        !           604: 
        !           605:                /* prepare header */
        !           606:                if (NULL == (ds = (data_string *)array_get_element(con->response.headers, "Last-Modified"))) {
        !           607:                        mtime = strftime_cache_get(srv, sce->st.st_mtime);
        !           608:                        response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime));
        !           609:                } else {
        !           610:                        mtime = ds->value;
        !           611:                }
        !           612: 
        !           613:                if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, mtime)) {
        !           614:                        return;
        !           615:                }
        !           616:        }
        !           617: 
        !           618:        if (con->request.http_range && con->conf.range_requests
        !           619:            && (200 == con->http_status || 0 == con->http_status)
        !           620:            && NULL == array_get_element(con->response.headers, "Content-Encoding")) {
        !           621:                int do_range_request = 1;
        !           622:                /* check if we have a conditional GET */
        !           623: 
        !           624:                if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "If-Range"))) {
        !           625:                        /* if the value is the same as our ETag, we do a Range-request,
        !           626:                         * otherwise a full 200 */
        !           627: 
        !           628:                        if (ds->value->ptr[0] == '"') {
        !           629:                                /**
        !           630:                                 * client wants a ETag
        !           631:                                 */
        !           632:                                if (!con->physical.etag) {
        !           633:                                        do_range_request = 0;
        !           634:                                } else if (!buffer_is_equal(ds->value, con->physical.etag)) {
        !           635:                                        do_range_request = 0;
        !           636:                                }
        !           637:                        } else if (!mtime) {
        !           638:                                /**
        !           639:                                 * we don't have a Last-Modified and can match the If-Range:
        !           640:                                 *
        !           641:                                 * sending all
        !           642:                                 */
        !           643:                                do_range_request = 0;
        !           644:                        } else if (!buffer_is_equal(ds->value, mtime)) {
        !           645:                                do_range_request = 0;
        !           646:                        }
        !           647:                }
        !           648: 
        !           649:                if (do_range_request) {
        !           650:                        /* content prepared, I'm done */
        !           651:                        con->file_finished = 1;
        !           652: 
        !           653:                        if (0 == http_response_parse_range(srv, con, path, sce)) {
        !           654:                                con->http_status = 206;
        !           655:                        }
        !           656:                        return;
        !           657:                }
        !           658:        }
        !           659: 
        !           660:        /* if we are still here, prepare body */
        !           661: 
        !           662:        /* we add it here for all requests
        !           663:         * the HEAD request will drop it afterwards again
        !           664:         */
        !           665:        if (0 == sce->st.st_size || 0 == http_chunk_append_file(srv, con, path)) {
        !           666:                con->http_status = 200;
        !           667:                con->file_finished = 1;
        !           668:        } else {
        !           669:                con->http_status = 403;
        !           670:        }
        !           671: }
        !           672: 
        !           673: void http_response_xsendfile (server *srv, connection *con, buffer *path, const array *xdocroot) {
        !           674:        const int status = con->http_status;
        !           675:        int valid = 1;
        !           676: 
        !           677:        con->file_started = 1;
        !           678: 
        !           679:        /* reset Content-Length, if set by backend
        !           680:         * Content-Length might later be set to size of X-Sendfile static file,
        !           681:         * determined by open(), fstat() to reduces race conditions if the file
        !           682:         * is modified between stat() (stat_cache_get_entry()) and open(). */
        !           683:        if (con->parsed_response & HTTP_CONTENT_LENGTH) {
        !           684:                data_string *ds = (data_string *) array_get_element(con->response.headers, "Content-Length");
        !           685:                if (ds) buffer_reset(ds->value);
        !           686:                con->parsed_response &= ~HTTP_CONTENT_LENGTH;
        !           687:                con->response.content_length = -1;
        !           688:        }
        !           689: 
        !           690:        buffer_urldecode_path(path);
        !           691:        buffer_path_simplify(path, path);
        !           692:        if (con->conf.force_lowercase_filenames) {
        !           693:                buffer_to_lower(path);
        !           694:        }
        !           695: 
        !           696:        /* check that path is under xdocroot(s)
        !           697:         * - xdocroot should have trailing slash appended at config time
        !           698:         * - con->conf.force_lowercase_filenames is not a server-wide setting,
        !           699:         *   and so can not be definitively applied to xdocroot at config time*/
        !           700:        if (xdocroot->used) {
        !           701:                size_t i, xlen = buffer_string_length(path);
        !           702:                for (i = 0; i < xdocroot->used; ++i) {
        !           703:                        data_string *ds = (data_string *)xdocroot->data[i];
        !           704:                        size_t dlen = buffer_string_length(ds->value);
        !           705:                        if (dlen <= xlen
        !           706:                            && (!con->conf.force_lowercase_filenames
        !           707:                                ? 0 == memcmp(path->ptr, ds->value->ptr, dlen)
        !           708:                                : 0 == strncasecmp(path->ptr, ds->value->ptr, dlen))) {
        !           709:                                break;
        !           710:                        }
        !           711:                }
        !           712:                if (i == xdocroot->used) {
        !           713:                        log_error_write(srv, __FILE__, __LINE__, "SBs",
        !           714:                                        "X-Sendfile (", path,
        !           715:                                        ") not under configured x-sendfile-docroot(s)");
        !           716:                        con->http_status = 403;
        !           717:                        valid = 0;
        !           718:                }
        !           719:        }
        !           720: 
        !           721:        if (valid) http_response_send_file(srv, con, path);
        !           722: 
        !           723:        if (con->http_status >= 400 && status < 300) {
        !           724:                con->mode = DIRECT;
        !           725:        } else if (0 != status && 200 != status) {
        !           726:                con->http_status = status;
        !           727:        }
        !           728: }
        !           729: 
        !           730: void http_response_backend_error (server *srv, connection *con) {
        !           731:        UNUSED(srv);
        !           732:        if (con->file_started) {
        !           733:                /*(response might have been already started, kill the connection)*/
        !           734:                /*(mode == DIRECT to avoid later call to http_response_backend_done())*/
        !           735:                con->mode = DIRECT;  /*(avoid sending final chunked block)*/
        !           736:                con->keep_alive = 0; /*(no keep-alive; final chunked block not sent)*/
        !           737:                con->file_finished = 1;
        !           738:        } /*(else error status set later by http_response_backend_done())*/
        !           739: }
        !           740: 
        !           741: void http_response_backend_done (server *srv, connection *con) {
        !           742:        /* (not CON_STATE_ERROR and not CON_STATE_RESPONSE_END,
        !           743:         *  i.e. not called from handle_connection_close or connection_reset
        !           744:         *  hooks, except maybe from errdoc handler, which later resets state)*/
        !           745:        switch (con->state) {
        !           746:        case CON_STATE_HANDLE_REQUEST:
        !           747:        case CON_STATE_READ_POST:
        !           748:                if (!con->file_started) {
        !           749:                        /* Send an error if we haven't sent any data yet */
        !           750:                        con->http_status = 500;
        !           751:                        con->mode = DIRECT;
        !           752:                        break;
        !           753:                } /* else fall through */
        !           754:        case CON_STATE_WRITE:
        !           755:                if (!con->file_finished) {
        !           756:                        http_chunk_close(srv, con);
        !           757:                        con->file_finished = 1;
        !           758:                }
        !           759:        default:
        !           760:                break;
        !           761:        }
1.1       misho     762: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>