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 /*(preserve existing stat_cache symlink checks)*/
40 stat_cache_entry
*sce
;
41 if (HANDLER_ERROR
== stat_cache_get_entry(srv
, con
, fn
, &sce
)) return -1;
44 return stat_cache_open_rdonly_fstat(fn
, st
, con
->conf
.follow_symlink
);
47 static void http_chunk_append_file_fd_range(server
*srv
, connection
*con
, buffer
*fn
, int fd
, off_t offset
, off_t len
) {
48 chunkqueue
*cq
= con
->write_queue
;
50 if (con
->response
.send_chunked
) {
51 http_chunk_append_len(srv
, con
, (uintmax_t)len
);
54 chunkqueue_append_file_fd(cq
, fn
, fd
, offset
, len
);
56 if (con
->response
.send_chunked
) {
57 chunkqueue_append_mem(cq
, CONST_STR_LEN("\r\n"));
61 int http_chunk_append_file_range(server
*srv
, connection
*con
, buffer
*fn
, off_t offset
, off_t len
) {
63 const int fd
= http_chunk_append_file_open_fstat(srv
, con
, fn
, &st
);
64 if (fd
< 0) return -1;
67 if (offset
>= st
.st_size
) {
69 return (offset
== st
.st_size
) ? 0 : -1;
71 len
= st
.st_size
- offset
;
72 } else if (st
.st_size
- offset
< len
) {
77 http_chunk_append_file_fd_range(srv
, con
, fn
, fd
, offset
, len
);
81 int http_chunk_append_file(server
*srv
, connection
*con
, buffer
*fn
) {
83 const int fd
= http_chunk_append_file_open_fstat(srv
, con
, fn
, &st
);
84 if (fd
< 0) return -1;
86 if (0 != st
.st_size
) {
87 http_chunk_append_file_fd_range(srv
, con
, fn
, fd
, 0, st
.st_size
);
94 static int http_chunk_append_to_tempfile(server
*srv
, connection
*con
, const char * mem
, size_t len
) {
95 chunkqueue
* const cq
= con
->write_queue
;
97 if (con
->response
.send_chunked
) {
98 buffer
*b
= http_chunk_header(srv
->tmp_chunk_len
, len
);
99 if (0 != chunkqueue_append_mem_to_tempfile(srv
, cq
, CONST_BUF_LEN(b
))) {
104 if (0 != chunkqueue_append_mem_to_tempfile(srv
, cq
, mem
, len
)) {
108 if (con
->response
.send_chunked
) {
109 if (0 != chunkqueue_append_mem_to_tempfile(srv
, cq
, CONST_STR_LEN("\r\n"))) {
117 static int http_chunk_append_cq_to_tempfile(server
*srv
, connection
*con
, chunkqueue
*src
, size_t len
) {
118 chunkqueue
* const cq
= con
->write_queue
;
120 if (con
->response
.send_chunked
) {
121 buffer
*b
= http_chunk_header(srv
->tmp_chunk_len
, len
);
122 if (0 != chunkqueue_append_mem_to_tempfile(srv
, cq
, CONST_BUF_LEN(b
))) {
127 if (0 != chunkqueue_steal_with_tempfiles(srv
, cq
, src
, len
)) {
131 if (con
->response
.send_chunked
) {
132 if (0!=chunkqueue_append_mem_to_tempfile(srv
,cq
,CONST_STR_LEN("\r\n"))){
140 static int http_chunk_uses_tempfile(server
*srv
, connection
*con
, size_t len
) {
141 chunkqueue
* const cq
= con
->write_queue
;
145 /* current usage does not append_mem or append_buffer after appending
146 * file, so not checking if users of this interface have appended large
147 * (references to) files to chunkqueue, which would not be in memory
148 * (but included in calculation for whether or not to use temp file) */
150 /*(allow slightly larger mem use if FDEVENT_STREAM_RESPONSE_BUFMIN
151 * to reduce creation of temp files when backend producer will be
152 * blocked until more data is sent to network to client)*/
154 if ((c
&& c
->type
== FILE_CHUNK
&& c
->file
.is_temp
)
155 || cq
->bytes_in
- cq
->bytes_out
+ len
156 > 1024 * ((con
->conf
.stream_response_body
& FDEVENT_STREAM_RESPONSE_BUFMIN
) ? 128 : 64)) {
163 int http_chunk_append_buffer(server
*srv
, connection
*con
, buffer
*mem
) {
164 chunkqueue
* const cq
= con
->write_queue
;
165 size_t len
= buffer_string_length(mem
);
166 if (0 == len
) return 0;
168 if (http_chunk_uses_tempfile(srv
, con
, len
)) {
169 return http_chunk_append_to_tempfile(srv
, con
, mem
->ptr
, len
);
172 if (con
->response
.send_chunked
) {
173 http_chunk_append_len(srv
, con
, len
);
176 /*(chunkqueue_append_buffer() might steal buffer contents)*/
177 chunkqueue_append_buffer(cq
, mem
);
179 if (con
->response
.send_chunked
) {
180 chunkqueue_append_mem(cq
, CONST_STR_LEN("\r\n"));
186 int http_chunk_append_mem(server
*srv
, connection
*con
, const char * mem
, size_t len
) {
187 chunkqueue
* const cq
= con
->write_queue
;
188 if (0 == len
) return 0;
189 force_assert(NULL
!= mem
);
191 if (http_chunk_uses_tempfile(srv
, con
, len
)) {
192 return http_chunk_append_to_tempfile(srv
, con
, mem
, len
);
195 if (con
->response
.send_chunked
) {
196 http_chunk_append_len(srv
, con
, len
);
199 chunkqueue_append_mem(cq
, mem
, len
);
201 if (con
->response
.send_chunked
) {
202 chunkqueue_append_mem(cq
, CONST_STR_LEN("\r\n"));
208 int http_chunk_transfer_cqlen(server
*srv
, connection
*con
, chunkqueue
*src
, size_t len
) {
209 chunkqueue
* const cq
= con
->write_queue
;
210 if (0 == len
) return 0;
212 if (http_chunk_uses_tempfile(srv
, con
, len
)) {
213 return http_chunk_append_cq_to_tempfile(srv
, con
, src
, len
);
216 if (con
->response
.send_chunked
) {
217 http_chunk_append_len(srv
, con
, len
);
220 chunkqueue_steal(cq
, src
, len
);
222 if (con
->response
.send_chunked
) {
223 chunkqueue_append_mem(cq
, CONST_STR_LEN("\r\n"));
229 void http_chunk_close(server
*srv
, connection
*con
) {
231 force_assert(NULL
!= con
);
233 if (con
->response
.send_chunked
) {
234 chunkqueue_append_mem(con
->write_queue
, CONST_STR_LEN("0\r\n\r\n"));