blob: 9f3d55da6d59fa6b7fcebcba01057040ae22269f [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 "fcgid_bucket.h"
#include "fcgid_protocol.h"
#include "fcgid_bridge.h"
#define FCGID_FEED_LEN 8192
static apr_status_t fcgid_feed_data(fcgid_bucket_ctx * ctx,
apr_bucket_alloc_t * bucketalloc,
char **buffer, apr_size_t * bufferlen)
{
apr_status_t rv;
if (!ctx->buffer) {
*buffer = apr_bucket_alloc(FCGID_FEED_LEN, bucketalloc);
*bufferlen = FCGID_FEED_LEN;
if ((rv =
proc_read_ipc(&ctx->ipc, *buffer,
bufferlen)) != APR_SUCCESS) {
ctx->has_error = 1;
apr_bucket_free(*buffer);
return rv;
}
ctx->buffer =
apr_bucket_heap_create(*buffer, FCGID_FEED_LEN,
apr_bucket_free, bucketalloc);
if (*bufferlen != FCGID_FEED_LEN) {
apr_bucket *buckettmp;
apr_bucket_split(ctx->buffer, *bufferlen);
buckettmp = APR_BUCKET_NEXT(ctx->buffer);
apr_bucket_delete(buckettmp);
}
} else {
apr_bucket_read(ctx->buffer, (const char **) buffer, bufferlen,
APR_BLOCK_READ);
}
return APR_SUCCESS;
}
static void fcgid_ignore_bytes(fcgid_bucket_ctx * ctx,
apr_size_t ignorebyte)
{
apr_bucket *buckettmp;
if (ignorebyte == ctx->buffer->length) {
apr_bucket_destroy(ctx->buffer);
ctx->buffer = NULL;
} else {
apr_bucket_split(ctx->buffer, ignorebyte);
buckettmp = ctx->buffer;
ctx->buffer = APR_BUCKET_NEXT(ctx->buffer);
apr_bucket_delete(buckettmp);
}
}
static apr_status_t fcgid_header_bucket_read(apr_bucket * b,
const char **str,
apr_size_t * len,
apr_read_type_e block)
{
fcgid_bucket_ctx *ctx = (fcgid_bucket_ctx *) b->data;
apr_status_t rv;
apr_size_t hasread, bodysize;
FCGI_Header header;
apr_bucket *curbucket = b;
/* Keep reading until I get a fastcgi header */
hasread = 0;
while (hasread < sizeof(header)) {
char *buffer;
apr_size_t bufferlen, putsize;
/* Feed some data if necessary */
if ((rv =
fcgid_feed_data(ctx, b->list, &buffer,
&bufferlen)) != APR_SUCCESS)
return rv;
/* Initialize header */
putsize = fcgid_min(bufferlen, sizeof(header) - hasread);
memcpy(&header + hasread, buffer, putsize);
hasread += putsize;
/* Ignore the bytes that have read */
fcgid_ignore_bytes(ctx, putsize);
}
/* Get the body size */
bodysize = header.contentLengthB1;
bodysize <<= 8;
bodysize += header.contentLengthB0;
/* Handle FCGI_STDERR body, write the content to log file */
if (header.type == FCGI_STDERR) {
char *logbuf = apr_bucket_alloc(APR_BUCKET_BUFF_SIZE, b->list);
char *line;
memset(logbuf, 0, APR_BUCKET_BUFF_SIZE);
hasread = 0;
while (hasread < bodysize) {
char *buffer;
apr_size_t bufferlen, canput, willput;
/* Feed some data if necessary */
if ((rv =
fcgid_feed_data(ctx, b->list, &buffer,
&bufferlen)) != APR_SUCCESS) {
apr_bucket_free(logbuf);
return rv;
}
canput = fcgid_min(bufferlen, bodysize - hasread);
willput =
fcgid_min(canput, APR_BUCKET_BUFF_SIZE - hasread - 1);
memcpy(logbuf + hasread, buffer, willput);
hasread += canput;
/* Ignore the "canput" bytes */
fcgid_ignore_bytes(ctx, canput);
}
/* Now I get the log data, write log and release the buffer */
line = logbuf;
while (*line) {
char *end = strpbrk(line, "\r\n");
if (end != NULL) {
*end = '\0';
}
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, ctx->ipc.request,
"mod_fcgid: stderr: %s", line);
if (end == NULL) {
break;
}
++end;
line = end + strspn(end, "\r\n");
}
apr_bucket_free(logbuf);
}
/* if( header.type==FCGI_STDERR ) */
/* Now handle FCGI_STDOUT */
else if (header.type == FCGI_STDOUT) {
hasread = 0;
while (hasread < bodysize) {
char *buffer;
apr_size_t bufferlen, canput;
apr_bucket *buckettmp;
/* Feed some data if necessary */
if ((rv =
fcgid_feed_data(ctx, b->list, &buffer,
&bufferlen)) != APR_SUCCESS)
return rv;
canput = fcgid_min(bufferlen, bodysize - hasread);
/* Change the current bucket to refer to what we read */
buckettmp = ctx->buffer;
if (canput == (bodysize - hasread)) {
apr_bucket_split(ctx->buffer, canput);
ctx->buffer = APR_BUCKET_NEXT(ctx->buffer);
APR_BUCKET_REMOVE(buckettmp);
} else {
/* canput==bufferlen */
ctx->buffer = NULL;
}
APR_BUCKET_INSERT_AFTER(curbucket, buckettmp);
curbucket = buckettmp;
hasread += canput;
} /* while( hasread<bodysize ) */
}
/* if( header.type==FCGI_STDOUT ) */
/* Now FCGI_END_REQUEST */
else if (header.type == FCGI_END_REQUEST) {
/* Just ignore the body */
hasread = 0;
while (hasread < bodysize) {
char *buffer;
apr_size_t bufferlen, canignore;
/* Feed some data if necessary */
if ((rv =
fcgid_feed_data(ctx, b->list, &buffer,
&bufferlen)) != APR_SUCCESS)
return rv;
canignore = fcgid_min(bufferlen, bodysize);
hasread += canignore;
/* Ignore the bytes */
fcgid_ignore_bytes(ctx, canignore);
}
}
/* Now ignore padding data */
hasread = 0;
while (hasread < header.paddingLength) {
char *buffer;
apr_size_t bufferlen, canignore;
/* Feed some data if necessary */
if ((rv =
fcgid_feed_data(ctx, b->list, &buffer,
&bufferlen)) != APR_SUCCESS)
return rv;
canignore = fcgid_min(bufferlen, header.paddingLength - hasread);
hasread += canignore;
/* Ignore the bytes */
fcgid_ignore_bytes(ctx, canignore);
}
/* Tail another fastcgi header bucket if it's not ending */
if (header.type != FCGI_END_REQUEST) {
apr_bucket *headerbucket =
ap_bucket_fcgid_header_create(b->list, ctx);
APR_BUCKET_INSERT_AFTER(curbucket, headerbucket);
} else {
/* Release the process ASAP */
if ((rv = apr_pool_cleanup_run(ctx->ipc.request->pool,
ctx,
bucket_ctx_cleanup)) != APR_SUCCESS)
return rv;
}
b = apr_bucket_immortal_make(b, "", 0);
return apr_bucket_read(b, str, len, APR_BLOCK_READ);
}
apr_bucket *ap_bucket_fcgid_header_make(apr_bucket * b,
fcgid_bucket_ctx * ctx)
{
b->length = (apr_size_t) (-1);
b->start = -1;
b->data = ctx;
b->type = &ap_bucket_type_fcgid_header;
return b;
}
apr_bucket *ap_bucket_fcgid_header_create(apr_bucket_alloc_t * list,
fcgid_bucket_ctx * ctx)
{
apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
APR_BUCKET_INIT(b);
b->free = apr_bucket_free;
b->list = list;
return ap_bucket_fcgid_header_make(b, ctx);
}
const apr_bucket_type_t ap_bucket_type_fcgid_header = {
"FCGID_HEADER", 5, APR_BUCKET_DATA,
apr_bucket_destroy_noop,
fcgid_header_bucket_read,
apr_bucket_setaside_notimpl,
apr_bucket_split_notimpl,
apr_bucket_copy_notimpl
};