
suggested by Craig Nelson in SF Tracker [ 1689382 ] DLINK DIR-625 patch. git-svn-id: https://pupnp.svn.sourceforge.net/svnroot/pupnp/trunk@158 119443c7-1b9e-41f8-b6fc-b9c35fce742c
2521 lines
76 KiB
C
2521 lines
76 KiB
C
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright (c) 2000-2003 Intel Corporation
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions are met:
|
|
//
|
|
// * Redistributions of source code must retain the above copyright notice,
|
|
// this list of conditions and the following disclaimer.
|
|
// * 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.
|
|
// * Neither name of Intel Corporation nor the names of its contributors
|
|
// may be used to endorse or promote products derived from this software
|
|
// without specific prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
// "AS IS" AND ANY EXPRESS 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 INTEL OR
|
|
// 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.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
/************************************************************************
|
|
* Purpose: This file contains functions for scanner and parser for http
|
|
* messages.
|
|
************************************************************************/
|
|
|
|
#include "config.h"
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <limits.h>
|
|
#include <stdarg.h>
|
|
#include "strintmap.h"
|
|
#include "httpparser.h"
|
|
#include "statcodes.h"
|
|
#include "unixutil.h"
|
|
|
|
// entity positions
|
|
|
|
#define NUM_HTTP_METHODS 9
|
|
static str_int_entry Http_Method_Table[NUM_HTTP_METHODS] = {
|
|
{"GET", HTTPMETHOD_GET},
|
|
{"HEAD", HTTPMETHOD_HEAD},
|
|
{"M-POST", HTTPMETHOD_MPOST},
|
|
{"M-SEARCH", HTTPMETHOD_MSEARCH},
|
|
{"NOTIFY", HTTPMETHOD_NOTIFY},
|
|
{"POST", HTTPMETHOD_POST},
|
|
{"SUBSCRIBE", HTTPMETHOD_SUBSCRIBE},
|
|
{"UNSUBSCRIBE", HTTPMETHOD_UNSUBSCRIBE},
|
|
{"POST", SOAPMETHOD_POST},
|
|
|
|
};
|
|
|
|
#define NUM_HTTP_HEADER_NAMES 33
|
|
str_int_entry Http_Header_Names[NUM_HTTP_HEADER_NAMES] = {
|
|
{"ACCEPT", HDR_ACCEPT},
|
|
{"ACCEPT-CHARSET", HDR_ACCEPT_CHARSET},
|
|
{"ACCEPT-ENCODING", HDR_ACCEPT_ENCODING},
|
|
{"ACCEPT-LANGUAGE", HDR_ACCEPT_LANGUAGE},
|
|
{"ACCEPT-RANGES", HDR_ACCEPT_RANGE},
|
|
{"CACHE-CONTROL", HDR_CACHE_CONTROL},
|
|
{"CALLBACK", HDR_CALLBACK},
|
|
{"CONTENT-ENCODING", HDR_CONTENT_ENCODING},
|
|
{"CONTENT-LANGUAGE", HDR_CONTENT_LANGUAGE},
|
|
{"CONTENT-LENGTH", HDR_CONTENT_LENGTH},
|
|
{"CONTENT-LOCATION", HDR_CONTENT_LOCATION},
|
|
{"CONTENT-RANGE", HDR_CONTENT_RANGE},
|
|
{"CONTENT-TYPE", HDR_CONTENT_TYPE},
|
|
{"DATE", HDR_DATE},
|
|
{"EXT", HDR_EXT},
|
|
{"HOST", HDR_HOST},
|
|
{"IF-RANGE", HDR_IF_RANGE},
|
|
{"LOCATION", HDR_LOCATION},
|
|
{"MAN", HDR_MAN},
|
|
{"MX", HDR_MX},
|
|
{"NT", HDR_NT},
|
|
{"NTS", HDR_NTS},
|
|
{"RANGE", HDR_RANGE},
|
|
{"SEQ", HDR_SEQ},
|
|
{"SERVER", HDR_SERVER},
|
|
{"SID", HDR_SID},
|
|
{"SOAPACTION", HDR_SOAPACTION},
|
|
{"ST", HDR_ST},
|
|
{"TE", HDR_TE},
|
|
{"TIMEOUT", HDR_TIMEOUT},
|
|
{"TRANSFER-ENCODING", HDR_TRANSFER_ENCODING},
|
|
{"USER-AGENT", HDR_USER_AGENT},
|
|
{"USN", HDR_USN}
|
|
};
|
|
|
|
/***********************************************************************/
|
|
|
|
/************* scanner **************/
|
|
|
|
/***********************************************************************/
|
|
|
|
#define TOKCHAR_CR 0xD
|
|
#define TOKCHAR_LF 0xA
|
|
|
|
/************************************************************************
|
|
* Function : scanner_init
|
|
*
|
|
* Parameters :
|
|
* OUT scanner_t* scanner ; Scanner Object to be initialized
|
|
* IN membuffer* bufptr ; Buffer to be copied
|
|
*
|
|
* Description : Intialize scanner
|
|
*
|
|
* Return : void ;
|
|
*
|
|
* Note :
|
|
************************************************************************/
|
|
static XINLINE void
|
|
scanner_init( OUT scanner_t * scanner,
|
|
IN membuffer * bufptr )
|
|
{
|
|
scanner->cursor = 0;
|
|
scanner->msg = bufptr;
|
|
scanner->entire_msg_loaded = FALSE;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function : is_separator_char
|
|
*
|
|
* Parameters :
|
|
* IN char c ; character to be tested against used separator values
|
|
*
|
|
* Description : Finds the separator character.
|
|
*
|
|
* Return : xboolean ;
|
|
*
|
|
* Note :
|
|
************************************************************************/
|
|
static XINLINE xboolean
|
|
is_separator_char( IN char c )
|
|
{
|
|
return strchr( " \t()<>@,;:\\\"/[]?={}", c ) != NULL;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function : is_identifier_char
|
|
*
|
|
* Parameters :
|
|
* IN char c ; character to be tested for separator values
|
|
*
|
|
* Description : Calls the function to indentify separator character
|
|
*
|
|
* Return : xboolean ;
|
|
*
|
|
* Note :
|
|
************************************************************************/
|
|
static XINLINE xboolean
|
|
is_identifier_char( IN char c )
|
|
{
|
|
return ( c >= 32 && c <= 126 ) && !is_separator_char( c );
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function : is_control_char
|
|
*
|
|
* Parameters :
|
|
* IN char c ; character to be tested for a control character
|
|
*
|
|
* Description : Determines if the passed value is a control character
|
|
*
|
|
* Return : xboolean ;
|
|
*
|
|
* Note :
|
|
************************************************************************/
|
|
static XINLINE xboolean
|
|
is_control_char( IN char c )
|
|
{
|
|
return ( ( c >= 0 && c <= 31 ) || ( c == 127 ) );
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function : is_qdtext_char
|
|
*
|
|
* Parameters :
|
|
* IN char cc ; character to be tested for CR/LF
|
|
*
|
|
* Description : Checks to see if the passed in value is CR/LF
|
|
*
|
|
* Return : xboolean ;
|
|
*
|
|
* Note :
|
|
************************************************************************/
|
|
static XINLINE xboolean
|
|
is_qdtext_char( IN char cc )
|
|
{
|
|
unsigned char c = ( unsigned char )cc;
|
|
|
|
// we don't check for this; it's checked in get_token()
|
|
assert( c != '"' );
|
|
|
|
if( ( c >= 32 && c != 127 ) ||
|
|
( c == TOKCHAR_CR || c == TOKCHAR_LF || c == '\t' )
|
|
) {
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function : scanner_get_token
|
|
*
|
|
* Parameters :
|
|
* INOUT scanner_t* scanner ; Scanner Object
|
|
* OUT memptr* token ; Token
|
|
* OUT token_type_t* tok_type ; Type of token
|
|
*
|
|
* Description : reads next token from the input stream
|
|
* note: 0 and is used as a marker, and will not be valid in a quote
|
|
*
|
|
* Return : parse_status_t ;
|
|
* PARSE_OK
|
|
* PARSE_INCOMPLETE -- not enuf chars to get a token
|
|
* PARSE_FAILURE -- bad msg format
|
|
*
|
|
* Note :
|
|
************************************************************************/
|
|
static parse_status_t
|
|
scanner_get_token( INOUT scanner_t * scanner,
|
|
OUT memptr * token,
|
|
OUT token_type_t * tok_type )
|
|
{
|
|
char *cursor;
|
|
char *null_terminator; // point to null-terminator in buffer
|
|
char c;
|
|
token_type_t token_type;
|
|
xboolean got_end_quote;
|
|
|
|
assert( scanner );
|
|
assert( token );
|
|
assert( tok_type );
|
|
|
|
// point to next char in buffer
|
|
cursor = scanner->msg->buf + scanner->cursor;
|
|
null_terminator = scanner->msg->buf + scanner->msg->length;
|
|
|
|
// not enough chars in input to parse
|
|
if( cursor == null_terminator ) {
|
|
return PARSE_INCOMPLETE;
|
|
}
|
|
|
|
c = *cursor;
|
|
if( is_identifier_char( c ) ) {
|
|
// scan identifier
|
|
|
|
token->buf = cursor++;
|
|
token_type = TT_IDENTIFIER;
|
|
|
|
while( is_identifier_char( *cursor ) ) {
|
|
cursor++;
|
|
}
|
|
|
|
if( !scanner->entire_msg_loaded && cursor == null_terminator ) {
|
|
// possibly more valid chars
|
|
return PARSE_INCOMPLETE;
|
|
}
|
|
// calc token length
|
|
token->length = cursor - token->buf;
|
|
} else if( c == ' ' || c == '\t' ) {
|
|
token->buf = cursor++;
|
|
token_type = TT_WHITESPACE;
|
|
|
|
while( *cursor == ' ' || *cursor == '\t' ) {
|
|
cursor++;
|
|
}
|
|
|
|
if( !scanner->entire_msg_loaded && cursor == null_terminator ) {
|
|
// possibly more chars
|
|
return PARSE_INCOMPLETE;
|
|
}
|
|
token->length = cursor - token->buf;
|
|
} else if( c == TOKCHAR_CR ) {
|
|
// scan CRLF
|
|
|
|
token->buf = cursor++;
|
|
if( cursor == null_terminator ) {
|
|
// not enuf info to determine CRLF
|
|
return PARSE_INCOMPLETE;
|
|
}
|
|
if( *cursor != TOKCHAR_LF ) {
|
|
// couldn't match CRLF; match as CR
|
|
token_type = TT_CTRL; // ctrl char
|
|
token->length = 1;
|
|
} else {
|
|
// got CRLF
|
|
token->length = 2;
|
|
token_type = TT_CRLF;
|
|
cursor++;
|
|
}
|
|
} else if( c == TOKCHAR_LF ) // accept \n as CRLF
|
|
{
|
|
token->buf = cursor++;
|
|
token->length = 1;
|
|
token_type = TT_CRLF;
|
|
} else if( c == '"' ) {
|
|
// quoted text
|
|
token->buf = cursor++;
|
|
token_type = TT_QUOTEDSTRING;
|
|
got_end_quote = FALSE;
|
|
|
|
while( cursor < null_terminator ) {
|
|
c = *cursor++;
|
|
if( c == '"' ) {
|
|
got_end_quote = TRUE;
|
|
break;
|
|
} else if( c == '\\' ) {
|
|
if( cursor < null_terminator ) {
|
|
c = *cursor++;
|
|
//if ( !(c > 0 && c <= 127) )
|
|
if( c == 0 ) {
|
|
return PARSE_FAILURE;
|
|
}
|
|
}
|
|
// else, while loop handles incomplete buf
|
|
} else if( is_qdtext_char( c ) ) {
|
|
// just accept char
|
|
} else {
|
|
// bad quoted text
|
|
return PARSE_FAILURE;
|
|
}
|
|
}
|
|
if( got_end_quote ) {
|
|
token->length = cursor - token->buf;
|
|
} else // incomplete
|
|
{
|
|
assert( cursor == null_terminator );
|
|
return PARSE_INCOMPLETE;
|
|
}
|
|
} else if( is_separator_char( c ) ) {
|
|
// scan separator
|
|
|
|
token->buf = cursor++;
|
|
token_type = TT_SEPARATOR;
|
|
token->length = 1;
|
|
} else if( is_control_char( c ) ) {
|
|
// scan ctrl char
|
|
|
|
token->buf = cursor++;
|
|
token_type = TT_CTRL;
|
|
token->length = 1;
|
|
} else {
|
|
return PARSE_FAILURE;
|
|
}
|
|
|
|
scanner->cursor += token->length; // move to next token
|
|
*tok_type = token_type;
|
|
return PARSE_OK;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function : scanner_get_str
|
|
*
|
|
* Parameters :
|
|
* IN scanner_t* scanner ; Scanner Object
|
|
*
|
|
* Description : returns ptr to next char in string
|
|
*
|
|
* Return : char* ;
|
|
*
|
|
* Note :
|
|
************************************************************************/
|
|
static XINLINE char *
|
|
scanner_get_str( IN scanner_t * scanner )
|
|
{
|
|
return scanner->msg->buf + scanner->cursor;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function : scanner_pushback
|
|
*
|
|
* Parameters :
|
|
* INOUT scanner_t* scanner ; Scanner Object
|
|
* IN size_t pushback_bytes ; Bytes to be moved back
|
|
*
|
|
* Description : Move back by a certain number of bytes.
|
|
* This is used to put back one or more tokens back into the input
|
|
*
|
|
* Return : void ;
|
|
*
|
|
* Note :
|
|
************************************************************************/
|
|
static XINLINE void
|
|
scanner_pushback( INOUT scanner_t * scanner,
|
|
IN size_t pushback_bytes )
|
|
{
|
|
scanner->cursor -= pushback_bytes;
|
|
}
|
|
|
|
/***********************************************************************/
|
|
|
|
/************* end of scanner **************/
|
|
|
|
/***********************************************************************/
|
|
|
|
/***********************************************************************/
|
|
|
|
/************* parser **************/
|
|
|
|
/***********************************************************************/
|
|
|
|
/***********************************************************************/
|
|
|
|
/************* http_message_t **************/
|
|
|
|
/***********************************************************************/
|
|
|
|
/************************************************************************
|
|
* Function : httpmsg_compare
|
|
*
|
|
* Parameters :
|
|
* void* param1 ;
|
|
* void* param2 ;
|
|
*
|
|
* Description : Compares name id in the http headers.
|
|
*
|
|
* Return : int ;
|
|
*
|
|
* Note :
|
|
************************************************************************/
|
|
static int
|
|
httpmsg_compare( void *param1,
|
|
void *param2 )
|
|
{
|
|
assert( param1 != NULL );
|
|
assert( param2 != NULL );
|
|
|
|
return ( ( http_header_t * ) param1 )->name_id ==
|
|
( ( http_header_t * ) param2 )->name_id;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function : httpheader_free
|
|
*
|
|
* Parameters :
|
|
* void *msg ;
|
|
*
|
|
* Description : Free memory allocated for the http header
|
|
*
|
|
* Return : void ;
|
|
*
|
|
* Note :
|
|
************************************************************************/
|
|
static void
|
|
httpheader_free( void *msg )
|
|
{
|
|
http_header_t *hdr = ( http_header_t * ) msg;
|
|
|
|
membuffer_destroy( &hdr->name_buf );
|
|
membuffer_destroy( &hdr->value );
|
|
free( hdr );
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function : httpmsg_init
|
|
*
|
|
* Parameters :
|
|
* INOUT http_message_t* msg ; HTTP Message Object
|
|
*
|
|
* Description : Initialize and allocate memory for http message
|
|
*
|
|
* Return : void ;
|
|
*
|
|
* Note :
|
|
************************************************************************/
|
|
void
|
|
httpmsg_init( INOUT http_message_t * msg )
|
|
{
|
|
msg->initialized = 1;
|
|
msg->entity.buf = NULL;
|
|
msg->entity.length = 0;
|
|
ListInit( &msg->headers, httpmsg_compare, httpheader_free );
|
|
membuffer_init( &msg->msg );
|
|
membuffer_init( &msg->status_msg );
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function : httpmsg_destroy
|
|
*
|
|
* Parameters :
|
|
* INOUT http_message_t* msg ; HTTP Message Object
|
|
*
|
|
* Description : Free memory allocated for the http message
|
|
*
|
|
* Return : void ;
|
|
*
|
|
* Note :
|
|
************************************************************************/
|
|
void
|
|
httpmsg_destroy( INOUT http_message_t * msg )
|
|
{
|
|
assert( msg != NULL );
|
|
|
|
if( msg->initialized == 1 ) {
|
|
ListDestroy( &msg->headers, 1 );
|
|
membuffer_destroy( &msg->msg );
|
|
membuffer_destroy( &msg->status_msg );
|
|
free( msg->urlbuf );
|
|
msg->initialized = 0;
|
|
}
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function : httpmsg_find_hdr_str
|
|
*
|
|
* Parameters :
|
|
* IN http_message_t* msg ; HTTP Message Object
|
|
* IN const char* header_name ; Header name to be compared with
|
|
*
|
|
* Description : Compares the header name with the header names stored
|
|
* in the linked list of messages
|
|
*
|
|
* Return : http_header_t* - Pointer to a header on success;
|
|
* NULL on failure
|
|
*
|
|
* Note :
|
|
************************************************************************/
|
|
http_header_t *
|
|
httpmsg_find_hdr_str( IN http_message_t * msg,
|
|
IN const char *header_name )
|
|
{
|
|
http_header_t *header;
|
|
|
|
ListNode *node;
|
|
|
|
node = ListHead( &msg->headers );
|
|
while( node != NULL ) {
|
|
|
|
header = ( http_header_t * ) node->item;
|
|
|
|
if( memptr_cmp_nocase( &header->name, header_name ) == 0 ) {
|
|
return header;
|
|
}
|
|
|
|
node = ListNext( &msg->headers, node );
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function : httpmsg_find_hdr
|
|
*
|
|
* Parameters :
|
|
* IN http_message_t* msg ; HTTP Message Object
|
|
* IN int header_name_id ; Header Name ID to be compared with
|
|
* OUT memptr* value ; Buffer to get the ouput to.
|
|
*
|
|
* Description : Finds header from a list, with the given 'name_id'.
|
|
*
|
|
* Return : http_header_t* - Pointer to a header on success; *
|
|
* NULL on failure
|
|
*
|
|
* Note :
|
|
************************************************************************/
|
|
http_header_t *
|
|
httpmsg_find_hdr( IN http_message_t * msg,
|
|
IN int header_name_id,
|
|
OUT memptr * value )
|
|
{
|
|
http_header_t header; // temp header for searching
|
|
|
|
ListNode *node;
|
|
|
|
http_header_t *data;
|
|
|
|
header.name_id = header_name_id;
|
|
|
|
node = ListFind( &msg->headers, NULL, &header );
|
|
|
|
if( node == NULL ) {
|
|
return NULL;
|
|
}
|
|
|
|
data = ( http_header_t * ) node->item;
|
|
|
|
if( value != NULL ) {
|
|
value->buf = data->value.buf;
|
|
value->length = data->value.length;
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
/***********************************************************************/
|
|
|
|
/************* http_parser_t **************/
|
|
|
|
/***********************************************************************/
|
|
|
|
/************************************************************************
|
|
* Function : skip_blank_lines
|
|
*
|
|
* Parameters :
|
|
* INOUT scanner_t* scanner ; Scanner Object
|
|
*
|
|
* Description : skips blank lines at the start of a msg.
|
|
*
|
|
* Return : int ;
|
|
*
|
|
* Note :
|
|
************************************************************************/
|
|
static XINLINE int
|
|
skip_blank_lines( INOUT scanner_t * scanner )
|
|
{
|
|
memptr token;
|
|
token_type_t tok_type;
|
|
parse_status_t status;
|
|
|
|
// skip ws, crlf
|
|
do {
|
|
status = scanner_get_token( scanner, &token, &tok_type );
|
|
} while( status == PARSE_OK &&
|
|
( tok_type == TT_WHITESPACE || tok_type == TT_CRLF ) );
|
|
|
|
if( status == PARSE_OK ) {
|
|
// pushback a non-whitespace token
|
|
scanner->cursor -= token.length;
|
|
//scanner_pushback( scanner, token.length );
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function : skip_lws
|
|
*
|
|
* Parameters :
|
|
* INOUT scanner_t* scanner ; Scanner Object
|
|
*
|
|
* Description : skip linear whitespace.
|
|
*
|
|
* Return : int ;
|
|
* PARSE_OK: (LWS)* removed from input
|
|
* PARSE_FAILURE: bad input
|
|
* PARSE_INCOMPLETE: incomplete input
|
|
*
|
|
* Note :
|
|
************************************************************************/
|
|
static XINLINE int
|
|
skip_lws( INOUT scanner_t * scanner )
|
|
{
|
|
memptr token;
|
|
token_type_t tok_type;
|
|
parse_status_t status;
|
|
size_t save_pos;
|
|
xboolean matched;
|
|
|
|
do {
|
|
save_pos = scanner->cursor;
|
|
matched = FALSE;
|
|
|
|
// get CRLF or WS
|
|
status = scanner_get_token( scanner, &token, &tok_type );
|
|
if( status == PARSE_OK ) {
|
|
if( tok_type == TT_CRLF ) {
|
|
// get WS
|
|
status = scanner_get_token( scanner, &token, &tok_type );
|
|
}
|
|
|
|
if( status == PARSE_OK && tok_type == TT_WHITESPACE ) {
|
|
matched = TRUE;
|
|
} else {
|
|
// did not match LWS; pushback token(s)
|
|
scanner->cursor = save_pos;
|
|
}
|
|
}
|
|
} while( matched );
|
|
|
|
// if entire msg is loaded, ignore an 'incomplete' warning
|
|
if( status == PARSE_INCOMPLETE && scanner->entire_msg_loaded ) {
|
|
status = PARSE_OK;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function : match_non_ws_string
|
|
*
|
|
* Parameters :
|
|
* INOUT scanner_t* scanner ; Scanner Object
|
|
* OUT memptr* str ; Buffer to get the scanner buffer contents.
|
|
*
|
|
* Description : Match a string without whitespace or CRLF (%S)
|
|
*
|
|
* Return : XINLINE parse_status_t ;
|
|
* PARSE_OK
|
|
* PARSE_NO_MATCH
|
|
* PARSE_FAILURE
|
|
* PARSE_INCOMPLETE
|
|
*
|
|
* Note :
|
|
************************************************************************/
|
|
static XINLINE parse_status_t
|
|
match_non_ws_string( INOUT scanner_t * scanner,
|
|
OUT memptr * str )
|
|
{
|
|
memptr token;
|
|
token_type_t tok_type;
|
|
parse_status_t status;
|
|
xboolean done = FALSE;
|
|
size_t save_cursor;
|
|
|
|
save_cursor = scanner->cursor;
|
|
|
|
str->length = 0;
|
|
str->buf = scanner_get_str( scanner ); // point to next char in input
|
|
|
|
while( !done ) {
|
|
status = scanner_get_token( scanner, &token, &tok_type );
|
|
if( status == PARSE_OK &&
|
|
tok_type != TT_WHITESPACE && tok_type != TT_CRLF ) {
|
|
// append non-ws token
|
|
str->length += token.length;
|
|
} else {
|
|
done = TRUE;
|
|
}
|
|
}
|
|
|
|
if( status == PARSE_OK ) {
|
|
// last token was WS; push it back in
|
|
scanner->cursor -= token.length;
|
|
}
|
|
// tolerate 'incomplete' msg
|
|
if( status == PARSE_OK ||
|
|
( status == PARSE_INCOMPLETE && scanner->entire_msg_loaded )
|
|
) {
|
|
if( str->length == 0 ) {
|
|
// no strings found
|
|
return PARSE_NO_MATCH;
|
|
} else {
|
|
return PARSE_OK;
|
|
}
|
|
} else {
|
|
// error -- pushback tokens
|
|
scanner->cursor = save_cursor;
|
|
return status;
|
|
}
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function : match_raw_value
|
|
*
|
|
* Parameters :
|
|
* INOUT scanner_t* scanner ; Scanner Object
|
|
* OUT memptr* raw_value ; Buffer to get the scanner buffer
|
|
* contents
|
|
*
|
|
* Description : Matches a raw value in a the input; value's length
|
|
* can be 0 or more. Whitespace after value is trimmed. On success,
|
|
* scanner points the CRLF that ended the value
|
|
*
|
|
* Return : parse_status_t ;
|
|
* PARSE_OK
|
|
* PARSE_INCOMPLETE
|
|
* PARSE_FAILURE
|
|
*
|
|
* Note :
|
|
************************************************************************/
|
|
static XINLINE parse_status_t
|
|
match_raw_value( INOUT scanner_t * scanner,
|
|
OUT memptr * raw_value )
|
|
{
|
|
memptr token;
|
|
token_type_t tok_type;
|
|
parse_status_t status;
|
|
xboolean done = FALSE;
|
|
xboolean saw_crlf = FALSE;
|
|
size_t pos_at_crlf = 0;
|
|
size_t save_pos;
|
|
char c;
|
|
|
|
save_pos = scanner->cursor;
|
|
|
|
// value points to start of input
|
|
raw_value->buf = scanner_get_str( scanner );
|
|
raw_value->length = 0;
|
|
|
|
while( !done ) {
|
|
status = scanner_get_token( scanner, &token, &tok_type );
|
|
if( status == PARSE_OK ) {
|
|
if( !saw_crlf ) {
|
|
if( tok_type == TT_CRLF ) {
|
|
// CRLF could end value
|
|
saw_crlf = TRUE;
|
|
|
|
// save input position at start of CRLF
|
|
pos_at_crlf = scanner->cursor - token.length;
|
|
}
|
|
// keep appending value
|
|
raw_value->length += token.length;
|
|
} else // already seen CRLF
|
|
{
|
|
if( tok_type == TT_WHITESPACE ) {
|
|
// start again; forget CRLF
|
|
saw_crlf = FALSE;
|
|
raw_value->length += token.length;
|
|
} else {
|
|
// non-ws means value ended just before CRLF
|
|
done = TRUE;
|
|
|
|
// point to the crlf which ended the value
|
|
scanner->cursor = pos_at_crlf;
|
|
}
|
|
}
|
|
} else {
|
|
// some kind of error; restore scanner position
|
|
scanner->cursor = save_pos;
|
|
done = TRUE;
|
|
}
|
|
}
|
|
|
|
if( status == PARSE_OK ) {
|
|
// trim whitespace on right side of value
|
|
while( raw_value->length > 0 ) {
|
|
// get last char
|
|
c = raw_value->buf[raw_value->length - 1];
|
|
|
|
if( c != ' ' && c != '\t' &&
|
|
c != TOKCHAR_CR && c != TOKCHAR_LF ) {
|
|
// done; no more whitespace
|
|
break;
|
|
}
|
|
// remove whitespace
|
|
raw_value->length--;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function: match_int
|
|
*
|
|
* Parameters:
|
|
* INOUT scanner_t* scanner ; Scanner Object
|
|
* IN int base : Base of number in the string;
|
|
* valid values: 10 or 16
|
|
* OUT int* value ; Number stored here
|
|
*
|
|
* Description: Matches an unsigned integer value in the input. The
|
|
* integer is returned in 'value'. Except for PARSE_OK result, the
|
|
* scanner's cursor is moved back to its original position on error.
|
|
*
|
|
* Returns:
|
|
* PARSE_OK
|
|
* PARSE_NO_MATCH -- got different kind of token
|
|
* PARSE_FAILURE -- bad input
|
|
* PARSE_INCOMPLETE
|
|
************************************************************************/
|
|
static XINLINE int
|
|
match_int( INOUT scanner_t * scanner,
|
|
IN int base,
|
|
OUT int *value )
|
|
{
|
|
memptr token;
|
|
token_type_t tok_type;
|
|
parse_status_t status;
|
|
long num;
|
|
char *end_ptr;
|
|
size_t save_pos;
|
|
|
|
save_pos = scanner->cursor;
|
|
|
|
status = scanner_get_token( scanner, &token, &tok_type );
|
|
if( status == PARSE_OK ) {
|
|
if( tok_type == TT_IDENTIFIER ) {
|
|
errno = 0;
|
|
|
|
num = strtol( token.buf, &end_ptr, base );
|
|
if( ( num < 0 )
|
|
// all and only those chars in token should be used for num
|
|
|| ( end_ptr != token.buf + token.length )
|
|
|| ( ( num == LONG_MIN || num == LONG_MAX )
|
|
&& ( errno == ERANGE ) )
|
|
) {
|
|
status = PARSE_NO_MATCH;
|
|
}
|
|
|
|
*value = num; // save result
|
|
} else {
|
|
status = PARSE_NO_MATCH; // token must be an identifier
|
|
}
|
|
}
|
|
|
|
if( status != PARSE_OK ) {
|
|
// restore scanner position for bad values
|
|
scanner->cursor = save_pos;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function: read_until_crlf
|
|
*
|
|
* Parameters:
|
|
* INOUT scanner_t* scanner ; Scanner Object
|
|
* OUT memptr* str ; Buffer to copy scanner buffer contents to
|
|
*
|
|
* Description: Reads data until end of line; the crlf at the end of
|
|
* line is not consumed. On error, scanner is not restored. On
|
|
* success, 'str' points to a string that runs until eol
|
|
*
|
|
* Returns:
|
|
* PARSE_OK
|
|
* PARSE_FAILURE
|
|
* PARSE_INCOMPLETE
|
|
************************************************************************/
|
|
static XINLINE int
|
|
read_until_crlf( INOUT scanner_t * scanner,
|
|
OUT memptr * str )
|
|
{
|
|
memptr token;
|
|
token_type_t tok_type;
|
|
parse_status_t status;
|
|
size_t start_pos;
|
|
|
|
start_pos = scanner->cursor;
|
|
str->buf = scanner_get_str( scanner );
|
|
|
|
// read until we hit a crlf
|
|
do {
|
|
status = scanner_get_token( scanner, &token, &tok_type );
|
|
} while( status == PARSE_OK && tok_type != TT_CRLF );
|
|
|
|
if( status == PARSE_OK ) {
|
|
// pushback crlf in stream
|
|
scanner->cursor -= token.length;
|
|
|
|
// str should include all strings except crlf at the end
|
|
str->length = scanner->cursor - start_pos;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function: skip_to_end_of_header
|
|
*
|
|
* Parameters:
|
|
* INOUT scanner_t* scanner ; Scanner Object
|
|
*
|
|
* Description: Skip to end of header
|
|
*
|
|
* Returns:
|
|
* PARSE_OK
|
|
* PARSE_FAILURE
|
|
* PARSE_INCOMPLETE
|
|
************************************************************************/
|
|
static XINLINE int
|
|
skip_to_end_of_header( INOUT scanner_t * scanner )
|
|
{
|
|
memptr dummy_raw_value;
|
|
parse_status_t status;
|
|
|
|
status = match_raw_value( scanner, &dummy_raw_value );
|
|
return status;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function: match_char
|
|
*
|
|
* Parameters:
|
|
* INOUT scanner_t* scanner ; Scanner Object
|
|
* IN char c ; Character to be compared with
|
|
* IN xboolean case_sensitive; Flag indicating whether comparison should
|
|
* be case sensitive
|
|
*
|
|
* Description: Compares a character to the next char in the scanner;
|
|
* on error, scanner chars are not restored
|
|
*
|
|
* Returns:
|
|
* PARSE_OK
|
|
* PARSE_NO_MATCH
|
|
* PARSE_INCOMPLETE
|
|
************************************************************************/
|
|
static XINLINE parse_status_t
|
|
match_char( INOUT scanner_t * scanner,
|
|
IN char c,
|
|
IN xboolean case_sensitive )
|
|
{
|
|
char scan_char;
|
|
|
|
if( scanner->cursor >= scanner->msg->length ) {
|
|
return PARSE_INCOMPLETE;
|
|
}
|
|
// read next char from scanner
|
|
scan_char = scanner->msg->buf[scanner->cursor++];
|
|
|
|
if( case_sensitive ) {
|
|
return c == scan_char ? PARSE_OK : PARSE_NO_MATCH;
|
|
} else {
|
|
return tolower( c ) == tolower( scan_char ) ?
|
|
PARSE_OK : PARSE_NO_MATCH;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// args for ...
|
|
// %d, int * (31-bit positive integer)
|
|
// %x, int * (31-bit postive number encoded as hex)
|
|
// %s, memptr* (simple identifier)
|
|
// %q, memptr* (quoted string)
|
|
// %S, memptr* (non-whitespace string)
|
|
// %R, memptr* (raw value)
|
|
// %U, uri_type* (url)
|
|
// %L, memptr* (string until end of line)
|
|
// %P, int * (current index of the string being scanned)
|
|
//
|
|
// no args for
|
|
// ' ' LWS*
|
|
// \t whitespace
|
|
// "%%" matches '%'
|
|
// "% " matches ' '
|
|
// %c matches CRLF
|
|
// %i ignore case in literal matching
|
|
// %n case-sensitive matching in literals
|
|
// %w optional whitespace; (similar to '\t',
|
|
// except whitespace is optional)
|
|
// %0 (zero) match null-terminator char '\0'
|
|
// (can only be used as last char in fmt)
|
|
// use only in matchstr(), not match()
|
|
// other chars match literally
|
|
//
|
|
// returns:
|
|
// PARSE_OK
|
|
// PARSE_INCOMPLETE
|
|
// PARSE_FAILURE -- bad input
|
|
// PARSE_NO_MATCH -- input does not match pattern
|
|
|
|
/************************************************************************
|
|
* Function : vfmatch
|
|
*
|
|
* Parameters :
|
|
* INOUT scanner_t* scanner ; Scanner Object
|
|
* IN const char* fmt ; Pattern Format
|
|
* va_list argp ; List of variable arguments
|
|
*
|
|
* Description : Extracts variable parameters depending on the passed
|
|
* in format parameter. Parses data also based on the passed in
|
|
* format parameter.
|
|
*
|
|
* Return : int ;
|
|
* PARSE_OK
|
|
* PARSE_INCOMPLETE
|
|
* PARSE_FAILURE - bad input
|
|
* PARSE_NO_MATCH - input does not match pattern
|
|
*
|
|
* Note :
|
|
************************************************************************/
|
|
static int
|
|
vfmatch( INOUT scanner_t * scanner,
|
|
IN const char *fmt,
|
|
va_list argp )
|
|
{
|
|
char c;
|
|
const char *fmt_ptr = fmt;
|
|
parse_status_t status;
|
|
memptr *str_ptr;
|
|
memptr temp_str;
|
|
int *int_ptr;
|
|
uri_type *uri_ptr;
|
|
size_t save_pos;
|
|
int stat;
|
|
xboolean case_sensitive = TRUE;
|
|
memptr token;
|
|
token_type_t tok_type;
|
|
int base;
|
|
|
|
assert( scanner != NULL );
|
|
assert( fmt != NULL );
|
|
|
|
// save scanner pos; to aid error recovery
|
|
save_pos = scanner->cursor;
|
|
|
|
status = PARSE_OK;
|
|
while( ( ( c = *fmt_ptr++ ) != 0 ) && ( status == PARSE_OK )
|
|
) {
|
|
if( c == '%' ) {
|
|
c = *fmt_ptr++;
|
|
|
|
switch ( c ) {
|
|
|
|
case 'R': // raw value
|
|
str_ptr = va_arg( argp, memptr * );
|
|
assert( str_ptr != NULL );
|
|
status = match_raw_value( scanner, str_ptr );
|
|
break;
|
|
|
|
case 's': // simple identifier
|
|
str_ptr = va_arg( argp, memptr * );
|
|
assert( str_ptr != NULL );
|
|
status = scanner_get_token( scanner, str_ptr,
|
|
&tok_type );
|
|
if( status == PARSE_OK && tok_type != TT_IDENTIFIER ) {
|
|
// not an identifier
|
|
status = PARSE_NO_MATCH;
|
|
}
|
|
break;
|
|
|
|
case 'c': // crlf
|
|
status = scanner_get_token( scanner,
|
|
&token, &tok_type );
|
|
if( status == PARSE_OK && tok_type != TT_CRLF ) {
|
|
// not CRLF token
|
|
status = PARSE_NO_MATCH;
|
|
}
|
|
break;
|
|
|
|
case 'd': // integer
|
|
case 'x': // hex number
|
|
int_ptr = va_arg( argp, int * );
|
|
|
|
assert( int_ptr != NULL );
|
|
base = ( c == 'd' ? 10 : 16 );
|
|
status = match_int( scanner, base, int_ptr );
|
|
break;
|
|
|
|
case 'S': // non-whitespace string
|
|
case 'U': // uri
|
|
if( c == 'S' ) {
|
|
str_ptr = va_arg( argp, memptr * );
|
|
} else {
|
|
str_ptr = &temp_str;
|
|
}
|
|
assert( str_ptr != NULL );
|
|
status = match_non_ws_string( scanner, str_ptr );
|
|
if( c == 'U' && status == PARSE_OK ) {
|
|
uri_ptr = va_arg( argp, uri_type * );
|
|
assert( uri_ptr != NULL );
|
|
stat = parse_uri( str_ptr->buf, str_ptr->length,
|
|
uri_ptr );
|
|
if( stat != HTTP_SUCCESS ) {
|
|
status = PARSE_NO_MATCH;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'L': // string till eol
|
|
str_ptr = va_arg( argp, memptr * );
|
|
assert( str_ptr != NULL );
|
|
status = read_until_crlf( scanner, str_ptr );
|
|
break;
|
|
|
|
case ' ': // match space
|
|
case '%': // match percentage symbol
|
|
status = match_char( scanner, c, case_sensitive );
|
|
break;
|
|
|
|
case 'n': // case-sensitive match
|
|
case_sensitive = TRUE;
|
|
break;
|
|
|
|
case 'i': // ignore case
|
|
case_sensitive = FALSE;
|
|
break;
|
|
|
|
case 'q': // quoted string
|
|
str_ptr = ( memptr * ) va_arg( argp, memptr * );
|
|
status =
|
|
scanner_get_token( scanner, str_ptr, &tok_type );
|
|
if( status == PARSE_OK && tok_type != TT_QUOTEDSTRING ) {
|
|
status = PARSE_NO_MATCH; // not a quoted string
|
|
}
|
|
break;
|
|
|
|
case 'w': // optional whitespace
|
|
status = scanner_get_token( scanner,
|
|
&token, &tok_type );
|
|
if( status == PARSE_OK && tok_type != TT_WHITESPACE ) {
|
|
// restore non-whitespace token
|
|
scanner->cursor -= token.length;
|
|
}
|
|
break;
|
|
|
|
case 'P': // current pos of scanner
|
|
int_ptr = va_arg( argp, int * );
|
|
|
|
assert( int_ptr != NULL );
|
|
*int_ptr = scanner->cursor;
|
|
break;
|
|
|
|
// valid only in matchstr()
|
|
case '0': // end of msg?
|
|
// check that we are 1 beyond last char
|
|
if( scanner->cursor == scanner->msg->length &&
|
|
scanner->msg->buf[scanner->cursor] == '\0' ) {
|
|
status = PARSE_OK;
|
|
} else {
|
|
status = PARSE_NO_MATCH;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
assert( 0 ); // unknown option
|
|
}
|
|
} else {
|
|
switch ( c ) {
|
|
case ' ': // LWS*
|
|
status = skip_lws( scanner );
|
|
break;
|
|
|
|
case '\t': // Whitespace
|
|
status = scanner_get_token( scanner,
|
|
&token, &tok_type );
|
|
if( status == PARSE_OK && tok_type != TT_WHITESPACE ) {
|
|
// not whitespace token
|
|
status = PARSE_NO_MATCH;
|
|
}
|
|
break;
|
|
|
|
default: // match characters
|
|
{
|
|
status = match_char( scanner, c, case_sensitive );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( status != PARSE_OK ) {
|
|
// on error, restore original scanner pos
|
|
scanner->cursor = save_pos;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function: match
|
|
*
|
|
* Parameters:
|
|
* INOUT scanner_t* scanner ; Scanner Object
|
|
* IN const char* fmt; Pattern format
|
|
* ...
|
|
*
|
|
* Description: matches a variable parameter list and takes necessary
|
|
* actions based on the data type specified.
|
|
*
|
|
* Returns:
|
|
* PARSE_OK
|
|
* PARSE_NO_MATCH
|
|
* PARSE_INCOMPLETE
|
|
************************************************************************/
|
|
static int
|
|
match( INOUT scanner_t * scanner,
|
|
IN const char *fmt,
|
|
... )
|
|
{
|
|
int ret_code;
|
|
va_list args;
|
|
|
|
va_start( args, fmt );
|
|
ret_code = vfmatch( scanner, fmt, args );
|
|
va_end( args );
|
|
|
|
return ret_code;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function: matchstr
|
|
*
|
|
* Parameters:
|
|
* IN char *str ; String to be matched
|
|
* IN size_t slen ; Length of the string
|
|
* IN const char* fmt ; Pattern format
|
|
* ...
|
|
*
|
|
* Description: Matches a variable parameter list with a string
|
|
* and takes actions based on the data type specified.
|
|
*
|
|
* Returns:
|
|
* PARSE_OK
|
|
* PARSE_NO_MATCH -- failure to match pattern 'fmt'
|
|
* PARSE_FAILURE -- 'str' is bad input
|
|
************************************************************************/
|
|
int
|
|
matchstr( IN char *str,
|
|
IN size_t slen,
|
|
IN const char *fmt,
|
|
... )
|
|
{
|
|
int ret_code;
|
|
char save_char;
|
|
scanner_t scanner;
|
|
membuffer buf;
|
|
va_list arg_list;
|
|
|
|
// null terminate str
|
|
save_char = str[slen];
|
|
str[slen] = '\0';
|
|
|
|
membuffer_init( &buf );
|
|
|
|
// under no circumstances should this buffer be modifed because its memory
|
|
// might have not come from malloc()
|
|
membuffer_attach( &buf, str, slen );
|
|
|
|
scanner_init( &scanner, &buf );
|
|
scanner.entire_msg_loaded = TRUE;
|
|
|
|
va_start( arg_list, fmt );
|
|
ret_code = vfmatch( &scanner, fmt, arg_list );
|
|
va_end( arg_list );
|
|
|
|
// restore str
|
|
str[slen] = save_char;
|
|
|
|
// don't destroy buf
|
|
|
|
return ret_code;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function: parser_init
|
|
*
|
|
* Parameters:
|
|
* OUT http_parser_t* parser ; HTTP Parser object
|
|
*
|
|
* Description: Initializes the parser object.
|
|
*
|
|
* Returns:
|
|
* void
|
|
************************************************************************/
|
|
static XINLINE void
|
|
parser_init( OUT http_parser_t * parser )
|
|
{
|
|
memset( parser, 0, sizeof( http_parser_t ) );
|
|
|
|
parser->http_error_code = HTTP_BAD_REQUEST; // err msg by default
|
|
parser->ent_position = ENTREAD_DETERMINE_READ_METHOD;
|
|
parser->valid_ssdp_notify_hack = FALSE;
|
|
|
|
httpmsg_init( &parser->msg );
|
|
scanner_init( &parser->scanner, &parser->msg.msg );
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function: parser_parse_requestline
|
|
*
|
|
* Parameters:
|
|
* INOUT http_parser_t* parser ; HTTP Parser object
|
|
*
|
|
* Description: Get HTTP Method, URL location and version information.
|
|
*
|
|
* Returns:
|
|
* PARSE_OK
|
|
* PARSE_SUCCESS
|
|
* PARSE_FAILURE
|
|
************************************************************************/
|
|
static parse_status_t
|
|
parser_parse_requestline( INOUT http_parser_t * parser )
|
|
{
|
|
parse_status_t status;
|
|
http_message_t *hmsg = &parser->msg;
|
|
memptr method_str;
|
|
memptr version_str;
|
|
int index;
|
|
char save_char;
|
|
int num_scanned;
|
|
memptr url_str;
|
|
|
|
assert( parser->position == POS_REQUEST_LINE );
|
|
|
|
status = skip_blank_lines( &parser->scanner );
|
|
if( status != PARSE_OK ) {
|
|
return status;
|
|
}
|
|
//simple get http 0.9 as described in http 1.0 spec
|
|
|
|
status =
|
|
match( &parser->scanner, "%s\t%S%w%c", &method_str, &url_str );
|
|
|
|
if( status == PARSE_OK ) {
|
|
|
|
index =
|
|
map_str_to_int( method_str.buf, method_str.length,
|
|
Http_Method_Table, NUM_HTTP_METHODS, TRUE );
|
|
|
|
if( index < 0 ) {
|
|
// error; method not found
|
|
parser->http_error_code = HTTP_NOT_IMPLEMENTED;
|
|
return PARSE_FAILURE;
|
|
}
|
|
|
|
if( Http_Method_Table[index].id != HTTPMETHOD_GET ) {
|
|
parser->http_error_code = HTTP_BAD_REQUEST;
|
|
return PARSE_FAILURE;
|
|
}
|
|
|
|
hmsg->method = HTTPMETHOD_SIMPLEGET;
|
|
|
|
// store url
|
|
hmsg->urlbuf = str_alloc( url_str.buf, url_str.length );
|
|
if( hmsg->urlbuf == NULL ) {
|
|
// out of mem
|
|
parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
|
|
return PARSE_FAILURE;
|
|
}
|
|
if( parse_uri( hmsg->urlbuf, url_str.length, &hmsg->uri ) !=
|
|
HTTP_SUCCESS ) {
|
|
return PARSE_FAILURE;
|
|
}
|
|
|
|
parser->position = POS_COMPLETE; // move to headers
|
|
|
|
return PARSE_SUCCESS;
|
|
}
|
|
|
|
status = match( &parser->scanner,
|
|
"%s\t%S\t%ihttp%w/%w%L%c", &method_str, &url_str,
|
|
&version_str );
|
|
if( status != PARSE_OK ) {
|
|
return status;
|
|
}
|
|
// store url
|
|
hmsg->urlbuf = str_alloc( url_str.buf, url_str.length );
|
|
if( hmsg->urlbuf == NULL ) {
|
|
// out of mem
|
|
parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
|
|
return PARSE_FAILURE;
|
|
}
|
|
if( parse_uri( hmsg->urlbuf, url_str.length, &hmsg->uri ) !=
|
|
HTTP_SUCCESS ) {
|
|
return PARSE_FAILURE;
|
|
}
|
|
// scan version
|
|
save_char = version_str.buf[version_str.length];
|
|
version_str.buf[version_str.length] = '\0'; // null-terminate
|
|
num_scanned = sscanf( version_str.buf, "%d . %d",
|
|
&hmsg->major_version, &hmsg->minor_version );
|
|
version_str.buf[version_str.length] = save_char; // restore
|
|
if( num_scanned != 2 ||
|
|
hmsg->major_version < 0 || hmsg->minor_version < 0 ) {
|
|
// error; bad http version
|
|
return PARSE_FAILURE;
|
|
}
|
|
|
|
index =
|
|
map_str_to_int( method_str.buf, method_str.length,
|
|
Http_Method_Table, NUM_HTTP_METHODS, TRUE );
|
|
if( index < 0 ) {
|
|
// error; method not found
|
|
parser->http_error_code = HTTP_NOT_IMPLEMENTED;
|
|
return PARSE_FAILURE;
|
|
}
|
|
|
|
hmsg->method = Http_Method_Table[index].id;
|
|
parser->position = POS_HEADERS; // move to headers
|
|
|
|
return PARSE_OK;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function: parser_parse_responseline
|
|
*
|
|
* Parameters:
|
|
* INOUT http_parser_t* parser ; HTTP Parser object
|
|
*
|
|
* Description: Get HTTP Method, URL location and version information.
|
|
*
|
|
* Returns:
|
|
* PARSE_OK
|
|
* PARSE_SUCCESS
|
|
* PARSE_FAILURE
|
|
************************************************************************/
|
|
parse_status_t
|
|
parser_parse_responseline( INOUT http_parser_t * parser )
|
|
{
|
|
parse_status_t status;
|
|
http_message_t *hmsg = &parser->msg;
|
|
memptr line;
|
|
char save_char;
|
|
int num_scanned;
|
|
int i;
|
|
char *p;
|
|
|
|
assert( parser->position == POS_RESPONSE_LINE );
|
|
|
|
status = skip_blank_lines( &parser->scanner );
|
|
if( status != PARSE_OK ) {
|
|
return status;
|
|
}
|
|
// response line
|
|
//status = match( &parser->scanner, "%ihttp%w/%w%d\t.\t%d\t%d\t%L%c",
|
|
// &hmsg->major_version, &hmsg->minor_version,
|
|
// &hmsg->status_code, &hmsg->status_msg );
|
|
|
|
status = match( &parser->scanner, "%ihttp%w/%w%L%c", &line );
|
|
if( status != PARSE_OK ) {
|
|
return status;
|
|
}
|
|
|
|
save_char = line.buf[line.length];
|
|
line.buf[line.length] = '\0'; // null-terminate
|
|
|
|
// scan http version and ret code
|
|
num_scanned = sscanf( line.buf, "%d . %d %d",
|
|
&hmsg->major_version, &hmsg->minor_version,
|
|
&hmsg->status_code );
|
|
|
|
line.buf[line.length] = save_char; // restore
|
|
|
|
if( num_scanned != 3 ||
|
|
hmsg->major_version < 0 ||
|
|
hmsg->minor_version < 0 || hmsg->status_code < 0 ) {
|
|
// bad response line
|
|
return PARSE_FAILURE;
|
|
}
|
|
//
|
|
// point to status msg
|
|
//
|
|
|
|
p = line.buf;
|
|
|
|
// skip 3 ints
|
|
for( i = 0; i < 3; i++ ) {
|
|
// go to start of num
|
|
while( !isdigit( *p ) ) {
|
|
p++;
|
|
}
|
|
|
|
// skip int
|
|
while( isdigit( *p ) ) {
|
|
p++;
|
|
}
|
|
}
|
|
|
|
// whitespace must exist after status code
|
|
if( *p != ' ' && *p != '\t' ) {
|
|
return PARSE_FAILURE;
|
|
}
|
|
// skip whitespace
|
|
while( *p == ' ' || *p == '\t' ) {
|
|
p++;
|
|
}
|
|
|
|
// now, p is at start of status msg
|
|
if( membuffer_assign( &hmsg->status_msg, p,
|
|
line.length - ( p - line.buf ) ) != 0 ) {
|
|
// out of mem
|
|
parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
|
|
return PARSE_FAILURE;
|
|
}
|
|
|
|
parser->position = POS_HEADERS; // move to headers
|
|
|
|
return PARSE_OK;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function: parser_parse_headers
|
|
*
|
|
* Parameters:
|
|
* INOUT http_parser_t* parser ; HTTP Parser object
|
|
*
|
|
* Description: Get HTTP Method, URL location and version information.
|
|
*
|
|
* Returns:
|
|
* PARSE_OK
|
|
* PARSE_SUCCESS
|
|
* PARSE_FAILURE
|
|
************************************************************************/
|
|
parse_status_t
|
|
parser_parse_headers( INOUT http_parser_t * parser )
|
|
{
|
|
parse_status_t status;
|
|
memptr token;
|
|
memptr hdr_value;
|
|
token_type_t tok_type;
|
|
scanner_t *scanner = &parser->scanner;
|
|
size_t save_pos;
|
|
http_header_t *header;
|
|
int header_id;
|
|
int ret = 0;
|
|
int index;
|
|
http_header_t *orig_header;
|
|
char save_char;
|
|
int ret2;
|
|
|
|
assert( parser->position == POS_HEADERS ||
|
|
parser->ent_position == ENTREAD_CHUNKY_HEADERS );
|
|
|
|
while( TRUE ) {
|
|
save_pos = scanner->cursor;
|
|
|
|
//
|
|
// check end of headers
|
|
//
|
|
status = scanner_get_token( scanner, &token, &tok_type );
|
|
if( status != PARSE_OK ) {
|
|
return status;
|
|
}
|
|
|
|
if( tok_type == TT_CRLF ) {
|
|
|
|
// end of headers
|
|
if( ( parser->msg.is_request )
|
|
&& ( parser->msg.method == HTTPMETHOD_POST ) ) {
|
|
parser->position = POS_COMPLETE; //post entity parsing
|
|
//is handled separately
|
|
return PARSE_SUCCESS;
|
|
}
|
|
|
|
parser->position = POS_ENTITY; // read entity next
|
|
return PARSE_OK;
|
|
}
|
|
//
|
|
// not end; read header
|
|
//
|
|
if( tok_type != TT_IDENTIFIER ) {
|
|
return PARSE_FAILURE; // didn't see header name
|
|
}
|
|
|
|
status = match( scanner, " : %R%c", &hdr_value );
|
|
if( status != PARSE_OK ) {
|
|
// pushback tokens; useful only on INCOMPLETE error
|
|
scanner->cursor = save_pos;
|
|
return status;
|
|
}
|
|
//
|
|
// add header
|
|
//
|
|
|
|
// find header
|
|
index = map_str_to_int( token.buf, token.length, Http_Header_Names,
|
|
NUM_HTTP_HEADER_NAMES, FALSE );
|
|
if( index != -1 ) {
|
|
|
|
//Check if it is a soap header
|
|
if( Http_Header_Names[index].id == HDR_SOAPACTION ) {
|
|
parser->msg.method = SOAPMETHOD_POST;
|
|
}
|
|
|
|
header_id = Http_Header_Names[index].id;
|
|
orig_header =
|
|
httpmsg_find_hdr( &parser->msg, header_id, NULL );
|
|
} else {
|
|
header_id = HDR_UNKNOWN;
|
|
|
|
save_char = token.buf[token.length];
|
|
token.buf[token.length] = '\0';
|
|
|
|
orig_header = httpmsg_find_hdr_str( &parser->msg, token.buf );
|
|
|
|
token.buf[token.length] = save_char; // restore
|
|
}
|
|
|
|
if( orig_header == NULL ) {
|
|
//
|
|
// add new header
|
|
//
|
|
|
|
header = ( http_header_t * ) malloc( sizeof( http_header_t ) );
|
|
if( header == NULL ) {
|
|
parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
|
|
return PARSE_FAILURE;
|
|
}
|
|
membuffer_init( &header->name_buf );
|
|
membuffer_init( &header->value );
|
|
|
|
// value can be 0 length
|
|
if( hdr_value.length == 0 ) {
|
|
hdr_value.buf = "\0";
|
|
hdr_value.length = 1;
|
|
}
|
|
// save in header in buffers
|
|
if( membuffer_assign
|
|
( &header->name_buf, token.buf, token.length ) != 0
|
|
|| membuffer_assign( &header->value, hdr_value.buf,
|
|
hdr_value.length ) != 0 ) {
|
|
// not enuf mem
|
|
free (header);
|
|
parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
|
|
return PARSE_FAILURE;
|
|
}
|
|
|
|
header->name.buf = header->name_buf.buf;
|
|
header->name.length = header->name_buf.length;
|
|
header->name_id = header_id;
|
|
|
|
ListAddTail( &parser->msg.headers, header );
|
|
|
|
//NNS: ret = dlist_append( &parser->msg.headers, header );
|
|
/** TODO: remove that? */
|
|
if( ret == UPNP_E_OUTOF_MEMORY ) {
|
|
parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
|
|
return PARSE_FAILURE;
|
|
}
|
|
/** end of remove that? */
|
|
} else if( hdr_value.length > 0 ) {
|
|
//
|
|
// append value to existing header
|
|
//
|
|
|
|
// append space
|
|
ret = membuffer_append_str( &orig_header->value, ", " );
|
|
|
|
// append continuation of header value
|
|
ret2 = membuffer_append( &orig_header->value,
|
|
hdr_value.buf, hdr_value.length );
|
|
|
|
if( ret == UPNP_E_OUTOF_MEMORY || ret2 == UPNP_E_OUTOF_MEMORY ) {
|
|
// not enuf mem
|
|
parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
|
|
return PARSE_FAILURE;
|
|
}
|
|
}
|
|
} // end while
|
|
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
#ifdef HIGHLY_UNLIKELY
|
|
// **************
|
|
static parse_status_t
|
|
parser_parse_headers_old( INOUT http_parser_t * parser )
|
|
{
|
|
parse_status_t status;
|
|
memptr token;
|
|
memptr hdr_value;
|
|
token_type_t tok_type;
|
|
scanner_t *scanner = &parser->scanner;
|
|
size_t save_pos;
|
|
http_header_t *header;
|
|
int header_id;
|
|
int ret = 0;
|
|
int index;
|
|
http_header_t *orig_header;
|
|
char save_char;
|
|
int ret2,
|
|
ret3;
|
|
|
|
assert( parser->position == POS_HEADERS ||
|
|
parser->ent_position == ENTREAD_CHUNKY_HEADERS );
|
|
|
|
while( TRUE ) {
|
|
save_pos = scanner->cursor;
|
|
|
|
//
|
|
// check end of headers
|
|
//
|
|
status = scanner_get_token( scanner, &token, &tok_type );
|
|
if( status != PARSE_OK ) {
|
|
return status;
|
|
}
|
|
|
|
if( tok_type == TT_CRLF ) {
|
|
// end of headers
|
|
parser->position = POS_ENTITY; // read entity next
|
|
return PARSE_OK;
|
|
}
|
|
//
|
|
// not end; read header
|
|
//
|
|
if( tok_type != TT_IDENTIFIER ) {
|
|
return PARSE_FAILURE; // didn't see header name
|
|
}
|
|
|
|
status = match( scanner, " : %R%c", &hdr_value );
|
|
if( status != PARSE_OK ) {
|
|
// pushback tokens; useful only on INCOMPLETE error
|
|
scanner->cursor = save_pos;
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// add header
|
|
//
|
|
|
|
// find header
|
|
index = map_str_to_int( token.buf, token.length, Http_Header_Names,
|
|
NUM_HTTP_HEADER_NAMES, FALSE );
|
|
if( index != -1 ) {
|
|
header_id = Http_Header_Names[index].id;
|
|
|
|
orig_header =
|
|
httpmsg_find_hdr( &parser->msg, header_id, NULL );
|
|
} else {
|
|
header_id = HDR_UNKNOWN;
|
|
|
|
save_char = token.buf[token.length];
|
|
token.buf[token.length] = '\0';
|
|
|
|
orig_header = httpmsg_find_hdr_str( &parser->msg, token.buf );
|
|
|
|
token.buf[token.length] = save_char; // restore
|
|
}
|
|
|
|
if( orig_header == NULL ) {
|
|
//
|
|
// add new header
|
|
//
|
|
|
|
header = ( http_header_t * ) malloc( sizeof( http_header_t ) );
|
|
if( header == NULL ) {
|
|
parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
|
|
return PARSE_FAILURE;
|
|
}
|
|
membuffer_init( &header->multi_hdr_buf );
|
|
|
|
header->name = token;
|
|
header->value = hdr_value;
|
|
header->name_id = header_id;
|
|
|
|
ret = dlist_append( &parser->msg.headers, header );
|
|
if( ret == UPNP_E_OUTOF_MEMORY ) {
|
|
parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
|
|
return PARSE_FAILURE;
|
|
}
|
|
} else if( hdr_value.length > 0 ) {
|
|
//
|
|
// append value to existing header
|
|
//
|
|
|
|
if( orig_header->multi_hdr_buf.buf == NULL ) {
|
|
// store in buffer
|
|
ret = membuffer_append( &orig_header->multi_hdr_buf,
|
|
orig_header->value.buf,
|
|
orig_header->value.length );
|
|
}
|
|
// append space
|
|
ret2 =
|
|
membuffer_append( &orig_header->multi_hdr_buf, ", ", 2 );
|
|
|
|
// append continuation of header value
|
|
ret3 = membuffer_append( &orig_header->multi_hdr_buf,
|
|
hdr_value.buf, hdr_value.length );
|
|
|
|
if( ret == UPNP_E_OUTOF_MEMORY ||
|
|
ret2 == UPNP_E_OUTOF_MEMORY ||
|
|
ret3 == UPNP_E_OUTOF_MEMORY ) {
|
|
// not enuf mem
|
|
parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
|
|
return PARSE_FAILURE;
|
|
}
|
|
// header value points to allocated buf
|
|
orig_header->value.buf = orig_header->multi_hdr_buf.buf;
|
|
orig_header->value.length = orig_header->multi_hdr_buf.length;
|
|
}
|
|
} // end while
|
|
|
|
}
|
|
#endif
|
|
// ******************************
|
|
|
|
/************************************************************************
|
|
* Function: parser_parse_entity_using_clen
|
|
*
|
|
* Parameters:
|
|
* INOUT http_parser_t* parser ; HTTP Parser object
|
|
*
|
|
* Description: reads entity using content-length
|
|
*
|
|
* Returns:
|
|
* PARSE_INCOMPLETE
|
|
* PARSE_FAILURE -- entity length > content-length value
|
|
* PARSE_SUCCESS
|
|
************************************************************************/
|
|
static XINLINE parse_status_t
|
|
parser_parse_entity_using_clen( INOUT http_parser_t * parser )
|
|
{
|
|
//int entity_length;
|
|
|
|
assert( parser->ent_position == ENTREAD_USING_CLEN );
|
|
|
|
// determine entity (i.e. body) length so far
|
|
//entity_length = parser->msg.msg.length - parser->entity_start_position;
|
|
parser->msg.entity.length =
|
|
parser->msg.msg.length - parser->entity_start_position;
|
|
|
|
if( parser->msg.entity.length < parser->content_length ) {
|
|
// more data to be read
|
|
return PARSE_INCOMPLETE;
|
|
} else {
|
|
if( parser->msg.entity.length > parser->content_length ) {
|
|
// silently discard extra data
|
|
parser->msg.msg.buf[parser->entity_start_position +
|
|
parser->content_length] = '\0';
|
|
}
|
|
// save entity length
|
|
parser->msg.entity.length = parser->content_length;
|
|
|
|
// save entity start ptr; (the very last thing to do)
|
|
parser->msg.entity.buf = parser->msg.msg.buf +
|
|
parser->entity_start_position;
|
|
|
|
// done reading entity
|
|
parser->position = POS_COMPLETE;
|
|
return PARSE_SUCCESS;
|
|
}
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function: parser_parse_chunky_body
|
|
*
|
|
* Parameters:
|
|
* INOUT http_parser_t* parser ; HTTP Parser object
|
|
*
|
|
* Description: Read data in the chunks
|
|
*
|
|
* Returns:
|
|
* PARSE_INCOMPLETE
|
|
* PARSE_FAILURE -- entity length > content-length value
|
|
* PARSE_SUCCESS
|
|
************************************************************************/
|
|
static XINLINE parse_status_t
|
|
parser_parse_chunky_body( INOUT http_parser_t * parser )
|
|
{
|
|
parse_status_t status;
|
|
size_t save_pos;
|
|
|
|
// if 'chunk_size' of bytes have been read; read next chunk
|
|
if( ( int )( parser->msg.msg.length - parser->scanner.cursor ) >=
|
|
parser->chunk_size ) {
|
|
// move to next chunk
|
|
parser->scanner.cursor += parser->chunk_size;
|
|
save_pos = parser->scanner.cursor;
|
|
|
|
//discard CRLF
|
|
status = match( &parser->scanner, "%c" );
|
|
if( status != PARSE_OK ) {
|
|
parser->scanner.cursor -= parser->chunk_size; //move back
|
|
//parser->scanner.cursor = save_pos;
|
|
return status;
|
|
}
|
|
|
|
membuffer_delete( &parser->msg.msg, save_pos,
|
|
( parser->scanner.cursor - save_pos ) );
|
|
parser->scanner.cursor = save_pos;
|
|
parser->msg.entity.length += parser->chunk_size; //update temp
|
|
parser->ent_position = ENTREAD_USING_CHUNKED;
|
|
return PARSE_CONTINUE_1;
|
|
} else {
|
|
return PARSE_INCOMPLETE; // need more data for chunk
|
|
}
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function: parser_parse_chunky_headers
|
|
*
|
|
* Parameters:
|
|
* INOUT http_parser_t* parser ; HTTP Parser object
|
|
*
|
|
* Description: Read headers at the end of the chunked entity
|
|
*
|
|
* Returns:
|
|
* PARSE_INCOMPLETE
|
|
* PARSE_FAILURE -- entity length > content-length value
|
|
* PARSE_SUCCESS
|
|
************************************************************************/
|
|
static XINLINE parse_status_t
|
|
parser_parse_chunky_headers( INOUT http_parser_t * parser )
|
|
{
|
|
parse_status_t status;
|
|
size_t save_pos;
|
|
|
|
save_pos = parser->scanner.cursor;
|
|
status = parser_parse_headers( parser );
|
|
if( status == PARSE_OK ) {
|
|
// finally, done with the whole msg
|
|
parser->position = POS_COMPLETE;
|
|
|
|
// save entity start ptr as the very last thing to do
|
|
parser->msg.entity.buf = parser->msg.msg.buf +
|
|
parser->entity_start_position;
|
|
|
|
membuffer_delete( &parser->msg.msg, save_pos,
|
|
( parser->scanner.cursor - save_pos ) );
|
|
parser->scanner.cursor = save_pos;
|
|
|
|
return PARSE_SUCCESS;
|
|
} else {
|
|
return status;
|
|
}
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function: parser_parse_chunky_entity
|
|
*
|
|
* Parameters:
|
|
* INOUT http_parser_t* parser - HTTP Parser Object
|
|
*
|
|
* Description: Read headers at the end of the chunked entity
|
|
*
|
|
* Returns:
|
|
* PARSE_INCOMPLETE
|
|
* PARSE_FAILURE -- entity length > content-length value
|
|
* PARSE_SUCCESS
|
|
* PARSE_CONTINUE_1
|
|
************************************************************************/
|
|
static XINLINE parse_status_t
|
|
parser_parse_chunky_entity( INOUT http_parser_t * parser )
|
|
{
|
|
scanner_t *scanner = &parser->scanner;
|
|
parse_status_t status;
|
|
size_t save_pos;
|
|
memptr dummy;
|
|
|
|
assert( parser->ent_position == ENTREAD_USING_CHUNKED );
|
|
|
|
save_pos = scanner->cursor;
|
|
|
|
// get size of chunk, discard extension, discard CRLF
|
|
status = match( scanner, "%x%L%c", &parser->chunk_size, &dummy );
|
|
if( status != PARSE_OK ) {
|
|
scanner->cursor = save_pos;
|
|
DBGONLY( UpnpPrintf
|
|
( UPNP_INFO, HTTP, __FILE__, __LINE__,
|
|
"CHUNK COULD NOT BE PARSED\n" ); )
|
|
return status;
|
|
}
|
|
// remove chunk info just matched; just retain data
|
|
membuffer_delete( &parser->msg.msg, save_pos,
|
|
( scanner->cursor - save_pos ) );
|
|
scanner->cursor = save_pos; // adjust scanner too
|
|
|
|
if( parser->chunk_size == 0 ) {
|
|
// done reading entity; determine length of entity
|
|
parser->msg.entity.length = parser->scanner.cursor -
|
|
parser->entity_start_position;
|
|
|
|
// read entity headers
|
|
parser->ent_position = ENTREAD_CHUNKY_HEADERS;
|
|
} else {
|
|
// read chunk body
|
|
parser->ent_position = ENTREAD_CHUNKY_BODY;
|
|
}
|
|
|
|
return PARSE_CONTINUE_1; // continue to reading body
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function: parser_parse_entity_until_close
|
|
*
|
|
* Parameters:
|
|
* INOUT http_parser_t* parser ; HTTP Parser object
|
|
*
|
|
* Description: Read headers at the end of the chunked entity
|
|
*
|
|
* Returns:
|
|
* PARSE_INCOMPLETE_ENTITY
|
|
************************************************************************/
|
|
static XINLINE parse_status_t
|
|
parser_parse_entity_until_close( INOUT http_parser_t * parser )
|
|
{
|
|
size_t cursor;
|
|
|
|
assert( parser->ent_position == ENTREAD_UNTIL_CLOSE );
|
|
|
|
// eat any and all data
|
|
cursor = parser->msg.msg.length;
|
|
|
|
// update entity length
|
|
parser->msg.entity.length = cursor - parser->entity_start_position;
|
|
|
|
// update pointer
|
|
parser->msg.entity.buf =
|
|
parser->msg.msg.buf + parser->entity_start_position;
|
|
|
|
parser->scanner.cursor = cursor;
|
|
|
|
return PARSE_INCOMPLETE_ENTITY; // add anything
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function: parser_get_entity_read_method
|
|
*
|
|
* Parameters:
|
|
* INOUT http_parser_t* parser ; HTTP Parser object
|
|
*
|
|
* Description: Determines method to read entity
|
|
*
|
|
* Returns:
|
|
* PARSE_OK
|
|
* PARSE_FAILURE
|
|
* PARSE_COMPLETE -- no more reading to do
|
|
************************************************************************/
|
|
XINLINE parse_status_t
|
|
parser_get_entity_read_method( INOUT http_parser_t * parser )
|
|
{
|
|
http_message_t *hmsg = &parser->msg;
|
|
int response_code;
|
|
memptr hdr_value;
|
|
|
|
assert( parser->ent_position == ENTREAD_DETERMINE_READ_METHOD );
|
|
|
|
// entity points to start of msg body
|
|
parser->msg.entity.buf = scanner_get_str( &parser->scanner );
|
|
parser->msg.entity.length = 0;
|
|
|
|
// remember start of body
|
|
parser->entity_start_position = parser->scanner.cursor;
|
|
|
|
// std http rules for determining content length
|
|
|
|
// * no body for 1xx, 204, 304 and HEAD, GET,
|
|
// SUBSCRIBE, UNSUBSCRIBE
|
|
if( hmsg->is_request ) {
|
|
switch ( hmsg->method ) {
|
|
case HTTPMETHOD_HEAD:
|
|
case HTTPMETHOD_GET:
|
|
//case HTTPMETHOD_POST:
|
|
case HTTPMETHOD_SUBSCRIBE:
|
|
case HTTPMETHOD_UNSUBSCRIBE:
|
|
case HTTPMETHOD_MSEARCH:
|
|
// no body; mark as done
|
|
parser->position = POS_COMPLETE;
|
|
return PARSE_SUCCESS;
|
|
break;
|
|
|
|
default:
|
|
; // do nothing
|
|
}
|
|
} else // response
|
|
{
|
|
response_code = hmsg->status_code;
|
|
|
|
if( response_code == 204 ||
|
|
response_code == 304 ||
|
|
( response_code >= 100 && response_code <= 199 ) ||
|
|
hmsg->request_method == HTTPMETHOD_HEAD ||
|
|
hmsg->request_method == HTTPMETHOD_MSEARCH ||
|
|
hmsg->request_method == HTTPMETHOD_SUBSCRIBE ||
|
|
hmsg->request_method == HTTPMETHOD_UNSUBSCRIBE ||
|
|
hmsg->request_method == HTTPMETHOD_NOTIFY ) {
|
|
parser->position = POS_COMPLETE;
|
|
return PARSE_SUCCESS;
|
|
}
|
|
}
|
|
|
|
// * transfer-encoding -- used to indicate chunked data
|
|
if( httpmsg_find_hdr( hmsg, HDR_TRANSFER_ENCODING, &hdr_value ) ) {
|
|
if( raw_find_str( &hdr_value, "chunked" ) >= 0 ) {
|
|
// read method to use chunked transfer encoding
|
|
parser->ent_position = ENTREAD_USING_CHUNKED;
|
|
DBGONLY( UpnpPrintf
|
|
( UPNP_INFO, HTTP, __FILE__, __LINE__,
|
|
"Found Chunked Encoding ....\n" ); )
|
|
|
|
return PARSE_CONTINUE_1;
|
|
}
|
|
}
|
|
// * use content length
|
|
if( httpmsg_find_hdr( hmsg, HDR_CONTENT_LENGTH, &hdr_value ) ) {
|
|
parser->content_length = raw_to_int( &hdr_value, 10 );
|
|
if( parser->content_length < 0 ) {
|
|
// bad content-length
|
|
return PARSE_FAILURE;
|
|
}
|
|
parser->ent_position = ENTREAD_USING_CLEN;
|
|
return PARSE_CONTINUE_1;
|
|
}
|
|
// * multi-part/byteranges not supported (yet)
|
|
|
|
// * read until connection is closed
|
|
if( hmsg->is_request ) {
|
|
// set hack flag for NOTIFY methods; if set to true this is
|
|
// a valid SSDP notify msg
|
|
if( hmsg->method == HTTPMETHOD_NOTIFY ) {
|
|
parser->valid_ssdp_notify_hack = TRUE;
|
|
}
|
|
|
|
parser->http_error_code = HTTP_LENGTH_REQUIRED;
|
|
return PARSE_FAILURE;
|
|
}
|
|
|
|
parser->ent_position = ENTREAD_UNTIL_CLOSE;
|
|
return PARSE_CONTINUE_1;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function: parser_parse_entity
|
|
*
|
|
* Parameters:
|
|
* INOUT http_parser_t* parser ; HTTP Parser object
|
|
*
|
|
* Description: Determines method to read entity
|
|
*
|
|
* Returns:
|
|
* PARSE_OK
|
|
* PARSE_FAILURE
|
|
* PARSE_COMPLETE -- no more reading to do
|
|
************************************************************************/
|
|
XINLINE parse_status_t
|
|
parser_parse_entity( INOUT http_parser_t * parser )
|
|
{
|
|
parse_status_t status = PARSE_OK;
|
|
|
|
assert( parser->position == POS_ENTITY );
|
|
|
|
do {
|
|
switch ( parser->ent_position ) {
|
|
case ENTREAD_USING_CLEN:
|
|
status = parser_parse_entity_using_clen( parser );
|
|
break;
|
|
|
|
case ENTREAD_USING_CHUNKED:
|
|
status = parser_parse_chunky_entity( parser );
|
|
break;
|
|
|
|
case ENTREAD_CHUNKY_BODY:
|
|
status = parser_parse_chunky_body( parser );
|
|
break;
|
|
|
|
case ENTREAD_CHUNKY_HEADERS:
|
|
status = parser_parse_chunky_headers( parser );
|
|
break;
|
|
|
|
case ENTREAD_UNTIL_CLOSE:
|
|
status = parser_parse_entity_until_close( parser );
|
|
break;
|
|
|
|
case ENTREAD_DETERMINE_READ_METHOD:
|
|
status = parser_get_entity_read_method( parser );
|
|
break;
|
|
|
|
default:
|
|
assert( 0 );
|
|
}
|
|
|
|
} while( status == PARSE_CONTINUE_1 );
|
|
|
|
return status;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function: parser_request_init
|
|
*
|
|
* Parameters:
|
|
* OUT http_parser_t* parser ; HTTP Parser object
|
|
*
|
|
* Description: Initializes parser object for a request
|
|
*
|
|
* Returns:
|
|
* void
|
|
************************************************************************/
|
|
void
|
|
parser_request_init( OUT http_parser_t * parser )
|
|
{
|
|
parser_init( parser );
|
|
parser->msg.is_request = TRUE;
|
|
parser->position = POS_REQUEST_LINE;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function: parser_response_init
|
|
*
|
|
* Parameters:
|
|
* OUT http_parser_t* parser ; HTTP Parser object
|
|
* IN http_method_t request_method ; Request method
|
|
*
|
|
* Description: Initializes parser object for a response
|
|
*
|
|
* Returns:
|
|
* void
|
|
************************************************************************/
|
|
void
|
|
parser_response_init( OUT http_parser_t * parser,
|
|
IN http_method_t request_method )
|
|
{
|
|
parser_init( parser );
|
|
parser->msg.is_request = FALSE;
|
|
parser->msg.request_method = request_method;
|
|
parser->position = POS_RESPONSE_LINE;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function: parser_parse
|
|
*
|
|
* Parameters:
|
|
* INOUT http_parser_t* parser ; HTTP Parser object
|
|
*
|
|
* Description: The parser function. Depending on the position of the
|
|
* parser object the actual parsing function is invoked
|
|
*
|
|
* Returns:
|
|
* void
|
|
************************************************************************/
|
|
parse_status_t
|
|
parser_parse( INOUT http_parser_t * parser )
|
|
{
|
|
parse_status_t status;
|
|
|
|
//takes an http_parser_t with memory already allocated
|
|
//in the message
|
|
assert( parser != NULL );
|
|
|
|
do {
|
|
switch ( parser->position ) {
|
|
case POS_ENTITY:
|
|
status = parser_parse_entity( parser );
|
|
|
|
break;
|
|
|
|
case POS_HEADERS:
|
|
status = parser_parse_headers( parser );
|
|
|
|
break;
|
|
|
|
case POS_REQUEST_LINE:
|
|
status = parser_parse_requestline( parser );
|
|
|
|
break;
|
|
|
|
case POS_RESPONSE_LINE:
|
|
status = parser_parse_responseline( parser );
|
|
|
|
break;
|
|
|
|
default:
|
|
{
|
|
status = PARSE_FAILURE;
|
|
assert( 0 );
|
|
}
|
|
}
|
|
|
|
} while( status == PARSE_OK );
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function: parser_append
|
|
*
|
|
* Parameters:
|
|
* INOUT http_parser_t* parser ; HTTP Parser Object
|
|
* IN const char* buf ; buffer to be appended to the parser
|
|
* buffer
|
|
* IN size_t buf_length ; Size of the buffer
|
|
*
|
|
* Description: The parser function. Depending on the position of the
|
|
* parser object the actual parsing function is invoked
|
|
*
|
|
* Returns:
|
|
* void
|
|
************************************************************************/
|
|
parse_status_t
|
|
parser_append( INOUT http_parser_t * parser,
|
|
IN const char *buf,
|
|
IN size_t buf_length )
|
|
{
|
|
int ret_code;
|
|
|
|
assert( parser != NULL );
|
|
assert( buf != NULL );
|
|
|
|
// append data to buffer
|
|
ret_code = membuffer_append( &parser->msg.msg, buf, buf_length );
|
|
if( ret_code != 0 ) {
|
|
// set failure status
|
|
parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
|
|
return PARSE_FAILURE;
|
|
}
|
|
|
|
return parser_parse( parser );
|
|
}
|
|
|
|
/************************************************************************
|
|
********** end of parser ***********
|
|
************************************************************************/
|
|
|
|
/************************************************************************
|
|
* Function: raw_to_int
|
|
*
|
|
* Parameters:
|
|
* IN memptr* raw_value ; Buffer to be converted
|
|
* IN int base ; Base to use for conversion
|
|
*
|
|
* Description: Converts raw character data to long-integer value
|
|
*
|
|
* Returns:
|
|
* int
|
|
************************************************************************/
|
|
int
|
|
raw_to_int( IN memptr * raw_value,
|
|
IN int base )
|
|
{
|
|
long num;
|
|
char *end_ptr;
|
|
|
|
if( raw_value->length == 0 ) {
|
|
return -1;
|
|
}
|
|
|
|
errno = 0;
|
|
num = strtol( raw_value->buf, &end_ptr, base );
|
|
if( ( num < 0 )
|
|
// all and only those chars in token should be used for num
|
|
|| ( end_ptr != raw_value->buf + raw_value->length )
|
|
|| ( ( num == LONG_MIN || num == LONG_MAX )
|
|
&& ( errno == ERANGE ) )
|
|
) {
|
|
return -1;
|
|
}
|
|
return num;
|
|
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function: raw_find_str
|
|
*
|
|
* Parameters:
|
|
* IN memptr* raw_value ; Buffer containg the string
|
|
* IN const char* str ; Substring to be found
|
|
*
|
|
* Description: Find a substring from raw character string buffer
|
|
*
|
|
* Returns:
|
|
* int - index at which the substring is found.
|
|
************************************************************************/
|
|
int
|
|
raw_find_str( IN memptr * raw_value,
|
|
IN const char *str )
|
|
{
|
|
char c;
|
|
char *ptr;
|
|
|
|
c = raw_value->buf[raw_value->length]; // save
|
|
raw_value->buf[raw_value->length] = 0; // null-terminate
|
|
|
|
// Use strcasestr because the string may not always be exact case
|
|
ptr = strcasestr( raw_value->buf, str );
|
|
|
|
raw_value->buf[raw_value->length] = c; // restore
|
|
|
|
if( ptr == 0 ) {
|
|
return -1;
|
|
}
|
|
|
|
return ptr - raw_value->buf; // return index
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function: method_to_str
|
|
*
|
|
* Parameters:
|
|
* IN http_method_t method ; HTTP method
|
|
*
|
|
* Description: A wrapper function that maps a method id to a method
|
|
* nameConverts a http_method id stored in the HTTP Method
|
|
*
|
|
* Returns:
|
|
* const char* ptr - Ptr to the HTTP Method *
|
|
************************************************************************/
|
|
const char *
|
|
method_to_str( IN http_method_t method )
|
|
{
|
|
int index;
|
|
|
|
index = map_int_to_str( method, Http_Method_Table, NUM_HTTP_METHODS );
|
|
|
|
assert( index != -1 );
|
|
|
|
return index == -1 ? NULL : Http_Method_Table[index].name;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function: print_http_headers
|
|
*
|
|
* Parameters:
|
|
* http_message_t* hmsg ; HTTP Message object
|
|
*
|
|
* Description:
|
|
*
|
|
* Returns:
|
|
* void
|
|
************************************************************************/
|
|
void
|
|
print_http_headers( http_message_t * hmsg )
|
|
{
|
|
|
|
ListNode *node;
|
|
|
|
//NNS: dlist_node *node;
|
|
http_header_t *header;
|
|
|
|
// print start line
|
|
if( hmsg->is_request ) {
|
|
//printf( "method = %d, version = %d.%d, url = %.*s\n",
|
|
// hmsg->method, hmsg->major_version, hmsg->minor_version,
|
|
// hmsg->uri.pathquery.size, hmsg->uri.pathquery.buff);
|
|
} else {
|
|
// printf( "resp status = %d, version = %d.%d, status msg = %.*s\n",
|
|
// hmsg->status_code, hmsg->major_version, hmsg->minor_version,
|
|
// (int)hmsg->status_msg.length, hmsg->status_msg.buf);
|
|
}
|
|
|
|
// print headers
|
|
|
|
node = ListHead( &hmsg->headers );
|
|
//NNS: node = dlist_first_node( &hmsg->headers );
|
|
while( node != NULL ) {
|
|
|
|
header = ( http_header_t * ) node->item;
|
|
//NNS: header = (http_header_t *)node->data;
|
|
//printf( "hdr name: %.*s, value: %.*s\n",
|
|
// (int)header->name.length, header->name.buf,
|
|
// (int)header->value.length, header->value.buf );
|
|
|
|
node = ListNext( &hmsg->headers, node );
|
|
|
|
//NNS: node = dlist_next( &hmsg->headers, node );
|
|
}
|
|
}
|