* replace strerror with g_strerror, kill errno from logging
* starting chunked encoding filter * network write cleanup + sendfile backend (default for now)personal/stbuehler/wip
parent
99a7b35e44
commit
1bc8c85266
@ -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