improved transformation system

This commit is contained in:
Unbit
2013-05-01 19:36:13 +02:00
parent e925dbaad6
commit ec6acfbc1c
6 changed files with 71 additions and 33 deletions
+4 -1
View File
@@ -494,6 +494,7 @@ static int transform_flush(struct wsgi_request *wsgi_req, struct uwsgi_transform
int ret = uwsgi_response_write_body_do(wsgi_req, ut->chunk->buf, ut->chunk->pos);
wsgi_req->transformed_chunk = NULL;
wsgi_req->transformed_chunk_len = 0;
ut->flushed = 1;
return ret;
}
static int uwsgi_router_flush_func(struct wsgi_request *wsgi_req, struct uwsgi_route *route) {
@@ -514,7 +515,9 @@ static int transform_fixcl(struct wsgi_request *wsgi_req, struct uwsgi_transform
wsgi_req->write_errors++;
return -1;
}
return uwsgi_response_add_header(wsgi_req, "Content-Length", 14, buf, ret);
// do not check for errors !!!
uwsgi_response_add_header(wsgi_req, "Content-Length", 14, buf, ret);
return 0;
}
static int uwsgi_router_fixcl_func(struct wsgi_request *wsgi_req, struct uwsgi_route *route) {
uwsgi_add_transformation(wsgi_req, transform_fixcl, NULL);
+46 -21
View File
@@ -4,10 +4,11 @@
uWSGI transformations
responses can be buffered in the wsgi_request structure (instead of being sent to the client)
Before closing the request, the transformations are applied in chain to the response buffer
each body chunk is passed (in chain) to every transformation
Finally the resulting buffer is sent to the client
some of them supports streaming, other requires buffering
at the end of the request the "final chain" is called (and the whole chain freed)
Transformations (if required) could completely swallow already set headers
@@ -22,6 +23,7 @@ int uwsgi_apply_transformations(struct wsgi_request *wsgi_req, char *buf, size_t
struct uwsgi_transformation *ut = wsgi_req->transformations;
char *t_buf = buf;
size_t t_len = len;
uint8_t flushed = 0;
while(ut) {
// allocate the buffer (if needed)
if (!ut->chunk) {
@@ -36,10 +38,13 @@ int uwsgi_apply_transformations(struct wsgi_request *wsgi_req, char *buf, size_t
// if the transformation cannot stream, continue buffering (the func will be called at the end)
if (!ut->can_stream) return 1;
ut->round++;
if (ut->func(wsgi_req, ut)) {
return -1;
}
if (ut->flushed) flushed = 1;
t_buf = ut->chunk->buf;
t_len = ut->chunk->pos;
// we reset the buffer, so we do not waste memory
@@ -50,54 +55,74 @@ next:
// if we are here we can tell the writer to send the body to the client
// no buffering please
wsgi_req->transformed_chunk = t_buf;
wsgi_req->transformed_chunk_len = t_len;
if (!flushed) {
wsgi_req->transformed_chunk = t_buf;
wsgi_req->transformed_chunk_len = t_len;
}
return 0;
}
/*
if a transformation is "final", we need to call it independently by the write status
run all the remaining (or buffered) transformations
*/
int uwsgi_apply_final_transformations(struct wsgi_request *wsgi_req) {
int ret = 0;
struct uwsgi_transformation *ut = wsgi_req->transformations;
wsgi_req->transformed_chunk = NULL;
wsgi_req->transformed_chunk_len = 0;
char *t_buf = NULL;
size_t t_len = 0;
uint8_t flushed = 0;
int found_nostream = 0;
while(ut) {
if (ut->chunk) {
t_buf = ut->chunk->buf;
t_len = ut->chunk->pos;
// if the transformation can stream and has a chunk, we already applied it
if (ut->can_stream) goto next;
if (!found_nostream) {
if (!ut->can_stream) {
found_nostream = 1;
}
else {
t_buf = ut->chunk->buf;
t_len = ut->chunk->pos;
goto next;
}
}
else if (t_len > 0) {
ut->chunk = uwsgi_buffer_new(t_len);
if (uwsgi_buffer_append(ut->chunk, t_buf, t_len)) {
return -1;
if (!ut->chunk) {
if (t_len > 0) {
ut->chunk = uwsgi_buffer_new(t_len);
}
else {
ut->chunk = uwsgi_buffer_new(uwsgi.page_size);
}
}
if (t_len > 0) {
if (uwsgi_buffer_append(ut->chunk, t_buf, t_len)) {
return -1;
}
}
// run the transformation
ut->round++;
if (ut->func(wsgi_req, ut)) {
ret = -1;
return -1;
}
if (ut->flushed) flushed = 1;
t_buf = ut->chunk->buf;
t_len = ut->chunk->pos;
next:
ut = ut->next;
}
// if we are here, all of the transformations are applied
wsgi_req->transformed_chunk = t_buf;
wsgi_req->transformed_chunk_len = t_len;
return ret;
if (!flushed) {
wsgi_req->transformed_chunk = t_buf;
wsgi_req->transformed_chunk_len = t_len;
}
return 0;
}
void uwsgi_free_transformations(struct wsgi_request *wsgi_req) {
+12 -6
View File
@@ -10,16 +10,22 @@
static int transform_chunked(struct wsgi_request *wsgi_req, struct uwsgi_transformation *ut) {
struct uwsgi_buffer *ub = ut->chunk;
if (!wsgi_req->headers_sent) {
if (uwsgi_response_add_header(wsgi_req, "Transfer-Encoding", 17, "chunked", 7)) return -1;
}
if (ut->is_final) {
if (uwsgi_buffer_insert_chunked(ub, 0, 0)) return -1;
if (uwsgi_buffer_append(ub, "0\r\n\r\n", 5)) return -1;
return 0;
}
else {
if (ut->round == 1) {
// do not check for errors !!!
uwsgi_response_add_header(wsgi_req, "Transfer-Encoding", 17, "chunked", 7);
}
if (ub->pos > 0) {
if (uwsgi_buffer_insert_chunked(ub, 0, ub->pos)) return -1;
if (uwsgi_buffer_append(ub, "\r\n", 2)) return -1;
}
if (uwsgi_buffer_append(ub, "\r\n", 2)) return -1;
return 0;
}
+5 -4
View File
@@ -22,11 +22,8 @@ extern char gzheader[];
static int transform_gzip(struct wsgi_request *wsgi_req, struct uwsgi_transformation *ut) {
struct uwsgi_transformation_gzip *utgz = (struct uwsgi_transformation_gzip *) ut->data;
struct uwsgi_buffer *ub = ut->chunk;
if (!wsgi_req->headers_sent) {
if (uwsgi_response_add_header(wsgi_req, "Content-Encoding", 16, "gzip", 4)) return -1;
}
if (ut->is_final) {
uwsgi_log("size = %d %d\n", ub->pos, utgz->len);
if (uwsgi_gzip_fix(&utgz->z, utgz->crc32, ub, utgz->len)) {
free(utgz);
return -1;
@@ -34,17 +31,21 @@ static int transform_gzip(struct wsgi_request *wsgi_req, struct uwsgi_transforma
free(utgz);
return 0;
}
size_t dlen = 0;
char *gzipped = uwsgi_gzip_chunk(&utgz->z, &utgz->crc32, ub->buf, ub->pos, &dlen);
if (!gzipped) return -1;
utgz->len += ub->pos;
uwsgi_buffer_map(ub, gzipped, dlen);
if (!utgz->header) {
// do not check for errors !!!
uwsgi_response_add_header(wsgi_req, "Content-Encoding", 16, "gzip", 4);
utgz->header = 1;
if (uwsgi_buffer_insert(ub, 0, gzheader, 10)) {
return -1;
}
}
return 0;
}
+2 -1
View File
@@ -28,7 +28,7 @@ static int transform_tofile(struct wsgi_request *wsgi_req, struct uwsgi_transfor
// store only successfull response
if (wsgi_req->write_errors == 0 && wsgi_req->status == 200 && ub->pos > 0) {
if (uttc->filename) {
int fd = open(uttc->filename->buf, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
int fd = open(uttc->filename->buf, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
if (fd < 0) {
uwsgi_error_open(uttc->filename->buf);
goto end;
@@ -58,6 +58,7 @@ end:
// free resources
if (uttc->filename) uwsgi_buffer_destroy(uttc->filename);
free(uttc);
// reset the buffer
return 0;
}
+2
View File
@@ -1199,7 +1199,9 @@ struct uwsgi_transformation {
struct uwsgi_buffer *chunk;
uint8_t can_stream;
uint8_t is_final;
uint8_t flushed;
void *data;
uint64_t round;
struct uwsgi_transformation *next;
};