123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279 |
|
/*******************************************************************************
copyright: Copyright (c) 2004 Kris Bell. All rights reserved
license: BSD style: $(LICENSE)
version: Initial release: April 2004
author: Kris, John Reimer
*******************************************************************************/
module tango.net.http.HttpStack;
private import tango.core.Exception;
/******************************************************************************
Unix doesn't appear to have a memicmp() ... JJR notes that the
strncasecmp() is available instead.
******************************************************************************/
version (Win32)
{
extern (C) int memicmp (const(char) *, const(char) *, size_t);
}
version (Posix)
{
extern (C) int strncasecmp (const(char) *, const(char)*, size_t);
}
extern (C) void* memmove (void* dst, const(void)* src, int n);
/******************************************************************************
Internal representation of a token
******************************************************************************/
class Token
{
private const(char)[] value;
Token set (const(char)[] value)
{
this.value = value;
return this;
}
override string toString ()
{
return value.idup;
}
}
/******************************************************************************
A stack of Tokens, used for capturing http headers. The tokens
themselves are typically mapped onto the content of a Buffer,
or some other external content, so there's minimal allocation
involved (typically zero).
******************************************************************************/
class HttpStack
{
private int depth;
private Token[] tokens;
private static const int MaxHttpStackSize = 256;
/**********************************************************************
Construct a HttpStack with the specified initial size.
The stack will later be resized as necessary.
**********************************************************************/
this (int size = 10)
{
tokens = new Token[0];
resize (tokens, size);
}
/**********************************************************************
Clone this stack of tokens
**********************************************************************/
HttpStack clone ()
{
// setup a new HttpStack of the same depth
HttpStack clone = new HttpStack(depth);
clone.depth = depth;
// duplicate the content of each original token
for (int i=0; i < depth; ++i)
clone.tokens[i].set (tokens[i].toString());
return clone;
}
/**********************************************************************
Iterate over all tokens in stack
**********************************************************************/
int opApply (scope int delegate(ref Token) dg)
{
int result = 0;
for (int i=0; i < depth; ++i)
if ((result = dg (tokens[i])) != 0)
break;
return result;
}
/**********************************************************************
Pop the stack all the way back to zero
**********************************************************************/
final void reset ()
{
depth = 0;
}
/**********************************************************************
Scan the tokens looking for the first one with a matching
name. Returns the matching Token, or null if there is no
such match.
**********************************************************************/
final Token findToken (const(char)[] match)
{
Token tok;
for (int i=0; i < depth; ++i)
{
tok = tokens[i];
if (isMatch (tok, match))
return tok;
}
return null;
}
/**********************************************************************
Scan the tokens looking for the first one with a matching
name, and remove it. Returns true if a match was found, or
false if not.
**********************************************************************/
final bool removeToken (const(char)[] match)
{
for (int i=0; i < depth; ++i)
if (isMatch (tokens[i], match))
{
tokens[i].value = null;
return true;
}
return false;
}
/**********************************************************************
Return the current stack depth
**********************************************************************/
@property final int size ()
{
return depth;
}
/**********************************************************************
Push a new token onto the stack, and set it content to
that provided. Returns the new Token.
**********************************************************************/
final Token push (const(char)[] content)
{
return push().set (content);
}
/**********************************************************************
Push a new token onto the stack, and set it content to
be that of the specified token. Returns the new Token.
**********************************************************************/
final Token push (ref Token token)
{
return push (token.toString());
}
/**********************************************************************
Push a new token onto the stack, and return it.
**********************************************************************/
final Token push ()
{
if (depth == tokens.length)
resize (tokens, depth * 2);
return tokens[depth++];
}
/**********************************************************************
Pop the stack by one.
**********************************************************************/
final void pop ()
{
if (depth)
--depth;
else
throw new IOException ("illegal attempt to pop Token stack");
}
/**********************************************************************
See if the given token matches the specified text. The
two must match the minimal extent exactly.
**********************************************************************/
final static bool isMatch (ref Token token, const(char)[] match)
{
const(char)[] target = token.toString();
size_t length = target.length;
if (length > match.length)
length = match.length;
if (length is 0)
return false;
version (Win32)
return memicmp (target.ptr, match.ptr, length) is 0;
version (Posix)
return strncasecmp (target.ptr, match.ptr, length) is 0;
}
/**********************************************************************
Resize this stack by extending the array.
**********************************************************************/
final static void resize (ref Token[] tokens, int size)
{
size_t i = tokens.length;
// this should *never* realistically happen
if (size > MaxHttpStackSize)
throw new IOException ("Token stack exceeds maximum depth");
for (tokens.length=size; i < tokens.length; ++i)
tokens[i] = new Token();
}
}
|