AM_CONFIG_HEADER([config.h])
SquidInline="yes"
-AC_ARG_ENABLE(inline,
-[ --disable-inline Don't compile trivial methods as inline. Squid
- is coded with much of the code able to be inlined.< Inlining is good for production builds, but not
- good for development. During development, use
- --disable-inline to reduce compilation times and
- allow incremental builds to be quick. For
- production builds, or load tests, use
- --enable-inline to have squid make all trivial
- methods inlinable by the compiler.],
-[ if test "$enableval" = "no" ; then
- SquidInline="no"
- fi
-])
-
-if test "$SquidInline" = "yes" ; then
- AC_DEFINE(_SQUID_INLINE_, inline, [Keyword used by squid for inlining methods])
- AC_DEFINE(_USE_INLINE_,, [Include inline methods into header file])
-else
- AC_DEFINE(_SQUID_INLINE_,, [Keyword used by squid for inlining methods])
-fi
+ AC_ARG_ENABLE(inline,
+ [ --disable-inline Don't compile trivial methods as inline. Squid
+ is coded with much of the code able to be inlined.< Inlining is good for production builds, but not
+ good for development. During development, use
+ --disable-inline to reduce compilation times and
+ allow incremental builds to be quick. For
+ production builds, or load tests, use
+ --enable-inline to have squid make all trivial
+ methods inlinable by the compiler.],
+ [ if test "$enableval" = "no" ; then
+ SquidInline="no"
+ fi
+ ])
+
+ if test "$SquidInline" = "yes" ; then
+ AC_DEFINE(_SQUID_INLINE_, inline, [Keyword used by squid for inlining methods])
+ AC_DEFINE(_USE_INLINE_,, [Include inline methods into header file])
+ else
+ AC_DEFINE(_SQUID_INLINE_,, [Keyword used by squid for inlining methods])
+ fi
# Checks for programs.
-AC_PROG_CXX
-AC_PROG_CC
-AC_PROG_MAKE_SET
+ AC_PROG_CXX
+ AC_PROG_CC
+ AC_PROG_MAKE_SET
# for old automakes - like squid-cache.orgs!
-AM_INIT_AUTOMAKE(libTrie, 0.1)
-AM_MAINTAINER_MODE
-AC_PROG_RANLIB
+ AM_INIT_AUTOMAKE(libTrie, 0.1)
+ AM_MAINTAINER_MODE
+ AC_PROG_RANLIB
+
+ dnl set useful flags
+ if test "$GCC" = "yes"; then
+ TRIE_CFLAGS="-Werror -Wall -Wpointer-arith -Wwrite-strings -Wmissing-prototypes -Wmissing-declarations -Wcomments"
+ TRIE_CXXFLAGS="-Werror -Wall -Wpointer-arith -Wwrite-strings -Wmissing-prototypes -Wcomments"
+ else
+ TRIE_CFLAGS=
+ TRIE_CXXFLAGS=
+ fi
+ AC_SUBST(TRIE_CFLAGS)
+ AC_SUBST(TRIE_CXXFLAGS)
# Checks for libraries.
# Checks for header files.
# Checks for typedefs, structures, and compiler characteristics.
-AC_C_CONST
-AC_TYPE_SIZE_T
+ AC_C_CONST
+ AC_TYPE_SIZE_T
# Checks for library functions.
-AC_CONFIG_FILES([Makefile
- include/Makefile
- src/Makefile
- test/Makefile])
-AC_OUTPUT
+ AC_CONFIG_FILES([Makefile
+ include/Makefile
+ src/Makefile
+ test/Makefile])
+ AC_OUTPUT
-noinst_HEADERS = Trie.h TrieNode.h
+noinst_HEADERS = Trie.h TrieNode.h TrieCharTransform.h
/*
- * Copyright (c) 2002 Robert Collins <rbtcollins@hotmail.com>
+ * Copyright (c) 2002,2003 Robert Collins <rbtcollins@hotmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Trie::find (char const *aString, size_t theLength)
{
if (head)
- return head->find (aString, theLength);
+ return head->find (aString, theLength, transform, false);
+
return NULL;
}
Trie::findPrefix (char const *aString, size_t theLength)
{
if (head)
- return head->find (aString, theLength, true);
+ return head->find (aString, theLength, transform, true);
+
return NULL;
}
+
#endif
/*
- * Copyright (c) 2002 Robert Collins <rbtcollins@hotmail.com>
+ * Copyright (c) 2002,2003 Robert Collins <rbtcollins@hotmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
/* C bindings */
#ifndef __cplusplus
+/* TODO: provide parameterisation for C bindings */
void *TrieCreate ();
void TrieDestroy (void *);
void *TrieFind (void *, char const *, size_t);
/* C++ bindings */
#else
+class TrieCharTransform;
+
class TrieNode;
/* TODO: parameterize this to be more generic -
{
public:
- Trie();
+ Trie(TrieCharTransform *aTransform = 0);
~Trie();
Trie (Trie const &);
Trie &operator= (Trie const &);
private:
TrieNode *head;
+
+ /* transfor each 8 bits in the element */
+ TrieCharTransform *transform;
};
#endif /* __cplusplus */
--- /dev/null
+/*
+ * Copyright (c) 2003 Robert Collins <rbtcollins@hotmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ */
+
+#ifndef LIBTRIE_TRIECHARTRANSFORM_H
+#define LIBTRIE_TRIECHARTRANSFORM_H
+
+/* This is an internal header for libTrie.
+ * libTrie provides both limited C and full C++
+ * bindings.
+ * libTrie itself is written in C++.
+ * For C bindings see Trie.h
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/* C bindings */
+#ifndef __cplusplus
+
+/* C++ bindings */
+#else
+#include <sys/types.h>
+#include <utility>
+#include <ctype.h>
+
+/* TODO: parameterize this to be more generic -
+* i.e. M-ary internal node sizes etc
+*/
+
+class TrieCharTransform
+{
+
+public:
+ virtual ~TrieCharTransform() {}
+
+ virtual char const operator () (char const) = 0;
+};
+
+class TrieCaseless : public TrieCharTransform
+{
+ virtual char const operator () (char const aChar) {return tolower(aChar);}
+};
+
+#endif /* __cplusplus */
+
+#endif /* LIBTRIE_TRIECHARTRANSFORM_H */
/*
- * Copyright (c) 2002 Robert Collins <rbtcollins@hotmail.com>
+ * Copyright (c) 2002,2003 Robert Collins <rbtcollins@hotmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#ifdef __cplusplus
#include "TrieNode.h"
+#include "TrieCharTransform.h"
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
/* recursive. TODO? make iterative */
void *
-TrieNode::find (char const *aString, size_t theLength, bool prefix) const
+TrieNode::find (char const *aString, size_t theLength, TrieCharTransform *transform, bool const prefix) const
{
if (theLength) {
- int index = -1;
- if (internal[*aString])
- index = *aString;
- else if (internal[tolower(*aString)])
- index = tolower(*aString);
- if (index > -1) {
- void *result;
- result = internal[index]->find(aString + 1, theLength - 1, prefix);
- if (result)
- return result;
- }
- if (prefix)
- return _privateData;
- return NULL;
+ int index = -1;
+ unsigned char pos = transform ? (*transform) (*aString) : *aString;
+
+ if (internal[pos])
+ index = pos;
+
+ if (index > -1) {
+ void *result;
+ result = internal[index]->find(aString + 1, theLength - 1, transform, prefix);
+
+ if (result)
+ return result;
+ }
+
+ if (prefix)
+ return _privateData;
+
+ return NULL;
} else {
- /* terminal node */
- return _privateData;
+ /* terminal node */
+ return _privateData;
}
}
+
#endif
/*
- * Copyright (c) 2002 Robert Collins <rbtcollins@hotmail.com>
+ * Copyright (c) 2002,2003 Robert Collins <rbtcollins@hotmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* i.e. M-ary internal node sizes etc
*/
+class TrieCharTransform;
+
class TrieNode
{
* If found, return the private data.
* If not found, return NULL.
*/
- _SQUID_INLINE_ void *find (char const *, size_t, bool prefix = false) const;
+ _SQUID_INLINE_ void *find (char const *, size_t, TrieCharTransform *, bool const prefix) const;
/* Add a string.
* returns false if the string is already
*/
bool add
- (char const *, size_t, void *);
+ (char const *, size_t, void *, TrieCharTransform *);
private:
/* 256-way Trie */
INCLUDES = -I$(top_srcdir)/include
+AM_CFLAGS = @TRIE_CFLAGS@
+AM_CXXFLAGS = @TRIE_CXXFLAGS@
+
noinst_LIBRARIES = libTrie.a
libTrie_a_SOURCES = Trie.cc \
/*
- * Copyright (c) 2002 Robert Collins <rbtcollins@hotmail.com>
+ * Copyright (c) 2002,2003 Robert Collins <rbtcollins@hotmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#include <unistd.h>
#endif
#include "TrieNode.h"
+#include "TrieCharTransform.h"
-Trie::Trie () : head (0)
+Trie::Trie (TrieCharTransform *aTransform) : head (0) , transform (aTransform)
{}
extern "C" void *TrieCreate ()
Trie::~Trie ()
{
delete head;
+ delete transform;
}
extern "C" void TrieDestroy (void *aTrie)
return false;
return head->add
- (aString, theLength, privatedata);
+ (aString, theLength, privatedata, transform);
}
head = new TrieNode;
return head->add
- (aString, theLength, privatedata);
+ (aString, theLength, privatedata, transform);
}
extern "C" int TrieAdd (void *aTrie, char const *aString, size_t theLength, void *privatedata)
/*
- * Copyright (c) 2002 Robert Collins <rbtcollins@hotmail.com>
+ * Copyright (c) 2002,2003 Robert Collins <rbtcollins@hotmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
*/
#include "TrieNode.h"
+#include "TrieCharTransform.h"
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
-#include <ctype.h>
-TrieNode::TrieNode ()
+TrieNode::TrieNode () : _privateData (NULL)
{
for (int i = 0; i < 256; ++i)
internal[i] = NULL;
bool
TrieNode::add
- (char const *aString, size_t theLength, void *privatedata)
+ (char const *aString, size_t theLength, void *privatedata, TrieCharTransform *transform)
{
/* We trust that privatedata and existant keys have already been checked */
if (theLength) {
- int index;
+ int index = transform ? (*transform) (*aString): *aString;
- if (internal[*aString])
- index = *aString;
- else if (internal[tolower(*aString)])
- index = tolower (*aString);
- else {
- index = *aString;
+ if (!internal[index])
internal[index] = new TrieNode;
- }
- internal[index]->add
- (aString + 1, theLength - 1, privatedata);
+ return internal[index]->add
+ (aString + 1, theLength - 1, privatedata, transform);
} else {
/* terminal node */
#ifndef _USE_INLINE_
#include "TrieNode.cci"
#endif
-
INCLUDES = -I$(top_srcdir)/include
+AM_CFLAGS = @TRIE_CFLAGS@
+AM_CXXFLAGS = @TRIE_CXXFLAGS@
+
TESTS = trie trie-c
check_PROGRAMS = trie trie-c
trie_c_SOURCES = trie-c.c
trie_c_LDADD = $(top_builddir)/src/libTrie.a -lstdc++
-
/*
- * Copyright (c) 2002 Robert Collins <rbtcollins@hotmail.com>
+ * Copyright (c) 2002,2003 Robert Collins <rbtcollins@hotmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
*/
#include "Trie.h"
+#include "TrieCharTransform.h"
#include <iostream>
-int main (int argc, char **argv)
+bool
+CaseSensitiveCheck()
{
Trie aTrie;
return 1;
}
+ if (!aTrie.add ("Uprefix", 8, (void *)3)) {
+ std::cerr << "Could not add Uprefix" << std::endl;
+ return 1;
+ }
+
+ if (aTrie.find ("User-Agent", 10) != (void *)1) {
+ std::cerr << "Could not find User-Agent" << std::endl;
+ return 1;
+ }
+
+ if (aTrie.find ("user-agent", 10) == (void *)1) {
+ std::cerr << "found user-agent" << std::endl;
+ return 1;
+ }
+
+ if (aTrie.findPrefix("User-AgentFoo" , 13) != (void *)1) {
+ std::cerr << "Could not find User prefix" << std::endl;
+ return 1;
+ }
+
+ if (aTrie.findPrefix("user-agentFoo" , 13) == (void *)1) {
+ std::cerr << "found user prefix" << std::endl;
+ return 1;
+ }
+
+ return 0;
+}
+
+bool
+CaseInsensitiveCheck()
+{
+ Trie aTrie(new TrieCaseless);
+
+ if (!aTrie.add ("User-Agent", 10, (void *)1)) {
+ std::cerr << "Could not add User-Agent" << std::endl;
+ return 1;
+ }
+
+ if (aTrie.add ("user-agent", 10, (void *)2)) {
+ std::cerr << "Could add duplicate User-Agent" << std::endl;
+ return 1;
+ }
+
+ if (!aTrie.add ("Alphabet", 8, (void *)3)) {
+ std::cerr << "Could not add Alphabet" << std::endl;
+ return 1;
+ }
+
+ if (!aTrie.add ("uprefix", 8, (void *)3)) {
+ std::cerr << "Could not add uprefix" << std::endl;
+ return 1;
+ }
+
if (aTrie.find ("User-Agent", 10) != (void *)1) {
std::cerr << "Could not find User-Agent" << std::endl;
return 1;
}
+ if (aTrie.find ("user-agent", 10) != (void *)1) {
+ std::cerr << "Could not find user-agent" << std::endl;
+ return 1;
+ }
+
+ if (aTrie.findPrefix("User-AgentFoo" , 13) != (void *)1) {
+ std::cerr << "Could not find User prefix" << std::endl;
+ return 1;
+ }
+
+ if (aTrie.findPrefix("user-agentFoo" , 13) != (void *)1) {
+ std::cerr << "Could not find user prefix" << std::endl;
+ return 1;
+ }
+
+ return 0;
+}
+
+int main (int argc, char **argv)
+{
+ if (CaseSensitiveCheck()) {
+ std::cerr << "Case sensitive check failure." << std::endl;
+ return 1;
+ }
+
+ if (CaseInsensitiveCheck()) {
+ std::cerr << "Case in-sensitive check failure." << std::endl;
+ return 1;
+ }
+
return 0;
}
/*
- * $Id: ACLChecklist.h,v 1.13 2003/07/11 01:40:34 robertc Exp $
+ * $Id: ACLChecklist.h,v 1.14 2003/07/14 14:15:55 robertc Exp $
*
*
* SQUID Web Proxy Cache http://www.squid-cache.org/
class ExternalACLEntry;
+class ConnStateData;
+
class ACLChecklist
{
/*
- * $Id: ACLReplyMIMEType.h,v 1.2 2003/07/11 01:40:34 robertc Exp $
+ * $Id: ACLReplyMIMEType.h,v 1.3 2003/07/14 14:15:55 robertc Exp $
*
*
* SQUID Web Proxy Cache http://www.squid-cache.org/
#include "ACLReplyHeaderStrategy.h"
#include "ACLStrategised.h"
#include "ACLChecklist.h"
+/* FIXME: TODO: this is broken - should be HttpReply checks!! */
#include "HttpRequest.h"
class ACLReplyMIMEType
/*
- * $Id: ESI.cc,v 1.3 2003/03/15 04:17:38 robertc Exp $
+ * $Id: ESI.cc,v 1.4 2003/07/14 14:15:55 robertc Exp $
*
* DEBUG: section 86 ESI processing
* AUTHOR: Robert Collins
#include "ESIAttempt.h"
#include "ESIExcept.h"
#include "client_side.h"
+#include "ESIVarState.h"
+#include "ESIAssign.h"
+#include "ESIExpression.h"
+#include "HttpRequest.h"
/* quick reference on behaviour here.
* The ESI specification 1.0 requires the ESI processor to be able to
class ESIStreamContext;
-typedef class ESIStreamContext esiStreamContext;
-
/* TODO: split this out into separate files ? */
/* Parsing: quick and dirty. ESI files are not valid XML, so a generic
* XML parser is not much use. Also we need a push parser not a pull
return lhs == rhs.getRaw();
}
-
-/* esi variable replacement logic */
-
-typedef enum {
- ESI_BROWSER_MSIE,
- ESI_BROWSER_MOZILLA,
- ESI_BROWSER_OTHER
-} esiBrowser_t;
-
-static char const * esiBrowsers[]=
- {"MSIE",
- "MOZILLA",
- "OTHER"
- };
-
-/* Recursive uses are not supported by design */
-
-struct _query_elem{char *var, *val;};
-
-struct esiVarState
-{
- ESISegment::Pointer extractList();
- char *extractChar();
- void feedData (const char *buf, size_t len);
- void buildVary (HttpReply *rep);
-
- void *operator new (size_t byteCount);
- void operator delete (void *address);
- void deleteSelf() const;
- void freeResources();
- esiVarState (HttpHeader const *hdr, char const *uri);
-
-private:
- char *getProductVersion (char const *s);
- ESISegment::Pointer input;
- ESISegment::Pointer output;
- HttpHeader hdr;
-
- struct _query_elem *query;
- size_t query_sz;
- size_t query_elements;
- char *query_string;
-
- struct
- {
-
-int language:
- 1;
-
-int cookie:
- 1;
-
-int host:
- 1;
-
-int referer:
- 1;
-
-int useragent:
- 1;
- }
-
- flags;
- esiBrowser_t browser;
- char *browserversion;
- enum esiVar_t {
- ESI_VAR_LANGUAGE,
- ESI_VAR_COOKIE,
- ESI_VAR_HOST,
- ESI_VAR_REFERER,
- ESI_VAR_USERAGENT,
- ESI_QUERY_STRING,
- ESI_VAR_OTHER
- };
- void doIt ();
- void eval (esiVar_t type, char const *, char const *);
- enum esiUserOs_t{
- ESI_OS_WIN,
- ESI_OS_MAC,
- ESI_OS_UNIX,
- ESI_OS_OTHER
- } UserOs;
- static char const * esiUserOs[];
- static esiVar_t GetVar(char *s, int len);
- bool validChar (char c);
-};
-
-CBDATA_TYPE (esiVarState);
-FREE esiVarStateFree;
-
-char const *esiVarState::esiUserOs[]=
- {
- "WIN",
- "MAC",
- "UNIX",
- "OTHER"
- };
-
-
-extern int esiExpressionEval (char const *);
-
typedef ESIContext::esiKick_t esiKick_t;
~esiComment();
esiComment();
Pointer makeCacheable() const;
- Pointer makeUsable(esiTreeParentPtr, esiVarState &) const;
+ Pointer makeUsable(esiTreeParentPtr, ESIVarState &) const;
void render(ESISegment::Pointer);
void finish();
MemPool * esiComment::pool = NULL;
-class esiInclude;
-typedef RefCount<esiInclude> esiIncludePtr;
-
-class ESIStreamContext : public RefCountable
-{
-
-public:
- typedef RefCount<ESIStreamContext> Pointer;
- void *operator new(size_t);
- void operator delete(void *);
- void deleteSelf() const;
- ESIStreamContext();
- ~ESIStreamContext();
- void freeResources();
- int finished;
- esiIncludePtr include;
- ESISegment::Pointer localbuffer;
- ESISegment::Pointer buffer;
-
-private:
- CBDATA_CLASS(ESIStreamContext);
-};
-
-CBDATA_CLASS_INIT (ESIStreamContext);
#include "ESILiteral.h"
MemPool *esiLiteral::pool = NULL;
#include "ESISequence.h"
-/* esiInclude */
-
-class esiInclude : public ESIElement
-{
-
-public:
- void *operator new (size_t byteCount);
- void operator delete (void *address);
- void deleteSelf() const;
-
- esiInclude(esiTreeParentPtr, int attributes, const char **attr, ESIContext *);
- ~esiInclude();
- void render(ESISegment::Pointer);
- esiProcessResult_t process (int dovars);
- Pointer makeCacheable() const;
- Pointer makeUsable(esiTreeParentPtr, esiVarState &) const;
- void subRequestDone (ESIStreamContext::Pointer, bool);
-
- struct
- {
-
-int onerrorcontinue:
- 1; /* on error return zero data */
-
-int failed:
- 1; /* Failed to process completely */
-
-int finished:
- 1; /* Finished getting subrequest data */
- }
-
- flags;
- ESIStreamContext::Pointer src;
- ESIStreamContext::Pointer alt;
- ESISegment::Pointer srccontent;
- ESISegment::Pointer altcontent;
- esiVarState *varState;
- char *srcurl, *alturl;
- void fail(ESIStreamContext::Pointer);
- void finish();
-
-private:
- static MemPool *Pool;
- static void Start (ESIStreamContext::Pointer, char const *, esiVarState *);
- esiTreeParentPtr parent;
- void start();
- bool started;
- bool sent;
- esiInclude(esiInclude const &);
- bool dataNeeded() const;
-};
-
-MemPool *esiInclude::Pool = NULL;
+#include "ESIInclude.h"
/* esiRemove */
void render(ESISegment::Pointer);
bool addElement (ESIElement::Pointer);
Pointer makeCacheable() const;
- Pointer makeUsable(esiTreeParentPtr, esiVarState &) const;
+ Pointer makeUsable(esiTreeParentPtr, ESIVarState &) const;
void finish();
};
void render(ESISegment::Pointer);
bool addElement (ESIElement::Pointer);
- void fail(ESIElement *);
+ void fail(ESIElement *, char const * = NULL);
esiProcessResult_t process (int dovars);
void provideData (ESISegment::Pointer data, ESIElement * source);
Pointer makeCacheable() const;
- Pointer makeUsable(esiTreeParentPtr, esiVarState &) const;
+ Pointer makeUsable(esiTreeParentPtr, ESIVarState &) const;
ESIElement::Pointer attempt;
ESIElement::Pointer except;
MemPool *esiTry::Pool = NULL;
-/* esiVar */
-
-struct esiVar:public esiSequence
-{
- // void *operator new (size_t byteCount);
- // void operator delete (void *address);
- void deleteSelf() const;
- esiVar(esiTreeParentPtr aParent) : esiSequence (aParent)
- {
- flags.dovars = 1;
- }
-};
+#include "ESIVar.h"
/* esiChoose */
void render(ESISegment::Pointer);
bool addElement (ESIElement::Pointer);
- void fail(ESIElement *);
+ void fail(ESIElement *, char const * = NULL);
esiProcessResult_t process (int dovars);
void provideData (ESISegment::Pointer data, ESIElement *source);
void makeCachableElements(esiChoose const &old);
- void makeUsableElements(esiChoose const &old, esiVarState &);
+ void makeUsableElements(esiChoose const &old, ESIVarState &);
Pointer makeCacheable() const;
- Pointer makeUsable(esiTreeParentPtr, esiVarState &) const;
+ Pointer makeUsable(esiTreeParentPtr, ESIVarState &) const;
void NULLUnChosen();
ElementList elements;
void *operator new (size_t byteCount);
void operator delete (void *address);
void deleteSelf() const;
- esiWhen(esiTreeParentPtr aParent, int attributes, const char **attr, esiVarState *);
+ esiWhen(esiTreeParentPtr aParent, int attributes, const char **attr, ESIVarState *);
~esiWhen();
Pointer makeCacheable() const;
- Pointer makeUsable(esiTreeParentPtr, esiVarState &) const;
+ Pointer makeUsable(esiTreeParentPtr, ESIVarState &) const;
bool testsTrue() const { return testValue;}
esiWhen (esiWhen const &);
bool testValue;
char const *unevaluatedExpression;
- esiVarState *varState;
+ ESIVarState *varState;
void evaluate();
};
/* Local functions */
/* ESIContext */
static ESIContext *ESIContextNew(HttpReply *, clientStreamNode *, clientHttpRequest *);
-/* esiStreamContext */
-static esiStreamContext *esiStreamContextNew (esiIncludePtr);
-
-/* other */
-static CSCB esiBufferRecipient;
-static CSD esiBufferDetach;
-/* ESI TO CONSIDER:
- * 1. retry failed upstream requests
- */
void *
ESIContext::operator new(size_t byteCount)
}
void
-ESIContext::fail (ESIElement * source)
+ESIContext::fail (ESIElement * source, char const *anError)
{
setError();
+ setErrorMessage (anError);
fail ();
send ();
}
break;
case ESI_PROCESS_FAILED:
- debug (86,0)("esiKick: esiProcess %p FAILED\n", this);
+ debug (86,2)("esiKick: esiProcess %p FAILED\n", this);
/* this can not happen - processing can't fail until we have data,
* and when we come here we have sent data to the client
*/
rv->thisNode = thisNode;
rv->http = http;
rv->flags.clientwantsdata = 1;
- rv->varState = new esiVarState (&http->request->header, http->uri);
+ rv->varState = new ESIVarState (&http->request->header, http->uri);
debug (86,5)("ESIContextNew: Client wants data (always created during reply cycle\n");
}
if (!strncmp (el + offset, "attempt", 7))
return ESI_ELEMENT_ATTEMPT;
+ if (!strncmp (el + offset, "assign", 6))
+ return ESI_ELEMENT_ASSIGN;
+
if (!strncmp (el + offset, "remove", 6))
return ESI_ELEMENT_REMOVE;
case ESIElement::ESI_ELEMENT_INCLUDE:
/* Put on the stack to allow skipping of 'invalid' markup */
- element = new esiInclude (parserState.top().getRaw(), specifiedattcount, attr, this);
+ element = new ESIInclude (parserState.top().getRaw(), specifiedattcount, attr, this);
break;
case ESIElement::ESI_ELEMENT_REMOVE:
case ESIElement::ESI_ELEMENT_VARS:
/* Put on the stack to allow skipping of 'invalid' markup */
- element = new esiVar (parserState.top().getRaw());
+ element = new ESIVar (parserState.top().getRaw());
break;
case ESIElement::ESI_ELEMENT_CHOOSE:
/* Put on the stack to allow skipping of 'invalid' markup */
element = new esiOtherwise (parserState.top().getRaw());
break;
+
+ case ESIElement::ESI_ELEMENT_ASSIGN:
+ /* Put on the stack to allow skipping of 'invalid' markup */
+ element = new ESIAssign (parserState.top().getRaw(), specifiedattcount, attr, this);
+ break;
}
addStackElement(element);
case ESIElement::ESI_ELEMENT_WHEN:
case ESIElement::ESI_ELEMENT_OTHERWISE:
+
+ case ESIElement::ESI_ELEMENT_ASSIGN:
/* pop of the stack */
parserState.stack[--parserState.stackdepth] = NULL;
break;
tempParser->errorString());
debug (86,0)("%s",tempstr);
- if (!errormessage)
- errormessage = xstrdup (tempstr);
+ setErrorMessage(tempstr);
}
debug (86,5)("ESIContext::parserComment: ESI <!-- block parsed\n");
parserState.theParser->errorString());
debug (86,0)("%s", tempstr);
- if (!errormessage)
- errormessage = xstrdup (tempstr);
+ setErrorMessage(tempstr);
assert (flags.error);
debug (86,0)("esiProcess: tree Processed FAILED\n");
setError();
- if (!errormessage)
- errormessage = xstrdup("esiProcess: ESI template Processing failed.");
+ setErrorMessage("esiProcess: ESI template Processing failed.");
PROF_stop(esiProcessing);
ESISegmentFreeList (buffered);
ESISegmentFreeList (outbound);
ESISegmentFreeList (outboundtail);
- cbdataFree (varState);
+ varState->deleteSelf();
/* don't touch incoming, it's a pointer into buffered anyway */
}
flags.error = 1;
/* create an error object */
ErrorState * err = clientBuildError(errorpage, errorstatus, NULL,
- http->conn ? &http->conn->peer.sin_addr : &no_addr, http->request);
+ http->getConn().getRaw() != NULL ? &http->getConn()->peer.sin_addr : &no_addr, http->request);
err->err_msg = errormessage;
errormessage = NULL;
rep = errorBuildReply (err);
*/
}
-/* Detach from a buffering stream
- */
-void
-esiBufferDetach (clientStreamNode *node, clientHttpRequest *http)
-{
- /* Detach ourselves */
- clientStreamDetach (node, http);
-}
-
-/*
- * Write a chunk of data to a client 'socket'.
- * If the reply is present, send the reply headers down the wire too,
- * and clean them up when finished.
- * Pre-condition:
- * The request is an internal ESI subrequest.
- * data context is not NULL
- * There are no more entries in the stream chain.
- */
-void
-esiBufferRecipient (clientStreamNode *node, clientHttpRequest *http, HttpReply *rep, StoreIOBuffer recievedData)
-{
- /* Test preconditions */
- assert (node != NULL);
- /* ESI TODO: handle thisNode rather than asserting
- * - it should only ever happen if we cause an
- * abort and the callback chain loops back to
- * here, so we can simply return. However, that
- * itself shouldn't happen, so it stays as an
- * assert for now. */
- assert (cbdataReferenceValid (node));
- assert (node->node.next == NULL);
- assert (http->conn == NULL);
-
- esiStreamContext::Pointer esiStream = dynamic_cast<esiStreamContext *>(node->data.getRaw());
- assert (esiStream.getRaw() != NULL);
- /* If segments become more flexible, ignore thisNode */
- assert (recievedData.length <= sizeof(esiStream->localbuffer->buf));
- assert (!esiStream->finished);
-
- debug (86,5) ("esiBufferRecipient rep %p body %p len %d\n", rep, recievedData.data, recievedData.length);
- assert (node->readBuffer.offset == recievedData.offset || recievedData.length == 0);
-
- /* trivial case */
-
- if (http->out.offset != 0) {
- assert(rep == NULL);
- } else {
- if (rep) {
- if (rep->sline.status != HTTP_OK) {
- httpReplyDestroy(rep);
- rep = NULL;
- esiStream->include->fail (esiStream);
- esiStream->finished = 1;
- httpRequestFree (http);
- return;
- }
-
-#if HEADERS_LOG
- /* should be done in the store rather than every recipient? */
- headersLog(0, 0, http->request->method, rep);
-
-#endif
-
- httpReplyDestroy(rep);
-
- rep = NULL;
- }
- }
-
- if (recievedData.data && recievedData.length) {
- http->out.offset += recievedData.length;
-
- if (recievedData.data >= esiStream->localbuffer->buf &&
- recievedData.data < &esiStream->localbuffer->buf[sizeof(esiStream->localbuffer->buf)]) {
- /* original static buffer */
-
- if (recievedData.data != esiStream->localbuffer->buf) {
- /* But not the start of it */
- xmemmove (esiStream->localbuffer->buf, recievedData.data, recievedData.length);
- }
-
- esiStream->localbuffer->len = recievedData.length;
- } else {
- assert (esiStream->buffer.getRaw() != NULL);
- esiStream->buffer->len = recievedData.length;
- }
- }
-
- /* EOF / Read error / aborted entry */
- if (rep == NULL && recievedData.data == NULL && recievedData.length == 0) {
- /* TODO: get stream status to test the entry for aborts */
- debug (86,5)("Finished reading upstream data in subrequest\n");
- esiStream->include->subRequestDone (esiStream, true);
- esiStream->finished = 1;
- httpRequestFree (http);
- return;
- }
-
-
- /* after the write to the user occurs, (ie here, or in a callback)
- * we call */
- if (clientHttpRequestStatus(-1, http)) {
- /* TODO: Does thisNode if block leak htto ? */
- /* XXX when reviewing ESI this is the first place to look */
- node->data = NULL;
- esiStream->finished = 1;
- esiStream->include->fail (esiStream);
- return;
- };
-
- switch (clientStreamStatus (node, http)) {
-
- case STREAM_UNPLANNED_COMPLETE: /* fallthru ok */
-
- case STREAM_COMPLETE: /* ok */
- debug (86,3)("ESI subrequest finished OK\n");
- esiStream->include->subRequestDone (esiStream, true);
- esiStream->finished = 1;
- httpRequestFree (http);
- return;
-
- case STREAM_FAILED:
- debug (86,1)("ESI subrequest failed transfer\n");
- esiStream->include->fail (esiStream);
- esiStream->finished = 1;
- httpRequestFree (http);
- return;
-
- case STREAM_NONE: {
- StoreIOBuffer tempBuffer;
-
- if (!esiStream->buffer.getRaw()) {
- esiStream->buffer = esiStream->localbuffer;
- }
-
- esiStream->buffer = esiStream->buffer->tail();
-
- if (esiStream->buffer->len) {
- esiStream->buffer->next = new ESISegment;
- esiStream->buffer = esiStream->buffer->next;
- }
-
- tempBuffer.offset = http->out.offset;
- tempBuffer.length = sizeof (esiStream->buffer->buf);
- tempBuffer.data = esiStream->buffer->buf;
- /* now just read into 'buffer' */
- clientStreamRead (node,
- http, tempBuffer);
- debug (86,5)("esiBufferRecipient: Requested more data for ESI subrequest\n");
- }
-
- break;
-
- default:
- fatal ("Hit unreachable code in esiBufferRecipient\n");
- }
-
-}
-
-/* esiStream functions */
-ESIStreamContext::~ESIStreamContext()
-{
- assert (this);
- freeResources();
-}
-
-void
-ESIStreamContext::freeResources()
-{
- debug (86,5)("Freeing stream context resources.\n");
- buffer = NULL;
- localbuffer = NULL;
- include = NULL;
-}
-
-void *
-ESIStreamContext::operator new(size_t byteCount)
-{
- assert (byteCount == sizeof (ESIStreamContext));
- CBDATA_INIT_TYPE(ESIStreamContext);
- ESIStreamContext *result = cbdataAlloc(ESIStreamContext);
- /* Mark result as being owned - we want the refcounter to do the
- * delete call
- */
- cbdataReference(result);
- return result;
-}
-
-void
-ESIStreamContext::operator delete (void *address)
-{
- ESIStreamContext *t = static_cast<ESIStreamContext *>(address);
- cbdataFree(t);
- /* And allow the memory to be freed */
- cbdataReferenceDone (address);
-}
-
-void
-ESIStreamContext::deleteSelf() const
-{
- delete this;
-}
-
-esiStreamContext *
-esiStreamContextNew (esiIncludePtr include)
-{
- esiStreamContext *rv = new ESIStreamContext;
- rv->include = include;
- return rv;
-}
-
/* Implementation of ESIElements */
/* esiComment */
}
ESIElement::Pointer
-esiComment::makeUsable(esiTreeParentPtr, esiVarState &) const
+esiComment::makeUsable(esiTreeParentPtr, ESIVarState &) const
{
fatal ("esiComment::Usable: unreachable code!\n");
return NULL;
}
ESIElement::Pointer
-esiLiteral::makeUsable(esiTreeParentPtr , esiVarState &newVarState) const
+esiLiteral::makeUsable(esiTreeParentPtr , ESIVarState &newVarState) const
{
debug (86,5)("esiLiteral::makeUsable: Creating usable literal\n");
esiLiteral * result = new esiLiteral (*this);
return result;
}
-/* esiInclude */
-esiInclude::~esiInclude()
-{
- debug (86,5)("esiInclude::Free %p\n", this);
- ESISegmentFreeList (srccontent);
- ESISegmentFreeList (altcontent);
- cbdataReferenceDone (varState);
- safe_free (srcurl);
- safe_free (alturl);
-}
-
+/* esiRemove */
void
-esiInclude::finish()
+esiRemoveFree (void *data)
{
- parent = NULL;
+ esiRemove *thisNode = (esiRemove *)data;
+ debug (86,5)("esiRemoveFree %p\n", thisNode);
}
void *
-esiInclude::operator new(size_t byteCount)
+esiRemove::operator new(size_t byteCount)
{
- assert (byteCount == sizeof (esiInclude));
-
- if (!Pool)
- Pool = memPoolCreate ("esiInclude", sizeof (esiInclude));
-
- return memPoolAlloc(Pool);
+ assert (byteCount == sizeof (esiRemove));
+ void *rv;
+ CBDATA_INIT_TYPE_FREECB(esiRemove, esiRemoveFree);
+ rv = (void *)cbdataAlloc (esiRemove);
+ return rv;
}
void
-esiInclude::operator delete (void *address)
+esiRemove::operator delete (void *address)
{
- memPoolFree (Pool, address);
+ cbdataFree (address);
}
void
-esiInclude::deleteSelf() const
+esiRemove::deleteSelf() const
{
delete this;
}
-ESIElement::Pointer
-esiInclude::makeCacheable() const
+ESIElement *
+esiRemoveNew ()
{
- return new esiInclude (*this);
+ return new esiRemove;
}
-ESIElement::Pointer
-esiInclude::makeUsable(esiTreeParentPtr newParent, esiVarState &newVarState) const
-{
- esiInclude *resultI = new esiInclude (*this);
- ESIElement::Pointer result = resultI;
- resultI->parent = newParent;
- resultI->varState = cbdataReference (&newVarState);
-
- if (resultI->srcurl)
- resultI->src = esiStreamContextNew (resultI);
+esiRemove::esiRemove()
+{}
- if (resultI->alturl)
- resultI->alt = esiStreamContextNew (resultI);
+void
+esiRemove::finish()
+{}
- return result;
+void
+esiRemove::render(ESISegment::Pointer output)
+{
+ /* Removes do nothing dude */
+ debug (86, 5)("esiRemoveRender: Rendering remove %p\n", this);
}
-esiInclude::esiInclude(esiInclude const &old) : parent (NULL), started (false), sent (false)
+/* Accept non-ESI children */
+bool
+esiRemove::addElement (ESIElement::Pointer element)
{
- varState = NULL;
- flags.onerrorcontinue = old.flags.onerrorcontinue;
-
- if (old.srcurl)
- srcurl = xstrdup (old.srcurl);
+ if (!dynamic_cast<esiLiteral*>(element.getRaw())) {
+ debug (86,5)("esiRemoveAdd: Failed for %p\n",this);
+ return false;
+ }
- if (old.alturl)
- alturl = xstrdup (old.alturl);
+ return true;
}
-void
-esiInclude::Start (ESIStreamContext::Pointer stream, char const *url, esiVarState *vars)
+ESIElement::Pointer
+esiRemove::makeCacheable() const
{
- HttpHeader tempheaders;
-
- if (!stream.getRaw())
- return;
-
- httpHeaderInit (&tempheaders, hoRequest);
+ debug (86,5)("esiRemove::makeCacheable: Returning NULL\n");
+ return NULL;
+}
- /* Ensure variable state is clean */
- vars->feedData(url, strlen (url));
+ESIElement::Pointer
+esiRemove::makeUsable(esiTreeParentPtr, ESIVarState &) const
+{
+ fatal ("esiRemove::Usable: unreachable code!\n");
+ return NULL;
+}
- /* tempUrl is eaten by the request */
- char const *tempUrl = vars->extractChar ();
+/* esiTry */
+esiTry::~esiTry()
+{
+ debug (86,5)("esiTry::~esiTry %p\n", this);
+}
- debug (86,5)("esiIncludeStart: Starting subrequest with url '%s'\n", tempUrl);
+void *
+esiTry::operator new(size_t byteCount)
+{
+ assert (byteCount == sizeof (esiTry));
- if (clientBeginRequest(METHOD_GET, tempUrl, esiBufferRecipient, esiBufferDetach, stream.getRaw(), &tempheaders, stream->localbuffer->buf, HTTP_REQBUF_SZ)) {
- debug (86,0) ("starting new ESI subrequest failed\n");
- }
+ if (!Pool)
+ Pool = memPoolCreate ("esiTry", sizeof(esiTry));
- httpHeaderClean (&tempheaders);
+ return memPoolAlloc (Pool);
}
-esiInclude::esiInclude (esiTreeParentPtr aParent, int attrcount, char const **attr, ESIContext *aContext) : parent (aParent), started (false), sent (false)
-{
- int i;
- assert (aContext);
-
- for (i = 0; i < attrcount && attr[i]; i += 2) {
- if (!strcmp(attr[i],"src")) {
- /* Start a request for thisNode url */
- debug (86,5)("esiIncludeNew: Requesting source '%s'\n",attr[i+1]);
- /* TODO: don't assert on thisNode, ignore the duplicate */
- assert (src.getRaw() == NULL);
- src = esiStreamContextNew (this);
- assert (src.getRaw() != NULL);
- srcurl = xstrdup ( attr[i+1]);
- } else if (!strcmp(attr[i],"alt")) {
- /* Start a secondary request for thisNode url */
- /* TODO: make a config parameter to wait on requesting alt's
- * for the src to fail
- */
- debug (86,5)("esiIncludeNew: Requesting alternate '%s'\n",attr[i+1]);
- assert (alt.getRaw() == NULL); /* TODO: FIXME */
- alt = esiStreamContextNew (this);
- assert (alt.getRaw() != NULL);
- alturl = xstrdup (attr[i+1]);
- } else if (!strcmp(attr[i],"onerror")) {
- if (!strcmp(attr[i+1], "continue")) {
- flags.onerrorcontinue = 1;
- } else {
- /* ignore mistyped attributes */
- debug (86, 1)("invalid value for onerror='%s'\n", attr[i+1]);
- }
- } else {
- /* ignore mistyped attributes. TODO:? error on these for user feedback - config parameter needed
- */
- }
- }
-
- varState = cbdataReference(aContext->varState);
-}
-
-void
-esiInclude::start()
-{
- /* prevent freeing ourselves */
- esiIncludePtr foo(this);
-
- if (started)
- return;
-
- started = true;
-
- if (src.getRaw()) {
- Start (src, srcurl, varState);
- Start (alt, alturl, varState);
- } else {
- alt = NULL;
-
- debug (86,1)("esiIncludeNew: esi:include with no src attributes\n");
-
- flags.failed = 1;
- }
-}
-
-void
-esiInclude::render(ESISegment::Pointer output)
-{
- if (sent)
- return;
-
- ESISegment::Pointer myout;
-
- debug (86, 5)("esiIncludeRender: Rendering include %p\n", this);
-
- assert (flags.finished || (flags.failed && flags.onerrorcontinue));
-
- if (flags.failed && flags.onerrorcontinue) {
- return;
- }
-
- /* Render the content */
- if (srccontent.getRaw()) {
- myout = srccontent;
- srccontent = NULL;
- } else if (altcontent.getRaw()) {
- myout = altcontent;
- altcontent = NULL;
- } else
- fatal ("esiIncludeRender called with no content, and no failure!\n");
-
- assert (output->next == NULL);
-
- output->next = myout;
-
- sent = true;
-}
-
-esiProcessResult_t
-esiInclude::process (int dovars)
-{
- start();
- debug (86, 5)("esiIncludeRender: Processing include %p\n", this);
-
- if (flags.failed) {
- if (flags.onerrorcontinue)
- return ESI_PROCESS_COMPLETE;
- else
- return ESI_PROCESS_FAILED;
- }
-
- if (!flags.finished) {
- if (flags.onerrorcontinue)
- return ESI_PROCESS_PENDING_WONTFAIL;
- else
- return ESI_PROCESS_PENDING_MAYFAIL;
- }
-
- return ESI_PROCESS_COMPLETE;
-}
-
-void
-esiInclude::fail (ESIStreamContext::Pointer stream)
-{
- subRequestDone (stream, false);
-}
-
-bool
-esiInclude::dataNeeded() const
-{
- return !(flags.finished || flags.failed);
-}
-
-void
-esiInclude::subRequestDone (ESIStreamContext::Pointer stream, bool success)
-{
- assert (this);
-
- if (!dataNeeded())
- return;
-
- if (stream == src) {
- debug (86,3)("esiInclude::subRequestDone: %s\n", srcurl);
-
- if (success) {
- /* copy the lead segment */
- debug (86,3)("esiIncludeSubRequestDone: Src OK - include PASSED.\n");
- assert (!srccontent.getRaw());
- ESISegment::ListTransfer (stream->localbuffer, srccontent);
- /* we're done! */
- flags.finished = 1;
- } else {
- /* Fail if there is no alt being retrieved */
- debug (86,3)("esiIncludeSubRequestDone: Src FAILED\n");
-
- if (!(alt.getRaw() || altcontent.getRaw())) {
- debug (86,3)("esiIncludeSubRequestDone: Include FAILED - No ALT\n");
- flags.failed = 1;
- } else if (altcontent.getRaw()) {
- debug (86,3)("esiIncludeSubRequestDone: Include PASSED - ALT already Complete\n");
- /* ALT was already retrieved, we are done */
- flags.finished = 1;
- }
- }
-
- src = NULL;
- } else if (stream == alt) {
- debug (86,3)("esiInclude::subRequestDone: %s\n", alturl);
-
- if (success) {
- debug (86,3)("esiIncludeSubRequestDone: ALT OK.\n");
- /* copy the lead segment */
- assert (!altcontent.getRaw());
- ESISegment::ListTransfer (stream->localbuffer, altcontent);
- /* we're done! */
-
- if (!(src.getRaw() || srccontent.getRaw())) {
- /* src already failed, kick ESI processor */
- debug (86,3)("esiIncludeSubRequestDone: Include PASSED - SRC already failed.\n");
- flags.finished = 1;
- }
- } else {
- if (!(src.getRaw() || srccontent.getRaw())) {
- debug (86,3)("esiIncludeSubRequestDone: ALT FAILED, Include FAILED - SRC already failed\n");
- /* src already failed */
- flags.failed = 1;
- }
- }
-
- alt = NULL;
- } else {
- fatal ("esiIncludeSubRequestDone: non-owned stream found!\n");
- }
-
- if (flags.finished || flags.failed) {
- /* Kick ESI Processor */
- debug (86,5)("esiInclude %p SubRequest %p completed, kicking processor , status %s\n", this, stream.getRaw(), flags.finished ? "OK" : "FAILED");
- assert (parent.getRaw());
-
- if (!flags.failed) {
- sent = true;
- parent->provideData (srccontent.getRaw() ? srccontent:altcontent,this);
-
- if (srccontent.getRaw())
- srccontent = NULL;
- else
- altcontent = NULL;
- } else if (flags.onerrorcontinue) {
- /* render nothing but inform of completion */
-
- if (!sent) {
- sent = true;
- parent->provideData (new ESISegment, this);
- } else
- assert (0);
- } else
- parent->fail(this);
- }
-}
-
-/* esiRemove */
-void
-esiRemoveFree (void *data)
-{
- esiRemove *thisNode = (esiRemove *)data;
- debug (86,5)("esiRemoveFree %p\n", thisNode);
-}
-
-void *
-esiRemove::operator new(size_t byteCount)
-{
- assert (byteCount == sizeof (esiRemove));
- void *rv;
- CBDATA_INIT_TYPE_FREECB(esiRemove, esiRemoveFree);
- rv = (void *)cbdataAlloc (esiRemove);
- return rv;
-}
-
-void
-esiRemove::operator delete (void *address)
-{
- cbdataFree (address);
-}
-
-void
-esiRemove::deleteSelf() const
-{
- delete this;
-}
-
-ESIElement *
-esiRemoveNew ()
-{
- return new esiRemove;
-}
-
-esiRemove::esiRemove()
-{}
-
-void
-esiRemove::finish()
-{}
-
-void
-esiRemove::render(ESISegment::Pointer output)
-{
- /* Removes do nothing dude */
- debug (86, 5)("esiRemoveRender: Rendering remove %p\n", this);
-}
-
-/* Accept non-ESI children */
-bool
-esiRemove::addElement (ESIElement::Pointer element)
-{
- if (!dynamic_cast<esiLiteral*>(element.getRaw())) {
- debug (86,5)("esiRemoveAdd: Failed for %p\n",this);
- return false;
- }
-
- return true;
-}
-
-ESIElement::Pointer
-esiRemove::makeCacheable() const
-{
- debug (86,5)("esiRemove::makeCacheable: Returning NULL\n");
- return NULL;
-}
-
-ESIElement::Pointer
-esiRemove::makeUsable(esiTreeParentPtr, esiVarState &) const
-{
- fatal ("esiRemove::Usable: unreachable code!\n");
- return NULL;
-}
-
-/* esiTry */
-esiTry::~esiTry()
-{
- debug (86,5)("esiTry::~esiTry %p\n", this);
-}
-
-void *
-esiTry::operator new(size_t byteCount)
-{
- assert (byteCount == sizeof (esiTry));
-
- if (!Pool)
- Pool = memPoolCreate ("esiTry", sizeof(esiTry));
-
- return memPoolAlloc (Pool);
-}
-
-void
-esiTry::operator delete (void *address)
+void
+esiTry::operator delete (void *address)
{
memPoolFree (Pool, address);
}
parent->provideData (exceptbuffer, this);
exceptbuffer = NULL;
} else if (flags.exceptfailed || except.getRaw() == NULL) {
- parent->fail (this);
+ parent->fail (this, "esi:try - except claused failed, or no except clause found");
}
}
}
void
-esiTry::fail(ESIElement *source)
+esiTry::fail(ESIElement *source, char const *anError)
{
assert (source);
assert (source == attempt || source == except);
- debug (86,5) ("esiTry::fail: this=%p, source=%p\n", this, source);
+ debug (86,5) ("esiTry::fail: this=%p, source=%p, message=%s\n", this, source, anError);
if (source == except) {
flags.exceptfailed = 1;
}
ESIElement::Pointer
-esiTry::makeUsable(esiTreeParentPtr newParent, esiVarState &newVarState) const
+esiTry::makeUsable(esiTreeParentPtr newParent, ESIVarState &newVarState) const
{
debug (86,5)("esiTry::makeUsable: making usable Try from %p\n",this);
esiTry *resultT = new esiTry (*this);
delete this;
}
-/* esiVar */
+/* ESIVar */
#if 0
void *
esiVar::operator new(size_t byteCount)
#endif
void
-esiVar::deleteSelf() const
+ESIVar::deleteSelf() const
{
delete this;
}
-/* esiVarState */
-void
-esiVarStateFree (void *data)
-{
- esiVarState *thisNode = (esiVarState*)data;
- thisNode->freeResources();
-}
-
-void
-esiVarState::freeResources()
-{
- input = NULL;
- ESISegmentFreeList (output);
- httpHeaderClean (&hdr);
-
- if (query) {
- unsigned int i;
-
- for (i = 0; i < query_elements; ++i) {
- safe_free(query[i].var);
- safe_free(query[i].val);
- }
-
- memFreeBuf (query_sz, query);
- }
-
- safe_free (query_string);
- safe_free (browserversion);
-}
-
-void *
-esiVarState::operator new(size_t byteCount)
-{
- assert (byteCount == sizeof (esiVarState));
- void *rv;
- CBDATA_INIT_TYPE_FREECB(esiVarState, esiVarStateFree);
- rv = (void *)cbdataAlloc (esiVarState);
- return rv;
-}
-
-void
-esiVarState::operator delete (void *address)
-{
- cbdataFree (address);
-}
-
-void
-esiVarState::deleteSelf() const
-{
- delete this;
-}
-
-char *
-esiVarState::getProductVersion (char const *s)
-{
- char const *t;
- int len;
- t = index (s,'/');
-
- if (!t || !*(++t))
- return xstrdup ("");
-
- len = strcspn (t, " \r\n()<>@,;:\\\"/[]?={}");
-
- return xstrndup (t, len);
-}
-
-esiVarState::esiVarState (HttpHeader const *aHeader, char const *uri)
- : output (NULL)
-{
- /* Fill out variable values */
- /* Count off the query elements */
- char const *query_start = strchr (uri, '?');
-
- if (query_start && query_start[1] != '\0' ) {
- unsigned int n;
- query_string = xstrdup (query_start + 1);
- query_elements = 1;
- char const *query_pos = query_start + 1;
-
- while ((query_pos = strchr (query_pos, '&'))) {
- ++query_elements;
- ++query_pos;
- }
-
- query = (_query_elem *)memReallocBuf(query, query_elements * sizeof (struct _query_elem),
- &query_sz);
- query_pos = query_start + 1;
- n = 0;
-
- while (query_pos) {
- char *next = strchr (query_pos, '&');
- char *div = strchr (query_pos, '=');
-
- if (next)
- ++next;
-
- assert (n < query_elements);
-
- if (!div)
- div = next;
-
- if (!(div - query_pos + 1))
- /* zero length between & and = or & and & */
- continue;
-
- query[n].var = xstrndup (query_pos, div - query_pos + 1) ;
-
- if (div == next) {
- query[n].val = xstrdup ("");
- } else {
- query[n].val = xstrndup (div + 1, next - div - 1);
- }
-
- query_pos = next;
- ++n;
- }
- } else {
- query_string = xstrdup ("");
- }
-
- if (query) {
- unsigned int n = 0;
- debug (86,6)("esiVarStateNew: Parsed Query string: '%s'\n",uri);
-
- while (n < query_elements) {
- debug (86,6)("esiVarStateNew: Parsed Query element %d '%s'='%s'\n",n + 1, query[n].var, query[n].val);
- ++n;
- }
- }
-
- /* Now setup the UserAgent values */
- /* An example:
- * User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.0.3705) */
- /* Grr thisNode is painful - RFC 2616 specifies that 'by convention' the tokens are in order of importance
- * in identifying the product. According to the RFC the above should be interpreted as:
- * Product - Mozilla version 4.0
- * in comments - compatible; .... 3705
- *
- * Useing the RFC a more appropriate header would be
- * User-Agent: MSIE/6.0 Mozilla/4.0 Windows-NT/5.1 .NET-CLR/1.0.3705
- * or something similar.
- *
- * Because we can't parse under those rules and get real-world useful answers, we follow the following
- * algorithm:
- * if the string Windows appears in the header, the OS is WIN.
- * If the string Mac appears in the header, the OS is MAC.
- * If the string nix, or BSD appears in the header, the OS is UNIX.
- * If the string MSIE appears in the header, the BROWSER is MSIE, and the version is the string from
- * MSIE<sp> to the first ;, or end of string.
- * If the String MSIE does not appear in the header, and MOZILLA does, we use the version from the
- * /version field.
- * if MOZILLA doesn't appear, the browser is set to OTHER.
- * In future, thisNode may be better implemented as a regexp.
- */
- /* TODO: only grab the needed headers */
- httpHeaderInit (&hdr, hoReply);
-
- httpHeaderAppend (&hdr, aHeader);
-
- if (httpHeaderHas(&hdr, HDR_USER_AGENT)) {
- char const *s = httpHeaderGetStr (&hdr, HDR_USER_AGENT);
- char const *t, *t1;
-
- if (strstr (s, "Windows"))
- UserOs = ESI_OS_WIN;
- else if (strstr (s, "Mac"))
- UserOs = ESI_OS_MAC;
- else if (strstr (s, "nix") || strstr (s, "BSD"))
- UserOs = ESI_OS_UNIX;
- else
- UserOs = ESI_OS_OTHER;
-
- /* Now the browser and version */
- if ((t = strstr (s, "MSIE"))) {
- browser = ESI_BROWSER_MSIE;
- t = index (t, ' ');
-
- if (!t)
- browserversion = xstrdup ("");
- else {
- t1 = index (t, ';');
-
- if (!t1)
- browserversion = xstrdup (t + 1);
- else
- browserversion = xstrndup (t + 1, t1-t);
- }
- } else if (strstr (s, "Mozilla")) {
- browser = ESI_BROWSER_MOZILLA;
- browserversion = getProductVersion(s);
- } else {
- browser = ESI_BROWSER_OTHER;
- browserversion = getProductVersion(s);
- }
- } else {
- UserOs = ESI_OS_OTHER;
- browser = ESI_BROWSER_OTHER;
- browserversion = xstrdup ("");
- }
-}
-
-void
-esiVarState::feedData (const char *buf, size_t len)
-{
- /* TODO: if needed - tune to skip segment iteration */
- debug (86,6)("esiVarState::feedData: accepting %d bytes\n", len);
- ESISegment::ListAppend (input, buf, len);
-}
-
-ESISegment::Pointer
-esiVarState::extractList()
-{
- doIt();
- ESISegment::Pointer rv = output;
- output = NULL;
- debug (86,6)("esiVarStateExtractList: Extracted list\n");
- return rv;
-}
-
-char *
-esiVarState::extractChar ()
-{
- if (!input.getRaw())
- fatal ("Attempt to extract variable state with no data fed in \n");
-
- doIt();
-
- char *rv = output->listToChar();
-
- ESISegmentFreeList (output);
-
- debug (86,6)("esiVarStateExtractList: Extracted char\n");
-
- return rv;
-}
-
-int
-httpHeaderHasListMember(const HttpHeader * hdr, http_hdr_type id, const char *member, const char separator);
-
-int
-httpHeaderHasListMember(const HttpHeader * hdr, http_hdr_type id, const char *member, const char separator)
-{
- int result = 0;
- const char *pos = NULL;
- const char *item;
- int ilen;
- int mlen = strlen(member);
-
- assert(hdr);
- assert(id >= 0);
-
- String header (httpHeaderGetStrOrList(hdr, id));
-
- while (strListGetItem(&header, separator, &item, &ilen, &pos)) {
- if (strncmp(item, member, mlen) == 0
- && (item[mlen] == '=' || item[mlen] == separator || item[mlen] == ';' || item[mlen] == '\0')) {
- result = 1;
- break;
- }
- }
-
- return result;
-}
-
-void
-esiVarState::eval (esiVar_t type, char const *subref, char const *found_default )
-{
- const char *s = NULL;
-
- if (!found_default)
- found_default = "";
-
- switch (type) {
-
- case ESI_VAR_HOST:
- flags.host = 1;
-
- if (!subref && httpHeaderHas(&hdr,HDR_HOST)) {
- s = httpHeaderGetStr (&hdr, HDR_HOST);
- } else
- s = found_default;
-
- ESISegment::ListAppend (output, s, strlen (s));
-
- break;
-
- case ESI_VAR_COOKIE:
- flags.cookie = 1;
-
- if (httpHeaderHas(&hdr, HDR_COOKIE)) {
- if (!subref)
- s = httpHeaderGetStr (&hdr, HDR_COOKIE);
- else {
- String S = httpHeaderGetListMember (&hdr, HDR_COOKIE, subref, ';');
-
- if (S.size())
- ESISegment::ListAppend (output, S.buf(), S.size());
- else if (found_default)
- ESISegment::ListAppend (output, found_default, strlen (found_default));
- }
- } else
- s = found_default;
-
- if (s)
- ESISegment::ListAppend (output, s, strlen (s));
-
- break;
-
- case ESI_VAR_REFERER:
- flags.referer = 1;
-
- if (!subref && httpHeaderHas(&hdr, HDR_REFERER))
- s = httpHeaderGetStr (&hdr, HDR_REFERER);
- else
- s = found_default;
-
- ESISegment::ListAppend (output, s, strlen (s));
-
- break;
-
- case ESI_QUERY_STRING:
- if (!subref)
- s = query_string;
- else {
- unsigned int i = 0;
-
- while (i < query_elements && !s) {
- if (!strcmp (subref, query[i].var))
- s = query[i].val;
-
- ++i;
- }
-
- if (!s)
- s = found_default;
- }
-
- ESISegment::ListAppend (output, s, strlen (s));
- break;
-
- case ESI_VAR_USERAGENT:
- flags.useragent = 1;
-
- if (httpHeaderHas(&hdr, HDR_USER_AGENT)) {
- if (!subref)
- s = httpHeaderGetStr (&hdr, HDR_USER_AGENT);
- else {
- if (!strcmp (subref, "os")) {
- s = esiUserOs[UserOs];
- } else if (!strcmp (subref, "browser")) {
- s = esiBrowsers[browser];
- } else if (!strcmp (subref, "version")) {
- s = browserversion;
- } else
- s = "";
- }
- } else
- s = found_default;
-
- ESISegment::ListAppend (output, s, strlen (s));
-
- break;
-
- case ESI_VAR_LANGUAGE:
- flags.language = 1;
-
- if (httpHeaderHas(&hdr, HDR_ACCEPT_LANGUAGE)) {
- if (!subref) {
- String S (httpHeaderGetList (&hdr, HDR_ACCEPT_LANGUAGE));
- ESISegment::ListAppend (output, S.buf(), S.size());
- } else {
- if (httpHeaderHasListMember (&hdr, HDR_ACCEPT_LANGUAGE, subref, ',')) {
- s = "true";
- } else {
- s = "false";
- }
-
- ESISegment::ListAppend (output, s, strlen (s));
- }
- } else {
- s = found_default;
- ESISegment::ListAppend (output, s, strlen (s));
- }
-
- break;
-
- case ESI_VAR_OTHER:
- /* No-op. We swallow it */
-
- if (found_default) {
- ESISegment::ListAppend (output, found_default, strlen (found_default));
- }
-
- break;
- }
-}
-
-bool
-esiVarState::validChar (char c)
-{
- if (('A' <= c && c <= 'Z') ||
- ('a' <= c && c <= 'z') ||
- '_' == c || '-' == c)
- return true;
-
- return false;
-}
-
-esiVarState::esiVar_t
-esiVarState::GetVar(char *s, int len)
-{
- assert (s);
-
- if (len == 9) {
- if (!strncmp (s, "HTTP_HOST", 9))
- return ESI_VAR_HOST;
- else
- return ESI_VAR_OTHER;
- }
-
- if (len == 11) {
- if (!strncmp (s, "HTTP_COOKIE", 11))
- return ESI_VAR_COOKIE;
- else
- return ESI_VAR_OTHER;
- }
-
- if (len == 12) {
- if (!strncmp (s, "HTTP_REFERER", 12))
- return ESI_VAR_REFERER;
- else if (!strncmp (s, "QUERY_STRING", 12))
- return ESI_QUERY_STRING;
- else
- return ESI_VAR_OTHER;
- }
-
- if (len == 15) {
- if (!strncmp (s, "HTTP_USER_AGENT", 15))
- return ESI_VAR_USERAGENT;
- else
- return ESI_VAR_OTHER;
- }
-
- if (len == 20) {
- if (!strncmp (s, "HTTP_ACCEPT_LANGUAGE", 20))
- return ESI_VAR_LANGUAGE;
- else
- return ESI_VAR_OTHER;
- }
-
- return ESI_VAR_OTHER;
-}
-
-/* because we are only used to process:
- * - include URL's
- * - non-esi elements
- * - choose clauses
- * buffering is ok - we won't delay the start of async activity, or
- * of output data preparation
- */
-void
-esiVarState::doIt ()
-{
- assert (output == NULL);
- int state = 0;
- char *string = input->listToChar();
- size_t len = strlen (string);
- size_t pos = 0;
- size_t var_pos = 0;
- size_t done_pos = 0;
- char * found_subref = NULL;
- char *found_default = NULL;
- esiVar_t vartype = ESI_VAR_OTHER;
- ESISegmentFreeList (input);
-
- while (pos < len) {
- switch (state) {
-
- case 0: /* skipping pre-variables */
-
- if (string[pos] != '$') {
- ++pos;
- } else {
- if (pos - done_pos)
- /* extract known good text */
- ESISegment::ListAppend (output, string + done_pos, pos - done_pos);
-
- done_pos = pos;
-
- state = 1;
-
- ++pos;
- }
-
- break;
-
- case 1:/* looking for ( */
-
- if (string[pos] != '(') {
- state = 0;
- } else {
- state = 2; /* extract a variable name */
- var_pos = ++pos;
- }
-
- break;
-
- case 2: /* looking for variable name */
-
- if (!validChar(string[pos])) {
- /* not a variable name char */
-
- if (pos - var_pos)
- vartype = GetVar (string + var_pos, pos - var_pos);
-
- state = 3;
- } else {
- ++pos;
- }
-
- break;
-
- case 3: /* looking for variable subref, end bracket or default indicator */
-
- if (string[pos] == ')') {
- /* end of string */
- eval(vartype, found_subref, found_default);
- done_pos = ++pos;
- safe_free(found_subref);
- safe_free(found_default);
- state = 0;
- } else if (!found_subref && !found_default && string[pos] == '{') {
- debug (86,6)("esiVarStateDoIt: Subref of some sort\n");
- /* subreference of some sort */
- /* look for the entry name */
- var_pos = ++pos;
- state = 4;
- } else if (!found_default && string[pos] == '|') {
- debug (86,6)("esiVarStateDoIt: Default present\n");
- /* extract default value */
- state = 5;
- var_pos = ++pos;
- } else {
- /* unexpected char, not a variable after all */
- debug (86,6)("esiVarStateDoIt: unexpected char after varname\n");
- state = 0;
- pos = done_pos + 2;
- }
-
- break;
-
- case 4: /* looking for variable subref */
-
- if (string[pos] == '}') {
- /* end of subref */
- found_subref = xstrndup (&string[var_pos], pos - var_pos + 1);
- debug (86,6)("esiVarStateDoIt: found end of variable subref '%s'\n", found_subref);
- state = 3;
- ++pos;
- } else if (!validChar (string[pos])) {
- debug (86,6)("esiVarStateDoIt: found invalid char in variable subref\n");
- /* not a valid subref */
- safe_free(found_subref);
- state = 0;
- pos = done_pos + 2;
- } else {
- ++pos;
- }
-
- break;
-
- case 5: /* looking for a default value */
-
- if (string[pos] == '\'') {
- /* begins with a quote */
- debug (86,6)("esiVarStateDoIt: found quoted default\n");
- state = 6;
- var_pos = ++pos;
- } else {
- /* doesn't */
- debug (86,6)("esiVarStateDoIt: found unquoted default\n");
- state = 7;
- ++pos;
- }
-
- break;
-
- case 6: /* looking for a quote terminate default value */
-
- if (string[pos] == '\'') {
- /* end of default */
- found_default = xstrndup (&string[var_pos], pos - var_pos + 1);
- debug (86,6)("esiVarStateDoIt: found end of quoted default '%s'\n", found_default);
- state = 3;
- }
-
- ++pos;
- break;
-
- case 7: /* looking for } terminate default value */
-
- if (string[pos] == ')') {
- /* end of default - end of variable*/
- found_default = xstrndup (&string[var_pos], pos - var_pos + 1);
- debug (86,6)("esiVarStateDoIt: found end of variable (w/ unquoted default) '%s'\n",found_default);
- eval(vartype,found_subref, found_default);
- done_pos = ++pos;
- safe_free(found_default);
- safe_free(found_subref);
- state = 0;
- }
-
- ++pos;
- break;
-
- default:
- fatal("esiVarStateDoIt: unexpected state\n");
- }
- }
-
- /* pos-done_pos chars are ready to copy */
- if (pos-done_pos)
- ESISegment::ListAppend (output, string+done_pos, pos - done_pos);
-
- safe_free (found_default);
-
- safe_free (found_subref);
-}
-
-/* XXX FIXME: this should be comma delimited, no? */
-void
-esiVarState::buildVary (HttpReply *rep)
-{
- char tempstr[1024];
- tempstr[0]='\0';
-
- if (flags.language)
- strcat (tempstr, "Accept-Language ");
-
- if (flags.cookie)
- strcat (tempstr, "Cookie ");
-
- if (flags.host)
- strcat (tempstr, "Host ");
-
- if (flags.referer)
- strcat (tempstr, "Referer ");
-
- if (flags.useragent)
- strcat (tempstr, "User-Agent ");
-
- if (!tempstr[0])
- return;
-
- String strVary (httpHeaderGetList (&rep->header, HDR_VARY));
-
- if (!strVary.size() || strVary.buf()[0] != '*') {
- httpHeaderPutStr (&rep->header, HDR_VARY, tempstr);
- }
-}
-
/* esiChoose */
esiChoose::~esiChoose()
{
}
void
-esiChoose::fail(ESIElement * source)
+esiChoose::fail(ESIElement * source, char const *anError)
{
checkValidSource (source);
elements.setNULL (0, elements.size());
otherwise = NULL;
- parent->fail(this);
+ parent->fail(this, anError);
parent = NULL;
}
}
void
-esiChoose::makeUsableElements(esiChoose const &old, esiVarState &newVarState)
+esiChoose::makeUsableElements(esiChoose const &old, ESIVarState &newVarState)
{
for (size_t counter = 0; counter < old.elements.size(); ++counter) {
ESIElement::Pointer newElement = old.elements[counter]->makeUsable (this, newVarState);
}
ESIElement::Pointer
-esiChoose::makeUsable(esiTreeParentPtr newParent, esiVarState &newVarState) const
+esiChoose::makeUsable(esiTreeParentPtr newParent, ESIVarState &newVarState) const
{
esiChoose *resultC = new esiChoose (*this);
ESIElement::Pointer result = resultC;
delete this;
}
-esiWhen::esiWhen (esiTreeParentPtr aParent, int attrcount, const char **attr,esiVarState *aVar) : esiSequence (aParent)
+esiWhen::esiWhen (esiTreeParentPtr aParent, int attrcount, const char **attr,ESIVarState *aVar) : esiSequence (aParent)
{
varState = NULL;
char const *expression = NULL;
for (int loopCounter = 0; loopCounter < attrcount && attr[loopCounter]; loopCounter += 2) {
if (!strcmp(attr[loopCounter],"test")) {
/* evaluate test */
- debug (86,5)("esiIncludeNew: Evaluating '%s'\n",attr[loopCounter+1]);
+ debug (86,5)("esiWhen::esiWhen: Evaluating '%s'\n",attr[loopCounter+1]);
/* TODO: warn the user instead of asserting */
assert (expression == NULL);
expression = attr[loopCounter+1];
char const *expression = varState->extractChar ();
- setTestResult(esiExpressionEval (expression));
+ setTestResult(ESIExpression::Evaluate (expression));
safe_free (expression);
}
}
ESIElement::Pointer
-esiWhen::makeUsable(esiTreeParentPtr newParent, esiVarState &newVarState) const
+esiWhen::makeUsable(esiTreeParentPtr newParent, ESIVarState &newVarState) const
{
esiWhen *resultW = new esiWhen (*this);
ESIElement::Pointer result = resultW;
--- /dev/null
+
+/*
+ * $Id: ESIAssign.cc,v 1.1 2003/07/14 14:15:55 robertc Exp $
+ *
+ * DEBUG: section 86 ESI processing
+ * AUTHOR: Robert Collins
+ *
+ * SQUID Web Proxy Cache http://www.squid-cache.org/
+ * ----------------------------------------------------------
+ *
+ * Squid is the result of efforts by numerous individuals from
+ * the Internet community; see the CONTRIBUTORS file for full
+ * details. Many organizations have provided support for Squid's
+ * development; see the SPONSORS file for full details. Squid is
+ * Copyrighted (C) 2001 by the Regents of the University of
+ * California; see the COPYRIGHT file for full details. Squid
+ * incorporates software developed and/or copyrighted by other
+ * sources; see the CREDITS file for full details.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ ; but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ */
+
+#include "squid.h"
+#include "ESIAssign.h"
+#include "ESIContext.h"
+#include "ESISequence.h"
+
+MemPool *ESIAssign::Pool = NULL;
+
+void *
+ESIAssign::operator new (size_t byteCount)
+{
+ assert (byteCount == sizeof (ESIAssign));
+
+ if (!Pool)
+ Pool = memPoolCreate ("ESIAssign", sizeof (ESIAssign));
+
+ return memPoolAlloc(Pool);
+}
+
+void
+ESIAssign::operator delete (void *address)
+{
+ memPoolFree (Pool, address);
+}
+
+void
+ESIAssign::deleteSelf() const
+{
+ delete this;
+}
+
+ESIAssign::~ESIAssign()
+{
+ if (value)
+ delete value;
+}
+
+ESIAssign::ESIAssign (ESIAssign const &old) : parent (NULL), varState (NULL), name (old.name), value (old.value ? new ESIVariableExpression (*old.value): NULL), variable (NULL), unevaluatedVariable(old.unevaluatedVariable)
+{}
+
+ESIAssign::ESIAssign (esiTreeParentPtr aParent, int attrcount, char const **attr, ESIContext *aContext) : parent (aParent), varState (NULL), name(), value (NULL), variable (NULL), unevaluatedVariable()
+{
+ /* TODO: grab content IFF no value was specified */
+ assert (aContext);
+
+ for (int i = 0; i < attrcount && attr[i]; i += 2) {
+ if (!strcmp(attr[i],"name")) {
+ /* the variables name is ... */
+ debug (86,5)("ESIAssign::ESIAssign: Variable name '%s'\n",attr[i+1]);
+ /* If there are duplicate name attributes, we simply use the
+ * last one
+ */
+ name = attr[i+1];
+ } else if (!strcmp(attr[i],"value")) {
+ /* short form assignment: */
+ debug (86,5)("ESIAssign::ESIAssign: Unevaluated variable '%s'\n",attr[i+1]);
+ /* Again, if there are duplicate attributes, we use the last */
+ unevaluatedVariable = attr[i+1];
+ } else {
+ /* ignore mistyped attributes. TODO:? error on these for user feedback - config parameter needed
+ */
+ }
+ }
+
+ varState = cbdataReference(aContext->varState);
+}
+
+void
+ESIAssign::evaluateVariable()
+{
+ if (variable.getRaw())
+ variable->process (false);
+
+ variable = NULL;
+
+ if (unevaluatedVariable.size()) {
+ varState->feedData(unevaluatedVariable.buf(), unevaluatedVariable.size());
+ char const *result = varState->extractChar ();
+
+ /* Consider activating this, when we want to evaluate variables to a
+ * value
+ */
+ // setTestResult(ESIExpression::Evaluate (expression));
+
+ value = new ESIVariableExpression (result);
+
+ safe_free (result);
+ }
+}
+
+void
+ESIAssign::provideData (ESISegment::Pointer data, ESIElement * source)
+{
+ assert (source == variable.getRaw());
+ char *result = data->listToChar();
+ unevaluatedVariable = result;
+ safe_free (result);
+}
+
+esiProcessResult_t
+ESIAssign::process (int dovars)
+{
+ assert (varState);
+
+ if (!value)
+ evaluateVariable();
+
+ if (!value)
+ return ESI_PROCESS_COMPLETE;
+
+ varState->addVariable (name.buf(), name.size(), value);
+
+ value = NULL;
+
+ debug (86,5) ("ESIAssign: Processed %p\n",this);
+
+ return ESI_PROCESS_COMPLETE;
+}
+
+void
+ESIAssign::render(ESISegment::Pointer)
+{}
+
+ESIAssign::Pointer
+ESIAssign::makeCacheable() const
+{
+ ESIAssign *result = new ESIAssign (*this);
+
+ if (variable.getRaw())
+ result->variable = variable->makeCacheable();
+
+ return result;
+}
+
+ESIAssign::Pointer
+ESIAssign::makeUsable(esiTreeParentPtr aParent, ESIVarState &aVarState) const
+{
+ ESIAssign *result = new ESIAssign (*this);
+ result->parent = aParent;
+ result->varState = cbdataReference(&aVarState);
+
+ if (variable.getRaw())
+ result->variable = variable->makeUsable(result, aVarState);
+
+ return result;
+}
+
+void
+ESIAssign::finish()
+{
+ if (varState)
+ cbdataReferenceDone (varState);
+
+ if (parent.getRaw())
+ parent = NULL;
+}
+
+bool
+ESIAssign::addElement(ESIElement::Pointer anElement)
+{
+ /* we have a value, drop the element on the floor */
+
+ if (unevaluatedVariable.size())
+ return true;
+
+ if (!variable.getRaw())
+ variable = new esiSequence (this, false);
+
+ return variable->addElement (anElement);
+}
+
+ESIVariableExpression::~ESIVariableExpression()
+{}
+
+ESIVariableExpression::ESIVariableExpression (String const &aString) : expression (aString)
+{}
+
+void
+ESIVariableExpression::eval (ESIVarState &state, char const *subref, char const *defaultOnEmpty) const
+{
+ /* XXX: Implement evaluation of the expression */
+ ESISegment::ListAppend (state.getOutput(), expression.buf(), expression.size());
+}
--- /dev/null
+/*
+ * $Id: ESIAssign.h,v 1.1 2003/07/14 14:15:55 robertc Exp $
+ *
+ * DEBUG: section 86 ESI processing
+ * AUTHOR: Robert Collins
+ *
+ * SQUID Web Proxy Cache http://www.squid-cache.org/
+ * ----------------------------------------------------------
+ *
+ * Squid is the result of efforts by numerous individuals from
+ * the Internet community; see the CONTRIBUTORS file for full
+ * details. Many organizations have provided support for Squid's
+ * development; see the SPONSORS file for full details. Squid is
+ * Copyrighted (C) 2001 by the Regents of the University of
+ * California; see the COPYRIGHT file for full details. Squid
+ * incorporates software developed and/or copyrighted by other
+ * sources; see the CREDITS file for full details.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ * Copyright (c) 2003, Robert Collins <robertc@squid-cache.org>
+ */
+
+#ifndef SQUID_ESIASSIGN_H
+#define SQUID_ESIASSIGN_H
+
+#include "squid.h"
+#include "ESIElement.h"
+#include "SquidString.h"
+#include "ESIVarState.h"
+
+/* ESIVariableExpression */
+/* This is a variable that is itself and expression */
+
+class ESIVariableExpression : public ESIVarState::Variable
+{
+
+public:
+ ~ESIVariableExpression();
+ ESIVariableExpression (String const &value);
+ virtual void eval (ESIVarState &state, char const *, char const *) const;
+
+private:
+ String expression;
+};
+
+/* ESIAssign */
+
+class ESIContext;
+
+class ESIAssign : public ESIElement
+{
+
+public:
+ void *operator new (size_t byteCount);
+ void operator delete (void *address);
+ void deleteSelf() const;
+ ESIAssign (esiTreeParentPtr, int, const char **, ESIContext *);
+ ESIAssign (ESIAssign const &);
+ ESIAssign &operator=(ESIAssign const &);
+ ~ESIAssign();
+ esiProcessResult_t process (int dovars);
+ void render(ESISegment::Pointer);
+ bool addElement(ESIElement::Pointer);
+ void provideData (ESISegment::Pointer data, ESIElement * source);
+ Pointer makeCacheable() const;
+ Pointer makeUsable(esiTreeParentPtr, ESIVarState &) const;
+ void finish();
+
+private:
+ static MemPool *Pool;
+ void evaluateVariable();
+ esiTreeParentPtr parent;
+ ESIVarState *varState;
+ String name;
+ ESIVariableExpression * value;
+ ESIElement::Pointer variable;
+ String unevaluatedVariable;
+};
+
+#endif /* SQUID_ESIASSIGN_H */
/*
- * $Id: ESIContext.cc,v 1.1 2003/03/10 04:56:35 robertc Exp $
+ * $Id: ESIContext.cc,v 1.2 2003/07/14 14:15:56 robertc Exp $
*
* DEBUG: section 86 ESI processing
* AUTHOR: Robert Collins
ESIContext::updateCachedAST()
{
assert (http);
- assert (http->entry);
+ assert (http->storeEntry());
if (hasCachedAST()) {
- debug (86,5)("ESIContext::updateCachedAST: not updating AST cache for entry %p from ESI Context %p as there is already a cached AST.\n", http->entry, this);
+ debug (86,5)("ESIContext::updateCachedAST: not updating AST cache for entry %p from ESI Context %p as there is already a cached AST.\n", http->storeEntry(), this);
return;
}
ESIElement::Pointer treeToCache = tree->makeCacheable();
- debug (86,5)("ESIContext::updateCachedAST: Updating AST cache for entry %p with current value %p to new value %p\n", http->entry, http->entry->cachedESITree.getRaw(), treeToCache.getRaw());
+ debug (86,5)("ESIContext::updateCachedAST: Updating AST cache for entry %p with current value %p to new value %p\n", http->storeEntry(), http->storeEntry()->cachedESITree.getRaw(), treeToCache.getRaw());
- if (http->entry->cachedESITree.getRaw())
- http->entry->cachedESITree->finish();
+ if (http->storeEntry()->cachedESITree.getRaw())
+ http->storeEntry()->cachedESITree->finish();
- http->entry->cachedESITree = treeToCache;
+ http->storeEntry()->cachedESITree = treeToCache;
treeToCache = NULL;
}
ESIContext::hasCachedAST() const
{
assert (http);
- assert (http->entry);
+ assert (http->storeEntry());
- if (http->entry->cachedESITree.getRaw()) {
- debug (86,5)("ESIContext::hasCachedAST: %p - Cached AST present in store entry %p.\n", this, http->entry);
+ if (http->storeEntry()->cachedESITree.getRaw()) {
+ debug (86,5)("ESIContext::hasCachedAST: %p - Cached AST present in store entry %p.\n", this, http->storeEntry());
return true;
} else {
- debug (86,5)("ESIContext::hasCachedAST: %p - Cached AST not present in store entry %p.\n", this, http->entry);
+ debug (86,5)("ESIContext::hasCachedAST: %p - Cached AST not present in store entry %p.\n", this, http->storeEntry());
return false;
}
}
parserState.popAll();
- tree = http->entry->cachedESITree->makeUsable (this, *varState);
+ tree = http->storeEntry()->cachedESITree->makeUsable (this, *varState);
cachedASTInUse = true;
}
+
+void
+ESIContext::setErrorMessage(char const *anError)
+{
+ if (!errormessage)
+ errormessage = xstrdup (anError);
+}
/*
- * $Id: ESIContext.h,v 1.2 2003/03/15 04:17:38 robertc Exp $
+ * $Id: ESIContext.h,v 1.3 2003/07/14 14:15:56 robertc Exp $
*
*
* SQUID Web Proxy Cache http://www.squid-cache.org/
#include "ESIElement.h"
#include "clientStream.h"
-class esiVarState;
+class ESIVarState;
class ClientHttpRequest;
/* when esi processing completes */
void provideData(ESISegment::Pointer, ESIElement *source);
- void fail (ESIElement *source);
+ void fail (ESIElement *source, char const*anError = NULL);
void startRead();
void finishRead();
bool reading() const;
void setError();
+ void setErrorMessage(char const *);
void addStackElement (ESIElement::Pointer element);
void addLiteral (const char *s, int len);
}
parserState; /* todo factor this off somewhere else; */
- esiVarState *varState;
+ ESIVarState *varState;
ESIElement::Pointer tree;
esiKick_t kick ();
/*
- * $Id: ESICustomParser.cc,v 1.1 2003/03/10 04:56:35 robertc Exp $
+ * $Id: ESICustomParser.cc,v 1.2 2003/07/14 14:15:56 robertc Exp $
*
* DEBUG: section 86 ESI processing
* AUTHOR: Robert Collins
#include "squid.h"
#include "ESICustomParser.h"
#include "Trie.h"
+#include "TrieCharTransform.h"
#include "Array.h"
Trie *ESICustomParser::SearchTrie=NULL;
-bool ESICustomParser::TrieInited;
Trie *
ESICustomParser::GetTrie()
if (SearchTrie)
return SearchTrie;
- SearchTrie = new Trie;
+ SearchTrie = new Trie(new TrieCaseless);
assert (SearchTrie->add
("<esi:",5,(void *)ESITAG));
}
char const *
-ESICustomParser::findTag(char const *a, size_t b)
+ESICustomParser::findTag(char const *buffer, size_t bufferLength)
{
size_t myOffset (0);
void *resulttype (NULL);
- while (myOffset < b &&
- (resulttype =GetTrie()->findPrefix (a + myOffset, b + myOffset)) == NULL)
+ while (myOffset < bufferLength &&
+ (resulttype =GetTrie()->findPrefix (buffer + myOffset, bufferLength - myOffset)) == NULL)
++myOffset;
- if (myOffset == b)
+ if (myOffset == bufferLength)
return NULL;
debug (86,9)("ESICustomParser::findTag: found %p\n", resulttype);
/* Yuck! */
lastTag = static_cast<ESITAG_t>((int)resulttype);
- return a + myOffset;
+ return buffer + myOffset;
}
bool
/*
- * $Id: ESICustomParser.h,v 1.2 2003/06/09 05:12:04 robertc Exp $
+ * $Id: ESICustomParser.h,v 1.3 2003/07/14 14:15:56 robertc Exp $
*
*
* SQUID Web Proxy Cache http://www.squid-cache.org/
private:
static Trie *SearchTrie;
- static bool TrieInited;
static Trie *GetTrie();
enum ESITAG_t {
ESITAG=1,
/*
- * $Id: ESIElement.h,v 1.1 2003/03/10 04:56:35 robertc Exp $
+ * $Id: ESIElement.h,v 1.2 2003/07/14 14:15:56 robertc Exp $
*
*
* SQUID Web Proxy Cache http://www.squid-cache.org/
assert (0);
}
- virtual void fail(ESIElement * source) {}
+ virtual void fail(ESIElement * source, char const *reason = NULL) {}
virtual ~esiTreeParent(){}}
typedef RefCount<esiTreeParent> esiTreeParentPtr;
-class esiVarState;
+class ESIVarState;
struct ESIElement : public esiTreeParent
{
ESI_ELEMENT_VARS,
ESI_ELEMENT_CHOOSE,
ESI_ELEMENT_WHEN,
- ESI_ELEMENT_OTHERWISE
+ ESI_ELEMENT_OTHERWISE,
+ ESI_ELEMENT_ASSIGN
};
static ESIElementType_t IdentifyElement (const char *);
virtual bool addElement(ESIElement::Pointer)
}
virtual Pointer makeCacheable() const = 0;
- virtual Pointer makeUsable(esiTreeParentPtr, esiVarState &) const = 0;
+ virtual Pointer makeUsable(esiTreeParentPtr, ESIVarState &) const = 0;
/* The top level no longer needs this element */
virtual void finish() = 0;
/*
- * $Id: ESIExpression.cc,v 1.2 2003/06/09 05:22:33 robertc Exp $
+ * $Id: ESIExpression.cc,v 1.3 2003/07/14 14:15:56 robertc Exp $
*
* DEBUG: section 86 ESI processing
* AUTHOR: Robert Collins
*/
#include "squid.h"
+#include "ESIExpression.h"
/* stack precedence rules:
* before pushing an operator onto the stack, the
static void printliteral (stackmember s);
static void printmember (stackmember s);
-
-
-
-extern int
- esiExpressionEval (char const *s);
-
-
/* -2 = failed to compate
* -1 = a less than b
* 0 = a equal b
*endptr = origs;
} else {
*endptr = t + 1;
- rv.value.string = xstrndup (s + 1, t - s - 1);
+ /* Special case for zero length strings */
+
+ if (t - s - 1)
+ rv.value.string = xstrndup (s + 1, t - s - 1);
+ else
+ rv.value.string = static_cast<char *>(xcalloc (1,1));
+
rv.eval = evalliteral;
+
rv.valuestored = ESI_LITERAL_STRING;
+
rv.valuetype = ESI_EXPR_LITERAL;
+
rv.precedence = 1;
+
debug (86,6) ("found string '%s'\n", rv.value.string);
}
} else if ('(' == *s) {
}
int
-esiExpressionEval (char const *s)
+ESIExpression::Evaluate (char const *s)
{
stackmember stack[20];
int stackdepth = 0;
return stack[0].value.integral ? 1 : 0;
}
-
-#if TESTING
-int
-main ()
-{
- char const *expressions[] = {
- "!(1==1)", "!(1!=1)", "1!=1", "!1==1", "1==1",
- "1 <=1","2<=1", "1 < 1", "1 < 2", "-1 < 1","!-1<1",
- "1>2","2>1","2>=2", "2>3", "1==1&1==1","1==1&1==0",
- "!('a'<='c')",
- "(1==1)|('abc'=='def')",
- "(4!=5)&(4==5)",
- "(1==1)|(2==3)&(3==4)", /* should be true because of precedence */
- "(1 & 4)",
- "(\"abc\" | \"edf\")", "1==1==1",
- };
-
- int i = 0;
-
- while (strlen (expressions[i])) {
- printf("Expr '%s' = '%s'\n", expressions[i],
- expreval (expressions[i]) ? "true" : "false");
- ++i;
- }
-
- return 0;
-}
-
-#endif
--- /dev/null
+/*
+ * $Id: ESIExpression.h,v 1.1 2003/07/14 14:15:55 robertc Exp $
+ *
+ * DEBUG: section 86 ESI processing
+ * AUTHOR: Robert Collins
+ *
+ * SQUID Web Proxy Cache http://www.squid-cache.org/
+ * ----------------------------------------------------------
+ *
+ * Squid is the result of efforts by numerous individuals from
+ * the Internet community; see the CONTRIBUTORS file for full
+ * details. Many organizations have provided support for Squid's
+ * development; see the SPONSORS file for full details. Squid is
+ * Copyrighted (C) 2001 by the Regents of the University of
+ * California; see the COPYRIGHT file for full details. Squid
+ * incorporates software developed and/or copyrighted by other
+ * sources; see the CREDITS file for full details.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ * Copyright (c) 2003, Robert Collins <robertc@squid-cache.org>
+ */
+
+#ifndef SQUID_ESIEXPRESSION_H
+#define SQUID_ESIEXPRESSION_H
+
+#include "squid.h"
+
+class ESIExpression
+{
+
+public:
+ static int Evaluate (char const *);
+};
+
+#endif /* SQUID_ESIEXPRESSION_H */
--- /dev/null
+
+/*
+ * $Id: ESIInclude.cc,v 1.1 2003/07/14 14:15:55 robertc Exp $
+ *
+ * DEBUG: section 86 ESI processing
+ * AUTHOR: Robert Collins
+ *
+ * SQUID Web Proxy Cache http://www.squid-cache.org/
+ * ----------------------------------------------------------
+ *
+ * Squid is the result of efforts by numerous individuals from
+ * the Internet community; see the CONTRIBUTORS file for full
+ * details. Many organizations have provided support for Squid's
+ * development; see the SPONSORS file for full details. Squid is
+ * Copyrighted (C) 2001 by the Regents of the University of
+ * California; see the COPYRIGHT file for full details. Squid
+ * incorporates software developed and/or copyrighted by other
+ * sources; see the CREDITS file for full details.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ ; but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ * Copyright (c) 2003, Robert Collins <robertc@squid-cache.org>
+ */
+
+#include "squid.h"
+#include "ESIInclude.h"
+#include "ESIVarState.h"
+#include "client_side_request.h"
+#include "HttpReply.h"
+
+CBDATA_CLASS_INIT (ESIStreamContext);
+
+MemPool *ESIInclude::Pool = NULL;
+
+/* other */
+static CSCB esiBufferRecipient;
+static CSD esiBufferDetach;
+/* esiStreamContext */
+static ESIStreamContext *ESIStreamContextNew (ESIIncludePtr);
+
+/* ESI TO CONSIDER:
+ * 1. retry failed upstream requests
+ */
+
+/* Detach from a buffering stream
+ */
+void
+esiBufferDetach (clientStreamNode *node, clientHttpRequest *http)
+{
+ /* Detach ourselves */
+ clientStreamDetach (node, http);
+}
+
+/*
+ * Write a chunk of data to a client 'socket'.
+ * If the reply is present, send the reply headers down the wire too,
+ * and clean them up when finished.
+ * Pre-condition:
+ * The request is an internal ESI subrequest.
+ * data context is not NULL
+ * There are no more entries in the stream chain.
+ */
+void
+esiBufferRecipient (clientStreamNode *node, clientHttpRequest *http, HttpReply *rep, StoreIOBuffer recievedData)
+{
+ /* Test preconditions */
+ assert (node != NULL);
+ /* ESI TODO: handle thisNode rather than asserting
+ * - it should only ever happen if we cause an
+ * abort and the callback chain loops back to
+ * here, so we can simply return. However, that
+ * itself shouldn't happen, so it stays as an
+ * assert for now. */
+ assert (cbdataReferenceValid (node));
+ assert (node->node.next == NULL);
+ assert (http->getConn().getRaw() == NULL);
+
+ ESIStreamContext::Pointer esiStream = dynamic_cast<ESIStreamContext *>(node->data.getRaw());
+ assert (esiStream.getRaw() != NULL);
+ /* If segments become more flexible, ignore thisNode */
+ assert (recievedData.length <= sizeof(esiStream->localbuffer->buf));
+ assert (!esiStream->finished);
+
+ debug (86,5) ("esiBufferRecipient rep %p body %p len %d\n", rep, recievedData.data, recievedData.length);
+ assert (node->readBuffer.offset == recievedData.offset || recievedData.length == 0);
+
+ /* trivial case */
+
+ if (http->out.offset != 0) {
+ assert(rep == NULL);
+ } else {
+ if (rep) {
+ if (rep->sline.status != HTTP_OK) {
+ httpReplyDestroy(rep);
+ rep = NULL;
+ esiStream->include->fail (esiStream);
+ esiStream->finished = 1;
+ httpRequestFree (http);
+ return;
+ }
+
+#if HEADERS_LOG
+ /* should be done in the store rather than every recipient? */
+ headersLog(0, 0, http->request->method, rep);
+
+#endif
+
+ httpReplyDestroy(rep);
+
+ rep = NULL;
+ }
+ }
+
+ if (recievedData.data && recievedData.length) {
+ http->out.offset += recievedData.length;
+
+ if (recievedData.data >= esiStream->localbuffer->buf &&
+ recievedData.data < &esiStream->localbuffer->buf[sizeof(esiStream->localbuffer->buf)]) {
+ /* original static buffer */
+
+ if (recievedData.data != esiStream->localbuffer->buf) {
+ /* But not the start of it */
+ xmemmove (esiStream->localbuffer->buf, recievedData.data, recievedData.length);
+ }
+
+ esiStream->localbuffer->len = recievedData.length;
+ } else {
+ assert (esiStream->buffer.getRaw() != NULL);
+ esiStream->buffer->len = recievedData.length;
+ }
+ }
+
+ /* EOF / Read error / aborted entry */
+ if (rep == NULL && recievedData.data == NULL && recievedData.length == 0) {
+ /* TODO: get stream status to test the entry for aborts */
+ debug (86,5)("Finished reading upstream data in subrequest\n");
+ esiStream->include->subRequestDone (esiStream, true);
+ esiStream->finished = 1;
+ httpRequestFree (http);
+ return;
+ }
+
+
+ /* after the write to the user occurs, (ie here, or in a callback)
+ * we call */
+ if (clientHttpRequestStatus(-1, http)) {
+ /* TODO: Does thisNode if block leak htto ? */
+ /* XXX when reviewing ESI this is the first place to look */
+ node->data = NULL;
+ esiStream->finished = 1;
+ esiStream->include->fail (esiStream);
+ return;
+ };
+
+ switch (clientStreamStatus (node, http)) {
+
+ case STREAM_UNPLANNED_COMPLETE: /* fallthru ok */
+
+ case STREAM_COMPLETE: /* ok */
+ debug (86,3)("ESI subrequest finished OK\n");
+ esiStream->include->subRequestDone (esiStream, true);
+ esiStream->finished = 1;
+ httpRequestFree (http);
+ return;
+
+ case STREAM_FAILED:
+ debug (86,1)("ESI subrequest failed transfer\n");
+ esiStream->include->fail (esiStream);
+ esiStream->finished = 1;
+ httpRequestFree (http);
+ return;
+
+ case STREAM_NONE: {
+ StoreIOBuffer tempBuffer;
+
+ if (!esiStream->buffer.getRaw()) {
+ esiStream->buffer = esiStream->localbuffer;
+ }
+
+ esiStream->buffer = esiStream->buffer->tail();
+
+ if (esiStream->buffer->len) {
+ esiStream->buffer->next = new ESISegment;
+ esiStream->buffer = esiStream->buffer->next;
+ }
+
+ tempBuffer.offset = http->out.offset;
+ tempBuffer.length = sizeof (esiStream->buffer->buf);
+ tempBuffer.data = esiStream->buffer->buf;
+ /* now just read into 'buffer' */
+ clientStreamRead (node,
+ http, tempBuffer);
+ debug (86,5)("esiBufferRecipient: Requested more data for ESI subrequest\n");
+ }
+
+ break;
+
+ default:
+ fatal ("Hit unreachable code in esiBufferRecipient\n");
+ }
+
+}
+
+/* esiStream functions */
+ESIStreamContext::~ESIStreamContext()
+{
+ assert (this);
+ freeResources();
+}
+
+void
+ESIStreamContext::freeResources()
+{
+ debug (86,5)("Freeing stream context resources.\n");
+ buffer = NULL;
+ localbuffer = NULL;
+ include = NULL;
+}
+
+void *
+ESIStreamContext::operator new(size_t byteCount)
+{
+ assert (byteCount == sizeof (ESIStreamContext));
+ CBDATA_INIT_TYPE(ESIStreamContext);
+ ESIStreamContext *result = cbdataAlloc(ESIStreamContext);
+ /* Mark result as being owned - we want the refcounter to do the
+ * delete call
+ */
+ cbdataReference(result);
+ return result;
+}
+
+void
+ESIStreamContext::operator delete (void *address)
+{
+ ESIStreamContext *t = static_cast<ESIStreamContext *>(address);
+ cbdataFree(t);
+ /* And allow the memory to be freed */
+ cbdataReferenceDone (address);
+}
+
+void
+ESIStreamContext::deleteSelf() const
+{
+ delete this;
+}
+
+ESIStreamContext *
+ESIStreamContextNew (ESIIncludePtr include)
+{
+ ESIStreamContext *rv = new ESIStreamContext;
+ rv->include = include;
+ return rv;
+}
+
+
+
+/* ESIInclude */
+ESIInclude::~ESIInclude()
+{
+ debug (86,5)("ESIInclude::Free %p\n", this);
+ ESISegmentFreeList (srccontent);
+ ESISegmentFreeList (altcontent);
+ cbdataReferenceDone (varState);
+ safe_free (srcurl);
+ safe_free (alturl);
+}
+
+void
+ESIInclude::finish()
+{
+ parent = NULL;
+}
+
+void *
+ESIInclude::operator new(size_t byteCount)
+{
+ assert (byteCount == sizeof (ESIInclude));
+
+ if (!Pool)
+ Pool = memPoolCreate ("ESIInclude", sizeof (ESIInclude));
+
+ return memPoolAlloc(Pool);
+}
+
+void
+ESIInclude::operator delete (void *address)
+{
+ memPoolFree (Pool, address);
+}
+
+void
+ESIInclude::deleteSelf() const
+{
+ delete this;
+}
+
+ESIElement::Pointer
+ESIInclude::makeCacheable() const
+{
+ return new ESIInclude (*this);
+}
+
+ESIElement::Pointer
+ESIInclude::makeUsable(esiTreeParentPtr newParent, ESIVarState &newVarState) const
+{
+ ESIInclude *resultI = new ESIInclude (*this);
+ ESIElement::Pointer result = resultI;
+ resultI->parent = newParent;
+ resultI->varState = cbdataReference (&newVarState);
+
+ if (resultI->srcurl)
+ resultI->src = ESIStreamContextNew (resultI);
+
+ if (resultI->alturl)
+ resultI->alt = ESIStreamContextNew (resultI);
+
+ return result;
+}
+
+ESIInclude::ESIInclude(ESIInclude const &old) : parent (NULL), started (false), sent (false)
+{
+ varState = NULL;
+ flags.onerrorcontinue = old.flags.onerrorcontinue;
+
+ if (old.srcurl)
+ srcurl = xstrdup (old.srcurl);
+
+ if (old.alturl)
+ alturl = xstrdup (old.alturl);
+}
+
+void
+ESIInclude::prepareRequestHeaders(HttpHeader &tempheaders, ESIVarState *vars)
+{
+ httpHeaderInit (&tempheaders, hoRequest);
+
+ tempheaders.update (&vars->header(), NULL);
+ tempheaders.removeConnectionHeaderEntries();
+}
+
+
+void
+ESIInclude::Start (ESIStreamContext::Pointer stream, char const *url, ESIVarState *vars)
+{
+ HttpHeader tempheaders;
+
+ if (!stream.getRaw())
+ return;
+
+ prepareRequestHeaders(tempheaders, vars);
+
+ /* Ensure variable state is clean */
+ vars->feedData(url, strlen (url));
+
+ /* tempUrl is eaten by the request */
+ char const *tempUrl = vars->extractChar ();
+
+ debug (86,5)("ESIIncludeStart: Starting subrequest with url '%s'\n", tempUrl);
+
+ if (clientBeginRequest(METHOD_GET, tempUrl, esiBufferRecipient, esiBufferDetach, stream.getRaw(), &tempheaders, stream->localbuffer->buf, HTTP_REQBUF_SZ)) {
+ debug (86,0) ("starting new ESI subrequest failed\n");
+ }
+
+ httpHeaderClean (&tempheaders);
+}
+
+ESIInclude::ESIInclude (esiTreeParentPtr aParent, int attrcount, char const **attr, ESIContext *aContext) : parent (aParent), started (false), sent (false)
+{
+ int i;
+ assert (aContext);
+
+ for (i = 0; i < attrcount && attr[i]; i += 2) {
+ if (!strcmp(attr[i],"src")) {
+ /* Start a request for thisNode url */
+ debug (86,5)("ESIIncludeNew: Requesting source '%s'\n",attr[i+1]);
+ /* TODO: don't assert on thisNode, ignore the duplicate */
+ assert (src.getRaw() == NULL);
+ src = ESIStreamContextNew (this);
+ assert (src.getRaw() != NULL);
+ srcurl = xstrdup ( attr[i+1]);
+ } else if (!strcmp(attr[i],"alt")) {
+ /* Start a secondary request for thisNode url */
+ /* TODO: make a config parameter to wait on requesting alt's
+ * for the src to fail
+ */
+ debug (86,5)("ESIIncludeNew: Requesting alternate '%s'\n",attr[i+1]);
+ assert (alt.getRaw() == NULL); /* TODO: FIXME */
+ alt = ESIStreamContextNew (this);
+ assert (alt.getRaw() != NULL);
+ alturl = xstrdup (attr[i+1]);
+ } else if (!strcmp(attr[i],"onerror")) {
+ if (!strcmp(attr[i+1], "continue")) {
+ flags.onerrorcontinue = 1;
+ } else {
+ /* ignore mistyped attributes */
+ debug (86, 1)("invalid value for onerror='%s'\n", attr[i+1]);
+ }
+ } else {
+ /* ignore mistyped attributes. TODO:? error on these for user feedback - config parameter needed
+ */
+ }
+ }
+
+ varState = cbdataReference(aContext->varState);
+}
+
+void
+ESIInclude::start()
+{
+ /* prevent freeing ourselves */
+ ESIIncludePtr foo(this);
+
+ if (started)
+ return;
+
+ started = true;
+
+ if (src.getRaw()) {
+ Start (src, srcurl, varState);
+ Start (alt, alturl, varState);
+ } else {
+ alt = NULL;
+
+ debug (86,1)("ESIIncludeNew: esi:include with no src attributes\n");
+
+ flags.failed = 1;
+ }
+}
+
+void
+ESIInclude::render(ESISegment::Pointer output)
+{
+ if (sent)
+ return;
+
+ ESISegment::Pointer myout;
+
+ debug (86, 5)("ESIIncludeRender: Rendering include %p\n", this);
+
+ assert (flags.finished || (flags.failed && flags.onerrorcontinue));
+
+ if (flags.failed && flags.onerrorcontinue) {
+ return;
+ }
+
+ /* Render the content */
+ if (srccontent.getRaw()) {
+ myout = srccontent;
+ srccontent = NULL;
+ } else if (altcontent.getRaw()) {
+ myout = altcontent;
+ altcontent = NULL;
+ } else
+ fatal ("ESIIncludeRender called with no content, and no failure!\n");
+
+ assert (output->next == NULL);
+
+ output->next = myout;
+
+ sent = true;
+}
+
+esiProcessResult_t
+ESIInclude::process (int dovars)
+{
+ /* Prevent refcount race leading to free */
+ Pointer me (this);
+ start();
+ debug (86, 5)("ESIIncludeRender: Processing include %p\n", this);
+
+ if (flags.failed) {
+ if (flags.onerrorcontinue)
+ return ESI_PROCESS_COMPLETE;
+ else
+ return ESI_PROCESS_FAILED;
+ }
+
+ if (!flags.finished) {
+ if (flags.onerrorcontinue)
+ return ESI_PROCESS_PENDING_WONTFAIL;
+ else
+ return ESI_PROCESS_PENDING_MAYFAIL;
+ }
+
+ return ESI_PROCESS_COMPLETE;
+}
+
+void
+ESIInclude::fail (ESIStreamContext::Pointer stream)
+{
+ subRequestDone (stream, false);
+}
+
+bool
+ESIInclude::dataNeeded() const
+{
+ return !(flags.finished || flags.failed);
+}
+
+void
+ESIInclude::subRequestDone (ESIStreamContext::Pointer stream, bool success)
+{
+ assert (this);
+
+ if (!dataNeeded())
+ return;
+
+ if (stream == src) {
+ debug (86,3)("ESIInclude::subRequestDone: %s\n", srcurl);
+
+ if (success) {
+ /* copy the lead segment */
+ debug (86,3)("ESIIncludeSubRequestDone: Src OK - include PASSED.\n");
+ assert (!srccontent.getRaw());
+ ESISegment::ListTransfer (stream->localbuffer, srccontent);
+ /* we're done! */
+ flags.finished = 1;
+ } else {
+ /* Fail if there is no alt being retrieved */
+ debug (86,3)("ESIIncludeSubRequestDone: Src FAILED\n");
+
+ if (!(alt.getRaw() || altcontent.getRaw())) {
+ debug (86,3)("ESIIncludeSubRequestDone: Include FAILED - No ALT\n");
+ flags.failed = 1;
+ } else if (altcontent.getRaw()) {
+ debug (86,3)("ESIIncludeSubRequestDone: Include PASSED - ALT already Complete\n");
+ /* ALT was already retrieved, we are done */
+ flags.finished = 1;
+ }
+ }
+
+ src = NULL;
+ } else if (stream == alt) {
+ debug (86,3)("ESIInclude::subRequestDone: %s\n", alturl);
+
+ if (success) {
+ debug (86,3)("ESIIncludeSubRequestDone: ALT OK.\n");
+ /* copy the lead segment */
+ assert (!altcontent.getRaw());
+ ESISegment::ListTransfer (stream->localbuffer, altcontent);
+ /* we're done! */
+
+ if (!(src.getRaw() || srccontent.getRaw())) {
+ /* src already failed, kick ESI processor */
+ debug (86,3)("ESIIncludeSubRequestDone: Include PASSED - SRC already failed.\n");
+ flags.finished = 1;
+ }
+ } else {
+ if (!(src.getRaw() || srccontent.getRaw())) {
+ debug (86,3)("ESIIncludeSubRequestDone: ALT FAILED, Include FAILED - SRC already failed\n");
+ /* src already failed */
+ flags.failed = 1;
+ }
+ }
+
+ alt = NULL;
+ } else {
+ fatal ("ESIIncludeSubRequestDone: non-owned stream found!\n");
+ }
+
+ if (flags.finished || flags.failed) {
+ /* Kick ESI Processor */
+ debug (86,5)("ESIInclude %p SubRequest %p completed, kicking processor , status %s\n", this, stream.getRaw(), flags.finished ? "OK" : "FAILED");
+ assert (parent.getRaw());
+
+ if (!flags.failed) {
+ sent = true;
+ parent->provideData (srccontent.getRaw() ? srccontent:altcontent,this);
+
+ if (srccontent.getRaw())
+ srccontent = NULL;
+ else
+ altcontent = NULL;
+ } else if (flags.onerrorcontinue) {
+ /* render nothing but inform of completion */
+
+ if (!sent) {
+ sent = true;
+ parent->provideData (new ESISegment, this);
+ } else
+ assert (0);
+ } else
+ parent->fail(this, "esi:include could not be completed.");
+ }
+}
+
--- /dev/null
+/*
+ * $Id: ESIInclude.h,v 1.1 2003/07/14 14:15:55 robertc Exp $
+ *
+ * DEBUG: section 86 ESI processing
+ * AUTHOR: Robert Collins
+ *
+ * SQUID Web Proxy Cache http://www.squid-cache.org/
+ * ----------------------------------------------------------
+ *
+ * Squid is the result of efforts by numerous individuals from
+ * the Internet community; see the CONTRIBUTORS file for full
+ * details. Many organizations have provided support for Squid's
+ * development; see the SPONSORS file for full details. Squid is
+ * Copyrighted (C) 2001 by the Regents of the University of
+ * California; see the COPYRIGHT file for full details. Squid
+ * incorporates software developed and/or copyrighted by other
+ * sources; see the CREDITS file for full details.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ * Copyright (c) 2003, Robert Collins <robertc@squid-cache.org>
+ */
+
+#ifndef SQUID_ESIINCLUDE_H
+#define SQUID_ESIINCLUDE_H
+
+#include "squid.h"
+#include "ESISegment.h"
+#include "ESIElement.h"
+#include "ESIContext.h"
+
+class ESIInclude;
+typedef RefCount<ESIInclude> ESIIncludePtr;
+
+class ESIStreamContext : public RefCountable
+{
+
+public:
+ typedef RefCount<ESIStreamContext> Pointer;
+ void *operator new(size_t);
+ void operator delete(void *);
+ void deleteSelf() const;
+ ESIStreamContext();
+ ~ESIStreamContext();
+ void freeResources();
+ int finished;
+ ESIIncludePtr include;
+ ESISegment::Pointer localbuffer;
+ ESISegment::Pointer buffer;
+
+private:
+ CBDATA_CLASS(ESIStreamContext);
+};
+
+/* ESIInclude */
+
+class ESIInclude : public ESIElement
+{
+
+public:
+ void *operator new (size_t byteCount);
+ void operator delete (void *address);
+ void deleteSelf() const;
+
+ ESIInclude(esiTreeParentPtr, int attributes, const char **attr, ESIContext *);
+ ~ESIInclude();
+ void render(ESISegment::Pointer);
+ esiProcessResult_t process (int dovars);
+ Pointer makeCacheable() const;
+ Pointer makeUsable(esiTreeParentPtr, ESIVarState &) const;
+ void subRequestDone (ESIStreamContext::Pointer, bool);
+
+ struct
+ {
+
+int onerrorcontinue:
+ 1; /* on error return zero data */
+
+int failed:
+ 1; /* Failed to process completely */
+
+int finished:
+ 1; /* Finished getting subrequest data */
+ }
+
+ flags;
+ ESIStreamContext::Pointer src;
+ ESIStreamContext::Pointer alt;
+ ESISegment::Pointer srccontent;
+ ESISegment::Pointer altcontent;
+ ESIVarState *varState;
+ char *srcurl, *alturl;
+ void fail(ESIStreamContext::Pointer);
+ void finish();
+
+private:
+ static MemPool *Pool;
+ void Start (ESIStreamContext::Pointer, char const *, ESIVarState *);
+ esiTreeParentPtr parent;
+ void start();
+ bool started;
+ bool sent;
+ ESIInclude(ESIInclude const &);
+ bool dataNeeded() const;
+ void prepareRequestHeaders(HttpHeader &tempheaders, ESIVarState *vars);
+};
+
+#endif /* SQUID_ESIINCLUDE_H */
/*
- * $Id: ESILiteral.h,v 1.1 2003/03/10 04:56:35 robertc Exp $
+ * $Id: ESILiteral.h,v 1.2 2003/07/14 14:15:56 robertc Exp $
*
* DEBUG: section 86 ESI processing
* AUTHOR: Robert Collins
void render(ESISegment::Pointer);
esiProcessResult_t process (int dovars);
Pointer makeCacheable() const;
- Pointer makeUsable(esiTreeParentPtr, esiVarState &) const;
+ Pointer makeUsable(esiTreeParentPtr, ESIVarState &) const;
/* optimise copies away later */
ESISegment::Pointer buffer;
}
flags;
- esiVarState *varState;
+ ESIVarState *varState;
void finish();
private:
/*
- * $Id: ESISegment.h,v 1.1 2003/03/10 04:56:36 robertc Exp $
+ * $Id: ESISegment.h,v 1.2 2003/07/14 14:15:56 robertc Exp $
*
*
* SQUID Web Proxy Cache http://www.squid-cache.org/
*/
#include "RefCount.h"
+#include "SquidString.h"
class ESISegment : public RefCountable
{
/*
- * $Id: ESISequence.cc,v 1.1 2003/03/10 04:56:36 robertc Exp $
+ * $Id: ESISequence.cc,v 1.2 2003/07/14 14:15:56 robertc Exp $
*
* DEBUG: section 86 ESI processing
* AUTHOR: Robert Collins
}
void
-esiSequence::fail (ESIElement *source)
+esiSequence::fail (ESIElement *source, char const *anError)
{
failed = true;
}
debug (86,5)("esiSequence::fail: %p has failed.\n", this);
- parent->fail (this);
+ parent->fail (this, anError);
elements.setNULL(0, elements.size());
parent = NULL;
}
}
void
-esiSequence::makeUsableElements(esiSequence const &old, esiVarState &newVarState)
+esiSequence::makeUsableElements(esiSequence const &old, ESIVarState &newVarState)
{
for (size_t counter = 0; counter < old.elements.size(); ++counter) {
ESIElement::Pointer newElement = old.elements[counter]->makeUsable (this, newVarState);
}
ESIElement::Pointer
-esiSequence::makeUsable(esiTreeParentPtr newParent, esiVarState &newVarState) const
+esiSequence::makeUsable(esiTreeParentPtr newParent, ESIVarState &newVarState) const
{
debug (86,5)("esiSequence::makeUsable: Creating usable Sequence\n");
assert (processedcount == 0);
/*
- * $Id: ESISequence.h,v 1.1 2003/03/10 04:56:36 robertc Exp $
+ * $Id: ESISequence.h,v 1.2 2003/07/14 14:15:56 robertc Exp $
*
* DEBUG: section 86 ESI processing
* AUTHOR: Robert Collins
void provideData (ESISegment::Pointer, ESIElement*);
bool mayFail () const;
void wontFail();
- void fail(ESIElement *);
+ void fail(ESIElement *, char const *anError = NULL);
void makeCachableElements(esiSequence const &old);
Pointer makeCacheable() const;
- void makeUsableElements(esiSequence const &old, esiVarState &);
- Pointer makeUsable(esiTreeParentPtr, esiVarState &) const;
+ void makeUsableElements(esiSequence const &old, ESIVarState &);
+ Pointer makeUsable(esiTreeParentPtr, ESIVarState &) const;
ElementList elements; /* unprocessed or rendered nodes */
size_t processedcount;
--- /dev/null
+/*
+ * $Id: ESIVar.h,v 1.1 2003/07/14 14:15:55 robertc Exp $
+ *
+ * DEBUG: section 86 ESI processing
+ * AUTHOR: Robert Collins
+ *
+ * SQUID Web Proxy Cache http://www.squid-cache.org/
+ * ----------------------------------------------------------
+ *
+ * Squid is the result of efforts by numerous individuals from
+ * the Internet community; see the CONTRIBUTORS file for full
+ * details. Many organizations have provided support for Squid's
+ * development; see the SPONSORS file for full details. Squid is
+ * Copyrighted (C) 2001 by the Regents of the University of
+ * California; see the COPYRIGHT file for full details. Squid
+ * incorporates software developed and/or copyrighted by other
+ * sources; see the CREDITS file for full details.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ * Copyright (c) 2003, Robert Collins <robertc@squid-cache.org>
+ */
+
+#ifndef SQUID_ESIVAR_H
+#define SQUID_ESIVAR_H
+
+#include "squid.h"
+#include "ESIElement.h"
+#include "ESISequence.h"
+
+/* esiVar */
+
+class ESIVar:public esiSequence
+{
+
+public:
+ // void *operator new (size_t byteCount);
+ // void operator delete (void *address);
+ void deleteSelf() const;
+ ESIVar(esiTreeParentPtr aParent) : esiSequence (aParent)
+ {
+ flags.dovars = 1;
+ }
+};
+
+#endif /* SQUID_ESIVAR_H */
--- /dev/null
+
+/*
+ * $Id: ESIVarState.cc,v 1.1 2003/07/14 14:15:55 robertc Exp $
+ *
+ * DEBUG: section 86 ESI processing
+ * AUTHOR: Robert Collins
+ *
+ * SQUID Web Proxy Cache http://www.squid-cache.org/
+ * ----------------------------------------------------------
+ *
+ * Squid is the result of efforts by numerous individuals from
+ * the Internet community; see the CONTRIBUTORS file for full
+ * details. Many organizations have provided support for Squid's
+ * development; see the SPONSORS file for full details. Squid is
+ * Copyrighted (C) 2001 by the Regents of the University of
+ * California; see the COPYRIGHT file for full details. Squid
+ * incorporates software developed and/or copyrighted by other
+ * sources; see the CREDITS file for full details.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ ; but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ * Copyright (c) 2003, Robert Collins <robertc@squid-cache.org>
+ */
+
+#include "squid.h"
+#include "ESIVarState.h"
+#include "HttpReply.h"
+
+CBDATA_TYPE (ESIVarState);
+FREE ESIVarStateFree;
+
+char const *ESIVariableUserAgent::esiUserOs[]=
+ {
+ "WIN",
+ "MAC",
+ "UNIX",
+ "OTHER"
+ };
+
+char const * esiBrowsers[]=
+ {"MSIE",
+ "MOZILLA",
+ "OTHER"
+ };
+
+
+void
+ESIVarState::Variable::eval (ESIVarState &state, char const *subref, char const *found_default) const
+{
+ /* No-op. We swallow it */
+
+ if (found_default)
+ ESISegment::ListAppend (state.getOutput(), found_default, strlen (found_default));
+}
+
+void
+ESIVarState::hostUsed()
+{
+ flags.host = 1;
+}
+
+void
+ESIVarState::cookieUsed()
+{
+ flags.cookie = 1;
+}
+
+void
+ESIVarState::languageUsed()
+{
+ flags.language = 1;
+}
+
+void
+ESIVarState::refererUsed()
+{
+ flags.referer = 1;
+}
+
+void
+ESIVarState::useragentUsed()
+{
+ flags.useragent = 1;
+}
+
+HttpHeader &
+ESIVarState::header()
+{
+ return hdr;
+}
+
+ESISegment::Pointer &
+ESIVarState::getOutput()
+{
+ return output;
+}
+
+char const *
+ESIVariableQuery::queryString() const
+{
+ return query_string;
+}
+
+struct _query_elem const *
+ ESIVariableQuery::queryVector() const
+ {
+ return query;
+ }
+
+size_t const &
+ESIVariableQuery::queryElements() const
+{
+ return query_elements;
+}
+
+void
+ESIVarState::feedData (const char *buf, size_t len)
+{
+ /* TODO: if needed - tune to skip segment iteration */
+ debug (86,6)("esiVarState::feedData: accepting %d bytes\n", len);
+ ESISegment::ListAppend (input, buf, len);
+}
+
+ESISegment::Pointer
+ESIVarState::extractList()
+{
+ doIt();
+ ESISegment::Pointer rv = output;
+ output = NULL;
+ debug (86,6)("ESIVarStateExtractList: Extracted list\n");
+ return rv;
+}
+
+char *
+ESIVarState::extractChar ()
+{
+ if (!input.getRaw())
+ fatal ("Attempt to extract variable state with no data fed in \n");
+
+ doIt();
+
+ char *rv = output->listToChar();
+
+ ESISegmentFreeList (output);
+
+ debug (86,6)("ESIVarStateExtractList: Extracted char\n");
+
+ return rv;
+}
+
+/* ESIVarState */
+void
+esiVarStateFree (void *data)
+{
+ ESIVarState *thisNode = (ESIVarState*)data;
+ thisNode->freeResources();
+}
+
+ESIVarState::~ESIVarState()
+{
+ freeResources();
+
+ while (variablesForCleanup.size())
+ delete variablesForCleanup.pop_back();
+
+ delete defaultVariable;
+}
+
+void
+ESIVarState::freeResources()
+{
+ input = NULL;
+ ESISegmentFreeList (output);
+ httpHeaderClean (&hdr);
+}
+
+void *
+ESIVarState::operator new(size_t byteCount)
+{
+ assert (byteCount == sizeof (ESIVarState));
+ void *rv;
+ CBDATA_INIT_TYPE_FREECB(ESIVarState, esiVarStateFree);
+ rv = (void *)cbdataAlloc (ESIVarState);
+ return rv;
+}
+
+void
+ESIVarState::operator delete (void *address)
+{
+ cbdataFree (address);
+}
+
+void
+ESIVarState::deleteSelf() const
+{
+ delete this;
+}
+
+char *
+ESIVariableUserAgent::getProductVersion (char const *s)
+{
+ char const *t;
+ int len;
+ t = index (s,'/');
+
+ if (!t || !*(++t))
+ return xstrdup ("");
+
+ len = strcspn (t, " \r\n()<>@,;:\\\"/[]?={}");
+
+ return xstrndup (t, len + 1);
+}
+
+ESIVariableQuery::ESIVariableQuery(char const *uri) : query (NULL), query_sz (0), query_elements (0), query_string (NULL)
+{
+ /* Count off the query elements */
+ char const *query_start = strchr (uri, '?');
+
+ if (query_start && query_start[1] != '\0' ) {
+ unsigned int n;
+ query_string = xstrdup (query_start + 1);
+ query_elements = 1;
+ char const *query_pos = query_start + 1;
+
+ while ((query_pos = strchr (query_pos, '&'))) {
+ ++query_elements;
+ ++query_pos;
+ }
+
+ query = (_query_elem *)memReallocBuf(query, query_elements * sizeof (struct _query_elem),
+ &query_sz);
+ query_pos = query_start + 1;
+ n = 0;
+
+ while (query_pos) {
+ char *next = strchr (query_pos, '&');
+ char *div = strchr (query_pos, '=');
+
+ if (next)
+ ++next;
+
+ assert (n < query_elements);
+
+ if (!div)
+ div = next;
+
+ if (!(div - query_pos + 1))
+ /* zero length between & and = or & and & */
+ continue;
+
+ query[n].var = xstrndup (query_pos, div - query_pos + 1) ;
+
+ if (div == next) {
+ query[n].val = xstrdup ("");
+ } else {
+ query[n].val = xstrndup (div + 1, next - div - 1);
+ }
+
+ query_pos = next;
+ ++n;
+ }
+ } else {
+ query_string = xstrdup ("");
+ }
+
+ if (query) {
+ unsigned int n = 0;
+ debug (86,6)("esiVarStateNew: Parsed Query string: '%s'\n",uri);
+
+ while (n < query_elements) {
+ debug (86,6)("esiVarStateNew: Parsed Query element %d '%s'='%s'\n",n + 1, query[n].var, query[n].val);
+ ++n;
+ }
+ }
+}
+
+ESIVariableQuery::~ESIVariableQuery()
+{
+ if (query) {
+ unsigned int i;
+
+ for (i = 0; i < query_elements; ++i) {
+ safe_free(query[i].var);
+ safe_free(query[i].val);
+ }
+
+ memFreeBuf (query_sz, query);
+ }
+
+ safe_free (query_string);
+}
+
+ESIVarState::ESIVarState (HttpHeader const *aHeader, char const *uri)
+ : output (NULL)
+{
+ /* TODO: only grab the needed headers */
+ /* Note that as we pass these through to included requests, we
+ * cannot trim them */
+ httpHeaderInit (&hdr, hoReply);
+
+ httpHeaderAppend (&hdr, aHeader);
+
+ /* populate our variables trie with the available variables.
+ * Additional ones can be added during the parsing.
+ * If there is a lazy evaluation approach to this, consider it!
+ */
+ defaultVariable = new Variable;
+ addVariable ("HTTP_ACCEPT_LANGUAGE", 20, new ESIVariableLanguage);
+ addVariable ("HTTP_COOKIE", 11, new ESIVariableCookie);
+ addVariable ("HTTP_HOST", 9, new ESIVariableHost);
+ addVariable ("HTTP_REFERER", 12, new ESIVariableReferer);
+ addVariable ("HTTP_USER_AGENT", 15, new ESIVariableUserAgent(*this));
+ addVariable ("QUERY_STRING", 12, new ESIVariableQuery(uri));
+}
+
+void
+ESIVarState::removeVariable (String const &name)
+{
+ Variable *candidate = static_cast <Variable *>(variables.find (name.buf(), name.size()));
+
+ if (candidate) {
+ /* XXX: remove me */
+ /* Note - this involves:
+ * extend libTrie to have a remove() call.
+ * delete from the vector.
+ * delete the object.
+ */
+ }
+}
+
+void
+ESIVarState::addVariable(char const *name, size_t len, Variable *aVariable)
+{
+ String temp;
+ temp.limitInit (name, len);
+ removeVariable (temp);
+ variables.add(name, len, aVariable);
+ variablesForCleanup.push_back(aVariable);
+}
+
+ESIVariableUserAgent::~ESIVariableUserAgent()
+{
+ safe_free (browserversion);
+}
+
+ESIVariableUserAgent::ESIVariableUserAgent(ESIVarState &state)
+{
+ /* An example:
+ * User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.0.3705) */
+ /* Grr this Node is painful - RFC 2616 specifies that 'by convention' the tokens are in order of importance
+ * in identifying the product. According to the RFC the above should be interpreted as:
+ * Product - Mozilla version 4.0
+ * in comments - compatible; .... 3705
+ *
+ * Useing the RFC a more appropriate header would be
+ * User-Agent: MSIE/6.0 Mozilla/4.0 Windows-NT/5.1 .NET-CLR/1.0.3705
+ * or something similar.
+ *
+ * Because we can't parse under those rules and get real-world useful answers, we follow the following
+ * algorithm:
+ * if the string Windows appears in the header, the OS is WIN.
+ * If the string Mac appears in the header, the OS is MAC.
+ * If the string nix, or BSD appears in the header, the OS is UNIX.
+ * If the string MSIE appears in the header, the BROWSER is MSIE, and the version is the string from
+ * MSIE<sp> to the first ;, or end of string.
+ * If the String MSIE does not appear in the header, and MOZILLA does, we use the version from the
+ * /version field.
+ * if MOZILLA doesn't appear, the browser is set to OTHER.
+ * In future, this may be better implemented as a regexp.
+ */
+
+ if (httpHeaderHas(&state.header(), HDR_USER_AGENT)) {
+ char const *s = httpHeaderGetStr (&state.header(), HDR_USER_AGENT);
+ UserOs = identifyOs(s);
+ char const *t, *t1;
+
+ /* Now the browser and version */
+
+ if ((t = strstr (s, "MSIE"))) {
+ browser = ESI_BROWSER_MSIE;
+ t = index (t, ' ');
+
+ if (!t)
+ browserversion = xstrdup ("");
+ else {
+ t1 = index (t, ';');
+
+ if (!t1)
+ browserversion = xstrdup (t + 1);
+ else
+ browserversion = xstrndup (t + 1, t1-t);
+ }
+ } else if (strstr (s, "Mozilla")) {
+ browser = ESI_BROWSER_MOZILLA;
+ browserversion = getProductVersion(s);
+ } else {
+ browser = ESI_BROWSER_OTHER;
+ browserversion = getProductVersion(s);
+ }
+ } else {
+ UserOs = ESI_OS_OTHER;
+ browser = ESI_BROWSER_OTHER;
+ browserversion = xstrdup ("");
+ }
+}
+
+ESIVariableUserAgent::esiUserOs_t
+ESIVariableUserAgent::identifyOs(char const *s) const
+{
+ if (!s)
+ return ESI_OS_OTHER;
+
+ if (strstr (s, "Windows"))
+ return ESI_OS_WIN;
+ else if (strstr (s, "Mac"))
+ return ESI_OS_MAC;
+ else if (strstr (s, "nix") || strstr (s, "BSD"))
+ return ESI_OS_UNIX;
+ else
+ return ESI_OS_OTHER;
+}
+
+void
+ESIVariableCookie::eval (ESIVarState &state, char const *subref, char const *found_default) const
+{
+ const char *s = NULL;
+ state.cookieUsed();
+
+ if (httpHeaderHas(&state.header(), HDR_COOKIE)) {
+ if (!subref)
+ s = httpHeaderGetStr (&state.header(), HDR_COOKIE);
+ else {
+ String S = httpHeaderGetListMember (&state.header(), HDR_COOKIE, subref, ';');
+
+ if (S.size())
+ ESISegment::ListAppend (state.getOutput(), S.buf(), S.size());
+ else if (found_default)
+ ESISegment::ListAppend (state.getOutput(), found_default, strlen (found_default));
+ }
+ } else
+ s = found_default;
+
+ if (s)
+ ESISegment::ListAppend (state.getOutput(), s, strlen (s));
+}
+
+void
+ESIVariableHost::eval (ESIVarState &state, char const *subref, char const *found_default) const
+{
+ const char *s = NULL;
+ state.hostUsed();
+
+ if (!subref && httpHeaderHas(&state.header(),HDR_HOST)) {
+ s = httpHeaderGetStr (&state.header(), HDR_HOST);
+ } else
+ s = found_default;
+
+ ESISegment::ListAppend (state.getOutput(), s, strlen (s));
+}
+
+void
+ESIVariableLanguage::eval (ESIVarState &state, char const *subref, char const *found_default) const
+{
+ char const *s = NULL;
+ state.languageUsed();
+
+ if (httpHeaderHas(&state.header(), HDR_ACCEPT_LANGUAGE)) {
+ if (!subref) {
+ String S (httpHeaderGetList (&state.header(), HDR_ACCEPT_LANGUAGE));
+ ESISegment::ListAppend (state.getOutput(), S.buf(), S.size());
+ } else {
+ if (httpHeaderHasListMember (&state.header(), HDR_ACCEPT_LANGUAGE, subref, ',')) {
+ s = "true";
+ } else {
+ s = "false";
+ }
+
+ ESISegment::ListAppend (state.getOutput(), s, strlen (s));
+ }
+ } else {
+ s = found_default;
+ ESISegment::ListAppend (state.getOutput(), s, strlen (s));
+ }
+}
+
+void
+ESIVariableQuery::eval (ESIVarState &state, char const *subref, char const *found_default) const
+{
+ char const *s = NULL;
+
+ if (!subref)
+ s = queryString();
+ else {
+ unsigned int i = 0;
+
+ while (i < queryElements() && !s) {
+ if (!strcmp (subref, queryVector()[i].var))
+ s = queryVector()[i].val;
+
+ ++i;
+ }
+
+ if (!s)
+ s = found_default;
+ }
+
+ ESISegment::ListAppend (state.getOutput(), s, strlen (s));
+}
+
+void
+ESIVariableReferer::eval (ESIVarState &state, char const *subref, char const *found_default) const
+{
+ const char *s = NULL;
+ state.refererUsed();
+
+ if (!subref && httpHeaderHas(&state.header(), HDR_REFERER))
+ s = httpHeaderGetStr (&state.header(), HDR_REFERER);
+ else
+ s = found_default;
+
+ ESISegment::ListAppend (state.getOutput(), s, strlen (s));
+}
+
+void
+ESIVariableUserAgent::eval (ESIVarState &state, char const *subref, char const *found_default) const
+{
+ char const *s = NULL;
+ state.useragentUsed();
+
+ if (httpHeaderHas(&state.header(), HDR_USER_AGENT)) {
+ if (!subref)
+ s = httpHeaderGetStr (&state.header(), HDR_USER_AGENT);
+ else {
+ if (!strcmp (subref, "os")) {
+ s = esiUserOs[UserOs];
+ } else if (!strcmp (subref, "browser")) {
+ s = esiBrowsers[browser];
+ } else if (!strcmp (subref, "version")) {
+ s = browserVersion();
+ } else
+ s = "";
+ }
+ } else
+ s = found_default;
+
+ ESISegment::ListAppend (state.getOutput(), s, strlen (s));
+}
+
+/* thoughts on long term:
+ * get $
+ * get () handler
+ * hand off to handler.
+ * one handler for variables.
+ * one handler for each function.
+ */
+
+class ESIVariableProcessor;
+
+class ESIFunction
+{
+
+public:
+ static ESIFunction *GetFunction (char const *symbol, ESIVariableProcessor &);
+ ESIFunction(ESIVariableProcessor &);
+ void doIt();
+
+private:
+ ESIVariableProcessor &processor;
+
+};
+
+ESIFunction::ESIFunction(ESIVariableProcessor &aProcessor) : processor(aProcessor)
+{}
+
+ESIFunction *
+ESIFunction::GetFunction(char const *symbol, ESIVariableProcessor &aProcessor)
+{
+ if (*symbol == '(')
+ return new ESIFunction(aProcessor);
+
+ return NULL;
+}
+
+class ESIVariableProcessor
+{
+
+public:
+ ESIVariableProcessor(char *, ESISegment::Pointer &, Trie &, ESIVarState *);
+ ~ESIVariableProcessor();
+ void doIt();
+
+private:
+ bool validChar (char c);
+ void eval (ESIVarState::Variable *var, char const *subref, char const *found_default );
+ void doFunction();
+ void identifyFunction();
+ char *string;
+ ESISegment::Pointer &output;
+ Trie &variables;
+ ESIVarState *varState;
+ int state;
+ size_t len;
+ size_t pos;
+ size_t var_pos;
+ size_t done_pos;
+ char * found_subref;
+ char *found_default;
+ ESIVarState::Variable *vartype;
+ ESIFunction *currentFunction;
+};
+
+void
+ESIVariableProcessor::eval (ESIVarState::Variable *var, char const *subref, char const *found_default )
+{
+ assert (var);
+
+ if (!found_default)
+ found_default = "";
+
+ var->eval (*varState, subref, found_default);
+}
+
+bool
+ESIVariableProcessor::validChar (char c)
+{
+ if (('A' <= c && c <= 'Z') ||
+ ('a' <= c && c <= 'z') ||
+ '_' == c || '-' == c)
+ return true;
+
+ return false;
+}
+
+ESIVarState::Variable *
+ESIVarState::GetVar(char const *symbol, int len)
+{
+ assert (symbol);
+
+ void *result = variables.find (symbol, len);
+
+ if (result)
+ return static_cast<Variable *>(result);
+
+ return defaultVariable;
+}
+
+void
+ESIVarState::doIt ()
+{
+ char *string = input->listToChar();
+ ESISegmentFreeList (input);
+ ESIVariableProcessor theProcessor(string, output, variables, this);
+ theProcessor.doIt();
+ safe_free(string);
+}
+
+#define LOOKFORSTART 0
+ESIVariableProcessor::ESIVariableProcessor(char *aString, ESISegment::Pointer &aSegment, Trie &aTrie, ESIVarState *aState) :
+ string(aString), output (aSegment), variables(aTrie), varState (aState),
+ state(LOOKFORSTART), pos(0), var_pos(0), done_pos(0), found_subref (NULL),
+ found_default (NULL), currentFunction(NULL)
+{
+ len = strlen (string);
+ vartype = varState->GetVar("",0);
+}
+
+void
+ESIFunction::doIt()
+{}
+
+/* because we are only used to process:
+ * - include URL's
+ * - non-esi elements
+ * - choose clauses
+ * buffering is ok - we won't delay the start of async activity, or
+ * of output data preparation
+ */
+/* Should make these an enum or something...
+ */
+void
+ESIVariableProcessor::doFunction()
+{
+ if (!currentFunction)
+ return;
+
+ /* stay in here whilst operating */
+ while (pos < len && state)
+ switch (state) {
+
+ case 2: /* looking for variable name */
+
+ if (!validChar(string[pos])) {
+ /* not a variable name char */
+
+ if (pos - var_pos) {
+ vartype = varState->GetVar (string + var_pos, pos - var_pos);
+ }
+
+ state = 3;
+ } else {
+ ++pos;
+ }
+
+ break;
+
+ case 3: /* looking for variable subref, end bracket or default indicator */
+
+ if (string[pos] == ')') {
+ /* end of string */
+ eval(vartype, found_subref, found_default);
+ done_pos = ++pos;
+ safe_free(found_subref);
+ safe_free(found_default);
+ state = LOOKFORSTART;
+ } else if (!found_subref && !found_default && string[pos] == '{') {
+ debug (86,6)("ESIVarStateDoIt: Subref of some sort\n");
+ /* subreference of some sort */
+ /* look for the entry name */
+ var_pos = ++pos;
+ state = 4;
+ } else if (!found_default && string[pos] == '|') {
+ debug (86,6)("esiVarStateDoIt: Default present\n");
+ /* extract default value */
+ state = 5;
+ var_pos = ++pos;
+ } else {
+ /* unexpected char, not a variable after all */
+ debug (86,6)("esiVarStateDoIt: unexpected char after varname\n");
+ state = LOOKFORSTART;
+ pos = done_pos + 2;
+ }
+
+ break;
+
+ case 4: /* looking for variable subref */
+
+ if (string[pos] == '}') {
+ /* end of subref */
+ found_subref = xstrndup (&string[var_pos], pos - var_pos + 1);
+ debug (86,6)("esiVarStateDoIt: found end of variable subref '%s'\n", found_subref);
+ state = 3;
+ ++pos;
+ } else if (!validChar (string[pos])) {
+ debug (86,6)("esiVarStateDoIt: found invalid char in variable subref\n");
+ /* not a valid subref */
+ safe_free(found_subref);
+ state = LOOKFORSTART;
+ pos = done_pos + 2;
+ } else {
+ ++pos;
+ }
+
+ break;
+
+ case 5: /* looking for a default value */
+
+ if (string[pos] == '\'') {
+ /* begins with a quote */
+ debug (86,6)("esiVarStateDoIt: found quoted default\n");
+ state = 6;
+ var_pos = ++pos;
+ } else {
+ /* doesn't */
+ debug (86,6)("esiVarStateDoIt: found unquoted default\n");
+ state = 7;
+ ++pos;
+ }
+
+ break;
+
+ case 6: /* looking for a quote terminate default value */
+
+ if (string[pos] == '\'') {
+ /* end of default */
+ found_default = xstrndup (&string[var_pos], pos - var_pos + 1);
+ debug (86,6)("esiVarStateDoIt: found end of quoted default '%s'\n", found_default);
+ state = 3;
+ }
+
+ ++pos;
+ break;
+
+ case 7: /* looking for } terminate default value */
+
+ if (string[pos] == ')') {
+ /* end of default - end of variable*/
+ found_default = xstrndup (&string[var_pos], pos - var_pos + 1);
+ debug (86,6)("esiVarStateDoIt: found end of variable (w/ unquoted default) '%s'\n",found_default);
+ eval(vartype,found_subref, found_default);
+ done_pos = ++pos;
+ safe_free(found_default);
+ safe_free(found_subref);
+ state = LOOKFORSTART;
+ }
+
+ ++pos;
+ break;
+
+ default:
+ fatal("esiVarStateDoIt: unexpected state\n");
+ }
+}
+
+void
+ESIVariableProcessor::identifyFunction()
+{
+ delete currentFunction;
+ currentFunction = ESIFunction::GetFunction (&string[pos], *this);
+
+ if (!currentFunction) {
+ state = LOOKFORSTART;
+ } else {
+ state = 2; /* process a function */
+ /* advance past function name */
+ var_pos = ++pos;
+ }
+}
+
+void
+ESIVariableProcessor::doIt()
+{
+ assert (output == NULL);
+
+ while (pos < len) {
+ /* skipping pre-variables */
+
+ if (string[pos] != '$') {
+ ++pos;
+ } else {
+ if (pos - done_pos)
+ /* extract known plain text */
+ ESISegment::ListAppend (output, string + done_pos, pos - done_pos);
+
+ done_pos = pos;
+
+ ++pos;
+
+ identifyFunction();
+
+ doFunction();
+ }
+ }
+
+ /* pos-done_pos chars are ready to copy */
+ if (pos-done_pos)
+ ESISegment::ListAppend (output, string+done_pos, pos - done_pos);
+
+ safe_free (found_default);
+
+ safe_free (found_subref);
+}
+
+ESIVariableProcessor::~ESIVariableProcessor()
+{
+ delete currentFunction;
+}
+
+
+/* XXX FIXME: this should be comma delimited, no? */
+void
+ESIVarState::buildVary (HttpReply *rep)
+{
+ char tempstr[1024];
+ tempstr[0]='\0';
+
+ if (flags.language)
+ strcat (tempstr, "Accept-Language ");
+
+ if (flags.cookie)
+ strcat (tempstr, "Cookie ");
+
+ if (flags.host)
+ strcat (tempstr, "Host ");
+
+ if (flags.referer)
+ strcat (tempstr, "Referer ");
+
+ if (flags.useragent)
+ strcat (tempstr, "User-Agent ");
+
+ if (!tempstr[0])
+ return;
+
+ String strVary (httpHeaderGetList (&rep->header, HDR_VARY));
+
+ if (!strVary.size() || strVary.buf()[0] != '*') {
+ httpHeaderPutStr (&rep->header, HDR_VARY, tempstr);
+ }
+}
+
--- /dev/null
+
+/*
+ * $Id: ESIVarState.h,v 1.1 2003/07/14 14:15:55 robertc Exp $
+ *
+ *
+ * SQUID Web Proxy Cache http://www.squid-cache.org/
+ * ----------------------------------------------------------
+ *
+ * Squid is the result of efforts by numerous individuals from
+ * the Internet community; see the CONTRIBUTORS file for full
+ * details. Many organizations have provided support for Squid's
+ * development; see the SPONSORS file for full details. Squid is
+ * Copyrighted (C) 2001 by the Regents of the University of
+ * California; see the COPYRIGHT file for full details. Squid
+ * incorporates software developed and/or copyrighted by other
+ * sources; see the CREDITS file for full details.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ */
+
+#ifndef SQUID_ESIVARSTATE_H
+#define SQUID_ESIVARSTATE_H
+
+#include "ESISegment.h"
+#include "Trie.h"
+#include "Array.h"
+#include "HttpHeader.h"
+
+/* esi variable replacement logic */
+
+typedef enum {
+ ESI_BROWSER_MSIE,
+ ESI_BROWSER_MOZILLA,
+ ESI_BROWSER_OTHER
+} esiBrowser_t;
+
+extern char const * esiBrowsers[];
+
+/* Recursive uses are not supported by design */
+
+struct _query_elem{char *var, *val;};
+
+class ESIVarState
+{
+
+public:
+ ESISegment::Pointer extractList();
+ char *extractChar();
+ void feedData (const char *buf, size_t len);
+ void buildVary (HttpReply *rep);
+
+ class Variable;
+ void addVariable (char const *, size_t, Variable *);
+ void removeVariable (String const &);
+
+ void *operator new (size_t byteCount);
+ void operator delete (void *address);
+ void deleteSelf() const;
+ void freeResources();
+ ESIVarState (HttpHeader const *hdr, char const *uri);
+ ~ESIVarState();
+
+ /* For Variables */
+ void cookieUsed();
+ void hostUsed();
+ void languageUsed();
+ void refererUsed();
+ void useragentUsed();
+ ESISegment::Pointer &getOutput();
+ HttpHeader &header();
+
+private:
+ ESISegment::Pointer input;
+ ESISegment::Pointer output;
+ HttpHeader hdr;
+
+ struct
+ {
+
+int language:
+ 1;
+
+int cookie:
+ 1;
+
+int host:
+ 1;
+
+int referer:
+ 1;
+
+int useragent:
+ 1;
+ }
+
+ flags;
+
+public:
+
+ class Variable
+ {
+
+ public:
+ Variable () {}
+
+ virtual ~Variable(){}
+
+ /* prevent synthetics */
+ Variable (Variable const &) {}
+
+ Variable &operator= (Variable const &);
+ virtual void eval (ESIVarState &state, char const *, char const *) const;
+ };
+
+ Variable* GetVar(char const *s, int len);
+
+private:
+ void doIt ();
+ void setupUserAgent();
+ Trie variables;
+ Vector<Variable*> variablesForCleanup;
+ Variable *defaultVariable;
+};
+
+class ESIVariableCookie : public ESIVarState::Variable
+{
+
+public:
+ virtual void eval (ESIVarState &state, char const *, char const *) const;
+};
+
+class ESIVariableHost : public ESIVarState::Variable
+{
+
+public:
+ virtual void eval (ESIVarState &state, char const *, char const *) const;
+};
+
+class ESIVariableLanguage : public ESIVarState::Variable
+{
+
+public:
+ virtual void eval (ESIVarState &state, char const *, char const *) const;
+};
+
+class ESIVariableQuery : public ESIVarState::Variable
+{
+
+public:
+ ESIVariableQuery(char const *uri);
+ ~ESIVariableQuery();
+ virtual void eval (ESIVarState &state, char const *, char const *) const;
+ char const *queryString() const;
+
+ struct _query_elem const *queryVector() const;
+ size_t const &queryElements() const;
+
+ struct _query_elem *query;
+ size_t query_sz;
+ size_t query_elements;
+ char *query_string;
+};
+
+class ESIVariableReferer : public ESIVarState::Variable
+{
+
+public:
+ virtual void eval (ESIVarState &state, char const *, char const *) const;
+};
+
+class ESIVariableUserAgent : public ESIVarState::Variable
+{
+
+public:
+ ~ESIVariableUserAgent();
+ ESIVariableUserAgent (ESIVarState &state);
+ virtual void eval (ESIVarState &state, char const *, char const *) const;
+
+private:
+ static char const * esiUserOs[];
+ enum esiUserOs_t{
+ ESI_OS_WIN,
+ ESI_OS_MAC,
+ ESI_OS_UNIX,
+ ESI_OS_OTHER
+ };
+ esiUserOs_t identifyOs(char const *) const;
+ char const *browserVersion() const {return browserversion;}
+
+ char *getProductVersion (char const *s);
+ esiUserOs_t UserOs;
+ esiBrowser_t browser;
+ char *browserversion;
+};
+
+
+
+#endif /* SQUID_ESIVARSTATE_H */
/*
- * $Id: HttpHdrRange.cc,v 1.35 2003/06/19 13:12:01 robertc Exp $
+ * $Id: HttpHdrRange.cc,v 1.36 2003/07/14 14:15:56 robertc Exp $
*
* DEBUG: section 64 HTTP Range Header
* AUTHOR: Alex Rousskov
#include "Store.h"
#include "HttpHeaderRange.h"
#include "client_side_request.h"
+#include "HttpReply.h"
/*
* Currently only byte ranges are supported
/*
- * $Id: HttpHeader.cc,v 1.91 2003/07/14 08:21:56 robertc Exp $
+ * $Id: HttpHeader.cc,v 1.92 2003/07/14 14:15:56 robertc Exp $
*
* DEBUG: section 55 HTTP Header
* AUTHOR: Alex Rousskov
/* use fresh entries to replace old ones */
void
httpHeaderUpdate(HttpHeader * old, const HttpHeader * fresh, const HttpHeaderMask * denied_mask)
+{
+ assert (old);
+ old->update (fresh, denied_mask);
+}
+
+void
+HttpHeader::update (HttpHeader const *fresh, HttpHeaderMask const *denied_mask)
{
const HttpHeaderEntry *e;
HttpHeaderPos pos = HttpHeaderInitPos;
- assert(old && fresh);
- assert(old != fresh);
- debug(55, 7) ("updating hdr: %p <- %p\n", old, fresh);
+ assert(this && fresh);
+ assert(this != fresh);
+ debug(55, 7) ("updating hdr: %p <- %p\n", this, fresh);
while ((e = httpHeaderGetEntry(fresh, &pos))) {
/* deny bad guys (ok to check for HDR_OTHER) here */
if (denied_mask && CBIT_TEST(*denied_mask, e->id))
continue;
- httpHeaderDelByName(old, e->name.buf());
+ httpHeaderDelByName(this, e->name.buf());
- httpHeaderAddEntry(old, httpHeaderEntryClone(e));
+ httpHeaderAddEntry(this, httpHeaderEntryClone(e));
}
}
{
memPoolFree (Pool, address);
}
+
+int
+httpHeaderHasListMember(const HttpHeader * hdr, http_hdr_type id, const char *member, const char separator)
+{
+ int result = 0;
+ const char *pos = NULL;
+ const char *item;
+ int ilen;
+ int mlen = strlen(member);
+
+ assert(hdr);
+ assert(id >= 0);
+
+ String header (httpHeaderGetStrOrList(hdr, id));
+
+ while (strListGetItem(&header, separator, &item, &ilen, &pos)) {
+ if (strncmp(item, member, mlen) == 0
+ && (item[mlen] == '=' || item[mlen] == separator || item[mlen] == ';' || item[mlen] == '\0')) {
+ result = 1;
+ break;
+ }
+ }
+
+ return result;
+}
+
+void
+HttpHeader::removeConnectionHeaderEntries()
+{
+ if (httpHeaderHas(this, HDR_CONNECTION)) {
+ /* anything that matches Connection list member will be deleted */
+ String strConnection = httpHeaderGetList(this, HDR_CONNECTION);
+ const HttpHeaderEntry *e;
+ HttpHeaderPos pos = HttpHeaderInitPos;
+ /*
+ * think: on-average-best nesting of the two loops (hdrEntry
+ * and strListItem) @?@
+ */
+ /*
+ * maybe we should delete standard stuff ("keep-alive","close")
+ * from strConnection first?
+ */
+
+ while ((e = httpHeaderGetEntry(this, &pos))) {
+ if (strListIsMember(&strConnection, e->name.buf(), ','))
+ httpHeaderDelAt(this, pos);
+ }
+
+ httpHeaderDelById(this, HDR_CONNECTION);
+ strConnection.clean();
+ }
+}
/*
- * $Id: HttpHeader.h,v 1.4 2003/03/10 04:56:36 robertc Exp $
+ * $Id: HttpHeader.h,v 1.5 2003/07/14 14:15:56 robertc Exp $
*
*
* SQUID Web Proxy Cache http://www.squid-cache.org/
extern void httpHeaderPutSc(HttpHeader *hdr, const HttpHdrSc *sc);
extern HttpHdrSc *httpHeaderGetSc(const HttpHeader *hdr);
SQUIDCEXTERN void httpHeaderAddContRange(HttpHeader *, HttpHdrRangeSpec, ssize_t);
+extern int httpHeaderHasListMember(const HttpHeader * hdr, http_hdr_type id, const char *member, const char separator);
+SQUIDCEXTERN void httpHeaderUpdate(HttpHeader * old, const HttpHeader * fresh, const HttpHeaderMask * denied_mask);
+
+class HttpHeader
+{
+
+public:
+ /* Interface functions */
+ void update (HttpHeader const *fresh, HttpHeaderMask const *denied_mask);
+ void removeConnectionHeaderEntries();
+ /* protected, do not use these, use interface functions instead */
+ Array entries; /* parsed fields in raw format */
+ HttpHeaderMask mask; /* bit set <=> entry present */
+ http_hdr_owner_type owner; /* request or reply */
+ int len; /* length when packed, not counting terminating '\0' */
+};
#endif /* SQUID_HTTPHEADER_H */
/*
- * $Id: HttpReply.h,v 1.3 2003/05/11 13:53:03 hno Exp $
+ * $Id: HttpReply.h,v 1.4 2003/07/14 14:15:56 robertc Exp $
*
*
* SQUID Web Proxy Cache http://www.squid-cache.org/
#define SQUID_HTTPREPLY_H
#include "typedefs.h"
+#include "HttpHeader.h"
/* Http Reply */
extern void httpReplyInitModule(void);
extern int httpReplyBodySize(method_t, HttpReply const *);
extern int httpReplyValidatorsMatch (HttpReply const *, HttpReply const *);
+/* Sync changes here with HttpReply.cc */
+
+class HttpHdrContRange;
+
+class HttpReply
+{
+
+public:
+ void *operator new (size_t);
+ void operator delete (void *);
+ /* unsupported, writable, may disappear/change in the future */
+ int hdr_sz; /* sums _stored_ status-line, headers, and <CRLF> */
+
+ /* public, readable; never update these or their .hdr equivalents directly */
+ int content_length;
+ time_t date;
+ time_t last_modified;
+ time_t expires;
+ String content_type;
+ HttpHdrCc *cache_control;
+ HttpHdrSc *surrogate_control;
+ HttpHdrContRange *content_range;
+ short int keep_alive;
+
+ /* public, readable */
+ HttpMsgParseState pstate; /* the current parsing state */
+
+ /* public, writable, but use httpReply* interfaces when possible */
+ HttpStatusLine sline;
+ HttpHeader header;
+ HttpBody body; /* for small constant memory-resident text bodies only */
+
+private:
+ static MemPool *Pool;
+};
+
+
#endif /* SQUID_HTTPREPLY_H */
/*
- * $Id: HttpRequest.h,v 1.2 2003/07/11 01:40:35 robertc Exp $
+ * $Id: HttpRequest.h,v 1.3 2003/07/14 14:15:56 robertc Exp $
*
*
* SQUID Web Proxy Cache http://www.squid-cache.org/
#define SQUID_HTTPREQUEST_H
#include "typedefs.h"
+#include "HttpHeader.h"
#include "client_side.h"
+/* Http Request */
+extern request_t *requestCreate(method_t, protocol_t, const char *urlpath);
+extern void requestDestroy(request_t *);
+extern request_t *requestLink(request_t *);
+extern void requestUnlink(request_t *);
+extern int httpRequestParseHeader(request_t * req, const char *parse_start);
+extern void httpRequestSwapOut(const request_t * req, StoreEntry * e);
+extern void httpRequestPack(const request_t * req, Packer * p);
+extern int httpRequestPrefixLen(const request_t * req);
+extern int httpRequestHdrAllowed(const HttpHeaderEntry * e, String * strConnection);
+extern int httpRequestHdrAllowedByName(http_hdr_type id);
+
+class HttpHdrRange;
+
class request_t
{
String extacl_log; /* String to be used for access.log purposes */
};
-/* Http Request */
-extern request_t *requestCreate(method_t, protocol_t, const char *urlpath);
-extern void requestDestroy(request_t *);
-extern request_t *requestLink(request_t *);
-extern void requestUnlink(request_t *);
-extern int httpRequestParseHeader(request_t * req, const char *parse_start);
-extern void httpRequestSwapOut(const request_t * req, StoreEntry * e);
-extern void httpRequestPack(const request_t * req, Packer * p);
-extern int httpRequestPrefixLen(const request_t * req);
-extern int httpRequestHdrAllowed(const HttpHeaderEntry * e, String * strConnection);
-extern int httpRequestHdrAllowedByName(http_hdr_type id);
-
#endif /* SQUID_HTTPREQUEST_H */
#
# Makefile for the Squid Object Cache server
#
-# $Id: Makefile.am,v 1.81 2003/07/07 22:44:28 robertc Exp $
+# $Id: Makefile.am,v 1.82 2003/07/14 14:15:56 robertc Exp $
#
# Uncomment and customize the following to suit your needs:
#
ElementList.h \
ESI.cc \
ESI.h \
+ ESIAssign.cc \
+ ESIAssign.h \
ESIAttempt.h \
ESIContext.cc \
ESIContext.h \
ESIExpatParser.cc \
ESIExpatParser.h \
ESIExpression.cc \
+ ESIExpression.h \
+ ESIInclude.cc \
+ ESIInclude.h \
ESILiteral.h \
ESIParser.cc \
ESIParser.h \
ESISegment.cc \
ESISegment.h \
ESISequence.cc \
- ESISequence.h
+ ESISequence.h \
+ ESIVar.h \
+ ESIVarState.cc \
+ ESIVarState.h
if USE_ESI
ESI_SOURCE = $(ESI_ALL_SOURCE)
else
endif
if ENABLE_HTCP
-HTCPSOURCE = htcp.cc
+HTCPSOURCE = htcp.cc htcp.h
endif
if MAKE_LEAKFINDER
/*
- * $Id: asn.cc,v 1.94 2003/07/14 08:21:56 robertc Exp $
+ * $Id: asn.cc,v 1.95 2003/07/14 14:15:58 robertc Exp $
*
* DEBUG: section 53 AS Number handling
* AUTHOR: Duane Wessels, Kostas Anagnostakis
#include "ACLSourceASN.h"
#include "ACLDestinationASN.h"
#include "ACLDestinationIP.h"
+#include "HttpReply.h"
#define WHOIS_PORT 43
#define AS_REQBUF_SZ 4096
/*
- * $Id: auth_basic.cc,v 1.26 2003/07/11 01:40:39 robertc Exp $
+ * $Id: auth_basic.cc,v 1.27 2003/07/14 14:16:21 robertc Exp $
*
* DEBUG: section 29 Authenticator
* AUTHOR: Duane Wessels
#include "auth_basic.h"
#include "authenticate.h"
#include "Store.h"
+#include "HttpReply.h"
static void
authenticateStateFree(AuthenticateStateData * r)
/*
- * $Id: auth_digest.cc,v 1.26 2003/07/14 08:22:01 robertc Exp $
+ * $Id: auth_digest.cc,v 1.27 2003/07/14 14:16:21 robertc Exp $
*
* DEBUG: section 29 Authenticator
* AUTHOR: Robert Collins
#include "authenticate.h"
#include "Store.h"
#include "HttpRequest.h"
+#include "HttpReply.h"
extern AUTHSSETUP authSchemeSetup_digest;
/*
- * $Id: auth_ntlm.cc,v 1.33 2003/07/14 08:21:58 robertc Exp $
+ * $Id: auth_ntlm.cc,v 1.34 2003/07/14 14:16:21 robertc Exp $
*
* DEBUG: section 29 NTLM Authenticator
* AUTHOR: Robert Collins
#include "authenticate.h"
#include "Store.h"
#include "client_side.h"
+#include "HttpReply.h"
#include "HttpRequest.h"
extern AUTHSSETUP authSchemeSetup_ntlm;
/*
- * $Id: authenticate.cc,v 1.59 2003/07/11 01:40:35 robertc Exp $
+ * $Id: authenticate.cc,v 1.60 2003/07/14 14:15:59 robertc Exp $
*
* DEBUG: section 29 Authenticator
* AUTHOR: Robert Collins
#include "authenticate.h"
#include "ACL.h"
#include "client_side.h"
+#include "HttpReply.h"
#include "HttpRequest.h"
CBDATA_TYPE(auth_user_ip_t);
/*
- * $Id: authenticate.h,v 1.10 2003/07/12 12:39:56 robertc Exp $
+ * $Id: authenticate.h,v 1.11 2003/07/14 14:15:59 robertc Exp $
*
*
* SQUID Web Proxy Cache http://www.squid-cache.org/
/* Per scheme request data ABC */
+class ConnStateData;
+
class AuthUserRequestState
{
/*
- * $Id: client_side_reply.cc,v 1.58 2003/07/11 01:40:36 robertc Exp $
+ * $Id: client_side_reply.cc,v 1.59 2003/07/14 14:15:59 robertc Exp $
*
* DEBUG: section 88 Client-side Reply Routines
* AUTHOR: Robert Collins (Originally Duane Wessels in client_side.c)
clientReplyContext::obeyConnectionHeader()
{
HttpHeader *hdr = &holdingReply->header;
-
- if (httpHeaderHas(hdr, HDR_CONNECTION)) {
- /* anything that matches Connection list member will be deleted */
- String strConnection = httpHeaderGetList(hdr, HDR_CONNECTION);
- const HttpHeaderEntry *e;
- HttpHeaderPos pos = HttpHeaderInitPos;
- /*
- * think: on-average-best nesting of the two loops (hdrEntry
- * and strListItem) @?@
- */
- /*
- * maybe we should delete standard stuff ("keep-alive","close")
- * from strConnection first?
- */
-
- while ((e = httpHeaderGetEntry(hdr, &pos))) {
- if (strListIsMember(&strConnection, e->name.buf(), ','))
- httpHeaderDelAt(hdr, pos);
- }
-
- httpHeaderDelById(hdr, HDR_CONNECTION);
- strConnection.clean();
- }
+ hdr->removeConnectionHeaderEntries();
}
/*
/*
- * $Id: client_side_request.h,v 1.14 2003/07/11 01:40:36 robertc Exp $
+ * $Id: client_side_request.h,v 1.15 2003/07/14 14:16:00 robertc Exp $
*
*
* SQUID Web Proxy Cache http://www.squid-cache.org/
typedef class ClientHttpRequest clientHttpRequest;
+class ConnStateData;
+
class ClientHttpRequest
{
/*
- * $Id: forward.cc,v 1.106 2003/07/11 01:40:36 robertc Exp $
+ * $Id: forward.cc,v 1.107 2003/07/14 14:16:00 robertc Exp $
*
* DEBUG: section 17 Request Forwarding
* AUTHOR: Duane Wessels
#include "MemObject.h"
#include "ACLChecklist.h"
#include "ACL.h"
+#include "HttpReply.h"
static PSC fwdStartComplete;
static void fwdDispatch(FwdState *);
/*
- * $Id: htcp.cc,v 1.52 2003/02/23 00:08:04 robertc Exp $
+ * $Id: htcp.cc,v 1.53 2003/07/14 14:16:00 robertc Exp $
*
* DEBUG: section 31 Hypertext Caching Protocol
* AUTHOR: Duane Wesssels
*/
#include "squid.h"
+#include "htcp.h"
#include "Store.h"
#include "StoreClient.h"
#include "HttpRequest.h"
/*
- * $Id: htcp.h,v 1.2 2003/01/23 00:37:22 robertc Exp $
+ * $Id: htcp.h,v 1.3 2003/07/14 14:16:00 robertc Exp $
*
*
* SQUID Web Proxy Cache http://www.squid-cache.org/
#ifndef SQUID_HTCP_H
#define SQUID_HTCP_H
+#if USE_HTCP
+#include "HttpHeader.h"
+
+struct _htcpReplyData
+{
+ int hit;
+ HttpHeader hdr;
+ u_int32_t msg_id;
+ double version;
+
+ struct
+ {
+ /* cache-to-origin */
+ double rtt;
+ int samp;
+ int hops;
+ }
+
+ cto;
+};
+
+#endif
+
#endif /* SQUID_HTCP_H */
/*
- * $Id: http.cc,v 1.418 2003/07/11 01:40:36 robertc Exp $
+ * $Id: http.cc,v 1.419 2003/07/14 14:16:00 robertc Exp $
*
* DEBUG: section 11 Hypertext Transfer Protocol (HTTP)
* AUTHOR: Harvest Derived
static void httpCacheNegatively(StoreEntry *);
static void httpMakePrivate(StoreEntry *);
static void httpMakePublic(StoreEntry *);
-static int httpCachableReply(HttpStateData *);
static void httpMaybeRemovePublic(StoreEntry *, http_status);
static void copyOneHeaderFromClientsideRequestToUpstreamRequest(const HttpHeaderEntry *e, String strConnection, request_t * request, request_t * orig_request,
HttpHeader * hdr_out, int we_do_ranges, http_state_flags);
return 1;
}
-static int
-httpCachableReply(HttpStateData * httpState)
+int
+HttpStateData::cacheableReply()
{
- HttpReply const *rep = httpState->entry->getReply();
+ HttpReply const *rep = entry->getReply();
HttpHeader const *hdr = &rep->header;
const int cc_mask = (rep->cache_control) ? rep->cache_control->mask : 0;
const char *v;
- if (httpState->surrogateNoStore)
+ if (surrogateNoStore)
return 0;
if (!cacheControlAllowsCaching(rep->cache_control))
return 0;
- if (!httpState->ignoreCacheControl) {
+ if (!ignoreCacheControl) {
if (EBIT_TEST(cc_mask, CC_PRIVATE))
return 0;
return 0;
}
- if (httpState->request->flags.auth) {
+ if (request->flags.auth) {
/*
* Responses to requests with authorization may be cached
* only if a Cache-Control: public reply header is present.
if (!strncasecmp(v, "multipart/x-mixed-replace", 25))
return 0;
- switch (httpState->entry->getReply()->sline.status) {
+ switch (entry->getReply()->sline.status) {
/* Responses that are cacheable */
case HTTP_OK:
* unless we know how to refresh it.
*/
- if (!refreshIsCachable(httpState->entry))
+ if (!refreshIsCachable(entry))
return 0;
/* don't cache objects from peers w/o LMT, Date, or Expires */
return 1;
else if (rep->last_modified > -1)
return 1;
- else if (!httpState->_peer)
+ else if (!_peer)
return 1;
/* @?@ (here and 302): invalid expires header compiles to squid_curtime */
return 0;
default: /* Unknown status code */
- debug (11,0)("httpCachableReply: unknown http status code in reply\n");
+ debug (11,0)("HttpStateData::cacheableReply: unknown http status code in reply\n");
return 0;
if (neighbors_do_private_keys)
httpMaybeRemovePublic(entry, entry->getReply()->sline.status);
- switch (httpCachableReply(this)) {
+ switch (cacheableReply()) {
case 1:
/*
- * $Id: http.h,v 1.7 2003/03/10 04:56:38 robertc Exp $
+ * $Id: http.h,v 1.8 2003/07/14 14:16:00 robertc Exp $
*
*
* SQUID Web Proxy Cache http://www.squid-cache.org/
void processReplyData(const char *, size_t);
IOCB readReply;
void maybeReadData();
+ int cacheableReply();
StoreEntry *entry;
request_t *request;
/*
- * $Id: mem.cc,v 1.80 2003/07/11 01:40:36 robertc Exp $
+ * $Id: mem.cc,v 1.81 2003/07/14 14:16:00 robertc Exp $
*
* DEBUG: section 13 High Level Memory Pool Management
* AUTHOR: Harvest Derived
#include "Mem.h"
#include "memMeter.h"
#include "Store.h"
+#include "HttpRequest.h"
/* module globals */
/*
- * $Id: neighbors.cc,v 1.319 2003/03/02 23:13:49 hno Exp $
+ * $Id: neighbors.cc,v 1.320 2003/07/14 14:16:00 robertc Exp $
*
* DEBUG: section 15 Neighbor Routines
* AUTHOR: Harvest Derived
#include "HttpRequest.h"
#include "MemObject.h"
#include "ACLChecklist.h"
+#include "htcp.h"
/* count mcast group peers every 15 minutes */
#define MCAST_COUNT_RATE 900
/*
- * $Id: peer_select.cc,v 1.129 2003/02/21 22:50:10 robertc Exp $
+ * $Id: peer_select.cc,v 1.130 2003/07/14 14:16:00 robertc Exp $
*
* DEBUG: section 44 Peer Selection Algorithm
* AUTHOR: Duane Wessels
#include "ICP.h"
#include "HttpRequest.h"
#include "ACLChecklist.h"
+#include "htcp.h"
const char *hier_strings[] =
{
/*
- * $Id: protos.h,v 1.481 2003/07/06 21:50:56 hno Exp $
+ * $Id: protos.h,v 1.482 2003/07/14 14:16:01 robertc Exp $
*
*
* SQUID Web Proxy Cache http://www.squid-cache.org/
SQUIDCEXTERN void httpHeaderClean(HttpHeader * hdr);
/* append/update */
SQUIDCEXTERN void httpHeaderAppend(HttpHeader * dest, const HttpHeader * src);
-SQUIDCEXTERN void httpHeaderUpdate(HttpHeader * old, const HttpHeader * fresh, const HttpHeaderMask * denied_mask);
/* parse/pack */
SQUIDCEXTERN int httpHeaderParse(HttpHeader * hdr, const char *header_start, const char *header_end);
SQUIDCEXTERN void httpHeaderPackInto(const HttpHeader * hdr, Packer * p);
SQUIDCEXTERN void httpHeaderPutStr(HttpHeader * hdr, http_hdr_type type, const char *str);
SQUIDCEXTERN void httpHeaderPutAuth(HttpHeader * hdr, const char *auth_scheme, const char *realm);
SQUIDCEXTERN void httpHeaderPutCc(HttpHeader * hdr, const HttpHdrCc * cc);
+
+class HttpHdrContRange;
SQUIDCEXTERN void httpHeaderPutContRange(HttpHeader * hdr, const HttpHdrContRange * cr);
+
+class HttpHdrRange;
SQUIDCEXTERN void httpHeaderPutRange(HttpHeader * hdr, const HttpHdrRange * range);
SQUIDCEXTERN void httpHeaderPutExt(HttpHeader * hdr, const char *name, const char *value);
SQUIDCEXTERN int httpHeaderGetInt(const HttpHeader * hdr, http_hdr_type id);
SQUIDCEXTERN void redirectInit(void);
SQUIDCEXTERN void redirectShutdown(void);
-SQUIDCEXTERN void refreshAddToList(const char *, int, time_t, int, time_t);
-SQUIDCEXTERN int refreshIsCachable(const StoreEntry *);
-SQUIDCEXTERN int refreshCheckHTTP(const StoreEntry *, request_t *);
-SQUIDCEXTERN int refreshCheckICP(const StoreEntry *, request_t *);
-SQUIDCEXTERN int refreshCheckHTCP(const StoreEntry *, request_t *);
-SQUIDCEXTERN int refreshCheckDigest(const StoreEntry *, time_t delta);
-SQUIDCEXTERN time_t getMaxAge(const char *url);
-SQUIDCEXTERN void refreshInit(void);
-
-SQUIDCEXTERN void serverConnectionsClose(void);
-SQUIDCEXTERN void shut_down(int);
-
-
-SQUIDCEXTERN void start_announce(void *unused);
-SQUIDCEXTERN void waisStart(FwdState *);
-
extern void refreshAddToList(const char *, int, time_t, int, time_t);
extern int refreshIsCachable(const StoreEntry *);
extern int refreshCheckHTTP(const StoreEntry *, request_t *);
/*
- * $Id: redirect.cc,v 1.100 2003/07/11 01:40:36 robertc Exp $
+ * $Id: redirect.cc,v 1.101 2003/07/14 14:16:02 robertc Exp $
*
* DEBUG: section 61 Redirector
* AUTHOR: Duane Wessels
if (Config.accessList.redirector) {
ACLChecklist ch;
- ch.src_addr = http->getConn()->peer.sin_addr;
- ch.my_addr = http->getConn()->me.sin_addr;
- ch.my_port = ntohs(http->getConn()->me.sin_port);
+
+ if (conn.getRaw() != NULL) {
+ ch.src_addr = conn->peer.sin_addr;
+ ch.my_addr = conn->me.sin_addr;
+ ch.my_port = ntohs(conn->me.sin_port);
+ }
+
ch.request = requestLink(http->request);
if (!aclCheckFast(Config.accessList.redirector, &ch)) {
r = cbdataAlloc(redirectStateData);
r->orig_url = xstrdup(http->uri);
- r->client_addr = conn->log_addr;
+ r->client_addr = conn.getRaw() != NULL ? conn->log_addr : no_addr;
if (http->request->auth_user_request)
r->client_ident = authenticateUserRequestUsername(http->request->auth_user_request);
- else if (conn->rfc931[0]) {
+ else if (conn.getRaw() != NULL && conn->rfc931[0]) {
r->client_ident = conn->rfc931;
} else {
r->client_ident = dash_str;
/*
- * $Id: refresh.cc,v 1.61 2003/07/11 01:40:37 robertc Exp $
+ * $Id: refresh.cc,v 1.62 2003/07/14 14:16:02 robertc Exp $
*
* DEBUG: section 22 Refresh Calculation
* AUTHOR: Harvest Derived
#include "Store.h"
#include "MemObject.h"
#include "HttpRequest.h"
+#include "HttpReply.h"
typedef enum {
rcHTTP,
* 60 seconds delta, to avoid objects which expire almost
* immediately, and which can't be refreshed.
*/
- int reason = refreshCheck(entry, NULL, 60);
+ /* For ESI, we use a delta of 0, as ESI objects typically can be
+ * refreshed, but the expiry may be low to enforce regular
+ * checks
+ */
+ int reason = refreshCheck(entry, NULL, ESI ? 0 : 60);
refreshCounts[rcStore].total++;
refreshCounts[rcStore].status[reason]++;
/*
- * $Id: store_client.cc,v 1.130 2003/07/11 04:02:01 robertc Exp $
+ * $Id: store_client.cc,v 1.131 2003/07/14 14:16:02 robertc Exp $
*
* DEBUG: section 90 Storage Manager Client-Side Interface
* AUTHOR: Duane Wessels
}
#endif
+#include "HttpRequest.h"
/* add client with fd to client list */
store_client *
/*
- * $Id: store_log.cc,v 1.27 2003/02/21 22:50:12 robertc Exp $
+ * $Id: store_log.cc,v 1.28 2003/07/14 14:16:02 robertc Exp $
*
* DEBUG: section 20 Storage Manager Logging Functions
* AUTHOR: Duane Wessels
#include "squid.h"
#include "Store.h"
#include "MemObject.h"
+#include "HttpReply.h"
static const char *storeLogTags[] =
{
/*
- * $Id: structs.h,v 1.471 2003/07/11 01:40:37 robertc Exp $
+ * $Id: structs.h,v 1.472 2003/07/14 14:16:02 robertc Exp $
*
*
* SQUID Web Proxy Cache http://www.squid-cache.org/
static MemPool *Pool;
};
-struct _HttpHeader
-{
- /* protected, do not use these, use interface functions instead */
- Array entries; /* parsed fields in raw format */
- HttpHeaderMask mask; /* bit set <=> entry present */
- http_hdr_owner_type owner; /* request or reply */
- int len; /* length when packed, not counting terminating '\0' */
-};
-
/* http surogate control header field */
struct _HttpHdrScTarget
dlink_list targets;
};
-/* Sync changes here with HttpReply.c */
-
-class HttpHdrContRange;
-
-class HttpReply
-{
-
-public:
- void *operator new (size_t);
- void operator delete (void *);
- /* unsupported, writable, may disappear/change in the future */
- int hdr_sz; /* sums _stored_ status-line, headers, and <CRLF> */
-
- /* public, readable; never update these or their .hdr equivalents directly */
- int content_length;
- time_t date;
- time_t last_modified;
- time_t expires;
- String content_type;
- HttpHdrCc *cache_control;
- HttpHdrSc *surrogate_control;
- HttpHdrContRange *content_range;
- short int keep_alive;
-
- /* public, readable */
- HttpMsgParseState pstate; /* the current parsing state */
-
- /* public, writable, but use httpReply* interfaces when possible */
- HttpStatusLine sline;
- HttpHeader header;
- HttpBody body; /* for small constant memory-resident text bodies only */
-
-private:
- static MemPool *Pool;
-};
-
struct _http_state_flags
{
void (*Done) (RemovalPurgeWalker * walker);
};
-/* To hard to pull this into another file just yet.
- * SO, we stop globals.c seeing it
- */
-#ifdef __cplusplus
-
struct request_flags
{
request_flags():range(0),nocache(0),ims(0),auth(0),cachable(0),hierarchical(0),loopdetect(0),proxy_keepalive(0),proxying(0),refresh(0),redirected(0),need_validation(0),accelerated(0),transparent(0),internal(0),internalclient(0),body_sent(0),destinationIPLookedUp_(0)
struct _link_list *next;
};
-class HttpHdrRange;
-
-class ConnStateData;
-
-class request_t;
-#endif
-
struct _cachemgr_passwd
{
char *passwd;
flags;
};
-#if USE_HTCP
-
-struct _htcpReplyData
-{
- int hit;
- HttpHeader hdr;
- u_int32_t msg_id;
- double version;
-
- struct
- {
- /* cache-to-origin */
- double rtt;
- int samp;
- int hops;
- }
-
- cto;
-};
-
-#endif
-
struct _helper_request
{
/*
- * $Id: typedefs.h,v 1.164 2003/07/12 12:39:56 robertc Exp $
+ * $Id: typedefs.h,v 1.165 2003/07/14 14:16:02 robertc Exp $
*
*
* SQUID Web Proxy Cache http://www.squid-cache.org/
class HttpHeaderFieldInfo;
-typedef struct _HttpHeader HttpHeader;
+class HttpHeader;
typedef struct _HttpHdrCc HttpHdrCc;
/*
- * $Id: whois.cc,v 1.25 2003/07/11 01:40:37 robertc Exp $
+ * $Id: whois.cc,v 1.26 2003/07/14 14:16:02 robertc Exp $
*
* DEBUG: section 75 WHOIS protocol
* AUTHOR: Duane Wessels, Kostas Anagnostakis
#include "squid.h"
#include "Store.h"
#include "HttpReply.h"
+#include "HttpRequest.h"
#include "comm.h"
#include "HttpRequest.h"
--- /dev/null
+
+/*
+ * $Id: ESIExpressions.cc,v 1.1 2003/07/14 14:16:12 robertc Exp $
+ *
+ * DEBUG: section 86 ESI Expressions
+ * AUTHOR: Robert Collins
+ *
+ * SQUID Web Proxy Cache http://www.squid-cache.org/
+ * ----------------------------------------------------------
+ *
+ * Squid is the result of efforts by numerous individuals from
+ * the Internet community; see the CONTRIBUTORS file for full
+ * details. Many organizations have provided support for Squid's
+ * development; see the SPONSORS file for full details. Squid is
+ * Copyrighted (C) 2001 by the Regents of the University of
+ * California; see the COPYRIGHT file for full details. Squid
+ * incorporates software developed and/or copyrighted by other
+ * sources; see the CREDITS file for full details.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ */
+
+#include "squid.h"
+#include "ESIExpression.h"
+
+int
+main ()
+{
+ char const *expressions[] = {
+ "!(1==1)", "!(1!=1)", "1!=1", "!1==1", "1==1",
+ "1 <=1","2<=1", "1 < 1", "1 < 2", "-1 < 1","!-1<1",
+ "1>2","2>1","2>=2", "2>3", "1==1&1==1","1==1&1==0",
+ "!('a'<='c')",
+ "(1==1)|('abc'=='def')",
+ "(4!=5)&(4==5)",
+ "(1==1)|(2==3)&(3==4)", /* should be true because of precedence */
+ "(1 & 4)",
+ "(\"abc\" | \"edf\")", "1==1==1",
+ "!('')",
+ /* End of array */""
+ };
+
+ int results[] = {0, 1, 0, 0, 1,
+ 1, 0, 0, 1, 1,
+ 0, 0, 1, 1, 0,
+ 1, 0, 0, 1, 0,
+ 1, 0, 0, 0, 0,
+ 1, 0};
+
+ int i = 0;
+
+ while (strlen (expressions[i])) {
+ int result = ESIExpression::Evaluate (expressions[i]);
+#if VERBOSEDEBUG
+
+ printf("Expr '%s' = '%s' (expected %s)\n", expressions[i],
+ result ? "true" : "false",
+ results[i] ? "true" : "false");
+#endif
+
+ if (result != results[i])
+ return 1;
+
+ ++i;
+ }
+
+ return 0;
+}
#
# Makefile for the Squid Object Cache server
#
-# $Id: Makefile.am,v 1.13 2003/07/14 10:36:44 robertc Exp $
+# $Id: Makefile.am,v 1.14 2003/07/14 14:16:12 robertc Exp $
#
AUTOMAKE_OPTIONS = subdir-objects
MemPoolTest\
mem_node_test\
mem_hdr_test\
- http_range_test
+ http_range_test \
+ ESIExpressions
## Sort by alpha - any build failures are significant.
check_PROGRAMS= debug \
+ ESIExpressions \
http_range_test \
MemPoolTest\
mem_node_test\
syntheticoperators
LDADD = -L$(top_builddir)/lib -lmiscutil
-debug_SOURCES = debug.cc test_tools.cc
-debug_LDADD = $(top_builddir)/src/globals.o \
- $(LDADD)
+DEBUG_SOURCE = test_tools.cc
+DEBUG_OBJECTS = $(top_builddir)/src/globals.o
+debug_SOURCES = debug.cc $(DEBUG_SOURCE)
+debug_LDADD = $(DEBUG_OBJECTS) $(LDADD)
+ESIExpressions_SOURCES = ESIExpressions.cc $(DEBUG_SOURCE)
+ESIExpressions_LDADD = $(top_builddir)/src/ESIExpression.o \
+ $(DEBUG_OBJECTS) $(LDADD)
mem_node_test_SOURCES = mem_node_test.cc
mem_node_test_LDADD = $(top_builddir)/src/mem_node.o $(LDADD)
-mem_hdr_test_SOURCES = mem_hdr_test.cc test_tools.cc
+mem_hdr_test_SOURCES = mem_hdr_test.cc $(DEBUG_SOURCE)
mem_hdr_test_LDADD = $(top_builddir)/src/stmem.o \
- $(top_builddir)/src/globals.o \
+ $(DEBUG_OBJECTS) \
$(top_builddir)/src/mem_node.o $(LDADD)
MemPoolTest_SOURCES = MemPoolTest.cc
refcount_SOURCES = refcount.cc
-http_range_test_SOURCES = http_range_test.cc test_tools.cc
+http_range_test_SOURCES = http_range_test.cc $(DEBUG_SOURCE)
http_range_test_LDADD = $(top_builddir)/src/HttpHdrRange.o \
$(top_builddir)/src/HttpHeaderTools.o \
$(top_builddir)/src/MemBuf.o \
$(top_builddir)/src/Packer.o \
$(top_builddir)/src/String.o \
$(top_builddir)/src/mem.o \
- $(top_builddir)/src/globals.o \
- $(LDADD)
+ $(DEBUG_OBJECTS) $(LDADD)
splay_SOURCES = splay.cc
-StackTest_SOURCES = StackTest.cc test_tools.cc
+StackTest_SOURCES = StackTest.cc $(DEBUG_SOURCE)
-syntheticoperators_SOURCES = syntheticoperators.cc test_tools.cc
+syntheticoperators_SOURCES = syntheticoperators.cc $(DEBUG_SOURCE)
rfc1738_SOURCES = rfc1738.cc