#include "uwsgi.h" /* uWSGI transformations each body chunk is passed (in chain) to every transformation 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 */ extern struct uwsgi_server uwsgi; // -1 error, 0 = no buffer, send the body, 1 = buffer int uwsgi_apply_transformations(struct wsgi_request *wsgi_req, char *buf, size_t len) { wsgi_req->transformed_chunk = NULL; wsgi_req->transformed_chunk_len = 0; 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) { ut->chunk = uwsgi_buffer_new(t_len); } // skip final transformations before appending data if (ut->is_final) goto next; if (uwsgi_buffer_append(ut->chunk, t_buf, t_len)) { return -1; } // 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 ut->chunk->pos = 0; next: ut = ut->next; } // if we are here we can tell the writer to send the body to the client // no buffering please if (!flushed) { wsgi_req->transformed_chunk = t_buf; wsgi_req->transformed_chunk_len = t_len; } return 0; } /* run all the remaining (or buffered) transformations */ int uwsgi_apply_final_transformations(struct wsgi_request *wsgi_req) { 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 (!found_nostream) { if (!ut->can_stream) { found_nostream = 1; } else { // stop the chain if no chunk is available if (!ut->chunk) return 0; t_buf = ut->chunk->buf; t_len = ut->chunk->pos; goto next; } } 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)) { 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 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) { struct uwsgi_transformation *ut = wsgi_req->transformations; while(ut) { struct uwsgi_transformation *current_ut = ut; if (current_ut->chunk) { uwsgi_buffer_destroy(current_ut->chunk); } if (current_ut->ub) { uwsgi_buffer_destroy(current_ut->ub); } if (current_ut->fd > -1) { close(current_ut->fd); } ut = ut->next; free(current_ut); } } struct uwsgi_transformation *uwsgi_add_transformation(struct wsgi_request *wsgi_req, int (*func)(struct wsgi_request *, struct uwsgi_transformation *), void *data) { struct uwsgi_transformation *old_ut = NULL, *ut = wsgi_req->transformations; while(ut) { old_ut = ut; ut = ut->next; } ut = uwsgi_calloc(sizeof(struct uwsgi_transformation)); ut->func = func; ut->fd = -1; ut->data = data; if (old_ut) { old_ut->next = ut; } else { wsgi_req->transformations = ut; } return ut; }