11 #include "http_chunk.h"
12 #include "stat_cache.h"
16 #include <sys/types.h>
25 static buffer
* http_chunk_header(buffer
*b
, uintmax_t len
) {
27 buffer_append_uint_hex(b
, len
);
28 buffer_append_string_len(b
, CONST_STR_LEN("\r\n"));
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
)) {
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() */
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);
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
) {
92 const int fd
= http_chunk_append_file_open_fstat(srv
, con
, fn
, &st
);
93 if (fd
< 0) return -1;
96 if (offset
>= st
.st_size
) {
98 return (offset
== st
.st_size
) ? 0 : -1;
100 len
= st
.st_size
- offset
;
101 } else if (st
.st_size
- offset
< len
) {
106 http_chunk_append_file_fd_range(srv
, con
, fn
, fd
, offset
, len
);
110 int http_chunk_append_file(server
*srv
, connection
*con
, buffer
*fn
) {
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
);
118 int http_chunk_append_file_fd(server
*srv
, connection
*con
, buffer
*fn
, int fd
, off_t sz
) {
120 http_chunk_append_file_fd_range(srv
, con
, fn
, fd
, 0, sz
);
123 int rc
= (0 != sz
) /*(read small files into memory)*/
124 ? http_chunk_append_read_fd_range(srv
, con
, fn
, fd
, 0, sz
)
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
))) {
141 if (0 != chunkqueue_append_mem_to_tempfile(srv
, cq
, mem
, len
)) {
145 if (con
->response
.send_chunked
) {
146 if (0 != chunkqueue_append_mem_to_tempfile(srv
, cq
, CONST_STR_LEN("\r\n"))) {
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
))) {
164 if (0 != chunkqueue_steal_with_tempfiles(srv
, cq
, src
, len
)) {
168 if (con
->response
.send_chunked
) {
169 if (0!=chunkqueue_append_mem_to_tempfile(srv
,cq
,CONST_STR_LEN("\r\n"))){
177 static int http_chunk_uses_tempfile(server
*srv
, connection
*con
, size_t len
) {
178 chunkqueue
* const cq
= con
->write_queue
;
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)) {
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"));
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"));
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"));
266 void http_chunk_close(server
*srv
, connection
*con
) {
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"));