source: trunk/third-party/shttpd/io_file.c @ 6644

Last change on this file since 6644 was 6644, checked in by charles, 13 years ago

upgrade to shttpd 1.42

File size: 4.2 KB
Line 
1/*
2 * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
3 * All rights reserved
4 *
5 * "THE BEER-WARE LICENSE" (Revision 42):
6 * Sergey Lyubka wrote this file.  As long as you retain this notice you
7 * can do whatever you want with this stuff. If we meet some day, and you think
8 * this stuff is worth it, you can buy me a beer in return.
9 */
10
11#include "defs.h"
12
13static int
14write_file(struct stream *stream, const void *buf, size_t len)
15{
16        struct stat     st;
17        struct stream   *rem = &stream->conn->rem;
18        int             n, fd = stream->chan.fd;
19
20        assert(fd != -1);
21        n = write(fd, buf, len);
22
23        DBG(("put_file(%p, %d): %d bytes", (void *) stream, (int) len, n));
24
25        if (n <= 0 || (rem->io.total >= (big_int_t) rem->content_len)) {
26                (void) fstat(fd, &st);
27                stream->io.head = stream->headers_len =
28                    _shttpd_snprintf(stream->io.buf,
29                    stream->io.size, "HTTP/1.1 %d OK\r\n"
30                    "Content-Length: %lu\r\nConnection: close\r\n\r\n",
31                    stream->conn->status, st.st_size);
32                _shttpd_stop_stream(stream);
33        }
34
35        return (n);
36}
37
38static int
39read_file(struct stream *stream, void *buf, size_t len)
40{
41#ifdef USE_SENDFILE
42        struct  iovec   vec;
43        struct  sf_hdtr hd = {&vec, 1, NULL, 0}, *hdp = &hd;
44        int             sock, fd, n;
45        size_t          nbytes;
46        off_t           sent;
47
48        sock = stream->conn->rem.chan.sock;
49        fd = stream->chan.fd;
50
51        /* If this is the first call for this file, send the headers */
52        vec.iov_base = stream->io.buf;
53        vec.iov_len = stream->headers_len;
54        if (stream->io.total > 0)
55                hdp = NULL;
56
57        nbytes = stream->content_len - stream->io.total;
58        n = sendfile(fd, sock, lseek(fd, 0, SEEK_CUR), nbytes, hdp, &sent, 0);
59
60        if (n == -1 && ERRNO != EAGAIN) {
61                stream->flags &= ~FLAG_DONT_CLOSE;
62                return (n);
63        }
64
65        stream->conn->ctx->out += sent;
66
67        /* If we have sent the HTTP headers in this turn, clear them off */
68        if (stream->io.total == 0) {
69                assert(sent >= stream->headers_len);
70                sent -= stream->headers_len;
71                io_clear(&stream->io);
72        }
73
74        (void) lseek(fd, sent, SEEK_CUR);
75        stream->io.total += sent;
76        stream->flags |= FLAG_DONT_CLOSE;
77
78        return (0);
79#endif /* USE_SENDFILE */
80
81        assert(stream->chan.fd != -1);
82        return (read(stream->chan.fd, buf, len));
83}
84
85static void
86close_file(struct stream *stream)
87{
88        assert(stream->chan.fd != -1);
89        (void) close(stream->chan.fd);
90}
91
92void
93_shttpd_get_file(struct conn *c, struct stat *stp)
94{
95        char            date[64], lm[64], etag[64], range[64] = "";
96        size_t          n, status = 200;
97        unsigned long   r1, r2;
98        const char      *fmt = "%a, %d %b %Y %H:%M:%S GMT", *msg = "OK";
99        big_int_t       cl; /* Content-Length */
100
101        if (c->mime_type.len == 0)
102                _shttpd_get_mime_type(c->ctx, c->uri,
103                    strlen(c->uri), &c->mime_type); 
104        cl = (big_int_t) stp->st_size;
105
106        /* If Range: header specified, act accordingly */
107        if (c->ch.range.v_vec.len > 0 &&
108            (n = sscanf(c->ch.range.v_vec.ptr,"bytes=%lu-%lu",&r1, &r2)) > 0) {
109                status = 206;
110                (void) lseek(c->loc.chan.fd, r1, SEEK_SET);
111                cl = n == 2 ? r2 - r1 + 1: cl - r1;
112                (void) _shttpd_snprintf(range, sizeof(range),
113                    "Content-Range: bytes %lu-%lu/%lu\r\n",
114                    r1, r1 + cl - 1, (unsigned long) stp->st_size);
115                msg = "Partial Content";
116        }
117
118        /* Prepare Etag, Date, Last-Modified headers */
119        (void) strftime(date, sizeof(date),
120            fmt, localtime(&_shttpd_current_time));
121        (void) strftime(lm, sizeof(lm), fmt, localtime(&stp->st_mtime));
122        (void) _shttpd_snprintf(etag, sizeof(etag), "%lx.%lx",
123            (unsigned long) stp->st_mtime, (unsigned long) stp->st_size);
124
125        /*
126         * We do not do io_inc_head here, because it will increase 'total'
127         * member in io. We want 'total' to be equal to the content size,
128         * and exclude the headers length from it.
129         */
130        c->loc.io.head = c->loc.headers_len = _shttpd_snprintf(c->loc.io.buf,
131            c->loc.io.size,
132            "HTTP/1.1 %d %s\r\n"
133            "Date: %s\r\n"
134            "Last-Modified: %s\r\n"
135            "Etag: \"%s\"\r\n"
136            "Content-Type: %.*s\r\n"
137            "Content-Length: %lu\r\n"
138            "Accept-Ranges: bytes\r\n"
139            "%s\r\n",
140            status, msg, date, lm, etag,
141            c->mime_type.len, c->mime_type.ptr, cl, range);
142
143        c->status = status;
144        c->loc.content_len = cl;
145        c->loc.io_class = &_shttpd_io_file;
146        c->loc.flags |= FLAG_R | FLAG_ALWAYS_READY;
147
148        if (c->method == METHOD_HEAD)
149                _shttpd_stop_stream(&c->loc);
150}
151
152const struct io_class   _shttpd_io_file =  {
153        "file",
154        read_file,
155        write_file,
156        close_file
157};
Note: See TracBrowser for help on using the repository browser.