blob: 7f94913c680ec8aec437fdd2dfbced5c0005c5dc [file] [log] [blame]
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
*/
#include "serf.h"
#include "serf_bucket_util.h"
/* the limit on the length of a line in the status-line or headers */
#define LINE_LIMIT 8000
typedef struct {
serf_bucket_t *stream;
enum {
STATE_STATUS_LINE, /* reading status line */
STATE_HEADERS, /* reading headers */
STATE_BODY /* reading body */
} state;
enum {
LINE_EMPTY,
LINE_READY,
LINE_PARTIAL,
LINE_CRLF_SPLIT
} lstate;
apr_size_t line_used;
char line[LINE_LIMIT];
} response_context_t;
SERF_DECLARE(serf_bucket_t *) serf_bucket_response_create(
serf_bucket_t *stream,
serf_bucket_alloc_t *allocator)
{
response_context_t *ctx;
ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx));
ctx->stream = stream;
return serf_bucket_create(&serf_bucket_type_response, allocator, ctx);
}
static apr_status_t fetch_line(response_context_t *ctx,
serf_bucket_t *bkt)
{
/* If we had a complete line, then assume the caller has used it, so
* we can now reset the state.
*/
if (ctx->lstate == LINE_READY) {
ctx->lstate = LINE_EMPTY;
/* Reset the line_used, too, so we don't have to test the state
* before using this value.
*/
ctx->line_used = 0;
}
while (1) {
apr_status_t status;
const char *data;
apr_size_t len;
if (ctx->lstate == LINE_CRLF_SPLIT) {
/* On the previous read, we received just a CR. The LF might
* be present, but the bucket couldn't see it. We need to
* examine a single character to determine how to handle the
* split CRLF.
*/
status = serf_bucket_peek(bkt, &data, &len);
if (len > 0) {
if (*data == '\n') {
/* We saw the second part of CRLF. We don't need to
* save that character, so do an actual read to suck
* up that character.
*/
(void) serf_bucket_read(bkt, 1, &data, &len);
}
/* else:
* We got the first character of the next line. Thus,
* the current line is terminated by the CR. Just
* ignore whatever we peeked at. The next reader will
* see it and handle it as appropriate.
*/
/* Whatever was read, the line is now ready for use. */
ctx->lstate = LINE_READY;
}
/* else len == 0 */
/* ### status */
}
else {
int found;
/* RFC 2616 says that CRLF is the only line ending, but we
* can easily accept any kind of line ending.
*/
status = serf_bucket_readline(bkt, SERF_NEWLINE_ANY, &found,
&data, &len);
if (ctx->line_used + len > sizeof(ctx->line)) {
/* ### need a "line too long" error */
return APR_EGENERAL;
}
/* Note: our logic doesn't change for LINE_PARTIAL. That only
* affects how we fill the buffer. It is a communication to our
* caller on whether the line is ready or not.
*/
/* If we didn't see a newline, then we should mark the line
* buffer as partially complete.
*/
if (found == SERF_NEWLINE_NONE) {
ctx->lstate = LINE_PARTIAL;
}
else if (found == SERF_NEWLINE_CRLF_SPLIT) {
ctx->lstate = LINE_CRLF_SPLIT;
/* Toss the partial CR. We won't ever need it. */
--len;
}
else {
/* We got a newline (of some form). We don't need it
* in the line buffer, so back up the length. Then
* mark the line as ready.
*/
len -= 1 + (found == SERF_NEWLINE_CRLF);
ctx->lstate = LINE_READY;
}
/* ### it would be nice to avoid this copy if at all possible,
### and just return the a data/len pair to the caller. we're
### keeping it simple for now. */
memcpy(&ctx->line[ctx->line_used], data, len);
ctx->line_used += len;
}
/* If we saw anything besides "success. please read again", then
* we should return that status. If the line was completed, then
* we should also return.
*/
if (status || ctx->lstate == LINE_READY)
return status;
/* We got APR_SUCCESS and the line buffer is not complete. Let's
* loop to read some more data.
*/
}
/* NOTREACHED */
}
static apr_status_t run_machine(response_context_t *ctx)
{
return APR_SUCCESS;
}
static apr_status_t wait_for_sline(response_context_t *ctx)
{
/* Keep looping while we're still working on the Status-Line and we
* don't have any issues reading from the input stream.
*/
while (ctx->state == STATE_STATUS_LINE) {
apr_status_t status = run_machine(ctx);
if (status) {
/* we stop any anthing. */
return status;
}
}
return APR_SUCCESS;
}
SERF_DECLARE(apr_status_t) serf_bucket_response_status(
serf_bucket_t *bkt,
serf_status_line *sline)
{
response_context_t *ctx = bkt->data;
apr_status_t status;
if ((status = wait_for_sline(ctx)) != APR_SUCCESS)
return status;
return APR_SUCCESS;
}
/* ### need to implement */
#define serf_response_read NULL
#define serf_response_readline NULL
#define serf_response_read_iovec NULL
#define serf_response_read_for_sendfile NULL
#define serf_response_peek NULL
SERF_DECLARE_DATA const serf_bucket_type_t serf_bucket_type_response = {
"RESPONSE",
serf_response_read,
serf_response_readline,
serf_response_read_iovec,
serf_response_read_for_sendfile,
serf_default_read_bucket,
serf_response_peek,
serf_default_get_metadata,
serf_default_set_metadata,
serf_default_destroy_and_data,
};