Browse Source
* starting chunked encoding filter * network write cleanup + sendfile backend (default for now)personal/stbuehler/wip
13 changed files with 236 additions and 52 deletions
@ -0,0 +1,48 @@
|
||||
|
||||
#include "filter_chunked.h" |
||||
|
||||
/* len != 0 */ |
||||
static void http_chunk_append_len(chunkqueue *cq, size_t len) { |
||||
size_t i, olen = len, j; |
||||
GString *s; |
||||
|
||||
s = g_string_sized_new(sizeof(len) * 2 + 2); |
||||
|
||||
for (i = 0; i < 8 && len; i++) { |
||||
len >>= 4; |
||||
} |
||||
|
||||
/* i is the number of hex digits we have */ |
||||
g_string_set_size(s, i); |
||||
|
||||
for (j = i-1, len = olen; j+1 > 0; j--) { |
||||
s->str[j] = (len & 0xf) + (((len & 0xf) <= 9) ? '0' : 'a' - 10); |
||||
len >>= 4; |
||||
} |
||||
g_string_append_len(s, CONST_STR_LEN("\r\n")); |
||||
|
||||
chunkqueue_append_string(cq, s); |
||||
} |
||||
|
||||
|
||||
handler_t filter_chunked_encode(server *srv, connection *con, chunkqueue *out, chunkqueue *in) { |
||||
UNUSED(srv); |
||||
UNUSED(con); |
||||
|
||||
if (in->length > 0) { |
||||
http_chunk_append_len(out, in->length); |
||||
chunkqueue_steal_all(out, in); |
||||
} |
||||
if (in->is_closed) { |
||||
if (!out->is_closed) { |
||||
chunkqueue_append_mem(out, CONST_STR_LEN("0\r\n")); |
||||
out->is_closed = TRUE; |
||||
} |
||||
return HANDLER_FINISHED; |
||||
} |
||||
return HANDLER_GO_ON; |
||||
} |
||||
|
||||
handler_t filter_chunked_decode(server *srv, connection *con, chunkqueue *out, chunkqueue *in) { |
||||
return HANDLER_ERROR; |
||||
} |
@ -0,0 +1,9 @@
|
||||
#ifndef _LIGHTTPD_FILTER_CHUNKED_H_ |
||||
#define _LIGHTTPD_FILTER_CHUNKED_H_ |
||||
|
||||
#include "base.h" |
||||
|
||||
LI_API handler_t filter_chunked_encode(server *srv, connection *con, chunkqueue *out, chunkqueue *in); |
||||
LI_API handler_t filter_chunked_decode(server *srv, connection *con, chunkqueue *out, chunkqueue *in); |
||||
|
||||
#endif |
@ -0,0 +1,92 @@
|
||||
|
||||
#include "network.h" |
||||
|
||||
/* first chunk must be a FILE_CHUNK ! */ |
||||
network_status_t network_backend_sendfile(server *srv, connection *con, int fd, chunkqueue *cq, goffset *write_max) { |
||||
off_t file_offset, toSend; |
||||
ssize_t r; |
||||
gboolean did_write_something = FALSE; |
||||
chunkiter ci; |
||||
chunk *c; |
||||
|
||||
if (0 == cq->length) return NETWORK_STATUS_FATAL_ERROR; |
||||
|
||||
do { |
||||
ci = chunkqueue_iter(cq); |
||||
|
||||
if (FILE_CHUNK != (c = chunkiter_chunk(ci))->type) { |
||||
return did_write_something ? NETWORK_STATUS_SUCCESS : NETWORK_STATUS_FATAL_ERROR; |
||||
} |
||||
|
||||
switch (chunkfile_open(srv, con, c->file.file)) { |
||||
case HANDLER_GO_ON: |
||||
break; |
||||
case HANDLER_WAIT_FOR_FD: |
||||
return NETWORK_STATUS_WAIT_FOR_FD; |
||||
default: |
||||
return NETWORK_STATUS_FATAL_ERROR; |
||||
} |
||||
|
||||
file_offset = c->offset + c->file.start; |
||||
toSend = c->file.length - c->offset; |
||||
if (toSend > *write_max) toSend = *write_max; |
||||
|
||||
while (-1 == (r = sendfile(fd, c->file.file->fd, &file_offset, toSend))) { |
||||
switch (errno) { |
||||
case EAGAIN: |
||||
#if EWOULDBLOCK != EAGAIN |
||||
case EWOULDBLOCK |
||||
#endif |
||||
return did_write_something ? NETWORK_STATUS_SUCCESS : NETWORK_STATUS_WAIT_FOR_EVENT; |
||||
case ECONNRESET: |
||||
case EPIPE: |
||||
return NETWORK_STATUS_CONNECTION_CLOSE; |
||||
case EINTR: |
||||
break; /* try again */ |
||||
default: |
||||
CON_ERROR(srv, con, "oops, write to fd=%d failed: %s", fd, g_strerror(errno)); |
||||
return NETWORK_STATUS_FATAL_ERROR; |
||||
} |
||||
} |
||||
if (0 == r) { |
||||
/* don't care about cached stat - file is open */ |
||||
struct stat st; |
||||
if (-1 == fstat(fd, &st)) { |
||||
CON_ERROR(srv, con, "Couldn't fstat file: %s", g_strerror(errno)); |
||||
return NETWORK_STATUS_FATAL_ERROR; |
||||
} |
||||
|
||||
if (file_offset > st.st_size) { |
||||
/* file shrinked, close the connection */ |
||||
CON_ERROR(srv, con, "%s", "File shrinked, aborting"); |
||||
return NETWORK_STATUS_FATAL_ERROR; |
||||
} |
||||
return did_write_something ? NETWORK_STATUS_SUCCESS : NETWORK_STATUS_WAIT_FOR_EVENT; |
||||
} |
||||
chunkqueue_skip(cq, r); |
||||
*write_max -= r; |
||||
did_write_something = TRUE; |
||||
if (0 == cq->length) return NETWORK_STATUS_SUCCESS; |
||||
} while (r == toSend && *write_max > 0); |
||||
|
||||
return NETWORK_STATUS_SUCCESS; |
||||
} |
||||
|
||||
network_status_t network_write_sendfile(server *srv, connection *con, int fd, chunkqueue *cq) { |
||||
goffset write_max = 256*1024; // 256kB //;
|
||||
if (cq->length == 0) return NETWORK_STATUS_FATAL_ERROR; |
||||
do { |
||||
switch (chunkqueue_first_chunk(cq)->type) { |
||||
case MEM_CHUNK: |
||||
NETWORK_FALLBACK(network_backend_writev, &write_max); |
||||
break; |
||||
case FILE_CHUNK: |
||||
NETWORK_FALLBACK(network_backend_sendfile, &write_max); |
||||
break; |
||||
default: |
||||
return NETWORK_STATUS_FATAL_ERROR; |
||||
} |
||||
if (cq->length == 0) return NETWORK_STATUS_SUCCESS; |
||||
} while (write_max > 0); |
||||
return NETWORK_STATUS_SUCCESS; |
||||
} |
Loading…
Reference in new issue