mirror of
https://github.com/clearlinux/uwsgi.git
synced 2026-08-21 13:07:54 +00:00
a1e3015783
Commitff9bab4bfixed support for sending IO like handles or GLOBs via sendfile by first calling fileno on them. This was checked by calling Scalar::Util::reftype on the reference first, as per https://metacpan.org/pod/PSGI#Body . This may be sane in pure perl, but there are better methods in XS/C than string evalling some Perl, which will eventually also call some XS/C. This commit introduces more robust checking of whether we have a "real" filehandle, and also introduces a test that chucks a bunch of different scalars and references at uWSGI to make sure it doesn't choke. I wasn't sure how to hook this test up to travis, or wether that's even desireable, so just left the test in what I believe to be the right directories. Because this commit massively simplifies the code, it also had an effect on object size, on my machine (x64, GCC 4.9), psgi_plugin.o went from 63360 bytes to 62296 bytes. The indentation level of the ->getline for loop has been left at its original level, which is now incorrect, to make the diff easier to read. A whitespace cleanup commit could follow. In related news, does anyone know the purpose of the three other use lines in psgi_loader.c, two of them were added back in 2011 bycf08972e, but I can't work out what their inclusion has to do with memleak hunting. Basically I would love to remove these lines as I don't believe they're needed for uWSGI to function, and if apps running under uWSGI need them, they should load them themselves. Also related, would more PSGI refactorings be welcome? I get the impression that Perl is a bit of a second class citizen in uWSGI these days, and as both an avid user of uWSGI+PSGI and a hobby XS developer, I'd love to clean up more of the PSGI plugin.
196 lines
5.2 KiB
C
196 lines
5.2 KiB
C
#include "psgi.h"
|
|
|
|
extern struct uwsgi_server uwsgi;
|
|
|
|
int psgi_response(struct wsgi_request *wsgi_req, AV *response) {
|
|
|
|
SV **status_code, **hitem ;
|
|
AV *headers, *body =NULL;
|
|
STRLEN hlen, hlen2;
|
|
int i;
|
|
char *chitem, *chitem2;
|
|
SV **harakiri;
|
|
|
|
if (wsgi_req->async_force_again) {
|
|
|
|
wsgi_req->async_force_again = 0;
|
|
|
|
wsgi_req->switches++;
|
|
SV *chunk = uwsgi_perl_obj_call(wsgi_req->async_placeholder, "getline");
|
|
if (!chunk) {
|
|
uwsgi_500(wsgi_req);
|
|
return UWSGI_OK;
|
|
}
|
|
|
|
chitem = SvPV( chunk, hlen);
|
|
|
|
if (hlen <= 0) {
|
|
SvREFCNT_dec(chunk);
|
|
if (wsgi_req->async_force_again) {
|
|
return UWSGI_AGAIN;
|
|
}
|
|
SV *closed = uwsgi_perl_obj_call(wsgi_req->async_placeholder, "close");
|
|
if (closed) {
|
|
SvREFCNT_dec(closed);
|
|
}
|
|
|
|
// check for psgix.harakiri
|
|
harakiri = hv_fetch((HV*)SvRV( (SV*)wsgi_req->async_environ), "psgix.harakiri.commit", 21, 0);
|
|
if (harakiri) {
|
|
if (SvTRUE(*harakiri)) wsgi_req->async_plagued = 1;
|
|
}
|
|
|
|
SvREFCNT_dec(wsgi_req->async_result);
|
|
|
|
return UWSGI_OK;
|
|
}
|
|
|
|
uwsgi_response_write_body_do(wsgi_req, chitem, hlen);
|
|
uwsgi_pl_check_write_errors {
|
|
SvREFCNT_dec(chunk);
|
|
return UWSGI_OK;
|
|
}
|
|
SvREFCNT_dec(chunk);
|
|
wsgi_req->async_force_again = 1;
|
|
return UWSGI_AGAIN;
|
|
}
|
|
|
|
if (SvTYPE(response) != SVt_PVAV) {
|
|
uwsgi_log("invalid PSGI response type\n");
|
|
return UWSGI_OK;
|
|
}
|
|
|
|
status_code = av_fetch(response, 0, 0);
|
|
if (!status_code) { uwsgi_log("invalid PSGI status code\n"); return UWSGI_OK;}
|
|
|
|
char *status_str = SvPV(*status_code, hlen);
|
|
if (uwsgi_response_prepare_headers(wsgi_req, status_str, hlen)) return UWSGI_OK;
|
|
|
|
|
|
hitem = av_fetch(response, 1, 0);
|
|
if (!hitem || !SvRV(*hitem) || SvTYPE(SvRV(*hitem)) != SVt_PVAV) { uwsgi_log("invalid PSGI headers\n"); return UWSGI_OK;}
|
|
|
|
headers = (AV *) SvRV(*hitem);
|
|
if (!headers) { uwsgi_log("invalid PSGI headers\n"); return UWSGI_OK;}
|
|
|
|
// generate headers
|
|
int headers_len = (int) av_len(headers);
|
|
for(i=0; i<=headers_len; i++) {
|
|
hitem = av_fetch(headers,i,0);
|
|
if (!*hitem) {
|
|
uwsgi_log("invalid PSGI headers\n"); return UWSGI_OK;
|
|
}
|
|
chitem = SvPV(*hitem, hlen);
|
|
if (i+1 > headers_len) {
|
|
uwsgi_log("invalid PSGI headers\n"); return UWSGI_OK;
|
|
}
|
|
hitem = av_fetch(headers,i+1,0);
|
|
if (!*hitem) {
|
|
uwsgi_log("invalid PSGI headers\n"); return UWSGI_OK;
|
|
}
|
|
chitem2 = SvPV(*hitem, hlen2);
|
|
if (uwsgi_response_add_header(wsgi_req, chitem, hlen, chitem2, hlen2)) return UWSGI_OK;
|
|
i++;
|
|
}
|
|
|
|
hitem = av_fetch(response, 2, 0);
|
|
|
|
if (!hitem) {
|
|
return UWSGI_OK;
|
|
}
|
|
|
|
SV *rv = SvRV(*hitem);
|
|
|
|
if (!rv)
|
|
goto invalid_body;
|
|
|
|
IO *io = GvIO(rv);
|
|
|
|
if (io) {
|
|
const int fd = PerlIO_fileno(IoIFP(io));
|
|
|
|
if (fd >= 0) {
|
|
wsgi_req->sendfile_fd = fd;
|
|
uwsgi_response_sendfile_do(wsgi_req, wsgi_req->sendfile_fd, 0, 0);
|
|
// no need to close here as perl GC will do the close()
|
|
uwsgi_pl_check_write_errors {
|
|
// noop
|
|
}
|
|
return UWSGI_OK;
|
|
}
|
|
}
|
|
|
|
if (SvOBJECT(rv)) {
|
|
// check for path method
|
|
if (uwsgi_perl_obj_can(*hitem, "path", 4)) {
|
|
SV *p = uwsgi_perl_obj_call(*hitem, "path");
|
|
int fd = open(SvPV_nolen(p), O_RDONLY);
|
|
SvREFCNT_dec(p);
|
|
// the following function will close fd
|
|
uwsgi_response_sendfile_do(wsgi_req, fd, 0, 0);
|
|
uwsgi_pl_check_write_errors {
|
|
// noop
|
|
}
|
|
return UWSGI_OK;
|
|
}
|
|
else if (uwsgi_perl_obj_can(*hitem, STR_WITH_LEN("getline"))) {
|
|
for(;;) {
|
|
|
|
wsgi_req->switches++;
|
|
SV *chunk = uwsgi_perl_obj_call(*hitem, "getline");
|
|
if (!chunk) {
|
|
uwsgi_500(wsgi_req);
|
|
break;
|
|
}
|
|
|
|
chitem = SvPV( chunk, hlen);
|
|
if (hlen <= 0) {
|
|
SvREFCNT_dec(chunk);
|
|
if (uwsgi.async > 0 && wsgi_req->async_force_again) {
|
|
wsgi_req->async_placeholder = (SV *) *hitem;
|
|
return UWSGI_AGAIN;
|
|
}
|
|
break;
|
|
}
|
|
|
|
uwsgi_response_write_body_do(wsgi_req, chitem, hlen);
|
|
uwsgi_pl_check_write_errors {
|
|
SvREFCNT_dec(chunk);
|
|
break;
|
|
}
|
|
SvREFCNT_dec(chunk);
|
|
if (uwsgi.async > 0) {
|
|
wsgi_req->async_placeholder = (SV *) *hitem;
|
|
wsgi_req->async_force_again = 1;
|
|
return UWSGI_AGAIN;
|
|
}
|
|
}
|
|
|
|
|
|
SV *closed = uwsgi_perl_obj_call(*hitem, "close");
|
|
if (closed) {
|
|
SvREFCNT_dec(closed);
|
|
}
|
|
}
|
|
}
|
|
else if (SvTYPE(rv) == SVt_PVAV) {
|
|
|
|
body = (AV *) rv;
|
|
|
|
for(i=0; i<=av_len(body); i++) {
|
|
hitem = av_fetch(body,i,0);
|
|
chitem = SvPV(*hitem, hlen);
|
|
uwsgi_response_write_body_do(wsgi_req, chitem, hlen);
|
|
uwsgi_pl_check_write_errors {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
invalid_body:
|
|
uwsgi_log("invalid PSGI response body\n");
|
|
}
|
|
|
|
return UWSGI_OK;
|
|
}
|