Perl: added processing for IO:Handle-like object.

The application can return the body as an IO:Handle-like object
without file descriptor.
This commit is contained in:
Alexander Borisov
2019-02-27 17:27:41 +03:00
parent 5c9fe8c306
commit 379e4c75fd
2 changed files with 109 additions and 4 deletions

View File

@@ -51,6 +51,8 @@ static void nxt_perl_psgi_xs_init(pTHX);
static SV *nxt_perl_psgi_call_var_application(PerlInterpreter *my_perl,
SV *env, SV *app, nxt_unit_request_info_t *req);
static SV *nxt_perl_psgi_call_method(PerlInterpreter *my_perl, SV *obj,
const char *method, nxt_unit_request_info_t *req);
/* For currect load XS modules */
EXTERN_C void boot_DynaLoader(pTHX_ CV *cv);
@@ -84,8 +86,10 @@ static int nxt_perl_psgi_result_body(PerlInterpreter *my_perl,
SV *result, nxt_unit_request_info_t *req);
static int nxt_perl_psgi_result_body_ref(PerlInterpreter *my_perl,
SV *sv_body, nxt_unit_request_info_t *req);
static ssize_t nxt_perl_psgi_io_read(nxt_unit_read_info_t *read_info,
void *dst, size_t size);
static int nxt_perl_psgi_result_body_fh(PerlInterpreter *my_perl, SV *sv_body,
nxt_unit_request_info_t *req);
static ssize_t nxt_perl_psgi_io_read(nxt_unit_read_info_t *read_info, void *dst,
size_t size);
static int nxt_perl_psgi_result_array(PerlInterpreter *my_perl,
SV *result, nxt_unit_request_info_t *req);
@@ -251,6 +255,42 @@ nxt_perl_psgi_call_var_application(PerlInterpreter *my_perl,
}
static SV *
nxt_perl_psgi_call_method(PerlInterpreter *my_perl, SV *obj, const char *method,
nxt_unit_request_info_t *req)
{
SV *result;
dSP;
ENTER;
SAVETMPS;
PUSHMARK(sp);
XPUSHs(obj);
PUTBACK;
call_method(method, G_EVAL|G_SCALAR);
SPAGAIN;
if (SvTRUE(ERRSV)) {
nxt_unit_req_error(req, "PSGI: Failed to call method '%s':\n%s",
method, SvPV_nolen(ERRSV));
result = NULL;
} else {
result = SvREFCNT_inc(POPs);
}
PUTBACK;
FREETMPS;
LEAVE;
return result;
}
static u_char *
nxt_perl_psgi_module_create(nxt_task_t *task, const char *script)
{
@@ -749,6 +789,68 @@ nxt_perl_psgi_result_body(PerlInterpreter *my_perl, SV *sv_body,
}
static int
nxt_perl_psgi_result_body_ref(PerlInterpreter *my_perl, SV *sv_body,
nxt_unit_request_info_t *req)
{
SV *data, *old_rs, *old_perl_rs;
int rc;
size_t len;
const char *body;
/*
* Servers should set the $/ special variable to the buffer size
* when reading content from $body using the getline method.
* This is done by setting $/ with a reference to an integer ($/ = \8192).
*/
old_rs = PL_rs;
old_perl_rs = get_sv("/", GV_ADD);
PL_rs = sv_2mortal(newRV_noinc(newSViv(nxt_unit_buf_min())));
sv_setsv(old_perl_rs, PL_rs);
rc = NXT_UNIT_OK;
for ( ;; ) {
data = nxt_perl_psgi_call_method(my_perl, sv_body, "getline", req);
if (nxt_slow_path(data == NULL)) {
rc = NXT_UNIT_ERROR;
break;
}
body = SvPV(data, len);
if (len == 0) {
SvREFCNT_dec(data);
data = nxt_perl_psgi_call_method(my_perl, sv_body, "close", req);
if (nxt_fast_path(data != NULL)) {
SvREFCNT_dec(data);
}
break;
}
rc = nxt_unit_response_write(req, body, len);
SvREFCNT_dec(data);
if (nxt_slow_path(rc != NXT_UNIT_OK)) {
nxt_unit_req_error(req, "PSGI: Failed to write content from "
"Perl Application");
break;
}
};
PL_rs = old_rs;
sv_setsv(get_sv("/", GV_ADD), old_perl_rs);
return rc;
}
typedef struct {
PerlInterpreter *my_perl;
PerlIO *fp;
@@ -756,7 +858,7 @@ typedef struct {
static int
nxt_perl_psgi_result_body_ref(PerlInterpreter *my_perl, SV *sv_body,
nxt_perl_psgi_result_body_fh(PerlInterpreter *my_perl, SV *sv_body,
nxt_unit_request_info_t *req)
{
IO *io;
@@ -866,6 +968,10 @@ nxt_perl_psgi_result_array(PerlInterpreter *my_perl, SV *result,
return nxt_perl_psgi_result_body(my_perl, *sv_temp, req);
}
if (SvTYPE(SvRV(*sv_temp)) == SVt_PVGV) {
return nxt_perl_psgi_result_body_fh(my_perl, *sv_temp, req);
}
return nxt_perl_psgi_result_body_ref(my_perl, *sv_temp, req);
}

View File

@@ -185,7 +185,6 @@ class TestUnitPerlApplication(unit.TestUnitApplicationPerl):
self.assertEqual(resp['body'], '0123456789', 'keep-alive 2')
@unittest.expectedFailure
def test_perl_body_io_fake(self):
self.load('body_io_fake')