blob: c10031c3f383e6416c5321071dc855afb7401654 [file] [log] [blame]
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "mod_perl.h"
#ifdef MP_IO_TIE_PERLIO
/***************************
* The PerlIO Apache layer *
***************************/
/* PerlIO ":Apache2" layer is used to use the Apache callbacks to read
* from STDIN and write to STDOUT. The PerlIO API is documented in
* perliol.pod */
typedef struct {
struct _PerlIO base;
request_rec *r;
} PerlIOApache;
/* _open just allocates the layer, _pushed does the real job of
* filling the data in */
static PerlIO *
PerlIOApache_open(pTHX_ PerlIO_funcs *self, PerlIO_list_t *layers, IV n,
const char *mode, int fd, int imode, int perm,
PerlIO *f, int narg, SV **args)
{
if (!f) {
f = PerlIO_allocate(aTHX);
}
if ( (f = PerlIO_push(aTHX_ f, self, mode, args[0])) ) {
PerlIOBase(f)->flags |= PERLIO_F_OPEN;
}
MP_TRACE_o(MP_FUNC, "mode %s", mode);
return f;
}
/* this callback is used by pushed() and binmode() to add the layer */
static IV
PerlIOApache_pushed(pTHX_ PerlIO *f, const char *mode, SV *arg,
PerlIO_funcs *tab)
{
IV code;
PerlIOApache *st = PerlIOSelf(f, PerlIOApache);
if (arg) {
st->r = modperl_sv2request_rec(aTHX_ arg);
MP_TRACE_o(MP_FUNC, "stored request_rec obj: 0x%lx", st->r);
}
else {
Perl_croak(aTHX_"failed to insert the :Apache2 layer. "
"Apache2::RequestRec object argument is required");
/* XXX: try to get Apache2->request? */
}
/* this method also sets the right flags according to the
* 'mode' */
code = PerlIOBase_pushed(aTHX_ f, mode, (SV *)NULL, tab);
return code;
}
static SV *
PerlIOApache_getarg(pTHX_ PerlIO *f, CLONE_PARAMS *param, int flags)
{
PerlIOApache *st = PerlIOSelf(f, PerlIOApache);
SV *sv;
if (!st->r) {
Perl_croak(aTHX_ "an attempt to getarg from a stale io handle");
}
sv = newSV(0);
sv_setref_pv(sv, "Apache2::RequestRec", (void*)(st->r));
MP_TRACE_o(MP_FUNC, "retrieved request_rec obj: 0x%lx", st->r);
return sv;
}
static IV
PerlIOApache_fileno(pTHX_ PerlIO *f)
{
/* XXX: we could return STDIN => 0, STDOUT => 1, but that wouldn't
* be correct, as the IO goes through the socket, may be we should
* return the filedescriptor of the socket?
*
* -1 in this case indicates that the layer cannot provide fileno
*/
MP_TRACE_o(MP_FUNC, "did nothing");
return -1;
}
static SSize_t
PerlIOApache_read(pTHX_ PerlIO *f, void *vbuf, Size_t count)
{
PerlIOApache *st = PerlIOSelf(f, PerlIOApache);
request_rec *r = st->r;
if (!(PerlIOBase(f)->flags & PERLIO_F_CANREAD) ||
PerlIOBase(f)->flags & (PERLIO_F_EOF|PERLIO_F_ERROR)) {
return 0;
}
return modperl_request_read(aTHX_ r, (char*)vbuf, count);
}
static SSize_t
PerlIOApache_write(pTHX_ PerlIO *f, const void *vbuf, Size_t count)
{
PerlIOApache *st = PerlIOSelf(f, PerlIOApache);
modperl_config_req_t *rcfg = modperl_config_req_get(st->r);
apr_size_t bytes = 0;
if (!(PerlIOBase(f)->flags & PERLIO_F_CANWRITE)) {
return 0;
}
MP_CHECK_WBUCKET_INIT("print");
MP_TRACE_o(MP_FUNC, "%4db [%s]", count,
MP_TRACE_STR_TRUNC(rcfg->wbucket->pool, vbuf, count));
MP_RUN_CROAK(modperl_wbucket_write(aTHX_ rcfg->wbucket, vbuf, &count),
":Apache2 IO write");
bytes += count;
return (SSize_t) bytes;
}
static IV
PerlIOApache_flush(pTHX_ PerlIO *f)
{
PerlIOApache *st = PerlIOSelf(f, PerlIOApache);
modperl_config_req_t *rcfg;
if (!st->r) {
Perl_warn(aTHX_ "an attempt to flush a stale IO handle");
return -1;
}
/* no flush on readonly io handle */
if (! (PerlIOBase(f)->flags & PERLIO_F_CANWRITE) ) {
return -1;
}
rcfg = modperl_config_req_get(st->r);
MP_CHECK_WBUCKET_INIT("flush");
MP_TRACE_o(MP_FUNC, "%4db [%s]", rcfg->wbucket->outcnt,
MP_TRACE_STR_TRUNC(rcfg->wbucket->pool,
rcfg->wbucket->outbuf,
rcfg->wbucket->outcnt));
MP_RUN_CROAK_RESET_OK(st->r->server,
modperl_wbucket_flush(rcfg->wbucket, FALSE),
":Apache2 IO flush");
return 0;
}
/* 5.8.0 doesn't export PerlIOBase_noop_fail, so we duplicate it here */
static IV PerlIOApache_noop_fail(pTHX_ PerlIO *f)
{
return -1;
}
static IV
PerlIOApache_close(pTHX_ PerlIO *f)
{
IV code = PerlIOBase_close(aTHX_ f);
PerlIOApache *st = PerlIOSelf(f, PerlIOApache);
MP_TRACE_o(MP_FUNC, "done with request_rec obj: 0x%lx", st->r);
/* prevent possible bugs where a stale r will be attempted to be
* reused (e.g. dupped filehandle) */
st->r = NULL;
return code;
}
static IV
PerlIOApache_popped(pTHX_ PerlIO *f)
{
/* XXX: just temp for tracing */
MP_TRACE_o(MP_FUNC, "done");
return PerlIOBase_popped(aTHX_ f);
}
static PerlIO_funcs PerlIO_Apache = {
sizeof(PerlIO_funcs),
"Apache2",
sizeof(PerlIOApache),
PERLIO_K_MULTIARG | PERLIO_K_RAW,
PerlIOApache_pushed,
PerlIOApache_popped,
PerlIOApache_open,
PerlIOBase_binmode,
PerlIOApache_getarg,
PerlIOApache_fileno,
PerlIOBase_dup,
PerlIOApache_read,
PerlIOBase_unread,
PerlIOApache_write,
NULL, /* can't seek on STD{IN|OUT}, fail on call*/
NULL, /* can't tell on STD{IN|OUT}, fail on call*/
PerlIOApache_close,
PerlIOApache_flush,
PerlIOApache_noop_fail, /* fill */
PerlIOBase_eof,
PerlIOBase_error,
PerlIOBase_clearerr,
PerlIOBase_setlinebuf,
NULL, /* get_base */
NULL, /* get_bufsiz */
NULL, /* get_ptr */
NULL, /* get_cnt */
NULL, /* set_ptrcnt */
};
/* ***** End of PerlIOApache tab ***** */
MP_INLINE void modperl_io_apache_init(pTHX)
{
PerlIO_define_layer(aTHX_ &PerlIO_Apache);
}
#endif /* defined MP_IO_TIE_PERLIO */
/****** Other request IO functions *******/
MP_INLINE SSize_t modperl_request_read(pTHX_ request_rec *r,
char *buffer, Size_t len)
{
SSize_t total = 0;
Size_t wanted = len;
int seen_eos = 0;
char *tmp = buffer;
apr_bucket_brigade *bb;
if (len <= 0) {
return 0;
}
bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
if (bb == NULL) {
r->connection->keepalive = AP_CONN_CLOSE;
Perl_croak(aTHX_ "failed to create bucket brigade");
}
do {
apr_size_t read;
apr_status_t rc;
rc = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES,
APR_BLOCK_READ, len);
if (rc != APR_SUCCESS) {
/* if we fail here, we want to stop trying to read data
* from the client.
*/
r->connection->keepalive = AP_CONN_CLOSE;
apr_brigade_destroy(bb);
modperl_croak(aTHX_ rc, "Apache2::RequestIO::read");
}
/* If this fails, it means that a filter is written
* incorrectly and that it needs to learn how to properly
* handle APR_BLOCK_READ requests by returning data when
* requested.
*/
if (APR_BRIGADE_EMPTY(bb)) {
apr_brigade_destroy(bb);
/* we can't tell which filter is broken, since others may
* just pass data through */
Perl_croak(aTHX_ "Apache2::RequestIO::read: "
"Aborting read from client. "
"One of the input filters is broken. "
"It returned an empty bucket brigade for "
"the APR_BLOCK_READ mode request");
}
if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) {
seen_eos = 1;
}
read = len;
rc = apr_brigade_flatten(bb, tmp, &read);
if (rc != APR_SUCCESS) {
apr_brigade_destroy(bb);
modperl_croak(aTHX_ rc, "Apache2::RequestIO::read");
}
total += read;
tmp += read;
len -= read;
/* XXX: what happens if the downstream filter returns more
* data than the caller has asked for? We can't return more
* data than requested, so it needs to be stored somewhere and
* dealt with on the subsequent calls to this function. or may
* be we should just assert, blaming a bad filter. at the
* moment I couldn't find a spec telling whether it's wrong
* for the filter to return more data than it was asked for in
* the AP_MODE_READBYTES mode.
*/
apr_brigade_cleanup(bb);
} while (len > 0 && !seen_eos);
apr_brigade_destroy(bb);
MP_TRACE_o(MP_FUNC, "wanted %db, read %db [%s]", wanted, total,
MP_TRACE_STR_TRUNC(r->pool, buffer, total));
return total;
}
/*
* Local Variables:
* c-basic-offset: 4
* indent-tabs-mode: nil
* End:
*/