- next is 1.4.56
[lighttpd.git] / src / http_chunk.c
blob87af3e7c022fec60c9f612bb3d510c1363ed5d98
1 #include "first.h"
3 /**
4 * the HTTP chunk-API
7 */
9 #include "base.h"
10 #include "chunk.h"
11 #include "http_chunk.h"
12 #include "stat_cache.h"
13 #include "fdevent.h"
14 #include "log.h"
16 #include <sys/types.h>
17 #include <sys/stat.h>
19 #include <stdlib.h>
20 #include <unistd.h>
22 #include <errno.h>
23 #include <string.h>
25 static buffer * http_chunk_header(buffer *b, uintmax_t len) {
26 buffer_clear(b);
27 buffer_append_uint_hex(b, len);
28 buffer_append_string_len(b, CONST_STR_LEN("\r\n"));
29 return b;
32 static void http_chunk_append_len(server *srv, connection *con, uintmax_t len) {
33 buffer *b = http_chunk_header(srv->tmp_chunk_len, len);
34 chunkqueue_append_mem(con->write_queue, CONST_BUF_LEN(b));
37 static int http_chunk_append_file_open_fstat(server *srv, connection *con, buffer *fn, struct stat *st) {
38 if (!con->conf.follow_symlink
39 && 0 != stat_cache_path_contains_symlink(srv, fn)) {
40 return -1;
43 return stat_cache_open_rdonly_fstat(fn, st, con->conf.follow_symlink);
46 static int http_chunk_append_read_fd_range(server *srv, connection *con, buffer *fn, int fd, off_t offset, off_t len) {
47 /* note: this routine should not be used for range requests
48 * unless the total size of ranges requested is small */
49 /* note: future: could read into existing MEM_CHUNK in cq->last if
50 * there is sufficient space, but would need to adjust for existing
51 * offset in for cq->bytes_in in chunkqueue_append_buffer_commit() */
52 UNUSED(fn);
54 if (con->response.send_chunked) {
55 http_chunk_append_len(srv, con, (uintmax_t)len);
58 if (0 != offset && -1 == lseek(fd, offset, SEEK_SET)) return -1;
59 chunkqueue * const cq = con->write_queue;
60 buffer * const b = chunkqueue_append_buffer_open_sz(cq, len+2);
61 ssize_t rd;
62 offset = 0;
63 do {
64 rd = read(fd, b->ptr+offset, len-offset);
65 } while (rd > 0 ? (offset += rd, len -= rd) : errno == EINTR);
66 buffer_commit(b, offset);
68 if (con->response.send_chunked) {
69 buffer_append_string_len(b, CONST_STR_LEN("\r\n"));
72 chunkqueue_append_buffer_commit(cq);
73 return (rd >= 0) ? 0 : -1;
76 static void http_chunk_append_file_fd_range(server *srv, connection *con, buffer *fn, int fd, off_t offset, off_t len) {
77 chunkqueue *cq = con->write_queue;
79 if (con->response.send_chunked) {
80 http_chunk_append_len(srv, con, (uintmax_t)len);
83 chunkqueue_append_file_fd(cq, fn, fd, offset, len);
85 if (con->response.send_chunked) {
86 chunkqueue_append_mem(cq, CONST_STR_LEN("\r\n"));
90 int http_chunk_append_file_range(server *srv, connection *con, buffer *fn, off_t offset, off_t len) {
91 struct stat st;
92 const int fd = http_chunk_append_file_open_fstat(srv, con, fn, &st);
93 if (fd < 0) return -1;
95 if (-1 == len) {
96 if (offset >= st.st_size) {
97 close(fd);
98 return (offset == st.st_size) ? 0 : -1;
100 len = st.st_size - offset;
101 } else if (st.st_size - offset < len) {
102 close(fd);
103 return -1;
106 http_chunk_append_file_fd_range(srv, con, fn, fd, offset, len);
107 return 0;
110 int http_chunk_append_file(server *srv, connection *con, buffer *fn) {
111 struct stat st;
112 const int fd = http_chunk_append_file_open_fstat(srv, con, fn, &st);
113 if (fd < 0) return -1;
114 http_chunk_append_file_fd(srv, con, fn, fd, st.st_size);
115 return 0;
118 int http_chunk_append_file_fd(server *srv, connection *con, buffer *fn, int fd, off_t sz) {
119 if (sz > 32768) {
120 http_chunk_append_file_fd_range(srv, con, fn, fd, 0, sz);
121 return 0;
122 } else {
123 int rc = (0 != sz) /*(read small files into memory)*/
124 ? http_chunk_append_read_fd_range(srv, con, fn, fd, 0, sz)
125 : 0;
126 close(fd);
127 return rc;
131 static int http_chunk_append_to_tempfile(server *srv, connection *con, const char * mem, size_t len) {
132 chunkqueue * const cq = con->write_queue;
134 if (con->response.send_chunked) {
135 buffer *b = http_chunk_header(srv->tmp_chunk_len, len);
136 if (0 != chunkqueue_append_mem_to_tempfile(srv, cq, CONST_BUF_LEN(b))) {
137 return -1;
141 if (0 != chunkqueue_append_mem_to_tempfile(srv, cq, mem, len)) {
142 return -1;
145 if (con->response.send_chunked) {
146 if (0 != chunkqueue_append_mem_to_tempfile(srv, cq, CONST_STR_LEN("\r\n"))) {
147 return -1;
151 return 0;
154 static int http_chunk_append_cq_to_tempfile(server *srv, connection *con, chunkqueue *src, size_t len) {
155 chunkqueue * const cq = con->write_queue;
157 if (con->response.send_chunked) {
158 buffer *b = http_chunk_header(srv->tmp_chunk_len, len);
159 if (0 != chunkqueue_append_mem_to_tempfile(srv, cq, CONST_BUF_LEN(b))) {
160 return -1;
164 if (0 != chunkqueue_steal_with_tempfiles(srv, cq, src, len)) {
165 return -1;
168 if (con->response.send_chunked) {
169 if (0!=chunkqueue_append_mem_to_tempfile(srv,cq,CONST_STR_LEN("\r\n"))){
170 return -1;
174 return 0;
177 static int http_chunk_uses_tempfile(server *srv, connection *con, size_t len) {
178 chunkqueue * const cq = con->write_queue;
179 chunk *c = cq->last;
180 UNUSED(srv);
182 /* current usage does not append_mem or append_buffer after appending
183 * file, so not checking if users of this interface have appended large
184 * (references to) files to chunkqueue, which would not be in memory
185 * (but included in calculation for whether or not to use temp file) */
187 /*(allow slightly larger mem use if FDEVENT_STREAM_RESPONSE_BUFMIN
188 * to reduce creation of temp files when backend producer will be
189 * blocked until more data is sent to network to client)*/
191 if ((c && c->type == FILE_CHUNK && c->file.is_temp)
192 || cq->bytes_in - cq->bytes_out + len
193 > 1024 * ((con->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN) ? 128 : 64)) {
194 return 1;
197 return 0;
200 int http_chunk_append_buffer(server *srv, connection *con, buffer *mem) {
201 chunkqueue * const cq = con->write_queue;
202 size_t len = buffer_string_length(mem);
203 if (0 == len) return 0;
205 if (http_chunk_uses_tempfile(srv, con, len)) {
206 return http_chunk_append_to_tempfile(srv, con, mem->ptr, len);
209 if (con->response.send_chunked) {
210 http_chunk_append_len(srv, con, len);
213 /*(chunkqueue_append_buffer() might steal buffer contents)*/
214 chunkqueue_append_buffer(cq, mem);
216 if (con->response.send_chunked) {
217 chunkqueue_append_mem(cq, CONST_STR_LEN("\r\n"));
220 return 0;
223 int http_chunk_append_mem(server *srv, connection *con, const char * mem, size_t len) {
224 chunkqueue * const cq = con->write_queue;
225 if (0 == len) return 0;
226 force_assert(NULL != mem);
228 if (http_chunk_uses_tempfile(srv, con, len)) {
229 return http_chunk_append_to_tempfile(srv, con, mem, len);
232 if (con->response.send_chunked) {
233 http_chunk_append_len(srv, con, len);
236 chunkqueue_append_mem(cq, mem, len);
238 if (con->response.send_chunked) {
239 chunkqueue_append_mem(cq, CONST_STR_LEN("\r\n"));
242 return 0;
245 int http_chunk_transfer_cqlen(server *srv, connection *con, chunkqueue *src, size_t len) {
246 chunkqueue * const cq = con->write_queue;
247 if (0 == len) return 0;
249 if (http_chunk_uses_tempfile(srv, con, len)) {
250 return http_chunk_append_cq_to_tempfile(srv, con, src, len);
253 if (con->response.send_chunked) {
254 http_chunk_append_len(srv, con, len);
257 chunkqueue_steal(cq, src, len);
259 if (con->response.send_chunked) {
260 chunkqueue_append_mem(cq, CONST_STR_LEN("\r\n"));
263 return 0;
266 void http_chunk_close(server *srv, connection *con) {
267 UNUSED(srv);
268 force_assert(NULL != con);
270 if (con->response.send_chunked) {
271 chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("0\r\n\r\n"));