ESI feature has a number of bugs and security vulnerabilities.
It is also rarely used and a survey of active community members
has not revealed a need to keep maintaining this code.
==============================================================================
-src/esi/Libxml2Parser.cc,
-src/esi/Libxml2Parser.h:
-
- * The ESI Libxml2 parser is Copyright (c) 2004 by Joachim Bauch
- * http://www.joachim-bauch.de
- * mail@joachim-bauch.de
-
-==============================================================================
-
src/external_acl.c:
Copyright (C) 2002 MARA Systems AB, Sweden <info@marasystems.com>
dnl disable generic/common adaptation support by default
squid_opt_use_adaptation=no
-AH_TEMPLATE([USE_SQUID_ESI],[whether to enable ESI processing])
-AC_ARG_ENABLE(esi,[
- AS_HELP_STRING([--enable-esi],
- [Enable ESI for accelerators. ESI requires expat or xml2 library.
- Enabling ESI will cause squid reverse proxies to be capable
- of the Edge Acceleration Specification (www.esi.org).])
-],[
- SQUID_DEFINE_BOOL(USE_SQUID_ESI,$enable_esi)
-])
-AC_MSG_NOTICE([Enable ESI processor: ${enable_esi:=no (auto)}])
-
-# ESI support libraries: expat
-SQUID_AUTO_LIB(expat,[ESI expat library],[LIBEXPAT])
-SQUID_CHECK_LIB_WORKS(expat,[
- PKG_CHECK_MODULES([LIBEXPAT],[expat],[:],[:])
- CPPFLAGS="$LIBEXPAT_CFLAGS $CPPFLAGS"
- AC_CHECK_HEADERS(expat.h)
-])
-
-# ESI support libraries: xml2
-SQUID_AUTO_LIB(xml2,[ESI xml2 library],[LIBXML2])
-SQUID_CHECK_LIB_WORKS(xml2,[
- PKG_CHECK_MODULES([LIBXML2],[libxml-2.0],[:],[:])
- CPPFLAGS="$LIBXML2_CFLAGS $CPPFLAGS"
- AC_CHECK_HEADERS(libxml/parser.h libxml/HTMLparser.h libxml/HTMLtree.h)
-])
-
-AS_IF([test "x$enable_esi" = "xyes" -a "x$LIBXML2_LIBS" = "x" -a "x$LIBEXPAT_LIBS" = "x"],[
- AC_MSG_ERROR([ESI processor requires libxml2 or libexpat])
-])
-AM_CONDITIONAL(ENABLE_ESI,[test "x$enable_esi" = "xyes"])
-
AC_ARG_ENABLE(icap-client,
AS_HELP_STRING([--disable-icap-client],[Disable the ICAP client.]),[
SQUID_YESNO([$enableval],[--enable-icap-client])
errors/Makefile
icons/Makefile
lib/Makefile
- lib/libTrie/Makefile
- lib/libTrie/test/Makefile
lib/ntlmauth/Makefile
lib/rfcnb/Makefile
lib/smblib/Makefile
src/DiskIO/IpcIo/Makefile
src/DiskIO/Mmapped/Makefile
src/error/Makefile
- src/esi/Makefile
src/eui/Makefile
src/format/Makefile
src/fs/Makefile
see (http://squid.nlanr.net/Squid/urn-support.html) URN Support in Squid
.
-\section ESI ESI
-\par
- ESI is an implementation of Edge Side Includes (http://www.esi.org).
- ESI is implemented as a client side stream and a small
- modification to client_side_reply.c to check whether
- ESI should be inserted into the reply stream or not.
-
*/
section 83 TLS session management
section 84 Helper process maintenance
section 85 Client-side Request Routines
-section 86 ESI Expressions
-section 86 ESI processing
section 87 Client-side Stream routines.
section 88 Client-side Reply Routines
section 89 EUI-48 Lookup
<sect1>Removed directives<label id="removeddirectives">
<p>
<descrip>
+ <tag>esi_parser</tag>
+ <p>Edge Side Includes (ESI) protocol is no longer supported natively.
+
<tag>mcast_miss_addr</tag>
<p>The corresponding code has not built for many years, indicating that the
feature is unused.
<sect1>Changes to existing options<label id="modifiedoptions">
<p>
<descrip>
- <tag>--disable-esi</tag>
- <p>The ESI feature is now disabled by default.
- Use <em>--enable-esi</em> if needed.
+ <p>No build options have changed behaviour in this version.
</descrip>
</p>
<tag>--enable-cachemgr-hostname=</tag>
<p>The <em>cachemgr.cgi</em> tool this option relates to has been removed.
+ <tag>--enable-esi</tag>
+ <p>Edge Side Includes (ESI) protocol is no longer supported natively.
+
+ <tag>--without-expat</tag>
+ <p>The ESI feature using libexpat has been removed.
+
<tag>--without-gnugss</tag>
<p>Renamed to <em>--without-gss</em>.
+ <tag>--without-xml2</tag>
+ <p>The ESI feature using libxml2 has been removed.
+
<tag>CPPFLAGS=-DHEADERS_LOG</tag>
<p>The code enabled by this preprocessor macro has not built for many
years, indicating that the feature is unused.
SUBDIRS=
EXTRA_DIST=
-if ENABLE_ESI
-SUBDIRS += libTrie
-endif
if ENABLE_SNMP
SUBDIRS += snmplib
endif
+++ /dev/null
-## Copyright (C) 1996-2023 The Squid Software Foundation and contributors
-##
-## Squid software is distributed under GPLv2+ license and includes
-## contributions from numerous individuals and organizations.
-## Please see the COPYING and CONTRIBUTORS files for details.
-##
-
-include $(top_srcdir)/src/Common.am
-
-SUBDIRS = . test
-
-noinst_LIBRARIES = libTrie.a
-
-noinst_HEADERS = Trie.h TrieNode.h TrieCharTransform.h
-
-libTrie_a_SOURCES = \
- Trie.cc \
- Trie.h \
- TrieCharTransform.h \
- TrieNode.cc \
- TrieNode.h
+++ /dev/null
-/*
- * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
- *
- * Squid software is distributed under GPLv2+ license and includes
- * contributions from numerous individuals and organizations.
- * Please see the COPYING and CONTRIBUTORS files for details.
- */
-
-#include "squid.h"
-#include "Trie.h"
-#include "TrieCharTransform.h"
-#include "TrieNode.h"
-
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
-Trie::Trie(TrieCharTransform *aTransform) : head(nullptr), transform(aTransform)
-{}
-
-Trie::~Trie()
-{
- delete head;
- delete transform;
-}
-
-bool
-Trie::add(char const *aString, size_t theLength, void *privatedata)
-{
- if (!privatedata)
- return false;
-
- if (head) {
- if (find(aString, theLength))
- return false;
-
- return head->add(aString, theLength, privatedata, transform);
- }
-
- head = new TrieNode;
-
- return head->add(aString, theLength, privatedata, transform);
-}
-
+++ /dev/null
-/*
- * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
- *
- * Squid software is distributed under GPLv2+ license and includes
- * contributions from numerous individuals and organizations.
- * Please see the COPYING and CONTRIBUTORS files for details.
- */
-
-#ifndef SQUID_LIB_LIBTRIE_TRIE_H
-#define SQUID_LIB_LIBTRIE_TRIE_H
-
-#include "TrieNode.h"
-#if HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-
-class TrieCharTransform;
-
-/* TODO: parameterize this to be more generic -
-* i.e. M-ary internal node sizes etc
-*/
-
-class Trie
-{
-
-public:
- Trie(TrieCharTransform *aTransform = nullptr);
- ~Trie();
- Trie (Trie const &);
- Trie &operator= (Trie const &);
-
- /* Find an exact match in the trie.
- * If found, return the private data.
- * If not found, return NULL.
- */
- inline void *find (char const *, size_t);
- /* find any element of the trie in the buffer from the
- * beginning thereof
- */
- inline void *findPrefix (char const *, size_t);
-
- /* Add a string.
- * returns false if the string is already
- * present or cannot be added.
- */
-
- bool add(char const *, size_t, void *);
-
-private:
- TrieNode *head;
-
- /* transfor each 8 bits in the element */
- TrieCharTransform *transform;
-};
-
-void *
-Trie::find (char const *aString, size_t theLength)
-{
- if (head)
- return head->find (aString, theLength, transform, false);
-
- return nullptr;
-}
-
-void *
-Trie::findPrefix (char const *aString, size_t theLength)
-{
- if (head)
- return head->find (aString, theLength, transform, true);
-
- return nullptr;
-}
-
-#endif /* SQUID_LIB_LIBTRIE_TRIE_H */
-
+++ /dev/null
-/*
- * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
- *
- * Squid software is distributed under GPLv2+ license and includes
- * contributions from numerous individuals and organizations.
- * Please see the COPYING and CONTRIBUTORS files for details.
- */
-
-#ifndef SQUID_LIB_LIBTRIE_TRIECHARTRANSFORM_H
-#define SQUID_LIB_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
- */
-
-/* C bindings */
-#ifndef __cplusplus
-
-/* C++ bindings */
-#else
-#include <cctype>
-#include <sys/types.h>
-#include <utility>
-
-/* TODO: parameterize this to be more generic -
-* i.e. M-ary internal node sizes etc
-*/
-
-class TrieCharTransform
-{
-
-public:
- virtual ~TrieCharTransform() {}
-
- virtual char operator () (char const) const = 0;
-};
-
-class TrieCaseless : public TrieCharTransform
-{
- char operator () (char const aChar) const override {return tolower(aChar);}
-};
-
-#endif /* __cplusplus */
-
-#endif /* SQUID_LIB_LIBTRIE_TRIECHARTRANSFORM_H */
-
+++ /dev/null
-/*
- * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
- *
- * Squid software is distributed under GPLv2+ license and includes
- * contributions from numerous individuals and organizations.
- * Please see the COPYING and CONTRIBUTORS files for details.
- */
-
-#include "squid.h"
-#include "TrieCharTransform.h"
-#include "TrieNode.h"
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
-TrieNode::TrieNode() : _privateData(nullptr)
-{
- for (int i = 0; i < 256; ++i)
- internal[i] = nullptr;
-}
-
-TrieNode::~TrieNode()
-{
- for (int i = 0; i < 256; ++i)
- delete internal[i];
-}
-
-/* as for find */
-bool
-TrieNode::add(char const *aString, size_t theLength, void *privatedata, TrieCharTransform *transform)
-{
- /* We trust that privatedata and existent keys have already been checked */
-
- if (theLength) {
- const unsigned char index = transform ? (*transform)(*aString): *aString;
-
- if (!internal[index])
- internal[index] = new TrieNode;
-
- return internal[index]->add(aString + 1, theLength - 1, privatedata, transform);
- } else {
- /* terminal node */
-
- if (_privateData)
- return false;
-
- _privateData = privatedata;
-
- return true;
- }
-}
-
+++ /dev/null
-/*
- * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
- *
- * Squid software is distributed under GPLv2+ license and includes
- * contributions from numerous individuals and organizations.
- * Please see the COPYING and CONTRIBUTORS files for details.
- */
-
-#ifndef SQUID_LIB_LIBTRIE_TRIENODE_H
-#define SQUID_LIB_LIBTRIE_TRIENODE_H
-
-#include "TrieCharTransform.h"
-
-#include <sys/types.h>
-#include <utility>
-
-/* TODO: parameterize this to be more generic -
-* i.e. M-ary internal node sizes etc
-*/
-
-class TrieNode
-{
-
-public:
- TrieNode();
- ~TrieNode();
- TrieNode(TrieNode const &);
- TrieNode &operator= (TrieNode const &);
-
- /* Find a string.
- * If found, return the private data.
- * If not found, return NULL.
- */
- inline void *find (char const *, size_t, TrieCharTransform *, bool const prefix) const;
-
- /* Add a string.
- * returns false if the string is already
- * present or can't be added.
- */
-
- bool add (char const *, size_t, void *, TrieCharTransform *);
-
-private:
- /* 256-way Trie */
- /* The char index into internal is an
- * 8-bit prefix to a string in the trie.
- * internal[0] is the terminal node for
- * a string and may not be used
- */
- TrieNode * internal[256];
-
- /* If a string ends here, non NULL */
- void *_privateData;
-};
-
-/* recursive. TODO? make iterative */
-void *
-TrieNode::find (char const *aString, size_t theLength, TrieCharTransform *transform, bool const prefix) const
-{
- if (theLength) {
- 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 nullptr;
- } else {
- /* terminal node */
- return _privateData;
- }
-}
-#endif /* SQUID_LIB_LIBTRIE_TRIENODE_H */
-
+++ /dev/null
-## Copyright (C) 1996-2023 The Squid Software Foundation and contributors
-##
-## Squid software is distributed under GPLv2+ license and includes
-## contributions from numerous individuals and organizations.
-## Please see the COPYING and CONTRIBUTORS files for details.
-##
-
-include $(top_srcdir)/src/Common.am
-
-AM_CPPFLAGS += -I$(top_srcdir)/include
-
-TESTS += trie
-
-check_PROGRAMS += trie
-
-trie_SOURCES = trie.cc
-trie_LDADD = $(top_builddir)/lib/libTrie/libTrie.a
+++ /dev/null
-/*
- * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
- *
- * Squid software is distributed under GPLv2+ license and includes
- * contributions from numerous individuals and organizations.
- * Please see the COPYING and CONTRIBUTORS files for details.
- */
-
-#include "squid.h"
-#include "libTrie/Trie.h"
-#include "libTrie/TrieCharTransform.h"
-
-#include <iostream>
-
-static bool
-CaseSensitiveCheck()
-{
- Trie aTrie;
-
- 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 << "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;
-}
-
-static 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, char **)
-{
- 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;
-}
-
EXCLUDE_PATTERNS = */CVS/* \
*/.git* \
- */lib/libTrie/* \
*/libltdl/* \
*/Programming-Guide/html/* \
*/Programming-guide/dyn/* \
USE_QOS_TOS \
USE_SELECT \
USE_SOLARIS_KRB5 \
- USE_SQUID_ESI \
USE_SQUID_EUI \
USE_SSL_CRTD \
USE_OPENSSL \
HttpHeader::addVia(const AnyP::ProtocolVersion &ver, const HttpHeader *from)
{
// TODO: do not add Via header for messages where Squid itself
- // generated the message (i.e., Downloader or ESI) there should be no Via header added at all.
+ // generated the message (i.e., Downloader) there should be no Via header added at all.
if (Config.onoff.via) {
SBuf buf;
SUBDIRS += adaptation
endif
-if ENABLE_ESI
-SUBDIRS += esi
-ESI_LIBS = \
- esi/libesi.la \
- $(top_builddir)/lib/libTrie/libTrie.a \
- $(LIBEXPAT_LIBS) \
- $(LIBXML2_LIBS)
-else
-ESI_LIBS =
-endif
-
DELAY_POOL_ALL_SOURCE = \
BandwidthBucket.cc \
BandwidthBucket.h \
$(CRYPTLIB) \
$(REGEXLIB) \
$(ADAPTATION_LIBS) \
- $(ESI_LIBS) \
html/libhtml.la \
$(SNMP_LIBS) \
mem/libmem.la \
format/libformat.la \
$(REPL_OBJS) \
$(ADAPTATION_LIBS) \
- $(ESI_LIBS) \
$(SSL_LIBS) \
ipc/libipc.la \
dns/libdns.la \
debug/libdebug.la \
$(REPL_OBJS) \
$(ADAPTATION_LIBS) \
- $(ESI_LIBS) \
$(top_builddir)/lib/libmisccontainers.la \
$(top_builddir)/lib/libmiscencoding.la \
$(top_builddir)/lib/libmiscutil.la \
format/libformat.la \
$(REPL_OBJS) \
$(ADAPTATION_LIBS) \
- $(ESI_LIBS) \
$(SSL_LIBS) \
anyp/libanyp.la \
ipc/libipc.la \
#include "StoreIOBuffer.h"
#include "StoreStats.h"
-#if USE_SQUID_ESI
-#include "esi/Element.h"
-#endif
-
#include <ostream>
class AsyncCall;
void *operator new(size_t byteCount);
void operator delete(void *address);
-#if USE_SQUID_ESI
- ESIElement::Pointer cachedESITree;
-#endif
int64_t objectLen() const { return mem().object_sz; }
int64_t contentLen() const { return objectLen() - mem().baseReply().hdr_sz; }
{"client", initClient},
{"peer-pool", initPeerPool},
{"certificate-fetching", initCertFetcher},
- {"esi", initEsi},
{"cache-digest", initCacheDigest},
{"server", initServer},
{"htcp", initHtcp},
initClient = 1 << 0, ///< HTTP or FTP client
initPeerPool = 1 << 1, ///< PeerPool manager
initCertFetcher = 1 << 2, ///< Missing intermediate certificates fetching code
- initEsi = 1 << 3, ///< ESI processing code
initCacheDigest = 1 << 4, ///< Cache Digest fetching code
initHtcp = 1<< 5, ///< HTCP client
initIcp = 1 << 6, ///< the ICP/neighbors subsystem
/// internally generated requests
static Initiators InternalInitiators() {
- return initPeerPool | initCertFetcher | initEsi | initCacheDigest | initIcp | initIcmp | initIpc | initAdaptation | initIcon | initPeerMcast;
+ return initPeerPool | initCertFetcher | initCacheDigest | initIcp | initIcmp | initIpc | initAdaptation | initIcon | initPeerMcast;
}
/// all initiators
#include "ssl/Config.h"
#include "ssl/support.h"
#endif
-#if USE_SQUID_ESI
-#include "esi/Parser.h"
-#endif
#if SQUID_SNMP
#include "snmp.h"
#endif
This option is not yet supported by Squid-3.
DOC_END
+# Options removed in 7.x
+NAME: esi_parser
+TYPE: obsolete
+DOC_START
+ Remove this line. Squid no longer supports this feature.
+DOC_END
+
+
# Options removed in 6.x
NAME: announce_file
TYPE: obsolete
# Matches transaction's initiator [fast]
#
# Supported initiators are:
- # esi: matches transactions fetching ESI resources
# certificate-fetching: matches transactions fetching
# a missing intermediate TLS certificate
# cache-digest: matches transactions fetching Cache Digests
as a forwarding attempt. Pure cache hits log zero, but cache hits
that triggered HTTP cache revalidation log the number of attempts
made when sending an internal revalidation request. DNS, ICMP,
- ICP, HTCP, ESI, ICAP, eCAP, helper, and other secondary requests
+ ICP, HTCP, ICAP, eCAP, helper, and other secondary requests
sent by Squid as a part of a master transaction do not increment
the counter logged for the received request.
In reverse proxy environments it might be desirable to honor
shorter object lifetimes. It is most likely better to make
your server return a meaningful Last-Modified header however.
-
- In ESI environments where page fragments often have short
- lifetimes, this will often be best set to 0.
DOC_END
NAME: store_avg_object_size
Set this to on to have squid behave as a remote surrogate.
DOC_END
-NAME: esi_parser
-IFDEF: USE_SQUID_ESI
-COMMENT: libxml2|expat
-TYPE: string
-LOC: ESIParser::Type
-DEFAULT: auto
-DEFAULT_DOC: Selects libxml2 if available at ./configure time or libexpat otherwise.
-DOC_START
- Selects the XML parsing library to use when interpreting responses with
- Edge Side Includes.
-
- To disable ESI handling completely, ./configure Squid with --disable-esi.
-DOC_END
-
COMMENT_START
DELAY POOL PARAMETERS
-----------------------------------------------------------------------------
define["USE_LOADABLE_MODULES"]="--enable-shared"
define["USE_OPENSSL"]="--with-openssl"
define["USE_QOS_TOS"]="--enable-zph-qos"
- define["USE_SQUID_ESI"]="--enable-esi"
define["USE_SQUID_EUI"]="--enable-eui"
define["USE_SSL_CRTD"]="--enable-ssl-crtd"
define["USE_UNLINKD"]="--enable-unlinkd"
#if USE_DELAY_POOLS
#include "DelayPools.h"
#endif
-#if USE_SQUID_ESI
-#include "esi/Esi.h"
-#endif
#include <memory>
(int) body_size << " bytes after " << reply->hdr_sz <<
" bytes of headers");
-#if USE_SQUID_ESI
-
- if (http->flags.accel && reply->sline.status() != Http::scForbidden &&
- !alwaysAllowResponse(reply->sline.status()) &&
- esiEnableProcessing(reply)) {
- debugs(88, 2, "Enabling ESI processing for " << http->uri);
- clientStreamInsertHead(&http->client_stream, esiStreamRead,
- esiProcessStream, esiStreamDetach, esiStreamStatus, nullptr);
- }
-
-#endif
-
if (http->request->method == Http::METHOD_HEAD) {
/* do not forward body for HEAD replies */
body_size = 0;
HttpHeader *req_hdr = &request->header;
ConnStateData *http_conn = http->getConn();
- /* Internal requests such as those from ESI includes may be without
- * a client connection
- */
+ // Internal requests may be without a client connection
if (!http_conn)
return;
+++ /dev/null
-/*
- * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
- *
- * Squid software is distributed under GPLv2+ license and includes
- * contributions from numerous individuals and organizations.
- * Please see the COPYING and CONTRIBUTORS files for details.
- */
-
-/* DEBUG: section 86 ESI processing */
-
-#include "squid.h"
-
-/* MS Visual Studio Projects are monolithic, so we need the following
- * #if to exclude the ESI code from compile process when not needed.
- */
-#if (USE_SQUID_ESI == 1)
-
-#include "esi/Assign.h"
-#include "esi/Context.h"
-#include "esi/Sequence.h"
-#include "HttpReply.h"
-
-ESIAssign::~ESIAssign()
-{
- if (value)
- delete value;
-}
-
-ESIAssign::ESIAssign (ESIAssign const &old) : parent (nullptr), varState (nullptr), name (old.name), value (old.value ? new ESIVariableExpression (*old.value): nullptr), variable (nullptr), unevaluatedVariable(old.unevaluatedVariable)
-{}
-
-ESIAssign::ESIAssign (esiTreeParentPtr aParent, int attrcount, char const **attr, ESIContext *aContext) : parent (aParent), varState (nullptr), name(), value (nullptr), variable (nullptr), 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 ... */
- debugs(86, 5, "ESIAssign::ESIAssign: Variable name '" << 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: */
- debugs(86, 5, "ESIAssign::ESIAssign: Unevaluated variable '" << 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 = nullptr;
-
- if (unevaluatedVariable.size()) {
- varState->feedData(unevaluatedVariable.rawBuf(), 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)
-{
- assert (varState);
-
- if (!value)
- evaluateVariable();
-
- if (!value)
- return ESI_PROCESS_COMPLETE;
-
- varState->addVariable (name.rawBuf(), name.size(), value);
-
- value = nullptr;
-
- debugs(86, 5, "ESIAssign: Processed " << 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()
-{
- cbdataReferenceDone(varState);
-
- if (parent.getRaw())
- parent = nullptr;
-}
-
-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 *, char const *) const
-{
- /* XXX: Implement evaluation of the expression */
- ESISegment::ListAppend (state.getOutput(), expression.rawBuf(), expression.size());
-}
-
-#endif /* USE_SQUID_ESI == 1 */
-
+++ /dev/null
-/*
- * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
- *
- * Squid software is distributed under GPLv2+ license and includes
- * contributions from numerous individuals and organizations.
- * Please see the COPYING and CONTRIBUTORS files for details.
- */
-
-/* DEBUG: section 86 ESI processing */
-
-#ifndef SQUID_SRC_ESI_ASSIGN_H
-#define SQUID_SRC_ESI_ASSIGN_H
-
-#include "esi/Element.h"
-#include "esi/VarState.h"
-#include "SquidString.h"
-
-/** This is a variable that is itself an expression */
-class ESIVariableExpression : public ESIVarState::Variable
-{
-public:
- ~ESIVariableExpression() override;
- ESIVariableExpression (String const &value);
- void eval (ESIVarState &state, char const *, char const *) const override;
-
-private:
- String expression;
-};
-
-class ESIContext;
-
-class ESIAssign : public ESIElement
-{
- MEMPROXY_CLASS(ESIAssign);
-
-public:
- ESIAssign (esiTreeParentPtr, int, const char **, ESIContext *);
- ESIAssign (ESIAssign const &);
- ESIAssign &operator=(ESIAssign const &);
- ~ESIAssign() override;
- esiProcessResult_t process (int dovars) override;
- void render(ESISegment::Pointer) override;
- bool addElement(ESIElement::Pointer) override;
- void provideData (ESISegment::Pointer data, ESIElement * source) override;
- Pointer makeCacheable() const override;
- Pointer makeUsable(esiTreeParentPtr, ESIVarState &) const override;
- void finish() override;
-
-private:
- void evaluateVariable();
- esiTreeParentPtr parent;
- ESIVarState *varState;
- String name;
- ESIVariableExpression * value;
- ESIElement::Pointer variable;
- String unevaluatedVariable;
-};
-
-#endif /* SQUID_SRC_ESI_ASSIGN_H */
-
+++ /dev/null
-/*
- * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
- *
- * Squid software is distributed under GPLv2+ license and includes
- * contributions from numerous individuals and organizations.
- * Please see the COPYING and CONTRIBUTORS files for details.
- */
-
-/* DEBUG: section 86 ESI processing */
-
-#ifndef SQUID_SRC_ESI_ATTEMPT_H
-#define SQUID_SRC_ESI_ATTEMPT_H
-
-#include "esi/Element.h"
-#include "esi/Sequence.h"
-
-struct esiAttempt : public esiSequence {
- esiAttempt(esiTreeParentPtr aParent) : esiSequence (aParent) {}
-};
-
-#endif /* SQUID_SRC_ESI_ATTEMPT_H */
-
+++ /dev/null
-/*
- * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
- *
- * Squid software is distributed under GPLv2+ license and includes
- * contributions from numerous individuals and organizations.
- * Please see the COPYING and CONTRIBUTORS files for details.
- */
-
-/* DEBUG: section 86 ESI processing */
-
-#include "squid.h"
-
-/* MS Visual Studio Projects are monolithic, so we need the following
- * #if to exclude the ESI code from compile process when not needed.
- */
-#if (USE_SQUID_ESI == 1)
-
-#include "client_side_request.h"
-#include "esi/Context.h"
-#include "http/Stream.h"
-#include "Store.h"
-
-void
-ESIContext::updateCachedAST()
-{
- assert (http);
- assert (http->storeEntry());
-
- if (hasCachedAST()) {
- debugs(86, 5, "ESIContext::updateCachedAST: not updating AST cache for entry " <<
- http->storeEntry() << " from ESI Context " << this <<
- " as there is already a cached AST.");
-
- return;
- }
-
- ESIElement::Pointer treeToCache = tree->makeCacheable();
- debugs(86, 5, "ESIContext::updateCachedAST: Updating AST cache for entry " <<
- http->storeEntry() << " with current value " <<
- http->storeEntry()->cachedESITree.getRaw() << " to new value " <<
- treeToCache.getRaw());
-
- if (http->storeEntry()->cachedESITree.getRaw())
- http->storeEntry()->cachedESITree->finish();
-
- http->storeEntry()->cachedESITree = treeToCache;
-
- treeToCache = nullptr;
-}
-
-bool
-ESIContext::hasCachedAST() const
-{
- assert (http);
- assert (http->storeEntry());
-
- if (http->storeEntry()->cachedESITree.getRaw()) {
- debugs(86, 5, "ESIContext::hasCachedAST: " << this <<
- " - Cached AST present in store entry " << http->storeEntry() << ".");
- return true;
- } else {
- debugs(86, 5, "ESIContext::hasCachedAST: " << this <<
- " - Cached AST not present in store entry " << http->storeEntry() << ".");
- return false;
- }
-}
-
-void
-ESIContext::getCachedAST()
-{
- if (cachedASTInUse)
- return;
-
- assert (hasCachedAST());
-
- assert (varState);
-
- parserState.popAll();
-
- tree = http->storeEntry()->cachedESITree->makeUsable (this, *varState);
-
- cachedASTInUse = true;
-}
-
-void
-ESIContext::setErrorMessage(char const *anError)
-{
- if (!errormessage)
- errormessage = xstrdup(anError);
-}
-
-#endif /* USE_SQUID_ESI == 1 */
-
+++ /dev/null
-/*
- * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
- *
- * Squid software is distributed under GPLv2+ license and includes
- * contributions from numerous individuals and organizations.
- * Please see the COPYING and CONTRIBUTORS files for details.
- */
-
-#ifndef SQUID_SRC_ESI_CONTEXT_H
-#define SQUID_SRC_ESI_CONTEXT_H
-
-#include "clientStream.h"
-#include "error/forward.h"
-#include "esi/Element.h"
-#include "esi/Esi.h"
-#include "esi/Parser.h"
-#include "http/forward.h"
-#include "http/StatusCode.h"
-#include "HttpReply.h"
-
-class ESIVarState;
-class ClientHttpRequest;
-
-/* ESIContext */
-
-class ESIContext : public esiTreeParent, public ESIParserClient
-{
- CBDATA_CLASS(ESIContext);
-
-public:
- typedef RefCount<ESIContext> Pointer;
- ESIContext() :
- thisNode(nullptr),
- http(nullptr),
- errorpage(ERR_NONE),
- errorstatus(Http::scNone),
- errormessage(nullptr),
- rep(nullptr),
- outbound_offset(0),
- readpos(0),
- pos(0),
- varState(nullptr),
- cachedASTInUse(false),
- reading_(true),
- processing(false) {
- memset(&flags, 0, sizeof(flags));
- }
-
- ~ESIContext() override;
-
- enum esiKick_t {
- ESI_KICK_FAILED,
- ESI_KICK_PENDING,
- ESI_KICK_SENT,
- ESI_KICK_INPROGRESS
- };
-
- /* when esi processing completes */
- void provideData(ESISegment::Pointer, ESIElement *source) override;
- void fail (ESIElement *source, char const*anError = nullptr) override;
- 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);
-
- void finishChildren ();
-
- clientStreamNode *thisNode; /* our stream node */
- /* the request we are processing. HMM: cbdataReferencing this will result
- * in a circular reference, so we don't. Note: we are automatically freed
- * when it is, so that's ok. */
- ClientHttpRequest *http;
-
- struct {
- unsigned int passthrough:1;
- unsigned int oktosend:1;
- unsigned int finished:1;
-
- /* an error has occurred, send full body replies
- * regardless. Note that we don't fail midstream
- * because we buffer until we can not fail
- */
- unsigned int error:1;
-
- unsigned int finishedtemplate:1; /* we've read the entire template */
- unsigned int clientwantsdata:1; /* we need to satisfy a read request */
- unsigned int kicked:1; /* note on reentering the kick routine */
- unsigned int detached:1; /* our downstream has detached */
- } flags;
-
- err_type errorpage; /* if we error what page to use */
- Http::StatusCode errorstatus; /* if we error, what code to return */
- char *errormessage; /* error to pass to error page */
- HttpReplyPointer rep; /* buffered until we pass data downstream */
- ESISegment::Pointer buffered; /* unprocessed data - for whatever reason */
- ESISegment::Pointer incoming;
- /* processed data we are waiting to send, or for
- * potential errors to be resolved
- */
- ESISegment::Pointer outbound;
- ESISegment::Pointer outboundtail; /* our write segment */
- /* the offset to the next character to send -
- * non zero if we haven't sent the entire segment
- * for some reason
- */
- size_t outbound_offset;
- int64_t readpos; /* the logical position we are reading from */
- int64_t pos; /* the logical position of outbound_offset in the data stream */
-
- class ParserState
- {
-
- public:
- ESIElement::Pointer stack[ESI_STACK_DEPTH_LIMIT]; /* a stack of esi elements that are open */
- int stackdepth; /* self explanatory */
- ESIParser::Pointer theParser;
- ESIElement::Pointer top();
- void init (ESIParserClient *);
- bool inited() const;
- ParserState();
- void freeResources();
- void popAll();
- unsigned int parsing:1; /* libexpat is not reentrant on the same context */
-
- private:
- bool inited_;
- }
- parserState; // TODO: refactor this to somewhere else
-
- ESIVarState *varState;
- ESIElement::Pointer tree;
-
- esiKick_t kick ();
- RefCount<ESIContext> cbdataLocker;
- bool failed() const {return flags.error != 0;}
-
- bool cachedASTInUse;
-
-private:
- void fail ();
- void freeResources();
- void fixupOutboundTail();
- void trimBlanks();
- size_t send ();
- bool reading_;
- void appendOutboundData(ESISegment::Pointer theData);
- esiProcessResult_t process ();
- void parse();
- void parseOneBuffer();
- void updateCachedAST();
- bool hasCachedAST() const;
- void getCachedAST();
- void start(const char *el, const char **attr, size_t attrCount) override;
- void end(const char *el) override;
- void parserDefault (const char *s, int len) override;
- void parserComment (const char *s) override;
- bool processing;
-};
-
-#endif /* SQUID_SRC_ESI_CONTEXT_H */
-
+++ /dev/null
-/*
- * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
- *
- * Squid software is distributed under GPLv2+ license and includes
- * contributions from numerous individuals and organizations.
- * Please see the COPYING and CONTRIBUTORS files for details.
- */
-
-#ifndef SQUID_SRC_ESI_ELEMENT_H
-#define SQUID_SRC_ESI_ELEMENT_H
-
-#include "base/RefCount.h"
-#include "debug/Stream.h"
-#include "esi/Segment.h"
-
-#include <vector>
-
-typedef enum {
- ESI_PROCESS_COMPLETE = 0,
- ESI_PROCESS_PENDING_WONTFAIL = 1,
- ESI_PROCESS_PENDING_MAYFAIL = 2,
- ESI_PROCESS_FAILED = 3
-} esiProcessResult_t;
-
-class ESIElement;
-
-struct esiTreeParent : public RefCountable {
- virtual void provideData (ESISegment::Pointer /* data */, ESIElement * /* source */ ) {
- /* make abstract when all functionality complete */
- assert (0);
- }
-
- virtual void fail(ESIElement * /* source */, char const * /* reason */ = nullptr) {}
-
- ~esiTreeParent() override {}
-};
-
-typedef RefCount<esiTreeParent> esiTreeParentPtr;
-
-class ESIVarState;
-
-class ESIElement : public esiTreeParent
-{
-
-public:
- typedef RefCount<ESIElement> Pointer;
-
- /* the types we have */
- enum ESIElementType_t {
- ESI_ELEMENT_NONE,
- ESI_ELEMENT_INCLUDE,
- ESI_ELEMENT_COMMENT,
- ESI_ELEMENT_REMOVE,
- ESI_ELEMENT_TRY,
- ESI_ELEMENT_ATTEMPT,
- ESI_ELEMENT_EXCEPT,
- ESI_ELEMENT_VARS,
- ESI_ELEMENT_CHOOSE,
- ESI_ELEMENT_WHEN,
- ESI_ELEMENT_OTHERWISE,
- ESI_ELEMENT_ASSIGN
- };
- static ESIElementType_t IdentifyElement (const char *);
- virtual bool addElement(ESIElement::Pointer) {
- /* Don't accept children */
- debugs(86,5, "ESIElement::addElement: Failed for " << this);
- return false;
- }
-
- virtual void render (ESISegment::Pointer) = 0;
- /* process this element */
- virtual esiProcessResult_t process(int) {
- debugs(86,5, "esiProcessComplete: Processed " << this);
- return ESI_PROCESS_COMPLETE;
- }
-
- virtual bool mayFail() const {
- return true;
- }
-
- virtual Pointer makeCacheable() const = 0;
- virtual Pointer makeUsable(esiTreeParentPtr, ESIVarState &) const = 0;
-
- /* The top level no longer needs this element */
- virtual void finish() = 0;
-};
-
-/// ESI protocol types and operators
-namespace Esi {
-
-/// an ordered set of ESI elements
-typedef std::vector<ESIElement::Pointer> Elements;
-
-} // namespace Esi
-
-/// Call finish() and set to nil the given element. Element may already be nil.
-/// When element is part of a set, use pos to indicate position/ID
-/// for debugging.
-extern void FinishAnElement(ESIElement::Pointer &, int pos = -1);
-
-// for all elements call finish() and set Pointer to nil
-extern void FinishAllElements(Esi::Elements &);
-
-#endif /* SQUID_SRC_ESI_ELEMENT_H */
-
+++ /dev/null
-/*
- * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
- *
- * Squid software is distributed under GPLv2+ license and includes
- * contributions from numerous individuals and organizations.
- * Please see the COPYING and CONTRIBUTORS files for details.
- */
-
-/* DEBUG: section 86 ESI processing */
-
-#include "squid.h"
-
-/* MS Visual Studio Projects are monolithic, so we need the following
- * #if to exclude the ESI code from compile process when not needed.
- */
-#if (USE_SQUID_ESI == 1)
-
-#include "client_side.h"
-#include "client_side_request.h"
-#include "clientStream.h"
-#include "comm/Connection.h"
-#include "errorpage.h"
-#include "esi/Assign.h"
-#include "esi/Attempt.h"
-#include "esi/Context.h"
-#include "esi/Element.h"
-#include "esi/Esi.h"
-#include "esi/Except.h"
-#include "esi/Expression.h"
-#include "esi/Segment.h"
-#include "esi/VarState.h"
-#include "FadingCounter.h"
-#include "fatal.h"
-#include "http/Stream.h"
-#include "HttpHdrSc.h"
-#include "HttpHdrScTarget.h"
-#include "HttpReply.h"
-#include "HttpRequest.h"
-#include "ip/Address.h"
-#include "log/forward.h"
-#include "MemBuf.h"
-#include "SquidConfig.h"
-
-/* quick reference on behaviour here.
- * The ESI specification 1.0 requires the ESI processor to be able to
- * return an error code at any point in the processing. To that end
- * we buffer the incoming esi body until we know we will be able to
- * satisfy the request. At that point we start streaming the queued
- * data downstream.
- *
- */
-
-class 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
- * parser, so LibXML is out.
- *
- * Interpreter methods:
- * Render: May only ever be called after Process returns PROCESS_COMPLETE.
- * Renders the resulting content into a ESISegment chain.
- * Process: returns the status of the node.
- * COMPLETE - processing is complete, rendering may staret
- * PENDING_WONTFAIL - process is incomplete, but the element *will*
- * be able to be rendered given time.
- * PENDING_MAYFAIL - processing is incomplete, and the element *may*
- * fail to be able to rendered.
- * FAILED - processing failed, return an error to the client.
- */
-
-/*
- * NOT TODO: esi:inline - out of scope.
- */
-
-/* make comparisons with refcount pointers easy */
-static bool
-operator == (ESIElement const *lhs, ESIElement::Pointer const &rhs)
-{
- return lhs == rhs.getRaw();
-}
-
-typedef ESIContext::esiKick_t esiKick_t;
-
-/* some core operators */
-
-class esiComment : public ESIElement
-{
- MEMPROXY_CLASS(esiComment);
-
-public:
- ~esiComment() override;
- esiComment();
- Pointer makeCacheable() const override;
- Pointer makeUsable(esiTreeParentPtr, ESIVarState &) const override;
-
- void render(ESISegment::Pointer) override;
- void finish() override;
-};
-
-#include "esi/Literal.h"
-
-#include "esi/Sequence.h"
-
-#include "esi/Include.h"
-
-/* esiRemove */
-
-class esiRemove : public ESIElement
-{
- MEMPROXY_CLASS(esiRemove);
-
-public:
- esiRemove() : ESIElement() {}
- ~esiRemove() override {}
-
- void render(ESISegment::Pointer) override;
- bool addElement (ESIElement::Pointer) override;
- Pointer makeCacheable() const override;
- Pointer makeUsable(esiTreeParentPtr, ESIVarState &) const override;
- void finish() override {}
-};
-
-class esiTry : public ESIElement
-{
- MEMPROXY_CLASS(esiTry);
-
-public:
- esiTry(esiTreeParentPtr aParent);
- ~esiTry() override;
-
- void render(ESISegment::Pointer) override;
- bool addElement (ESIElement::Pointer) override;
- void fail(ESIElement *, char const * = nullptr) override;
- esiProcessResult_t process (int dovars) override;
- void provideData (ESISegment::Pointer data, ESIElement * source) override;
- Pointer makeCacheable() const override;
- Pointer makeUsable(esiTreeParentPtr, ESIVarState &) const override;
-
- ESIElement::Pointer attempt;
- ESIElement::Pointer except;
-
- struct {
- unsigned int attemptok:1; /* the attempt branch process correctly */
- unsigned int exceptok:1; /* likewise */
- unsigned int attemptfailed:1; /* The attempt branch failed */
- unsigned int exceptfailed:1; /* the except branch failed */
- } flags;
- void finish() override;
-
-private:
- void notifyParent();
- esiTreeParentPtr parent;
- ESISegment::Pointer exceptbuffer;
- esiTry (esiTry const &);
- esiProcessResult_t bestAttemptRV() const;
-};
-
-#include "esi/Var.h"
-
-class esiChoose : public ESIElement
-{
- MEMPROXY_CLASS(esiChoose);
-
-public:
- esiChoose(const esiTreeParentPtr &);
- ~esiChoose() override;
-
- void render(ESISegment::Pointer) override;
- bool addElement (ESIElement::Pointer) override;
- void fail(ESIElement *, char const * = nullptr) override;
- esiProcessResult_t process (int dovars) override;
-
- void provideData (ESISegment::Pointer data, ESIElement *source) override;
- void makeCachableElements(esiChoose const &old);
- void makeUsableElements(esiChoose const &old, ESIVarState &);
- Pointer makeCacheable() const override;
- Pointer makeUsable(esiTreeParentPtr, ESIVarState &) const override;
- void NULLUnChosen();
-
- Esi::Elements elements;
- int chosenelement;
- ESIElement::Pointer otherwise;
- void finish() override;
-
-private:
- esiChoose(esiChoose const &);
- esiTreeParentPtr parent;
- void checkValidSource (ESIElement::Pointer source) const;
- void selectElement();
-};
-
-class esiWhen : public esiSequence
-{
- MEMPROXY_CLASS(esiWhen);
-
-public:
- esiWhen(esiTreeParentPtr aParent, int attributes, const char **attr, ESIVarState *);
- ~esiWhen() override;
- Pointer makeCacheable() const override;
- Pointer makeUsable(esiTreeParentPtr, ESIVarState &) const override;
-
- bool testsTrue() const { return testValue;}
-
- void setTestResult(bool aBool) {testValue = aBool;}
-
-private:
- esiWhen (esiWhen const &);
- bool testValue;
- char const *unevaluatedExpression;
- ESIVarState *varState;
- void evaluate();
-};
-
-struct esiOtherwise : public esiSequence {
- esiOtherwise(esiTreeParentPtr aParent) : esiSequence (aParent) {}
-};
-
-CBDATA_CLASS_INIT(ESIContext);
-
-void ESIContext::startRead()
-{
- assert (!reading_);
- reading_ = true;
-}
-
-void ESIContext::finishRead()
-{
- assert (reading_);
- reading_ = false;
-}
-
-bool ESIContext::reading() const
-{
- return reading_;
-}
-
-ESIStreamContext::ESIStreamContext() : finished(false), include (nullptr), localbuffer (new ESISegment), buffer (nullptr)
-{}
-
-/* Local functions */
-/* ESIContext */
-static ESIContext *ESIContextNew(HttpReply *, clientStreamNode *, ClientHttpRequest *);
-
-void
-ESIContext::setError()
-{
- errorpage = ERR_ESI;
- errorstatus = Http::scInternalServerError;
- flags.error = 1;
-}
-
-void
-ESIContext::appendOutboundData(ESISegment::Pointer theData)
-{
- if (!outbound.getRaw()) {
- outbound = theData;
- outboundtail = outbound;
- } else {
- assert (outboundtail->next.getRaw() == nullptr);
- outboundtail->next = theData;
- }
-
- fixupOutboundTail();
- debugs(86, 9, "ESIContext::appendOutboundData: outbound " << outbound.getRaw());
-}
-
-void
-ESIContext::provideData (ESISegment::Pointer theData, ESIElement * source)
-{
- debugs(86, 5, "ESIContext::provideData: " << this << " " << theData.getRaw() << " " << source);
- /* No callbacks permitted after finish() called on the tree */
- assert (tree.getRaw());
- assert (source == tree);
- appendOutboundData(theData);
- trimBlanks();
-
- if (!processing)
- send();
-}
-
-void
-ESIContext::fail(ESIElement *, char const *anError)
-{
- setError();
- setErrorMessage (anError);
- fail ();
- send ();
-}
-
-void
-ESIContext::fixupOutboundTail()
-{
- /* TODO: fixup thisNode outboundtail dross a little */
-
- if (outboundtail.getRaw())
- outboundtail = outboundtail->tail();
-}
-
-esiKick_t
-ESIContext::kick ()
-{
- if (flags.kicked) {
- debugs(86, 5, "esiKick: Re-entered whilst in progress");
- // return ESI_KICK_INPROGRESS;
- } else
- ++flags.kicked;
-
- if (flags.detached)
- /* we've been detached from - we can't do anything more */
- return ESI_KICK_FAILED;
-
- /* Something has occurred. Process any remaining nodes */
- if (!flags.finished)
- /* Process some of our data */
- switch (process ()) {
-
- case ESI_PROCESS_COMPLETE:
- debugs(86, 5, "esiKick: esiProcess OK");
- break;
-
- case ESI_PROCESS_PENDING_WONTFAIL:
- debugs(86, 5, "esiKick: esiProcess PENDING OK");
- break;
-
- case ESI_PROCESS_PENDING_MAYFAIL:
- debugs(86, 5, "esiKick: esiProcess PENDING UNKNOWN");
- break;
-
- case ESI_PROCESS_FAILED:
- debugs(86, 2, "esiKick: esiProcess " << this << " FAILED");
- /* this can not happen - processing can't fail until we have data,
- * and when we come here we have sent data to the client
- */
-
- if (pos == 0)
- fail ();
-
- --flags.kicked;
-
- return ESI_KICK_FAILED;
- }
-
- /* Render if we can to get maximal sent data */
- assert (tree.getRaw() || flags.error);
-
- if (!flags.finished && !outbound.getRaw()) {
- outboundtail = new ESISegment;
- outbound = outboundtail;
- }
-
- if (!flags.error && !flags.finished)
- tree->render(outboundtail);
-
- if (!flags.finished)
- fixupOutboundTail();
-
- /* Is there data to send? */
- if (send ()) {
- /* some data was sent. we're finished until the next read */
- --flags.kicked;
- return ESI_KICK_SENT;
- }
-
- --flags.kicked;
- /* nothing to send */
- return flags.error ? ESI_KICK_FAILED : ESI_KICK_PENDING;
-}
-
-/* request from downstream for more data
- */
-void
-esiStreamRead (clientStreamNode *thisNode, ClientHttpRequest *http)
-{
- clientStreamNode *next;
- /* Test preconditions */
- assert (thisNode != nullptr);
- assert (cbdataReferenceValid (thisNode));
- /* we are not in the chain until ESI is detected on a data callback */
- assert (thisNode->node.prev != nullptr);
- assert (thisNode->node.next != nullptr);
-
- ESIContext::Pointer context = dynamic_cast<ESIContext *>(thisNode->data.getRaw());
- assert (context.getRaw() != nullptr);
-
- if (context->flags.passthrough) {
- /* passthru mode - read into supplied buffers */
- next = thisNode->next();
- clientStreamRead (thisNode, http, next->readBuffer);
- return;
- }
-
- context->flags.clientwantsdata = 1;
- debugs(86, 5, "esiStreamRead: Client now wants data");
-
- /* Ok, not passing through */
-
- switch (context->kick ()) {
-
- case ESIContext::ESI_KICK_FAILED:
- /* this can not happen - processing can't fail until we have data,
- * and when we come here we have sent data to the client
- */
-
- case ESIContext::ESI_KICK_SENT:
-
- case ESIContext::ESI_KICK_INPROGRESS:
- return;
-
- case ESIContext::ESI_KICK_PENDING:
- break;
- }
-
- /* Nothing to send */
-
- if (context->flags.oktosend && (context->flags.finishedtemplate
- || context->cachedASTInUse) &&
- ! context->flags.finished) {
- /* we've started sending, finished reading, but not finished
- * processing. stop here, a callback will resume the stream
- * flow
- */
- debugs(86, 5, "esiStreamRead: Waiting for async resume of esi processing");
- return;
- }
-
- if (context->flags.oktosend && context->flags.finished && context->outbound.getRaw()) {
- debugs(86, 5, "all processing complete, but outbound data still buffered");
- assert (!context->flags.clientwantsdata);
- /* client MUST be processing the last reply */
- return;
- }
-
- if (context->flags.oktosend && context->flags.finished) {
- StoreIOBuffer tempBuffer;
- assert (!context->outbound.getRaw());
- /* We've finished processing, and there is no more data buffered */
- debugs(86, 5, "Telling recipient EOF on READ");
- clientStreamCallback (thisNode, http, nullptr, tempBuffer);
- return;
- }
-
- if (context->reading())
- return;
-
- /* no data that is ready to send, and still reading? well, lets get some */
- /* secure a buffer */
- if (!context->incoming.getRaw()) {
- /* create a new buffer segment */
- context->buffered = new ESISegment;
- context->incoming = context->buffered;
- }
-
- assert (context->incoming.getRaw() && context->incoming->len != HTTP_REQBUF_SZ);
- {
- StoreIOBuffer tempBuffer;
- tempBuffer.offset = context->readpos;
- tempBuffer.length = context->incoming->len - HTTP_REQBUF_SZ;
- tempBuffer.data = &context->incoming->buf[context->incoming->len];
- context->startRead();
- clientStreamRead (thisNode, http, tempBuffer);
- }
-}
-
-clientStream_status_t
-esiStreamStatus (clientStreamNode *thisNode, ClientHttpRequest *http)
-{
- /* Test preconditions */
- assert (thisNode != nullptr);
- assert (cbdataReferenceValid (thisNode));
- /* we are not in the chain until ESI is detected on a data callback */
- assert (thisNode->node.prev != nullptr);
- assert (thisNode->node.next != nullptr);
-
- ESIContext::Pointer context = dynamic_cast<ESIContext *>(thisNode->data.getRaw());
- assert (context.getRaw() != nullptr);
-
- if (context->flags.passthrough)
- return clientStreamStatus (thisNode, http);
-
- if (context->flags.oktosend && context->flags.finished &&
- !(context->outbound.getRaw() && context->outbound_offset < context->outbound->len)) {
- debugs(86, 5, "Telling recipient EOF on STATUS");
- return STREAM_UNPLANNED_COMPLETE; /* we don't know lengths in advance */
- }
-
- /* ?? RC: we can't be aborted / fail ? */
- return STREAM_NONE;
-}
-
-static int
-esiAlwaysPassthrough(Http::StatusCode sline)
-{
- int result;
-
- switch (sline) {
-
- case Http::scContinue: /* Should never reach us... but squid needs to alter to accommodate this */
-
- case Http::scSwitchingProtocols: /* Ditto */
-
- case Http::scProcessing: /* Unknown - some extension */
-
- case Http::scNoContent: /* no body, no esi */
-
- case Http::scNotModified: /* ESI does not affect assembled page headers, so 304s are valid */
- result = 1;
- /* unreached */
- break;
-
- default:
- result = 0;
- }
-
- return result;
-}
-
-void
-ESIContext::trimBlanks()
-{
- /* trim leading empty buffers ? */
-
- while (outbound.getRaw() && outbound->next.getRaw() && !outbound->len) {
- debugs(86, 5, "ESIContext::trimBlanks: " << this <<
- " skipping segment " << outbound.getRaw());
- outbound = outbound->next;
- }
-
- if (outboundtail.getRaw())
- assert (outbound.getRaw());
-}
-
-/* Send data downstream
- * Returns 0 if nothing was sent. Non-zero if data was sent.
- */
-size_t
-ESIContext::send ()
-{
- debugs(86, 5, "ESIContext::send: this=" << this);
- /* send any processed data */
-
- trimBlanks();
-
- if (!flags.clientwantsdata) {
- debugs(86, 5, "ESIContext::send: Client does not want data - not sending anything");
- return 0;
- }
-
- if (tree.getRaw() && tree->mayFail()) {
- debugs(86, 5, "ESIContext::send: Tree may fail. Not sending.");
- return 0;
- } else
- flags.oktosend = 1;
-
- if (!(rep || (outbound.getRaw() &&
- outbound->len && (outbound_offset <= outbound->len)))) {
- debugs(86, 5, "ESIContext::send: Nothing to send.");
- return 0;
- }
-
- debugs(86, 5, "ESIContext::send: Sending something...");
- /* Yes! Send it without asking for more upstream */
- /* memcopying because the client provided the buffer */
- /* TODO: skip data until pos == next->readoff; */
- assert (thisNode->data == this);
- clientStreamNode *next = thisNode->next();
- ESIContext *templock = cbdataReference (this);
- size_t len = 0;
-
- if (outbound.getRaw())
- len = min (next->readBuffer.length, outbound->len - outbound_offset);
-
- /* prevent corruption on range requests, even though we don't support them yet */
- assert (pos == next->readBuffer.offset);
-
- /* We must send data or a reply */
- assert (len != 0 || rep != nullptr);
-
- if (len) {
- memcpy(next->readBuffer.data, &outbound->buf[outbound_offset], len);
-
- if (len + outbound_offset == outbound->len) {
- ESISegment::Pointer temp = outbound->next;
- /* remove the used buffer */
- outbound_offset = 0;
- outbound = temp;
- }
-
- pos += len;
-
- if (!outbound.getRaw())
- outboundtail = nullptr;
-
- trimBlanks();
- }
-
- flags.clientwantsdata = 0;
- debugs(86, 5, "ESIContext::send: this=" << this << " Client no longer wants data ");
- /* Deal with re-entrancy */
- HttpReplyPointer temprep = rep;
- rep = nullptr; /* freed downstream */
-
- if (temprep && varState)
- varState->buildVary(temprep.getRaw());
-
- {
- StoreIOBuffer tempBuffer;
- tempBuffer.length = len;
- tempBuffer.offset = pos - len;
- tempBuffer.data = next->readBuffer.data;
- clientStreamCallback (thisNode, http, temprep.getRaw(), tempBuffer);
- }
-
- if (len == 0)
- len = 1; /* tell the caller we sent something (because we sent headers */
-
- cbdataReferenceDone (templock);
-
- debugs (86,5,"ESIContext::send: this=" << this << " sent " << len);
-
- return len;
-}
-
-void
-ESIContext::finishChildren()
-{
- if (tree.getRaw())
- tree->finish();
-
- tree = nullptr;
-}
-
-/* Detach event from a client Stream */
-void
-esiStreamDetach (clientStreamNode *thisNode, ClientHttpRequest *http)
-{
- /* if we have pending callbacks, tell them we're done. */
- /* test preconditions */
- assert (thisNode != nullptr);
- assert (cbdataReferenceValid (thisNode));
- ESIContext::Pointer context = dynamic_cast<ESIContext *>(thisNode->data.getRaw());
- assert (context.getRaw() != nullptr);
- /* detach from the stream */
- clientStreamDetach (thisNode,http);
- /* if we have pending callbacks (from subincludes), tell them we're done. */
- context->thisNode = nullptr;
- context->flags.detached = 1;
- context->finishChildren();
- /* HACK for parser stack not being emptied */
- context->parserState.stack[0] = nullptr;
- /* allow refcount logic to trigger */
- context->cbdataLocker = nullptr;
-}
-
-/* Process incoming data for ESI tags */
-/* ESI TODO: Long term: we should have a framework to parse html/xml and
- * callback to a set of processors like thisNode, to prevent multiple parsing
- * overhead. More thoughts on thisNode: We have to parse multiple times, because
- * the output of one processor may create a very different tree. What we could
- * do is something like DOM and pass that down to a final renderer. This is
- * getting into web server territory though...
- *
- * Preconditions:
- * This is not the last node in the stream.
- * ESI processing has been enabled.
- * There is context data or a reply structure
- */
-void
-esiProcessStream (clientStreamNode *thisNode, ClientHttpRequest *http, HttpReply *rep, StoreIOBuffer receivedData)
-{
- /* test preconditions */
- assert (thisNode != nullptr);
- /* 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 (thisNode));
- /*
- * if data is NULL thisNode is the first entrance. If rep is also NULL,
- * something is wrong.
- * */
- assert (thisNode->data.getRaw() != nullptr || rep);
- assert (thisNode->node.next != nullptr);
-
- if (!thisNode->data.getRaw())
- /* setup ESI context from reply headers */
- thisNode->data = ESIContextNew(rep, thisNode, http);
-
- ESIContext::Pointer context = dynamic_cast<ESIContext *>(thisNode->data.getRaw());
-
- assert (context.getRaw() != nullptr);
-
- context->finishRead();
-
- /* Skipping all ESI processing. All remaining data gets untouched.
- * Mainly used when an error or other non-ESI processable entity
- * has been detected to prevent ESI processing the error body
- */
- if (context->flags.passthrough) {
- clientStreamCallback (thisNode, http, rep, receivedData);
- return;
- }
-
- debugs(86, 3, "esiProcessStream: Processing thisNode " << thisNode <<
- " context " << context.getRaw() << " offset " <<
- (int) receivedData.offset << " length " <<
- (unsigned int)receivedData.length);
-
- /* once we finish the template, we *cannot* return here */
- assert (!context->flags.finishedtemplate);
- assert (!context->cachedASTInUse);
-
- /* Can we generate any data ?*/
-
- if (receivedData.data) {
- /* Increase our buffer area with incoming data */
- assert (receivedData.length <= HTTP_REQBUF_SZ);
- assert (thisNode->readBuffer.offset == receivedData.offset);
- debugs (86,5, "esiProcessStream found " << receivedData.length << " bytes of body data at offset " << receivedData.offset);
- /* secure the data for later use */
-
- if (!context->incoming.getRaw()) {
- /* create a new buffer segment */
- debugs(86, 5, "esiProcessStream: Setting up incoming buffer");
- context->buffered = new ESISegment;
- context->incoming = context->buffered;
- }
-
- if (receivedData.data != &context->incoming->buf[context->incoming->len]) {
- /* We have to copy the data out because we didn't supply thisNode buffer */
- size_t space = HTTP_REQBUF_SZ - context->incoming->len;
- size_t len = min (space, receivedData.length);
- debugs(86, 5, "Copying data from " << receivedData.data << " to " <<
- &context->incoming->buf[context->incoming->len] <<
- " because our buffer was not used");
-
- memcpy(&context->incoming->buf[context->incoming->len], receivedData.data, len);
- context->incoming->len += len;
-
- if (context->incoming->len == HTTP_REQBUF_SZ) {
- /* append another buffer */
- context->incoming->next = new ESISegment;
- context->incoming = context->incoming->next;
- }
-
- if (len != receivedData.length) {
- /* capture the remnants */
- memcpy(context->incoming->buf, &receivedData.data[len], receivedData.length - len);
- context->incoming->len = receivedData.length - len;
- }
-
- /* and note where we are up to */
- context->readpos += receivedData.length;
- } else {
- /* update our position counters, and if needed assign a new buffer */
- context->incoming->len += receivedData.length;
- assert (context->incoming->len <= HTTP_REQBUF_SZ);
-
- if (context->incoming->len > HTTP_REQBUF_SZ * 3 / 4) {
- /* allocate a new buffer - to stop us asking for ridiculously small amounts */
- context->incoming->next = new ESISegment;
- context->incoming = context->incoming->next;
- }
-
- context->readpos += receivedData.length;
- }
- }
-
- /* EOF / Read error / aborted entry */
- if (rep == nullptr && receivedData.data == nullptr && receivedData.length == 0 && !context->flags.finishedtemplate) {
- /* TODO: get stream status to test the entry for aborts */
- /* else flush the esi processor */
- debugs(86, 5, "esiProcess: " << context.getRaw() << " Finished reading upstream data");
- /* This is correct */
- context->flags.finishedtemplate = 1;
- }
-
- switch (context->kick()) {
-
- case ESIContext::ESI_KICK_FAILED:
- /* thisNode can not happen - processing can't fail until we have data,
- * and when we come here we have sent data to the client
- */
- return;
-
- case ESIContext::ESI_KICK_SENT:
-
- case ESIContext::ESI_KICK_INPROGRESS:
- return;
-
- case ESIContext::ESI_KICK_PENDING:
- break;
- }
-
- /* ok.. no data sent, try to pull more data in from upstream.
- * TODO: Don't try thisNode if we have finished reading the template
- */
- if (!context->flags.finishedtemplate && !context->reading()
- && !context->cachedASTInUse) {
- StoreIOBuffer tempBuffer;
- assert (context->incoming.getRaw() && context->incoming->len < HTTP_REQBUF_SZ);
- tempBuffer.offset = context->readpos;
- tempBuffer.length = HTTP_REQBUF_SZ - context->incoming->len;
- tempBuffer.data = &context->incoming->buf[context->incoming->len];
- context->startRead();
- clientStreamRead (thisNode, http, tempBuffer);
- return;
- }
-
- debugs(86, 3, "esiProcessStream: no data to send, no data to read, awaiting a callback");
-}
-
-ESIContext::~ESIContext()
-{
- freeResources ();
- /* Not freed by freeresources because esi::fail needs it */
- safe_free (errormessage);
- debugs(86, 3, "ESIContext::~ESIContext: Freed " << this);
-}
-
-ESIContext *
-ESIContextNew (HttpReply *rep, clientStreamNode *thisNode, ClientHttpRequest *http)
-{
- assert (rep);
- ESIContext *rv = new ESIContext;
- rv->rep = rep;
- rv->cbdataLocker = rv;
-
- if (esiAlwaysPassthrough(rep->sline.status())) {
- rv->flags.passthrough = 1;
- } else {
- /* remove specific headers for ESI to prevent
- * downstream cache confusion */
- HttpHeader *hdr = &rep->header;
- hdr->delById(Http::HdrType::ACCEPT_RANGES);
- hdr->delById(Http::HdrType::ETAG);
- hdr->delById(Http::HdrType::CONTENT_LENGTH);
- hdr->delById(Http::HdrType::CONTENT_MD5);
- rv->tree = new esiSequence (rv, true);
- rv->thisNode = thisNode;
- rv->http = http;
- rv->flags.clientwantsdata = 1;
- rv->varState = new ESIVarState (&http->request->header, http->uri);
- debugs(86, 5, "ESIContextNew: Client wants data (always created during reply cycle");
- }
-
- debugs(86, 5, "ESIContextNew: Create context " << rv);
- return rv;
-}
-
-ESIElement::ESIElementType_t
-ESIElement::IdentifyElement (const char *el)
-{
- int offset = 0;
- assert (el);
-
- if (strlen (el) < 5)
- return ESI_ELEMENT_NONE;
-
- if (!strncmp (el, "esi:", 4))
- offset = 4;
- else if (!strncmp (el, "http://www.edge-delivery.org/esi/1.0|", 37))
- offset = 37;
- else
- return ESI_ELEMENT_NONE;
-
- if (!strncmp (el + offset, "otherwise", 9))
- return ESI_ELEMENT_OTHERWISE;
-
- if (!strncmp (el + offset, "comment", 7))
- return ESI_ELEMENT_COMMENT;
-
- if (!strncmp (el + offset, "include", 7))
- return ESI_ELEMENT_INCLUDE;
-
- 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;
-
- if (!strncmp (el + offset, "except", 6))
- return ESI_ELEMENT_EXCEPT;
-
- if (!strncmp (el + offset, "choose", 6))
- return ESI_ELEMENT_CHOOSE;
-
- if (!strncmp (el + offset, "vars", 4))
- return ESI_ELEMENT_VARS;
-
- if (!strncmp (el + offset, "when", 4))
- return ESI_ELEMENT_WHEN;
-
- if (!strncmp (el + offset, "try", 3))
- return ESI_ELEMENT_TRY;
-
- return ESI_ELEMENT_NONE;
-}
-
-ESIElement::Pointer
-ESIContext::ParserState::top()
-{
- return stack[stackdepth-1];
-}
-
-ESIContext::ParserState::ParserState() :
- stackdepth(0),
- parsing(0),
- inited_(false)
-{}
-
-bool
-ESIContext::ParserState::inited() const
-{
- return inited_;
-}
-
-void
-ESIContext::addStackElement (ESIElement::Pointer element)
-{
- /* Put on the stack to allow skipping of 'invalid' markup */
-
- // throw an error if the stack location would be invalid
- if (parserState.stackdepth >= ESI_STACK_DEPTH_LIMIT)
- throw Esi::Error("ESI Too many nested elements");
- if (parserState.stackdepth < 0)
- throw Esi::Error("ESI elements stack error, probable error in ESI template");
-
- assert (!failed());
- debugs(86, 5, "ESIContext::addStackElement: About to add ESI Node " << element.getRaw());
-
- if (!parserState.top()->addElement(element)) {
- throw Esi::Error("ESIContext::addStackElement failed, probable error in ESI template");
- } else {
- /* added ok, push onto the stack */
- parserState.stack[parserState.stackdepth] = element;
- ++parserState.stackdepth;
- }
-}
-
-void
-ESIContext::start(const char *el, const char **attr, size_t attrCount)
-{
- int i;
- unsigned int ellen = strlen (el);
- char localbuf [HTTP_REQBUF_SZ];
- ESIElement::Pointer element;
- int specifiedattcount = attrCount * 2;
- char *position;
- Must(ellen < sizeof(localbuf)); /* prevent unexpected overruns. */
-
- debugs(86, 5, "ESIContext::Start: element '" << el << "' with " << specifiedattcount << " tags");
-
- if (failed())
- /* waiting for expat to finish the buffer we gave it */
- return;
-
- switch (ESIElement::IdentifyElement (el)) {
-
- case ESIElement::ESI_ELEMENT_NONE:
- /* Spit out elements we aren't interested in */
- localbuf[0] = '<';
- localbuf[1] = '\0';
- xstrncpy(&localbuf[1], el, sizeof(localbuf) - 2);
- position = localbuf + strlen (localbuf);
-
- for (i = 0; i < specifiedattcount && attr[i]; i += 2) {
- Must(static_cast<size_t>(position - localbuf) < sizeof(localbuf) - 1);
- *position = ' ';
- ++position;
- /* TODO: handle thisNode gracefully */
- xstrncpy(position, attr[i], sizeof(localbuf) - (position - localbuf));
- position += strlen (position);
- Must(static_cast<size_t>(position - localbuf) < sizeof(localbuf) - 2);
- *position = '=';
- ++position;
- *position = '\"';
- ++position;
- const char *chPtr = attr[i + 1];
- char ch;
- while ((ch = *chPtr++) != '\0') {
- if (ch == '\"') {
- Must(static_cast<size_t>(position - localbuf) < sizeof(localbuf) - 6);
- xstrncpy(position, """, sizeof(localbuf) - (position-localbuf));
- position += 6;
- } else {
- Must(static_cast<size_t>(position - localbuf) < sizeof(localbuf) - 1);
- *position = ch;
- ++position;
- }
- }
- Must(static_cast<size_t>(position - localbuf) < sizeof(localbuf) - 1);
- *position = '\"';
- ++position;
- }
-
- Must(static_cast<size_t>(position - localbuf) < sizeof(localbuf) - 2);
- *position = '>';
- ++position;
- *position = '\0';
-
- addLiteral (localbuf, position - localbuf);
- debugs(86, 5, "esi stack depth " << parserState.stackdepth);
- return;
- break;
-
- case ESIElement::ESI_ELEMENT_COMMENT:
- /* Put on the stack to allow skipping of 'invalid' markup */
- element = new esiComment ();
- break;
-
- case ESIElement::ESI_ELEMENT_INCLUDE:
- /* Put on the stack to allow skipping of 'invalid' markup */
- element = new ESIInclude (parserState.top().getRaw(), specifiedattcount, attr, this);
- break;
-
- case ESIElement::ESI_ELEMENT_REMOVE:
- /* Put on the stack to allow skipping of 'invalid' markup */
- element = new esiRemove();
- break;
-
- case ESIElement::ESI_ELEMENT_TRY:
- /* Put on the stack to allow skipping of 'invalid' markup */
- element = new esiTry (parserState.top().getRaw());
- break;
-
- case ESIElement::ESI_ELEMENT_ATTEMPT:
- /* Put on the stack to allow skipping of 'invalid' markup */
- element = new esiAttempt (parserState.top().getRaw());
- break;
-
- case ESIElement::ESI_ELEMENT_EXCEPT:
- /* Put on the stack to allow skipping of 'invalid' markup */
- element = new esiExcept (parserState.top().getRaw());
- break;
-
- case ESIElement::ESI_ELEMENT_VARS:
- /* Put on the stack to allow skipping of 'invalid' markup */
- element = new ESIVar (parserState.top().getRaw());
- break;
-
- case ESIElement::ESI_ELEMENT_CHOOSE:
- /* Put on the stack to allow skipping of 'invalid' markup */
- element = new esiChoose (parserState.top().getRaw());
- break;
-
- case ESIElement::ESI_ELEMENT_WHEN:
- /* Put on the stack to allow skipping of 'invalid' markup */
- element = new esiWhen (parserState.top().getRaw(), specifiedattcount, attr, varState);
- break;
-
- case ESIElement::ESI_ELEMENT_OTHERWISE:
- /* 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);
-
- debugs(86, 5, "esi stack depth " << parserState.stackdepth);
-
-} /* End of start handler */
-
-void
-ESIContext::end(const char *el)
-{
- unsigned int ellen = strlen (el);
- char localbuf [HTTP_REQBUF_SZ];
- char *position;
-
- if (flags.error)
- /* waiting for expat to finish the buffer we gave it */
- return;
-
- switch (ESIElement::IdentifyElement (el)) {
-
- case ESIElement::ESI_ELEMENT_NONE:
- Must(ellen < sizeof(localbuf) - 3); /* prevent unexpected overruns. */
- /* Add elements we aren't interested in */
- localbuf[0] = '<';
- localbuf[1] = '/';
- xstrncpy(&localbuf[2], el, sizeof(localbuf) - 3);
- position = localbuf + strlen (localbuf);
- *position = '>';
- ++position;
- *position = '\0';
- addLiteral (localbuf, position - localbuf);
- break;
-
- case ESIElement::ESI_ELEMENT_COMMENT:
-
- case ESIElement::ESI_ELEMENT_INCLUDE:
-
- case ESIElement::ESI_ELEMENT_REMOVE:
-
- case ESIElement::ESI_ELEMENT_TRY:
-
- case ESIElement::ESI_ELEMENT_ATTEMPT:
-
- case ESIElement::ESI_ELEMENT_EXCEPT:
-
- case ESIElement::ESI_ELEMENT_VARS:
-
- case ESIElement::ESI_ELEMENT_CHOOSE:
-
- case ESIElement::ESI_ELEMENT_WHEN:
-
- case ESIElement::ESI_ELEMENT_OTHERWISE:
-
- case ESIElement::ESI_ELEMENT_ASSIGN:
- /* pop of the stack */
- parserState.stack[--parserState.stackdepth] = nullptr;
- break;
- }
-} /* End of end handler */
-
-void
-ESIContext::parserDefault (const char *s, int len)
-{
- if (failed())
- return;
-
- /* handle any skipped data */
- addLiteral (s, len);
-}
-
-void
-ESIContext::parserComment (const char *s)
-{
- if (failed())
- return;
-
- if (!strncmp(s, "esi",3)) {
- debugs(86, 5, "ESIContext::parserComment: ESI <!-- block encountered");
- ESIParser::Pointer tempParser = ESIParser::NewParser (this);
-
- /* wrap the comment in some tags */
-
- if (!tempParser->parse("<div>", 5,0) ||
- !tempParser->parse(s + 3, strlen(s) - 3, 0) ||
- !tempParser->parse("</div>",6,1)) {
- debugs(86, DBG_CRITICAL, "ERROR: ESIContext::parserComment: Parsing fragment '" << s + 3 << "' failed.");
- setError();
- char tempstr[1024];
- snprintf(tempstr, 1023, "ESIContext::parserComment: Parse error at line %ld:\n%s\n",
- tempParser->lineNumber(),
- tempParser->errorString());
- debugs(86, DBG_CRITICAL, "" << tempstr << "");
-
- setErrorMessage(tempstr);
- }
-
- debugs(86, 5, "ESIContext::parserComment: ESI <!-- block parsed");
- return;
- } else {
- char localbuf [HTTP_REQBUF_SZ];
- unsigned int len;
- debugs(86, 5, "ESIContext::parserComment: Regenerating comment block");
- len = strlen (s);
-
- if (len > sizeof (localbuf) - 9) {
- debugs(86, DBG_CRITICAL, "ESIContext::parserComment: Truncating long comment");
- len = sizeof (localbuf) - 9;
- }
-
- xstrncpy(localbuf, "<!--", 5);
- xstrncpy(localbuf + 4, s, len + 1);
- xstrncpy(localbuf + 4 + len, "-->", 4);
- addLiteral (localbuf,len + 7);
- }
-}
-
-void
-ESIContext::addLiteral (const char *s, int len)
-{
- /* handle any skipped data */
- assert (len);
- debugs(86, 5, "literal length is " << len);
- /* give a literal to the current element */
- ESIElement::Pointer element (new esiLiteral (this, s, len));
-
- if (!parserState.top()->addElement(element))
- throw Esi::Error("ESIContext::addLiteral failed, probable error in ESI template");
-}
-
-void
-ESIContext::ParserState::init(ESIParserClient *userData)
-{
- theParser = ESIParser::NewParser (userData);
- inited_ = true;
-}
-
-void
-ESIContext::parseOneBuffer()
-{
- assert (buffered.getRaw());
-
- debugs (86,9,"ESIContext::parseOneBuffer: " << buffered->len << " bytes");
- bool lastBlock = buffered->next.getRaw() == nullptr && flags.finishedtemplate ? true : false;
-
- if (! parserState.theParser->parse(buffered->buf, buffered->len, lastBlock)) {
- setError();
- char tempstr[1024];
- snprintf (tempstr, 1023, "esiProcess: Parse error at line %ld:\n%s\n",
- parserState.theParser->lineNumber(),
- parserState.theParser->errorString());
- debugs(86, DBG_CRITICAL, "" << tempstr << "");
-
- setErrorMessage(tempstr);
-
- assert (flags.error);
-
- return;
- }
-
- if (flags.error) {
- setError();
- return;
- }
-
- ESISegment::Pointer temp = buffered;
- buffered = temp->next;
-}
-
-void
-ESIContext::parse()
-{
- if (!parserState.stackdepth) {
- debugs(86, 5, "empty parser stack, inserting the top level node");
- assert (tree.getRaw());
- parserState.stack[parserState.stackdepth] = tree;
- ++parserState.stackdepth;
- }
-
- if (rep && !parserState.inited())
- parserState.init(this);
-
- /* we have data */
- if (buffered.getRaw()) {
- parserState.parsing = 1;
- /* we don't keep any data around */
-
- try {
- while (buffered.getRaw() && !flags.error)
- parseOneBuffer();
-
- } catch (Esi::ErrorDetail &errMsg) { // XXX: non-const for c_str()
- // level-2: these are protocol/syntax errors from upstream
- debugs(86, 2, "WARNING: ESI syntax error: " << errMsg);
- setError();
- setErrorMessage(errMsg.c_str());
-
- } catch (...) {
- // DBG_IMPORTANT because these are local issues the admin needs to fix
- static FadingCounter logEntries; // TODO: set horizon less than infinity
- if (logEntries.count(1) < 100)
- debugs(86, DBG_IMPORTANT, "ERROR: ESI parser: " << CurrentException);
- setError();
- setErrorMessage("ESI parser error");
- }
-
- /* Tel the read code to allocate a new buffer */
- incoming = nullptr;
-
- parserState.parsing = 0;
- }
-}
-
-esiProcessResult_t
-ESIContext::process ()
-{
- /* parsing:
- * read through buffered, skipping plain text, and skipping any
- * <...> entry that is not an <esi: entry.
- * when it's found, hand an esiLiteral of the preceding data to our current
- * context
- */
-
- if (parserState.parsing) {
- /* in middle of parsing - finish here */
- return ESI_PROCESS_PENDING_MAYFAIL;
- }
-
- assert (flags.finished == 0);
-
- assert (!flags.error);
-
- if (!hasCachedAST())
- parse();
- else if (!flags.finishedtemplate)
- getCachedAST();
-
- if (flags.error) {
- debugs(86, 5, "ESIContext::process: Parsing failed");
- finishChildren ();
- parserState.popAll();
- return ESI_PROCESS_FAILED;
- }
-
- if (!flags.finishedtemplate && !incoming.getRaw() && !cachedASTInUse) {
- buffered = new ESISegment;
- incoming = buffered;
- }
-
- if (!flags.finishedtemplate && !cachedASTInUse) {
- return ESI_PROCESS_PENDING_MAYFAIL;
- }
-
- assert (flags.finishedtemplate || cachedASTInUse);
- updateCachedAST();
- /* ok, we've done all we can with the data. What can we process now?
- */
- {
- esiProcessResult_t status;
- processing = true;
- status = tree->process(0);
- processing = false;
-
- switch (status) {
-
- case ESI_PROCESS_COMPLETE:
- debugs(86, 5, "esiProcess: tree Processed OK");
- break;
-
- case ESI_PROCESS_PENDING_WONTFAIL:
- debugs(86, 5, "esiProcess: tree Processed PENDING OK");
- break;
-
- case ESI_PROCESS_PENDING_MAYFAIL:
- debugs(86, 5, "esiProcess: tree Processed PENDING UNKNOWN");
- break;
-
- case ESI_PROCESS_FAILED:
- debugs(86, DBG_CRITICAL, "ERROR: esiProcess: tree Processed FAILED");
- setError();
-
- setErrorMessage("esiProcess: ESI template Processing failed.");
- return ESI_PROCESS_FAILED;
-
- break;
- }
-
- if (status != ESI_PROCESS_PENDING_MAYFAIL && (flags.finishedtemplate || cachedASTInUse)) {
- /* We've read the entire template, and no nodes will
- * return failure
- */
- debugs(86, 5, "esiProcess, request will succeed");
- flags.oktosend = 1;
- }
-
- if (status == ESI_PROCESS_COMPLETE
- && (flags.finishedtemplate || cachedASTInUse)) {
- /* we've finished all processing. Render and send. */
- debugs(86, 5, "esiProcess, processing complete");
- flags.finished = 1;
- }
-
- return status; /* because we have no callbacks */
- }
-}
-
-void
-ESIContext::ParserState::freeResources()
-{
- theParser = nullptr;
- inited_ = false;
-}
-
-void
-ESIContext::ParserState::popAll()
-{
- while (stackdepth)
- stack[--stackdepth] = nullptr;
-}
-
-void
-ESIContext::freeResources ()
-{
- debugs(86, 5, "Freeing for this=" << this);
-
- rep = nullptr; // refcounted
-
- finishChildren ();
-
- if (parserState.inited()) {
- parserState.freeResources();
- }
-
- parserState.popAll();
- ESISegmentFreeList (buffered);
- ESISegmentFreeList (outbound);
- ESISegmentFreeList (outboundtail);
- delete varState;
- varState=nullptr;
- /* don't touch incoming, it's a pointer into buffered anyway */
-}
-
-ErrorState *clientBuildError(err_type, Http::StatusCode, char const *, const ConnStateData *, HttpRequest *, const AccessLogEntryPointer &);
-
-/* This can ONLY be used before we have sent *any* data to the client */
-void
-ESIContext::fail ()
-{
- debugs(86, 5, "ESIContext::fail: this=" << this);
- /* check preconditions */
- assert (pos == 0);
- /* cleanup current state */
- freeResources ();
- /* Stop altering thisNode request */
- flags.oktosend = 1;
- flags.finished = 1;
- /* don't honour range requests - for errors we send it all */
- flags.error = 1;
- /* create an error object */
- // XXX: with the in-direction on remote IP. does the http->getConn()->clientConnection exist?
- const auto err = clientBuildError(errorpage, errorstatus, nullptr, http->getConn(), http->request, http->al);
- err->err_msg = errormessage;
- errormessage = nullptr;
- rep = err->BuildHttpReply();
- // XXX: Leaking err!
- assert (rep->body.hasContent());
- size_t errorprogress = rep->body.contentSize();
- /* Tell esiSend where to start sending from */
- outbound_offset = 0;
- /* copy the membuf from the reply to outbound */
-
- while (errorprogress < (size_t)rep->body.contentSize()) {
- appendOutboundData(new ESISegment);
- errorprogress += outboundtail->append(rep->body.content() + errorprogress, rep->body.contentSize() - errorprogress);
- }
-
- /* the esiCode now thinks that the error is the outbound,
- * and all processing has finished. */
- /* Send as much as we can */
- send ();
-
- /* don't cancel anything. The stream nodes will clean up after
- * themselves when the reply is freed - and we don't know what to
- * clean anyway.
- */
-}
-
-/* Implementation of ESIElements */
-
-/* esiComment */
-esiComment::~esiComment()
-{
- debugs(86, 5, "esiComment::~esiComment " << this);
-}
-
-esiComment::esiComment()
-{}
-
-void
-esiComment::finish()
-{}
-
-void
-esiComment::render(ESISegment::Pointer output)
-{
- /* Comments do nothing dude */
- debugs(86, 5, "esiCommentRender: Rendering comment " << this << " into " << output.getRaw());
-}
-
-ESIElement::Pointer
-esiComment::makeCacheable() const
-{
- debugs(86, 5, "esiComment::makeCacheable: returning NULL");
- return nullptr;
-}
-
-ESIElement::Pointer
-esiComment::makeUsable(esiTreeParentPtr, ESIVarState &) const
-{
- fatal ("esiComment::Usable: unreachable code!\n");
- return nullptr;
-}
-
-/* esiLiteral */
-esiLiteral::~esiLiteral()
-{
- debugs(86, 5, "esiLiteral::~esiLiteral: " << this);
- ESISegmentFreeList (buffer);
- cbdataReferenceDone (varState);
-}
-
-esiLiteral::esiLiteral(ESISegment::Pointer aSegment) :
- buffer(aSegment),
- varState(nullptr)
-{
- /* Nothing to do */
- flags.donevars = 1;
-}
-
-void
-esiLiteral::finish()
-{}
-
-/* precondition: the buffer chain has at least start + length bytes of data
- */
-esiLiteral::esiLiteral(ESIContext *context, const char *s, int numberOfCharacters)
-{
- assert (s);
- flags.donevars = 0;
- buffer = new ESISegment;
- ESISegment::Pointer local = buffer;
- size_t start = 0;
- int remainingCharacters = numberOfCharacters;
-
- while (remainingCharacters > 0) {
- if (local->len == sizeof (local->buf)) {
- local->next = new ESISegment;
- local=local->next;
- }
-
- size_t len = local->append (&s[start], remainingCharacters);
- start += len;
- remainingCharacters -= len;
- }
-
- varState = cbdataReference(context->varState);
-}
-
-void
-esiLiteral::render (ESISegment::Pointer output)
-{
- debugs(86, 9, "esiLiteral::render: Rendering " << this);
- /* append the entire chain */
- assert (output->next.getRaw() == nullptr);
- output->next = buffer;
- buffer = nullptr;
-}
-
-esiProcessResult_t
-esiLiteral::process (int dovars)
-{
- if (flags.donevars)
- return ESI_PROCESS_COMPLETE;
-
- if (dovars) {
- ESISegment::Pointer temp = buffer;
- /* Ensure variable state is clean */
-
- while (temp.getRaw()) {
- varState->feedData(temp->buf,temp->len);
- temp = temp->next;
- }
-
- /* free the pre-processed content */
- ESISegmentFreeList (buffer);
-
- buffer = varState->extractList ();
- }
-
- flags.donevars = 1;
- return ESI_PROCESS_COMPLETE;
-}
-
-esiLiteral::esiLiteral(esiLiteral const &old) : buffer (old.buffer->cloneList()),
- varState (nullptr)
-{
- flags.donevars = 0;
-}
-
-ESIElement::Pointer
-esiLiteral::makeCacheable() const
-{
- return new esiLiteral (*this);
-}
-
-ESIElement::Pointer
-esiLiteral::makeUsable(esiTreeParentPtr, ESIVarState &newVarState) const
-{
- debugs(86, 5, "esiLiteral::makeUsable: Creating usable literal");
- esiLiteral * result = new esiLiteral (*this);
- result->varState = cbdataReference (&newVarState);
- return result;
-}
-
-/* esiRemove */
-void
-esiRemove::render(ESISegment::Pointer)
-{
- /* Removes do nothing dude */
- debugs(86, 5, "esiRemoveRender: Rendering remove " << this);
-}
-
-/* Accept non-ESI children */
-bool
-esiRemove::addElement (ESIElement::Pointer element)
-{
- if (!dynamic_cast<esiLiteral*>(element.getRaw())) {
- debugs(86, 5, "esiRemoveAdd: Failed for " << this);
- return false;
- }
-
- return true;
-}
-
-ESIElement::Pointer
-esiRemove::makeCacheable() const
-{
- debugs(86, 5, "esiRemove::makeCacheable: Returning NULL");
- return nullptr;
-}
-
-ESIElement::Pointer
-esiRemove::makeUsable(esiTreeParentPtr, ESIVarState &) const
-{
- fatal ("esiRemove::Usable: unreachable code!\n");
- return nullptr;
-}
-
-/* esiTry */
-esiTry::~esiTry()
-{
- debugs(86, 5, "esiTry::~esiTry " << this);
-}
-
-esiTry::esiTry(esiTreeParentPtr aParent) :
- parent(aParent),
- exceptbuffer(nullptr)
-{
- memset(&flags, 0, sizeof(flags));
-}
-
-void
-esiTry::render(ESISegment::Pointer output)
-{
- /* Try renders from it's children */
- assert (attempt.getRaw());
- assert (except.getRaw());
- debugs(86, 5, "esiTryRender: Rendering Try " << this);
-
- if (flags.attemptok) {
- attempt->render(output);
- } else if (flags.exceptok) {
- /* prerendered */
-
- if (exceptbuffer.getRaw())
- ESISegment::ListTransfer(exceptbuffer, output);
- else
- except->render(output);
- } else
- debugs(86, 5, "esiTryRender: Neither except nor attempt succeeded?!?");
-}
-
-/* Accept attempt and except only */
-bool
-esiTry::addElement(ESIElement::Pointer element)
-{
- debugs(86, 5, "esiTryAdd: Try " << this << " adding element " <<
- element.getRaw());
-
- if (dynamic_cast<esiLiteral*>(element.getRaw())) {
- /* Swallow whitespace */
- debugs(86, 5, "esiTryAdd: Try " << this << " skipping whitespace " << element.getRaw());
- return true;
- }
-
- if (dynamic_cast<esiAttempt*>(element.getRaw())) {
- if (attempt.getRaw()) {
- debugs(86, DBG_IMPORTANT, "ERROR: esiTryAdd: Failed for " << this << " - try already has an attempt node (section 3.4)");
- return false;
- }
-
- attempt = element;
- return true;
- }
-
- if (dynamic_cast<esiExcept*>(element.getRaw())) {
- if (except.getRaw()) {
- debugs(86, DBG_IMPORTANT, "ERROR: esiTryAdd: Failed for " << this << " - try already has an except node (section 3.4)");
- return false;
- }
-
- except = element;
- return true;
- }
-
- debugs(86, DBG_IMPORTANT, "ERROR: esiTryAdd: Failed to add element " << element.getRaw() << " to try " << this << ", incorrect element type (see section 3.4)");
- return false;
-}
-
-esiProcessResult_t
-esiTry::bestAttemptRV() const
-{
- if (flags.attemptfailed)
- return ESI_PROCESS_COMPLETE;
- else
- return ESI_PROCESS_PENDING_MAYFAIL;
-}
-
-esiProcessResult_t
-esiTry::process (int dovars)
-{
- esiProcessResult_t rv = ESI_PROCESS_PENDING_MAYFAIL;
-
- if (!attempt.getRaw()) {
- debugs(86, DBG_CRITICAL, "ERROR: esiTryProcess: Try has no attempt element - ESI template is invalid (section 3.4)");
- return ESI_PROCESS_FAILED;
- }
-
- if (!except.getRaw()) {
- debugs(86, DBG_CRITICAL, "ERROR: esiTryProcess: Try has no except element - ESI template is invalid (section 3.4)");
- return ESI_PROCESS_FAILED;
- }
-
- if (!flags.attemptfailed)
- /* Try the attempt branch */
- switch ((rv = attempt->process(dovars))) {
-
- case ESI_PROCESS_COMPLETE:
- debugs(86, 5, "esiTryProcess: attempt Processed OK");
- flags.attemptok = 1;
- return ESI_PROCESS_COMPLETE;
-
- case ESI_PROCESS_PENDING_WONTFAIL:
- debugs(86, 5, "esiTryProcess: attempt Processed PENDING OK");
- /* We're not done yet, but don't need to test except */
- return ESI_PROCESS_PENDING_WONTFAIL;
-
- case ESI_PROCESS_PENDING_MAYFAIL:
- debugs(86, 5, "eseSequenceProcess: element Processed PENDING UNKNOWN");
- break;
-
- case ESI_PROCESS_FAILED:
- debugs(86, 5, "esiSequenceProcess: element Processed FAILED");
- flags.attemptfailed = 1;
- break;
- }
-
- /* attempt is either MAYFAIL or FAILED */
- if (flags.exceptok)
- return bestAttemptRV();
-
- /* query except to see if it has a definite result */
- if (!flags.exceptfailed)
- /* Try the except branch */
- switch (except->process(dovars)) {
-
- case ESI_PROCESS_COMPLETE:
- debugs(86, 5, "esiTryProcess: except Processed OK");
- flags.exceptok = 1;
- return bestAttemptRV();
-
- case ESI_PROCESS_PENDING_WONTFAIL:
- debugs(86, 5, "esiTryProcess: attempt Processed PENDING OK");
- /* We're not done yet, but can't fail */
- return ESI_PROCESS_PENDING_WONTFAIL;
-
- case ESI_PROCESS_PENDING_MAYFAIL:
- debugs(86, 5, "eseSequenceProcess: element Processed PENDING UNKNOWN");
- /* The except branch fail fail */
- return ESI_PROCESS_PENDING_MAYFAIL;
-
- case ESI_PROCESS_FAILED:
- debugs(86, 5, "esiSequenceProcess: element Processed FAILED");
- flags.exceptfailed = 1;
- break;
- }
-
- if (flags.exceptfailed && flags.attemptfailed)
- return ESI_PROCESS_FAILED;
-
- /* one of attempt or except returned PENDING MAYFAIL */
- return ESI_PROCESS_PENDING_MAYFAIL;
-}
-
-void
-esiTry::notifyParent()
-{
- if (flags.attemptfailed) {
- if (flags.exceptok) {
- parent->provideData (exceptbuffer, this);
- exceptbuffer = nullptr;
- } else if (flags.exceptfailed || except.getRaw() == nullptr) {
- parent->fail (this, "esi:try - except claused failed, or no except clause found");
- }
- }
-
- /* nothing to do when except fails and attempt hasn't */
-}
-
-void
-esiTry::fail(ESIElement *source, char const *anError)
-{
- assert (source);
- assert (source == attempt || source == except);
- debugs(86, 5, "esiTry::fail: this=" << this << ", source=" << source << ", message=" << anError);
-
- if (source == except) {
- flags.exceptfailed = 1;
- } else {
- flags.attemptfailed = 1;
- }
-
- notifyParent();
-}
-
-void
-esiTry::provideData (ESISegment::Pointer data, ESIElement* source)
-{
- if (source == attempt) {
- flags.attemptok = 1;
- parent->provideData (data, this);
- } else if (source == except) {
- flags.exceptok = 1;
- assert (exceptbuffer == nullptr);
- ESISegment::ListTransfer (data, exceptbuffer);
- notifyParent();
- }
-}
-
-esiTry::esiTry(esiTry const &)
-{
- attempt = nullptr;
- except = nullptr;
- flags.attemptok = 0;
- flags.exceptok = 0;
- flags.attemptfailed = 0;
- flags.exceptfailed = 0;
- parent = nullptr;
- exceptbuffer = nullptr;
-}
-
-ESIElement::Pointer
-esiTry::makeCacheable() const
-{
- debugs(86, 5, "esiTry::makeCacheable: making cachable Try from " << this);
- esiTry *resultT = new esiTry (*this);
- ESIElement::Pointer result = resultT;
-
- if (attempt.getRaw())
- resultT->attempt = attempt->makeCacheable();
-
- if (except.getRaw())
- resultT->except = except->makeCacheable();
-
- return result;
-}
-
-ESIElement::Pointer
-esiTry::makeUsable(esiTreeParentPtr newParent, ESIVarState &newVarState) const
-{
- debugs(86, 5, "esiTry::makeUsable: making usable Try from " << this);
- esiTry *resultT = new esiTry (*this);
- ESIElement::Pointer result = resultT;
-
- resultT->parent = newParent;
-
- if (attempt.getRaw())
- resultT->attempt = attempt->makeUsable(resultT, newVarState);
-
- if (except.getRaw())
- resultT->except = except->makeUsable(resultT, newVarState);
-
- return result;
-}
-
-void
-esiTry::finish()
-{
- parent = nullptr;
-
- if (attempt.getRaw())
- attempt->finish();
-
- attempt = nullptr;
-
- if (except.getRaw())
- except->finish();
-
- except = nullptr;
-}
-
-/* esiChoose */
-esiChoose::~esiChoose()
-{
- debugs(86, 5, "esiChoose::~esiChoose " << this);
- FinishAllElements(elements); // finish if not already done
-}
-
-esiChoose::esiChoose(const esiTreeParentPtr & aParent) :
- elements(),
- chosenelement(-1),
- parent(aParent)
-{}
-
-void
-esiChoose::render(ESISegment::Pointer output)
-{
- /* append all processed elements, and trim processed and rendered elements */
- assert (output->next == nullptr);
- assert (elements.size() || otherwise.getRaw());
- debugs(86, 5, "esiChooseRender: rendering");
-
- if (chosenelement >= 0)
- elements[chosenelement]->render(output);
- else if (otherwise.getRaw())
- otherwise->render(output);
-}
-
-bool
-esiChoose::addElement(ESIElement::Pointer element)
-{
- /* add an element to the output list */
-
- if (dynamic_cast<esiLiteral*>(element.getRaw())) {
- /* Swallow whitespace */
- debugs(86, 5, "esiChooseAdd: Choose " << this << " skipping whitespace " << element.getRaw());
- return true;
- }
-
- /* Some elements require specific parents */
- if (!(dynamic_cast<esiWhen*>(element.getRaw()) || dynamic_cast<esiOtherwise*>(element.getRaw()))) {
- debugs(86, DBG_CRITICAL, "ERROR: esiChooseAdd: invalid child node for esi:choose (section 3.3)");
- return false;
- }
-
- if (dynamic_cast<esiOtherwise*>(element.getRaw())) {
- if (otherwise.getRaw()) {
- debugs(86, DBG_CRITICAL, "esiChooseAdd: only one otherwise node allowed for esi:choose (section 3.3)");
- return false;
- }
-
- otherwise = element;
- } else {
- elements.push_back (element);
-
- debugs (86,3, "esiChooseAdd: Added a new element, elements = " << elements.size());
-
- if (chosenelement == -1) {
- const esiWhen * topElement=dynamic_cast<esiWhen *>(element.getRaw());
- if (topElement && topElement->testsTrue()) {
- chosenelement = elements.size() - 1;
- debugs (86,3, "esiChooseAdd: Chose element " << elements.size());
- }
- }
- }
-
- return true;
-}
-
-void
-esiChoose::selectElement()
-{
- if (chosenelement > -1)
- return;
-
- for (size_t counter = 0; counter < elements.size(); ++counter) {
- const esiWhen *el = dynamic_cast<esiWhen *>(elements[counter].getRaw());
- if (el && el->testsTrue()) {
- chosenelement = counter;
- debugs (86,3, "esiChooseAdd: Chose element " << counter + 1);
- return;
- }
- }
-}
-
-// TODO: make ESIElement destructor call finish() instead so it is
-// a) only called when an element ref-count is 0, and
-// b) caller can elements.clear() instead of doing this
-void
-FinishAnElement(ESIElement::Pointer &element, int pos)
-{
- if (element)
- element->finish();
-
- debugs(86, 5, "setting index " << pos << ", pointer " << (void*)element.getRaw() << " to nil");
- element = nullptr;
-}
-
-void
-FinishAllElements(Esi::Elements &elements)
-{
- int pos = 0;
- for (auto &element : elements)
- FinishAnElement(element, pos++);
-}
-
-void
-esiChoose::finish()
-{
- FinishAllElements(elements);
-
- if (otherwise.getRaw())
- otherwise->finish();
-
- otherwise = nullptr;
- parent = nullptr;
-}
-
-void
-esiChoose::NULLUnChosen()
-{
- if (chosenelement >= 0) {
- if (otherwise.getRaw())
- otherwise->finish();
-
- otherwise = nullptr;
-
- int pos = 0;
- for (auto &element : elements) {
- if (pos != chosenelement)
- FinishAnElement(element, pos++);
- }
-
- } else if (otherwise.getRaw()) {
- FinishAllElements(elements);
- }
-}
-
-esiProcessResult_t
-esiChoose::process (int dovars)
-{
- /* process as much of the list as we can, stopping only on
- * failures
- */
- /* We MUST have a when clause */
- NULLUnChosen();
-
- if (!elements.size()) {
- parent->fail(this);
-
- if (otherwise.getRaw())
- otherwise->finish();
-
- otherwise = nullptr;
-
- parent = nullptr;
-
- return ESI_PROCESS_FAILED;
- }
-
- if (chosenelement >= 0) {
- return elements[chosenelement]->process(dovars);
- } else if (otherwise.getRaw())
- return otherwise->process(dovars);
- else
- return ESI_PROCESS_COMPLETE;
-}
-
-void
-esiChoose::checkValidSource (ESIElement::Pointer source) const
-{
- if (!elements.size())
- fatal ("invalid callback = no when clause\n");
-
- if (chosenelement >= 0)
- assert (source == elements[chosenelement]);
- else if (otherwise.getRaw())
- assert (source == otherwise);
- else
- fatal ("esiChoose::checkValidSource: invalid callback - no elements chosen\n");
-}
-
-void
-esiChoose::fail(ESIElement * source, char const *anError)
-{
- checkValidSource (source);
- FinishAllElements(elements);
-
- if (otherwise.getRaw())
- otherwise->finish();
-
- otherwise = nullptr;
-
- parent->fail(this, anError);
-
- parent = nullptr;
-}
-
-void
-esiChoose::provideData (ESISegment::Pointer data, ESIElement*source)
-{
- checkValidSource (source);
- parent->provideData (data, this);
-}
-
-esiChoose::esiChoose(esiChoose const &old) : chosenelement(-1), otherwise (nullptr), parent (nullptr)
-{
- for (size_t counter = 0; counter < old.elements.size(); ++counter) {
- ESIElement::Pointer newElement = old.elements[counter]->makeCacheable();
-
- if (newElement.getRaw())
- assert (addElement(newElement));
- }
-}
-
-void
-esiChoose::makeCachableElements(esiChoose const &old)
-{
- for (size_t counter = 0; counter < old.elements.size(); ++counter) {
- ESIElement::Pointer newElement = old.elements[counter]->makeCacheable();
-
- if (newElement.getRaw())
- assert (addElement(newElement));
- }
-}
-
-void
-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);
-
- if (newElement.getRaw())
- assert (addElement(newElement));
- }
-}
-
-ESIElement::Pointer
-esiChoose::makeCacheable() const
-{
- esiChoose *resultC = new esiChoose (*this);
- ESIElement::Pointer result = resultC;
- resultC->makeCachableElements(*this);
-
- if (otherwise.getRaw())
- resultC->otherwise = otherwise->makeCacheable();
-
- return result;
-}
-
-ESIElement::Pointer
-esiChoose::makeUsable(esiTreeParentPtr newParent, ESIVarState &newVarState) const
-{
- esiChoose *resultC = new esiChoose (*this);
- ESIElement::Pointer result = resultC;
- resultC->parent = newParent;
- resultC->makeUsableElements(*this, newVarState);
- resultC->selectElement();
-
- if (otherwise.getRaw())
- resultC->otherwise = otherwise->makeUsable(resultC, newVarState);
-
- return result;
-}
-
-/* esiWhen */
-esiWhen::esiWhen(esiTreeParentPtr aParent, int attrcount, const char **attr,ESIVarState *aVar) :
- esiSequence(aParent),
- testValue(false),
- unevaluatedExpression(nullptr),
- varState(nullptr)
-{
- char const *expression = nullptr;
-
- for (int loopCounter = 0; loopCounter < attrcount && attr[loopCounter]; loopCounter += 2) {
- if (!strcmp(attr[loopCounter],"test")) {
- /* evaluate test */
- debugs(86, 5, "esiWhen::esiWhen: Evaluating '" << attr[loopCounter+1] << "'");
- /* TODO: warn the user instead of asserting */
- assert (expression == nullptr);
- expression = attr[loopCounter+1];
- } else {
- /* ignore mistyped attributes.
- * TODO:? error on these for user feedback - config parameter needed
- */
- debugs(86, DBG_IMPORTANT, "Found misttyped attribute on ESI When clause");
- }
- }
-
- /* No expression ? default is not matching */
- if (!expression)
- return;
-
- unevaluatedExpression = xstrdup(expression);
-
- varState = cbdataReference (aVar);
-
- evaluate();
-}
-
-esiWhen::~esiWhen()
-{
- safe_free (unevaluatedExpression);
-
- cbdataReferenceDone(varState);
-}
-
-void
-esiWhen::evaluate()
-{
- if (!unevaluatedExpression)
- return;
-
- assert(varState);
-
- varState->feedData(unevaluatedExpression, strlen (unevaluatedExpression));
-
- char const *expression = varState->extractChar ();
-
- setTestResult(ESIExpression::Evaluate (expression));
-
- safe_free (expression);
-}
-
-esiWhen::esiWhen(esiWhen const &old) :
- esiSequence(old),
- testValue(false),
- unevaluatedExpression(nullptr),
- varState(nullptr)
-{
- if (old.unevaluatedExpression)
- unevaluatedExpression = xstrdup(old.unevaluatedExpression);
-}
-
-ESIElement::Pointer
-esiWhen::makeCacheable() const
-{
- return new esiWhen(*this);
-}
-
-ESIElement::Pointer
-esiWhen::makeUsable(esiTreeParentPtr newParent, ESIVarState &newVarState) const
-{
- esiWhen *resultW = new esiWhen (*this);
- ESIElement::Pointer result = resultW;
- resultW->parent = newParent;
- resultW->makeUsableElements(*this, newVarState);
- resultW->varState = cbdataReference (&newVarState);
- resultW->evaluate();
- return result;
-}
-
-/* TODO: implement surrogate targeting and control processing */
-int
-esiEnableProcessing (HttpReply *rep)
-{
- int rv = 0;
-
- if (rep->surrogate_control) {
- HttpHdrScTarget *sctusable =
- rep->surrogate_control->getMergedTarget(Config.Accel.surrogate_id);
-
- // found something targeted at us
- if (sctusable &&
- sctusable->hasContent() &&
- sctusable->content().pos("ESI/1.0")) {
- rv = 1;
- }
-
- delete sctusable;
- }
-
- return rv;
-}
-
-#endif /* USE_SQUID_ESI == 1 */
-
+++ /dev/null
-/*
- * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
- *
- * Squid software is distributed under GPLv2+ license and includes
- * contributions from numerous individuals and organizations.
- * Please see the COPYING and CONTRIBUTORS files for details.
- */
-
-#ifndef SQUID_SRC_ESI_ESI_H
-#define SQUID_SRC_ESI_ESI_H
-
-#include "clientStream.h"
-#include "sbuf/SBuf.h"
-
-#if !defined(ESI_STACK_DEPTH_LIMIT)
-#define ESI_STACK_DEPTH_LIMIT 20
-#endif
-
-/* ESI.c */
-extern CSR esiStreamRead;
-extern CSCB esiProcessStream;
-extern CSD esiStreamDetach;
-extern CSS esiStreamStatus;
-int esiEnableProcessing (HttpReply *);
-
-namespace Esi
-{
-
-typedef SBuf ErrorDetail;
-/// prepare an Esi::ErrorDetail for throw on ESI parser internal errors
-inline Esi::ErrorDetail Error(const char *msg) { return ErrorDetail(msg); }
-
-} // namespace Esi
-
-#endif /* SQUID_SRC_ESI_ESI_H */
-
+++ /dev/null
-/*
- * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
- *
- * Squid software is distributed under GPLv2+ license and includes
- * contributions from numerous individuals and organizations.
- * Please see the COPYING and CONTRIBUTORS files for details.
- */
-
-/* DEBUG: section 86 ESI processing */
-
-#ifndef SQUID_SRC_ESI_EXCEPT_H
-#define SQUID_SRC_ESI_EXCEPT_H
-
-#include "esi/Element.h"
-#include "esi/Sequence.h"
-
-/* esiExcept */
-
-class esiExcept : public esiSequence
-{
-
-public:
- esiExcept(esiTreeParentPtr aParent) : esiSequence (aParent) {}
-};
-
-#endif /* SQUID_SRC_ESI_EXCEPT_H */
-
+++ /dev/null
-/*
- * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
- *
- * Squid software is distributed under GPLv2+ license and includes
- * contributions from numerous individuals and organizations.
- * Please see the COPYING and CONTRIBUTORS files for details.
- */
-
-/* DEBUG: section 86 ESI processing */
-
-#include "squid.h"
-
-#if USE_SQUID_ESI && HAVE_LIBEXPAT
-
-#include "base/RunnersRegistry.h"
-#include "esi/ExpatParser.h"
-
-#include <memory>
-
-namespace Esi
-{
-
-class ExpatRr : public RegisteredRunner
-{
-public:
- void finalizeConfig() override
- {
- registration.reset(new ESIParser::Register("expat", &ESIExpatParser::NewParser));
- }
-
-private:
- std::unique_ptr<ESIParser::Register> registration;
-};
-
-}
-
-DefineRunnerRegistratorIn(Esi, ExpatRr);
-
-EsiParserDefinition(ESIExpatParser);
-
-ESIExpatParser::ESIExpatParser(ESIParserClient *aClient) : theClient (aClient)
-{
- /* TODO: grab the document encoding from the headers */
- p = XML_ParserCreateNS(nullptr,'|');
- XML_SetUserData (myParser(), static_cast<void *>(this));
- XML_SetElementHandler(myParser(), Start, End);
- XML_SetDefaultHandler(myParser(), Default);
- XML_SetCommentHandler(myParser(), Comment);
- XML_UseParserAsHandlerArg(myParser());
-}
-
-ESIExpatParser::~ESIExpatParser()
-{
- XML_ParserFree (myParser());
- p = nullptr;
-}
-
-void
-ESIExpatParser::Start(void *data,const XML_Char *el, const char **attr)
-{
- XML_Parser parser = static_cast<XML_Parser>(data);
- ESIExpatParser *me = (ESIExpatParser *)XML_GetUserData(parser);
- me->theClient->start (el, attr, XML_GetSpecifiedAttributeCount (parser));
-}
-
-void
-ESIExpatParser::End(void *data,const XML_Char *el)
-{
- XML_Parser parser = static_cast<XML_Parser>(data);
- ESIExpatParser *me = (ESIExpatParser *)XML_GetUserData(parser);
- me->theClient->end (el);
-}
-
-void
-ESIExpatParser::Default(void *data, const XML_Char *s, int len)
-{
- XML_Parser parser = static_cast<XML_Parser>(data);
- ESIExpatParser *me = (ESIExpatParser *)XML_GetUserData(parser);
- me->theClient->parserDefault (s, len);
-}
-
-void
-ESIExpatParser::Comment(void *data, const XML_Char *s)
-{
- XML_Parser parser = static_cast<XML_Parser>(data);
- ESIExpatParser *me = (ESIExpatParser *)XML_GetUserData(parser);
- me->theClient->parserComment (s);
-}
-
-bool
-ESIExpatParser::parse(char const *dataToParse, size_t const lengthOfData, bool const endOfStream)
-{
- return XML_Parse(myParser(), dataToParse, lengthOfData, endOfStream);
-}
-
-long int
-ESIExpatParser::lineNumber() const
-{
- return (long int)XML_GetCurrentLineNumber(myParser());
-}
-
-char const *
-ESIExpatParser::errorString() const
-{
- return XML_ErrorString(XML_GetErrorCode(myParser()));
-}
-
-#endif /* USE_SQUID_ESI */
-
+++ /dev/null
-/*
- * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
- *
- * Squid software is distributed under GPLv2+ license and includes
- * contributions from numerous individuals and organizations.
- * Please see the COPYING and CONTRIBUTORS files for details.
- */
-
-#ifndef SQUID_SRC_ESI_EXPATPARSER_H
-#define SQUID_SRC_ESI_EXPATPARSER_H
-
-#if USE_SQUID_ESI && HAVE_LIBEXPAT
-
-#include "esi/Parser.h"
-
-#if HAVE_EXPAT_H
-#include <expat.h>
-#endif
-
-class ESIExpatParser : public ESIParser
-{
-
-public:
- ESIExpatParser(ESIParserClient *);
- ~ESIExpatParser() override;
-
- /** \retval true on success */
- bool parse(char const *dataToParse, size_t const lengthOfData, bool const endOfStream) override;
-
- long int lineNumber() const override;
- char const * errorString() const override;
-
- EsiParserDeclaration;
-
-private:
- /** our parser */
- mutable XML_Parser p;
- static void Start(void *data, const XML_Char *el, const char **attr);
- static void End(void *data, const XML_Char *el);
- static void Default (void *data, const XML_Char *s, int len);
- static void Comment (void *data, const XML_Char *s);
- XML_Parser &myParser() const {return p;}
-
- ESIParserClient *theClient;
-};
-
-#endif /* USE_SQUID_ESI */
-
-#endif /* SQUID_SRC_ESI_EXPATPARSER_H */
-
+++ /dev/null
-/*
- * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
- *
- * Squid software is distributed under GPLv2+ license and includes
- * contributions from numerous individuals and organizations.
- * Please see the COPYING and CONTRIBUTORS files for details.
- */
-
-/* DEBUG: section 86 ESI processing */
-
-#include "squid.h"
-#include "debug/Stream.h"
-#include "esi/Esi.h"
-#include "esi/Expression.h"
-
-#include <cerrno>
-#include <cmath>
-
-/* stack precedence rules:
- * before pushing an operator onto the stack, the
- * top 2 elements are checked. if either has a higher
- * or equal precedence than the current operator, they
- * are evaluated.
- * Start of expression has 5 precedence,
- * end of expression has 0 precedence
- * literal has 1 as does expression results
- * | has 2
- * & has 3
- * ! has 4
- * == != < > <= >= has 5
- * ( has 5
- * ) has 0
- */
-
-typedef struct _stackmember stackmember;
-
-typedef int evaluate(stackmember * stack, int *depth, int whereAmI,
- stackmember * candidate);
-
-typedef enum {
- ESI_EXPR_INVALID,
- ESI_EXPR_LITERAL,
- ESI_EXPR_OR,
- ESI_EXPR_AND,
- ESI_EXPR_NOT,
- ESI_EXPR_START,
- ESI_EXPR_END,
- ESI_EXPR_EQ,
- ESI_EXPR_NOTEQ,
- ESI_EXPR_LESS,
- ESI_EXPR_LESSEQ,
- ESI_EXPR_MORE,
- ESI_EXPR_MOREEQ,
- ESI_EXPR_EXPR /* the result of an expr PRI 1 */
-} evaltype;
-
-typedef enum {
- ESI_LITERAL_STRING,
- ESI_LITERAL_FLOAT,
- ESI_LITERAL_INT,
- ESI_LITERAL_BOOL,
- ESI_LITERAL_INVALID
-} literalhint;
-
-struct _stackmember {
- evaluate *eval = nullptr;
- union Value {
- char *string;
- double floating;
- int integral;
- Value() { memset(this, 0, sizeof(*this)); }
- } value;
- literalhint valuestored = ESI_LITERAL_INVALID;
- evaltype valuetype = ESI_EXPR_INVALID;
- int precedence = 0;
-};
-
-static void cleanmember(stackmember *);
-static void stackpop(stackmember * s, int *depth);
-
-void
-cleanmember(stackmember * s)
-{
- if (s->valuetype == ESI_EXPR_LITERAL
- && s->valuestored == ESI_LITERAL_STRING) {
- safe_free(s->value.string);
- s->value.string = nullptr;
- }
-
-}
-
-void
-stackpop(stackmember * s, int *depth)
-{
- if (!(*depth)--)
- return;
-
- cleanmember(&s[*depth]);
-}
-
-static void
-stackpush(stackmember *stack, stackmember &item, int *depth)
-{
- if (*depth < 0)
- throw Esi::Error("ESIExpression stack has negative size");
- if (*depth >= ESI_STACK_DEPTH_LIMIT)
- throw Esi::Error("ESIExpression stack is full, cannot push");
-
- stack[(*depth)++] = item;
-}
-
-static evaluate evalnegate;
-static evaluate evalliteral;
-static evaluate evalor;
-static evaluate evaland;
-static evaluate evallesseq;
-static evaluate evallessthan;
-static evaluate evalmoreeq;
-static evaluate evalmorethan;
-static evaluate evalequals;
-static evaluate evalnotequals;
-static evaluate evalstartexpr;
-static evaluate evalendexpr;
-static evaluate evalexpr;
-static void dumpstack(stackmember * stack, int depth);
-static int addmember(stackmember * stack, int *stackdepth,
- stackmember * candidate);
-static int membercompare(stackmember a, stackmember b);
-static char const *trim(char const *s);
-static stackmember getsymbol(const char *s, char const **endptr);
-
-/* -2 = failed to compate
- * -1 = a less than b
- * 0 = a equal b
- * 2 - a more than b
- */
-int
-membercompare(stackmember a, stackmember b)
-{
- /* we can compare: sub expressions to sub expressions ,
- * literals to literals
- */
-
- if (!((a.valuetype == ESI_EXPR_LITERAL && b.valuetype == ESI_EXPR_LITERAL &&
- a.valuestored != ESI_LITERAL_INVALID && b.valuestored != ESI_LITERAL_INVALID) ||
- (a.valuetype == ESI_EXPR_EXPR && b.valuetype == ESI_EXPR_EXPR)))
- return -2;
-
- if (a.valuetype == ESI_EXPR_EXPR) {
- if (a.value.integral == b.value.integral)
- return 0;
- else
- return 1;
- } else if (a.valuestored == ESI_LITERAL_STRING) {
- if (b.valuestored == ESI_LITERAL_STRING) {
- int i =strcmp(a.value.string, b.value.string);
-
- if (i < 0)
- return -1;
-
- if (i > 0)
- return 1;
-
- return 0;
- } else {
- /* TODO: numeric to string conversion ? */
- debugs(86, DBG_IMPORTANT, "strcmp with non-string");
- return -2;
- }
- } else if (a.valuestored == ESI_LITERAL_FLOAT) {
- if (b.valuestored == ESI_LITERAL_INT) {
- if (fabs(a.value.floating - b.value.integral) < 0.00001)
- return 0;
- else if (a.value.floating < b.value.integral)
- return -1;
- else
- return 1;
- } else if (b.valuestored == ESI_LITERAL_FLOAT) {
- if (a.value.floating == b.value.floating)
- return 0;
- else if (a.value.floating < b.value.floating)
- return -1;
- else
- return 1;
- } else {
- /* TODO: attempt numeric converson again? */
- debugs(86, DBG_IMPORTANT, "floatcomp with non float or int");
- return -2;
- }
- } else if (a.valuestored == ESI_LITERAL_INT) {
- if (b.valuestored == ESI_LITERAL_INT) {
- if (a.value.integral == b.value.integral)
- return 0;
- else if (a.value.integral < b.value.integral)
- return -1;
- else
- return 1;
- } else if (b.valuestored == ESI_LITERAL_FLOAT) {
- if (fabs(a.value.integral - b.value.floating) < 0.00001)
- return 0;
- else if (a.value.integral < b.value.floating)
- return -1;
- else
- return 1;
- } else {
- /* TODO: attempt numeric converson again? */
- debugs(86, DBG_IMPORTANT, "intcomp vs non float non int");
- return -2;
- }
- }
-
- return -2;
-}
-
-/* return 0 on success, 1 on failure */
-int
-evalnegate(stackmember * stack, int *depth, int whereAmI, stackmember * candidate)
-{
- if (whereAmI != *depth - 2)
- /* invalid stack */
- return 1;
-
- if (whereAmI < 0)
- throw Esi::Error("negate expression location too small");
- if (*depth >= ESI_STACK_DEPTH_LIMIT)
- throw Esi::Error("negate expression too complex");
-
- if (stack[whereAmI + 1].valuetype != ESI_EXPR_EXPR)
- /* invalid operand */
- return 1;
-
- /* copy down */
- --(*depth);
-
- stack[whereAmI] = stack[(*depth)];
-
- cleanmember(candidate);
-
- if (stack[whereAmI].value.integral == 1)
- stack[whereAmI].value.integral = 0;
- else
- stack[whereAmI].value.integral = 1;
-
- return 0;
-}
-
-int
-evalliteral(stackmember * /* stack */, int * /* depth */, int /* whereAmI */, stackmember * /* candidate */)
-{
- debugs(86, DBG_IMPORTANT, "attempt to evaluate a literal");
- /* literals can't be evaluated */
- return 1;
-}
-
-int
-evalexpr(stackmember * /* stack */, int * /* depth */, int /* whereAmI */, stackmember * /* candidate */)
-{
- debugs(86, DBG_IMPORTANT, "attempt to evaluate a sub-expression result");
- /* sub-scpr's can't be evaluated */
- return 1;
-}
-
-int
-evalor(stackmember * stack, int *depth, int whereAmI, stackmember * candidate)
-{
- int rv;
- stackmember srv;
-
- if (*depth < 3)
- /* Not enough operands */
- return 1;
-
- if (whereAmI != *depth - 2)
- /* invalid stack */
- return 1;
-
- if (stack[whereAmI + 1].valuetype != ESI_EXPR_EXPR ||
- stack[whereAmI - 1].valuetype != ESI_EXPR_EXPR)
- /* invalid operand */
- return 1;
-
- rv = stack[whereAmI - 1].value.integral || stack[whereAmI + 1].value.integral;
-
- stackpop(stack, depth); /* arg rhs */
-
- stackpop(stack, depth); /* me */
-
- stackpop(stack, depth); /* arg lhs */
-
- srv.valuetype = ESI_EXPR_EXPR;
-
- srv.eval = evalliteral;
-
- srv.valuestored = ESI_LITERAL_BOOL;
-
- srv.value.integral = rv ? 1 : 0;
-
- srv.precedence = 1;
-
- stackpush(stack, srv, depth);
-
- /* we're out of way, try adding now */
- if (!addmember(stack, depth, candidate))
- /* Something wrong upstream */
- return 1;
-
- return 0;
-}
-
-int
-evaland(stackmember * stack, int *depth, int whereAmI, stackmember * candidate)
-{
- int rv;
- stackmember srv;
-
- if (*depth < 3)
- /* Not enough operands */
- return 1;
-
- if (whereAmI != *depth - 2)
- /* invalid stack */
- return 1;
-
- if (stack[whereAmI + 1].valuetype != ESI_EXPR_EXPR ||
- stack[whereAmI - 1].valuetype != ESI_EXPR_EXPR)
- /* invalid operand */
- return 1;
-
- rv = stack[whereAmI - 1].value.integral && stack[whereAmI + 1].value.integral;
-
- stackpop(stack, depth); /* arg rhs */
-
- stackpop(stack, depth); /* me */
-
- stackpop(stack, depth); /* arg lhs */
-
- srv.valuetype = ESI_EXPR_EXPR;
-
- srv.eval = evalexpr;
-
- srv.valuestored = ESI_LITERAL_BOOL;
-
- srv.value.integral = rv ? 1 : 0;
-
- srv.precedence = 1;
-
- stackpush(stack, srv, depth);
-
- /* we're out of way, try adding now */
- if (!addmember(stack, depth, candidate))
- /* Something wrong upstream */
- return 1;
-
- return 0;
-}
-
-int
-evallesseq(stackmember * stack, int *depth, int whereAmI, stackmember * candidate)
-{
- int rv;
- stackmember srv;
-
- if (*depth < 3)
- /* Not enough operands */
- return 1;
-
- if (whereAmI != *depth - 2)
- /* invalid stack */
- return 1;
-
- rv = membercompare(stack[whereAmI - 1], stack[whereAmI + 1]);
-
- if (rv == -2)
- /* invalid comparison */
- return 1;
-
- stackpop(stack, depth); /* arg rhs */
-
- stackpop(stack, depth); /* me */
-
- stackpop(stack, depth); /* arg lhs */
-
- srv.valuetype = ESI_EXPR_EXPR;
-
- srv.eval = evalexpr;
-
- srv.valuestored = ESI_LITERAL_BOOL;
-
- srv.value.integral = rv <= 0 ? 1 : 0;
-
- srv.precedence = 1;
-
- stackpush(stack, srv, depth);
-
- /* we're out of way, try adding now */
- if (!addmember(stack, depth, candidate))
- /* Something wrong upstream */
- return 1;
-
- /* debugs(86, DBG_IMPORTANT, "?= " << srv.value.integral << " "); */
- return 0;
-
-}
-
-int
-evallessthan(stackmember * stack, int *depth, int whereAmI, stackmember * candidate)
-{
- int rv;
- stackmember srv;
-
- if (*depth < 3)
- /* Not enough operands */
- return 1;
-
- if (whereAmI != *depth - 2)
- /* invalid stack */
- return 1;
-
- rv = membercompare(stack[whereAmI - 1], stack[whereAmI + 1]);
-
- if (rv == -2)
- /* invalid comparison */
- return 1;
-
- stackpop(stack, depth); /* arg rhs */
-
- stackpop(stack, depth); /* me */
-
- stackpop(stack, depth); /* arg lhs */
-
- srv.valuetype = ESI_EXPR_EXPR;
-
- srv.eval = evalexpr;
-
- srv.valuestored = ESI_LITERAL_BOOL;
-
- srv.value.integral = rv < 0 ? 1 : 0;
-
- srv.precedence = 1;
-
- stackpush(stack, srv, depth);
-
- /* we're out of way, try adding now */
- if (!addmember(stack, depth, candidate))
- /* Something wrong upstream */
- return 1;
-
- /* debugs(86, DBG_IMPORTANT, "?= " << srv.value.integral << " "); */
- return 0;
-
-}
-
-int
-evalmoreeq(stackmember * stack, int *depth, int whereAmI, stackmember * candidate)
-{
- int rv;
- stackmember srv;
-
- if (*depth < 3)
- /* Not enough operands */
- return 1;
-
- if (whereAmI != *depth - 2)
- /* invalid stack */
- return 1;
-
- rv = membercompare(stack[whereAmI - 1], stack[whereAmI + 1]);
-
- if (rv == -2)
- /* invalid comparison */
- return 1;
-
- stackpop(stack, depth); /* arg rhs */
-
- stackpop(stack, depth); /* me */
-
- stackpop(stack, depth); /* arg lhs */
-
- srv.valuetype = ESI_EXPR_EXPR;
-
- srv.eval = evalexpr;
-
- srv.valuestored = ESI_LITERAL_BOOL;
-
- srv.value.integral = rv >= 0 ? 1 : 0;
-
- srv.precedence = 1;
-
- stackpush(stack, srv, depth);
-
- /* we're out of way, try adding now */
- if (!addmember(stack, depth, candidate))
- /* Something wrong upstream */
- return 1;
-
- /* debugs(86, DBG_IMPORTANT, "?= " << srv.value.integral << " "); */
- return 0;
-
-}
-
-int
-evalmorethan(stackmember * stack, int *depth, int whereAmI, stackmember * candidate)
-{
- int rv;
- stackmember srv;
-
- if (*depth < 3)
- /* Not enough operands */
- return 1;
-
- if (whereAmI != *depth - 2)
- /* invalid stack */
- return 1;
-
- rv = membercompare(stack[whereAmI - 1], stack[whereAmI + 1]);
-
- if (rv == -2)
- /* invalid comparison */
- return 1;
-
- stackpop(stack, depth); /* arg rhs */
-
- stackpop(stack, depth); /* me */
-
- stackpop(stack, depth); /* arg lhs */
-
- srv.valuetype = ESI_EXPR_EXPR;
-
- srv.eval = evalexpr;
-
- srv.valuestored = ESI_LITERAL_BOOL;
-
- srv.value.integral = rv > 0 ? 1 : 0;
-
- srv.precedence = 1;
-
- stackpush(stack, srv, depth);
-
- /* we're out of way, try adding now */
- if (!addmember(stack, depth, candidate))
- /* Something wrong upstream */
- return 1;
-
- /* debugs(86, DBG_IMPORTANT, "?= " << srv.value.integral << " "); */
- return 0;
-
-}
-
-int
-evalequals(stackmember * stack, int *depth, int whereAmI,
- stackmember * candidate)
-{
- int rv;
- stackmember srv;
-
- if (*depth < 3)
- /* Not enough operands */
- return 1;
-
- if (whereAmI != *depth - 2)
- /* invalid stack */
- return 1;
-
- rv = membercompare(stack[whereAmI - 1], stack[whereAmI + 1]);
-
- if (rv == -2)
- /* invalid comparison */
- return 1;
-
- stackpop(stack, depth); /* arg rhs */
-
- stackpop(stack, depth); /* me */
-
- stackpop(stack, depth); /* arg lhs */
-
- srv.valuetype = ESI_EXPR_EXPR;
-
- srv.eval = evalexpr;
-
- srv.valuestored = ESI_LITERAL_BOOL;
-
- srv.value.integral = rv ? 0 : 1;
-
- srv.precedence = 1;
-
- stackpush(stack, srv, depth);
-
- /* we're out of way, try adding now */
- if (!addmember(stack, depth, candidate))
- /* Something wrong upstream */
- return 1;
-
- /* debugs(86, DBG_IMPORTANT, "?= " << srv.value.integral << " "); */
- return 0;
-}
-
-int
-evalnotequals(stackmember * stack, int *depth, int whereAmI, stackmember * candidate)
-{
- int rv;
- stackmember srv;
-
- if (*depth < 3)
- /* Not enough operands */
- return 1;
-
- if (whereAmI != *depth - 2)
- /* invalid stack */
- return 1;
-
- rv = membercompare(stack[whereAmI - 1], stack[whereAmI + 1]);
-
- if (rv == -2)
- /* invalid comparison */
- return 1;
-
- stackpop(stack, depth); /* arg rhs */
-
- stackpop(stack, depth); /* me */
-
- stackpop(stack, depth); /* arg lhs */
-
- srv.valuetype = ESI_EXPR_EXPR;
-
- srv.eval = evalexpr;
-
- srv.valuestored = ESI_LITERAL_BOOL;
-
- srv.value.integral = rv ? 1 : 0;
-
- srv.precedence = 1;
-
- stackpush(stack, srv, depth);
-
- /* we're out of way, try adding now */
- if (!addmember(stack, depth, candidate))
- /* Something wrong upstream */
- return 1;
-
- /* debugs(86, DBG_IMPORTANT, "?= " << srv.value.integral << " "); */
- return 0;
-}
-
-int
-evalstartexpr(stackmember * stack, int *depth, int whereAmI, stackmember * candidate)
-{
- /* debugs(86, DBG_IMPORTANT, "?("); */
-
- if (whereAmI != *depth - 2)
- /* invalid stack */
- return 1;
-
- /* Only valid when RHS is an end bracket */
- if (candidate->valuetype != ESI_EXPR_END)
- return 1;
-
- --(*depth);
-
- stack[whereAmI] = stack[(*depth)];
-
- cleanmember(candidate);
-
- return 0;
-}
-
-int
-evalendexpr(stackmember * /* stack */, int * /* depth */, int /* whereAmI */, stackmember * /* candidate */)
-{
- /* Can't evaluate ) brackets */
- return 1;
-}
-
-char const *
-trim(char const *s)
-{
- while (*s == ' ')
- ++s;
-
- return s;
-}
-
-stackmember
-getsymbol(const char *s, char const **endptr)
-{
- stackmember rv;
- char *end;
- char const *origs = s;
- /* trim whitespace */
- s = trim(s);
- rv.eval = nullptr; /* A literal */
- rv.valuetype = ESI_EXPR_INVALID;
- rv.valuestored = ESI_LITERAL_INVALID;
- rv.precedence = 1; /* A literal */
-
- if (('0' <= *s && *s <= '9') || *s == '-') {
- size_t length = strspn(s, "0123456789.");
- char const *point;
-
- if ((point = strchr(s, '.')) && point - s < (ssize_t)length) {
- /* floating point */
- errno=0; /* reset errno */
- rv.value.floating = strtod(s, &end);
-
- if (s == end || errno) {
- /* Couldn't convert to float */
- debugs(86, DBG_IMPORTANT, "ERROR: failed to convert '" << s << "' to float ");
- *endptr = origs;
- } else {
- debugs(86,6, "found " << rv.value.floating << " of length " << end - s);
- *endptr = end;
- rv.eval = evalliteral;
- rv.valuestored = ESI_LITERAL_FLOAT;
- rv.valuetype = ESI_EXPR_LITERAL;
- rv.precedence = 1;
- }
- } else {
- /* INT */
- errno=0; /* reset errno */
- rv.value.integral = strtol(s, &end, 0);
-
- if (s == end || errno) {
- /* Couldn't convert to int */
- debugs(86, DBG_IMPORTANT, "ERROR: failed to convert '" << s << "' to int ");
- *endptr = origs;
- } else {
- debugs(86,6, "found " << rv.value.integral << " of length " << end - s);
- *endptr = end;
- rv.eval = evalliteral;
- rv.valuestored = ESI_LITERAL_INT;
- rv.valuetype = ESI_EXPR_LITERAL;
- rv.precedence = 1;
- }
- }
- } else if ('!' == *s) {
- if ('=' == *(s + 1)) {
- debugs(86, 6, "found !=");
- *endptr = s + 2;
- rv.eval = evalnotequals;
- rv.valuetype = ESI_EXPR_NOTEQ;
- rv.precedence = 5;
- } else {
- debugs(86, 6, "found !");
- *endptr = s + 1;
- rv.valuetype = ESI_EXPR_NOT;
- rv.precedence = 4;
- rv.eval = evalnegate;
- }
- } else if ('\'' == *s) {
- char const *t = s + 1;
- debugs(86, 6, "found \'");
-
- while (*t != '\'' && *t)
- ++t;
-
- if (!*t) {
- debugs(86, DBG_IMPORTANT, "ERROR: missing end \' in '" << s << "'");
- *endptr = origs;
- } else {
- *endptr = t + 1;
- /* Special case for zero length strings */
-
- if (t - s - 1)
- rv.value.string = xstrndup(s + 1, t - (s + 1) + 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;
-
- debugs(86, 6, "found string '" << rv.value.string << "'");
- }
- } else if ('(' == *s) {
- debugs(86, 6, "found subexpr start");
- *endptr = s + 1;
- rv.valuetype = ESI_EXPR_START;
- rv.precedence = 5;
- rv.eval = evalstartexpr;
- } else if (')' == *s) {
- debugs(86, 6, "found subexpr end");
- *endptr = s + 1;
- rv.valuetype = ESI_EXPR_END;
- rv.precedence = 0;
- rv.eval = evalendexpr;
- } else if ('&' == *s) {
- debugs(86, 6, "found AND");
- *endptr = s + 1;
- rv.valuetype = ESI_EXPR_AND;
- rv.precedence = 3;
- rv.eval = evaland;
- } else if ('|' == *s) {
- debugs(86, 6, "found OR");
- *endptr = s + 1;
- rv.valuetype = ESI_EXPR_OR;
- rv.precedence = 2;
- rv.eval = evalor;
- } else if ('=' == *s) {
- if ('=' == *(s + 1)) {
- debugs(86, 6, "found equals");
- *endptr = s + 2;
- rv.valuetype = ESI_EXPR_EQ;
- rv.precedence = 5;
- rv.eval = evalequals;
- } else {
- debugs(86, DBG_IMPORTANT, "ERROR: invalid expr '" << s << "'");
- *endptr = origs;
- }
- } else if ('<' == *s) {
- if ('=' == *(s + 1)) {
- debugs(86, 6, "found less-equals");
- *endptr = s + 2;
- rv.valuetype = ESI_EXPR_LESSEQ;
- rv.precedence = 5;
- rv.eval = evallesseq;
- } else {
- debugs(86, 6, "found less than");
- *endptr = s + 1;
- rv.valuetype = ESI_EXPR_LESS;
- rv.precedence = 5;
- rv.eval = evallessthan;
- }
- } else if ('>' == *s) {
- if ('=' == *(s + 1)) {
- debugs(86, 6, "found more-equals");
- *endptr = s + 2;
- rv.valuetype = ESI_EXPR_MOREEQ;
- rv.precedence = 5;
- rv.eval = evalmoreeq;
- } else {
- debugs(86, 6, "found more than");
- *endptr = s + 1;
- rv.valuetype = ESI_EXPR_MORE;
- rv.precedence = 5;
- rv.eval = evalmorethan;
- }
- } else if (!strncmp(s, "false", 5)) {
- debugs(86, 5, "getsymbol: found variable result 'false'");
- *endptr = s + 5;
- rv.valuetype = ESI_EXPR_EXPR;
- rv.valuestored = ESI_LITERAL_BOOL;
- rv.value.integral = 0;
- rv.precedence = 1;
- rv.eval = evalexpr;
- } else if (!strncmp(s, "true", 4)) {
- debugs(86, 5, "getsymbol: found variable result 'true'");
- *endptr = s + 4;
- rv.valuetype = ESI_EXPR_EXPR;
- rv.valuestored = ESI_LITERAL_BOOL;
- rv.value.integral = 1;
- rv.precedence = 1;
- rv.eval = evalexpr;
- } else {
- debugs(86, DBG_IMPORTANT, "ERROR: invalid expr '" << s << "'");
- *endptr = origs;
- }
-
- return rv;
-}
-
-static void
-printLiteral(std::ostream &os, const stackmember &s)
-{
- switch (s.valuestored) {
-
- case ESI_LITERAL_INVALID:
- os << " Invalid ";
- break;
-
- case ESI_LITERAL_FLOAT:
- os << s.value.floating;
- break;
-
- case ESI_LITERAL_STRING:
- os << '\'' << s.value.string << '\'';
- break;
-
- case ESI_LITERAL_INT:
- os << s.value.integral;
- break;
-
- case ESI_LITERAL_BOOL:
- os << (s.value.integral ? "true" : "false");
- }
-}
-
-static std::ostream &
-operator <<(std::ostream &os, const stackmember &s)
-{
- switch (s.valuetype) {
-
- case ESI_EXPR_INVALID:
- os << " Invalid ";
- break;
-
- case ESI_EXPR_LITERAL:
- printLiteral(os, s);
- break;
-
- case ESI_EXPR_EXPR:
- os << (s.value.integral ? "true" : "false");
- break;
-
- case ESI_EXPR_OR:
- os << "|";
- break;
-
- case ESI_EXPR_AND:
- os << "&";
- break;
-
- case ESI_EXPR_NOT:
- os << "!";
- break;
-
- case ESI_EXPR_START:
- os << "(";
- break;
-
- case ESI_EXPR_END:
- os << ")";
- break;
-
- case ESI_EXPR_EQ:
- os << "==";
- break;
-
- case ESI_EXPR_NOTEQ:
- os << "!=";
- break;
-
- case ESI_EXPR_LESS:
- os << "<";
- break;
-
- case ESI_EXPR_LESSEQ:
- os << "<=";
- break;
-
- case ESI_EXPR_MORE:
- os << ">";
- break;
-
- case ESI_EXPR_MOREEQ:
- os << ">=";
- break;
- }
-
- return os;
-}
-
-void
-dumpstack(stackmember * stack, int depth)
-{
- if (depth) {
- std::ostringstream buf;
- for (int i = 0; i < depth; ++i)
- buf << stack[i];
- debugs(86,1, buf.str());
- }
-}
-
-int
-addmember(stackmember * stack, int *stackdepth, stackmember * candidate)
-{
- if (candidate->valuetype != ESI_EXPR_LITERAL && *stackdepth > 1) {
- /* !(!(a==b))) is why that's safe */
- /* strictly less than until we unwind */
-
- if (*stackdepth >= ESI_STACK_DEPTH_LIMIT)
- throw Esi::Error("ESI expression too complex to add member");
-
- if (candidate->precedence < stack[*stackdepth - 1].precedence ||
- candidate->precedence < stack[*stackdepth - 2].precedence) {
- /* must be an operator */
-
- if (stack[*stackdepth - 2].valuetype == ESI_EXPR_LITERAL ||
- stack[*stackdepth - 2].valuetype == ESI_EXPR_INVALID ||
- stack[*stackdepth - 2].eval(stack, stackdepth,
- *stackdepth - 2, candidate)) {
- /* cleanup candidate and stack */
- dumpstack(stack, *stackdepth);
- cleanmember(candidate);
- debugs(86, DBG_IMPORTANT, "ERROR: invalid expression");
- return 0;
- }
- } else {
- stackpush(stack, *candidate, stackdepth);
- }
- } else if (candidate->valuetype != ESI_EXPR_INVALID)
- stackpush(stack, *candidate, stackdepth);
-
- return 1;
-}
-
-int
-ESIExpression::Evaluate(char const *s)
-{
- stackmember stack[ESI_STACK_DEPTH_LIMIT];
- int stackdepth = 0;
- char const *end;
-
- while (*s) {
- stackmember candidate = getsymbol(s, &end);
-
- if (candidate.valuetype != ESI_EXPR_INVALID) {
- assert(s != end);
-
- if (!addmember(stack, &stackdepth, &candidate)) {
- return 0;
- }
-
- s = end;
- } else {
- assert (s == end);
- debugs(86, DBG_IMPORTANT, "ERROR: failed parsing expression");
- return 0;
- }
- }
-
- if (stackdepth > 1) {
- stackmember rv;
- rv.valuetype = ESI_EXPR_INVALID;
- rv.precedence = 0;
-
- if (stack[stackdepth - 2].
- eval(stack, &stackdepth, stackdepth - 2, &rv)) {
- /* special case - leading operator failed */
- debugs(86, DBG_IMPORTANT, "ERROR: invalid expression");
- return 0;
- }
- }
-
- if (stackdepth == 0) {
- /* Empty expression - evaluate to false */
- return 0;
- }
-
- /* if we hit here, we think we have a valid result */
- assert(stackdepth == 1);
-
- assert(stack[0].valuetype == ESI_EXPR_EXPR);
-
- return stack[0].value.integral ? 1 : 0;
-}
-
+++ /dev/null
-/*
- * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
- *
- * Squid software is distributed under GPLv2+ license and includes
- * contributions from numerous individuals and organizations.
- * Please see the COPYING and CONTRIBUTORS files for details.
- */
-
-/* DEBUG: section 86 ESI processing */
-
-#ifndef SQUID_SRC_ESI_EXPRESSION_H
-#define SQUID_SRC_ESI_EXPRESSION_H
-
-class ESIExpression
-{
-
-public:
- static int Evaluate (char const *);
-};
-
-#endif /* SQUID_SRC_ESI_EXPRESSION_H */
-
+++ /dev/null
-/*
- * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
- *
- * Squid software is distributed under GPLv2+ license and includes
- * contributions from numerous individuals and organizations.
- * Please see the COPYING and CONTRIBUTORS files for details.
- */
-
-/* DEBUG: section 86 ESI processing */
-
-#include "squid.h"
-
-#if USE_SQUID_ESI
-
-#include "client_side.h"
-#include "client_side_request.h"
-#include "esi/Include.h"
-#include "esi/VarState.h"
-#include "fatal.h"
-#include "http/Stream.h"
-#include "HttpReply.h"
-#include "log/access_log.h"
-
-CBDATA_CLASS_INIT (ESIStreamContext);
-
-/* 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.
- *
- * Pre-condition:
- * The request is an internal ESI subrequest.
- * data context is not NULL
- * There are no more entries in the stream chain.
- * The caller is responsible for creation and deletion of the Reply headers.
- *
- \note
- * Bug 975, bug 1566 : delete rep; 2006/09/02: TS, #975
- *
- * This was causing double-deletes. Its possible that not deleting
- * it here will cause memory leaks, but if so, this delete should
- * not be reinstated or it will trigger bug #975 again - RBC 20060903
- */
-void
-esiBufferRecipient (clientStreamNode *node, ClientHttpRequest *http, HttpReply *rep, StoreIOBuffer receivedData)
-{
- /* Test preconditions */
- assert (node != nullptr);
- /* 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 == nullptr);
- assert (http->getConn() == nullptr);
-
- ESIStreamContext::Pointer esiStream = dynamic_cast<ESIStreamContext *>(node->data.getRaw());
- assert (esiStream.getRaw() != nullptr);
- /* If segments become more flexible, ignore thisNode */
- assert (receivedData.length <= sizeof(esiStream->localbuffer->buf));
- assert (!esiStream->finished);
-
- debugs (86,5, "rep " << rep << " body " << receivedData.data << " len " << receivedData.length);
- assert (node->readBuffer.offset == receivedData.offset || receivedData.length == 0);
-
- /* trivial case */
-
- if (http->out.offset != 0) {
- assert(rep == nullptr);
- } else {
- if (rep) {
- if (rep->sline.status() != Http::scOkay) {
- rep = nullptr;
- esiStream->include->includeFail (esiStream);
- esiStream->finished = 1;
- httpRequestFree (http);
- return;
- }
-
- rep = nullptr;
- }
- }
-
- if (receivedData.data && receivedData.length) {
- http->out.offset += receivedData.length;
-
- if (receivedData.data >= esiStream->localbuffer->buf &&
- receivedData.data < &esiStream->localbuffer->buf[sizeof(esiStream->localbuffer->buf)]) {
- /* original static buffer */
-
- if (receivedData.data != esiStream->localbuffer->buf) {
- /* But not the start of it */
- memmove(esiStream->localbuffer->buf, receivedData.data, receivedData.length);
- }
-
- esiStream->localbuffer->len = receivedData.length;
- } else {
- assert (esiStream->buffer.getRaw() != nullptr);
- esiStream->buffer->len = receivedData.length;
- }
- }
-
- /* EOF / Read error / aborted entry */
- if (rep == nullptr && receivedData.data == nullptr && receivedData.length == 0) {
- /* TODO: get stream status to test the entry for aborts */
- debugs(86, 5, "Finished reading upstream data in subrequest");
- esiStream->include->subRequestDone (esiStream, true);
- esiStream->finished = 1;
- httpRequestFree (http);
- return;
- }
-
- switch (clientStreamStatus (node, http)) {
-
- case STREAM_UNPLANNED_COMPLETE:
- case STREAM_COMPLETE: /* ok */
- debugs(86, 3, "ESI subrequest finished OK");
- esiStream->include->subRequestDone (esiStream, true);
- esiStream->finished = 1;
- httpRequestFree (http);
- return;
-
- case STREAM_FAILED:
- debugs(86, DBG_IMPORTANT, "ERROR: ESI subrequest failed transfer");
- esiStream->include->includeFail (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);
- debugs(86, 5, "Requested more data for ESI subrequest");
- }
-
- break;
-
- default:
- fatal ("Hit unreachable code in esiBufferRecipient\n");
- }
-
-}
-
-/* esiStream functions */
-ESIStreamContext::~ESIStreamContext()
-{
- freeResources();
-}
-
-void
-ESIStreamContext::freeResources()
-{
- debugs(86, 5, "Freeing stream context resources.");
- buffer = nullptr;
- localbuffer = nullptr;
- include = nullptr;
-}
-
-ESIStreamContext *
-ESIStreamContextNew (ESIIncludePtr include)
-{
- ESIStreamContext *rv = new ESIStreamContext;
- rv->include = include;
- return rv;
-}
-
-/* ESIInclude */
-ESIInclude::~ESIInclude()
-{
- debugs(86, 5, "ESIInclude::Free " << this);
- ESISegmentFreeList (srccontent);
- ESISegmentFreeList (altcontent);
- cbdataReferenceDone (varState);
- safe_free (srcurl);
- safe_free (alturl);
-}
-
-void
-ESIInclude::finish()
-{
- parent = nullptr;
-}
-
-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) :
- varState(nullptr),
- srcurl(nullptr),
- alturl(nullptr),
- parent(nullptr),
- started(false),
- sent(false)
-{
- memset(&flags, 0, sizeof(flags));
- 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)
-{
- tempheaders.update(&vars->header());
- tempheaders.removeHopByHopEntries();
-}
-
-void
-ESIInclude::Start (ESIStreamContext::Pointer stream, char const *url, ESIVarState *vars)
-{
- if (!stream.getRaw())
- return;
-
- HttpHeader tempheaders(hoRequest);
-
- prepareRequestHeaders(tempheaders, vars);
-
- /* Ensure variable state is clean */
- vars->feedData(url, strlen (url));
-
- /* tempUrl is eaten by the request */
- char const *tempUrl = vars->extractChar ();
-
- debugs(86, 5, "ESIIncludeStart: Starting subrequest with url '" << tempUrl << "'");
- const auto mx = MasterXaction::MakePortless<XactionInitiator::initEsi>();
- if (clientBeginRequest(Http::METHOD_GET, tempUrl, esiBufferRecipient, esiBufferDetach, stream, &tempheaders, stream->localbuffer->buf, HTTP_REQBUF_SZ, mx)) {
- debugs(86, DBG_CRITICAL, "ERROR: starting new ESI subrequest failed");
- }
-
- tempheaders.clean();
-}
-
-ESIInclude::ESIInclude(esiTreeParentPtr aParent, int attrcount, char const **attr, ESIContext *aContext) :
- varState(nullptr),
- srcurl(nullptr),
- alturl(nullptr),
- parent(aParent),
- started(false),
- sent(false)
-{
- assert (aContext);
- memset(&flags, 0, sizeof(flags));
-
- for (int i = 0; i < attrcount && attr[i]; i += 2) {
- if (!strcmp(attr[i],"src")) {
- /* Start a request for thisNode url */
- debugs(86, 5, "ESIIncludeNew: Requesting source '" << attr[i+1] << "'");
-
- /* TODO: don't assert on thisNode, ignore the duplicate */
- assert (src.getRaw() == nullptr);
- src = ESIStreamContextNew (this);
- assert (src.getRaw() != nullptr);
- 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
- */
- debugs(86, 5, "ESIIncludeNew: Requesting alternate '" << attr[i+1] << "'");
-
- assert (alt.getRaw() == nullptr); /* TODO: fix? */
- alt = ESIStreamContextNew (this);
- assert (alt.getRaw() != nullptr);
- alturl = xstrdup(attr[i+1]);
- } else if (!strcmp(attr[i],"onerror")) {
- if (!strcmp(attr[i+1], "continue")) {
- flags.onerrorcontinue = 1;
- } else {
- /* ignore mistyped attributes */
- debugs(86, DBG_IMPORTANT, "ERROR: invalid value for onerror='" << 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 = nullptr;
-
- debugs(86, DBG_IMPORTANT, "ESIIncludeNew: esi:include with no src attributes");
-
- flags.failed = 1;
- }
-}
-
-void
-ESIInclude::render(ESISegment::Pointer output)
-{
- if (sent)
- return;
-
- ESISegment::Pointer myout;
-
- debugs(86, 5, "ESIIncludeRender: Rendering include " << this);
-
- assert (flags.finished || (flags.failed && flags.onerrorcontinue));
-
- if (flags.failed && flags.onerrorcontinue) {
- return;
- }
-
- /* Render the content */
- if (srccontent.getRaw()) {
- myout = srccontent;
- srccontent = nullptr;
- } else if (altcontent.getRaw()) {
- myout = altcontent;
- altcontent = nullptr;
- } else
- fatal ("ESIIncludeRender called with no content, and no failure!\n");
-
- assert (output->next == nullptr);
-
- output->next = myout;
-
- sent = true;
-}
-
-esiProcessResult_t
-ESIInclude::process(int)
-{
- /* Prevent refcount race leading to free */
- Pointer me (this);
- start();
- debugs(86, 5, "ESIIncludeRender: Processing include " << 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::includeFail (ESIStreamContext::Pointer stream)
-{
- subRequestDone (stream, false);
-}
-
-bool
-ESIInclude::dataNeeded() const
-{
- return !(flags.finished || flags.failed);
-}
-
-void
-ESIInclude::subRequestDone (ESIStreamContext::Pointer stream, bool success)
-{
- if (!dataNeeded())
- return;
-
- if (stream == src) {
- debugs(86, 3, "ESIInclude::subRequestDone: " << srcurl);
-
- if (success) {
- /* copy the lead segment */
- debugs(86, 3, "ESIIncludeSubRequestDone: Src OK - include PASSED.");
- assert (!srccontent.getRaw());
- ESISegment::ListTransfer (stream->localbuffer, srccontent);
- /* we're done! */
- flags.finished = 1;
- } else {
- /* Fail if there is no alt being retrieved */
- debugs(86, 3, "ESIIncludeSubRequestDone: Src FAILED");
-
- if (!(alt.getRaw() || altcontent.getRaw())) {
- debugs(86, 3, "ESIIncludeSubRequestDone: Include FAILED - No ALT");
- flags.failed = 1;
- } else if (altcontent.getRaw()) {
- debugs(86, 3, "ESIIncludeSubRequestDone: Include PASSED - ALT already Complete");
- /* ALT was already retrieved, we are done */
- flags.finished = 1;
- }
- }
-
- src = nullptr;
- } else if (stream == alt) {
- debugs(86, 3, "ESIInclude::subRequestDone: " << alturl);
-
- if (success) {
- debugs(86, 3, "ESIIncludeSubRequestDone: ALT OK.");
- /* 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 */
- debugs(86, 3, "ESIIncludeSubRequestDone: Include PASSED - SRC already failed.");
- flags.finished = 1;
- }
- } else {
- if (!(src.getRaw() || srccontent.getRaw())) {
- debugs(86, 3, "ESIIncludeSubRequestDone: ALT FAILED, Include FAILED - SRC already failed");
- /* src already failed */
- flags.failed = 1;
- }
- }
-
- alt = nullptr;
- } else {
- fatal ("ESIIncludeSubRequestDone: non-owned stream found!\n");
- }
-
- if (flags.finished || flags.failed) {
- /* Kick ESI Processor */
- debugs (86, 5, "ESIInclude " << this <<
- " SubRequest " << stream.getRaw() <<
- " completed, kicking processor , status " <<
- (flags.finished ? "OK" : "FAILED"));
- /* There is a race condition - and we have no reproducible test case -
- * during a subrequest the parent will get set to NULL, which is not
- * meant to be possible. Rather than killing squid, we let it leak
- * memory but complain in the log.
- *
- * Someone wanting to debug this could well start by running squid with
- * a hardware breakpoint set to this location.
- * Its probably due to parent being set to null - by a call to
- * 'this.finish' while the subrequest is still not completed.
- */
- if (parent.getRaw() == nullptr) {
- debugs(86, DBG_CRITICAL, "ERROR: Squid Bug #951: ESIInclude::subRequestDone: Sub request completed "
- "after finish() called and parent unlinked. Unable to "
- "continue handling the request, and may be memory leaking. "
- "See http://www.squid-cache.org/bugs/show_bug.cgi?id=951 - we "
- "are looking for a reproducible test case. This will require "
- "an ESI template with includes, probably with alt-options, "
- "and we're likely to need traffic dumps to allow us to "
- "reconstruct the exact tcp handling sequences to trigger this "
- "rather elusive bug.");
- return;
- }
- assert (parent.getRaw());
-
- if (!flags.failed) {
- sent = true;
- parent->provideData (srccontent.getRaw() ? srccontent:altcontent,this);
-
- if (srccontent.getRaw())
- srccontent = nullptr;
- else
- altcontent = nullptr;
- } 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.");
- }
-}
-
-#endif /* USE_SQUID_ESI */
-
+++ /dev/null
-/*
- * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
- *
- * Squid software is distributed under GPLv2+ license and includes
- * contributions from numerous individuals and organizations.
- * Please see the COPYING and CONTRIBUTORS files for details.
- */
-
-/* DEBUG: section 86 ESI processing */
-
-#ifndef SQUID_SRC_ESI_INCLUDE_H
-#define SQUID_SRC_ESI_INCLUDE_H
-
-#include "esi/Context.h"
-#include "esi/Element.h"
-#include "esi/Segment.h"
-#include "HttpHeader.h"
-
-class ESIInclude;
-typedef RefCount<ESIInclude> ESIIncludePtr;
-
-class ESIStreamContext : public RefCountable
-{
- CBDATA_CLASS(ESIStreamContext);
-
-public:
- typedef RefCount<ESIStreamContext> Pointer;
- ESIStreamContext();
- ~ESIStreamContext() override;
- void freeResources();
- int finished;
- ESIIncludePtr include;
- ESISegment::Pointer localbuffer;
- ESISegment::Pointer buffer;
-};
-
-class ESIInclude : public ESIElement
-{
- MEMPROXY_CLASS(ESIInclude);
-
-public:
- ESIInclude(esiTreeParentPtr, int attributes, const char **attr, ESIContext *);
- ~ESIInclude() override;
- void render(ESISegment::Pointer) override;
- esiProcessResult_t process (int dovars) override;
- Pointer makeCacheable() const override;
- Pointer makeUsable(esiTreeParentPtr, ESIVarState &) const override;
- void subRequestDone (ESIStreamContext::Pointer, bool);
-
- struct {
- unsigned int onerrorcontinue:1; /* on error return zero data */
- unsigned int failed:1; /* Failed to process completely */
- unsigned 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 includeFail(ESIStreamContext::Pointer);
- void finish() override;
-
-private:
- 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_SRC_ESI_INCLUDE_H */
-
+++ /dev/null
-/*
- * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
- *
- * Squid software is distributed under GPLv2+ license and includes
- * contributions from numerous individuals and organizations.
- * Please see the COPYING and CONTRIBUTORS files for details.
- */
-
-/*
- * The ESI Libxml2 parser is Copyright (c) 2004 by Joachim Bauch
- * http://www.joachim-bauch.de
- * mail@joachim-bauch.de
- */
-
-#include "squid.h"
-
-#if USE_SQUID_ESI && HAVE_LIBXML2
-
-#include "base/RunnersRegistry.h"
-#include "esi/Libxml2Parser.h"
-
-#include <memory>
-
-namespace Esi
-{
-
-class Libxml2Rr : public RegisteredRunner
-{
-public:
- void finalizeConfig() override
- {
- registration.reset(new ESIParser::Register("libxml2", &ESILibxml2Parser::NewParser));
- }
-
-private:
- std::unique_ptr<ESIParser::Register> registration;
-};
-
-}
-
-DefineRunnerRegistratorIn(Esi, Libxml2Rr);
-
-// the global document that will store the resolved entity
-// definitions
-static htmlDocPtr entity_doc = nullptr;
-
-EsiParserDefinition(ESILibxml2Parser);
-
-// the SAX callback functions
-static void
-esi_startElementSAXFunc(void * ctx, const xmlChar * name, const xmlChar ** atts)
-{
- int count=0;
- xmlChar **tmp = (xmlChar **)atts;
-
- while (tmp && *tmp != nullptr) {
- ++count;
- ++tmp;
- }
-
- // we increased on every key and value
- count /= 2;
-
- ESILibxml2Parser *p = (ESILibxml2Parser *)ctx;
-
- p->getClient()->start((const char *)name, (const char **)atts, count);
-}
-
-static void
-esi_endElementSAXFunc(void *ctx, const xmlChar *name)
-{
- ESILibxml2Parser *p = (ESILibxml2Parser *)ctx;
- p->getClient()->end((const char *)name);
-}
-
-static void
-esi_commentSAXFunc(void *ctx, const xmlChar *value)
-{
- ESILibxml2Parser *p = (ESILibxml2Parser *)ctx;
- p->getClient()->parserComment((const char *)value);
-}
-
-static void
-esi_charactersSAXFunc(void *ctx, const xmlChar *ch, int len)
-{
- ESILibxml2Parser *p = (ESILibxml2Parser *)ctx;
- p->getClient()->parserDefault((const char *)ch, len);
-}
-
-static xmlEntityPtr
-esi_getEntitySAXFunc(void * /* ctx */, const xmlChar *name)
-{
- xmlEntityPtr res = xmlGetDocEntity(entity_doc, name);
-
- if (res == nullptr) {
- const htmlEntityDesc *ent = htmlEntityLookup(name);
-
- if (ent != nullptr) {
- char tmp[32];
- snprintf(tmp, 32, "&#%d;", ent->value);
- res = xmlAddDocEntity(entity_doc, (const xmlChar *)name, XML_INTERNAL_GENERAL_ENTITY, nullptr, nullptr, (const xmlChar *)tmp);
- }
- }
-
- return res;
-}
-
-ESILibxml2Parser::ESILibxml2Parser(ESIParserClient *aClient) : theClient (aClient)
-{
- xmlSAXHandler sax;
- xmlInitParser();
- memset(&sax, 0, sizeof(sax));
- sax.startElement = esi_startElementSAXFunc;
- sax.endElement = esi_endElementSAXFunc;
- sax.comment = esi_commentSAXFunc;
- sax.characters = esi_charactersSAXFunc;
- sax.getEntity = esi_getEntitySAXFunc;
-
- /* TODO: grab the document encoding from the headers */
- parser = xmlCreatePushParserCtxt(&sax, static_cast<void *>(this), nullptr, 0, nullptr);
-
- if (entity_doc == nullptr)
- entity_doc = htmlNewDoc(nullptr, nullptr);
-}
-
-ESILibxml2Parser::~ESILibxml2Parser()
-{
- xmlFreeParserCtxt(parser);
- parser = nullptr;
-}
-
-bool
-ESILibxml2Parser::parse(char const *dataToParse, size_t const lengthOfData, bool const endOfStream)
-{
- return (xmlParseChunk(parser, dataToParse, lengthOfData, endOfStream) == 0);
-}
-
-long int
-ESILibxml2Parser::lineNumber() const
-{
- return (long int)xmlSAX2GetLineNumber(parser);
-}
-
-char const *
-ESILibxml2Parser::errorString() const
-{
- const auto error = xmlGetLastError();
-
- if (error == nullptr)
- return nullptr;
-
- return error->message;
-}
-
-#endif /* USE_SQUID_ESI */
-
+++ /dev/null
-/*
- * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
- *
- * Squid software is distributed under GPLv2+ license and includes
- * contributions from numerous individuals and organizations.
- * Please see the COPYING and CONTRIBUTORS files for details.
- */
-
-/*
- * The ESI Libxml2 parser is Copyright (c) 2004 by Joachim Bauch
- * http://www.joachim-bauch.de
- * mail@joachim-bauch.de
- */
-
-#ifndef SQUID_SRC_ESI_LIBXML2PARSER_H
-#define SQUID_SRC_ESI_LIBXML2PARSER_H
-
-#if USE_SQUID_ESI && HAVE_LIBXML2
-
-#include "esi/Parser.h"
-// workaround for definition of "free" that prevents include of
-// parser.h from libxml2 without errors
-#ifdef free
-#define OLD_FREE free
-#undef free
-#endif
-
-#if __clang__
-// workaround for clang complaining of unknown attributes in libxml2 on fedora22
-#ifdef LIBXML_ATTR_ALLOC_SIZE
-#undef LIBXML_ATTR_ALLOC_SIZE
-#endif
-#define LIBXML_ATTR_ALLOC_SIZE(x)
-#endif /* __clang__ */
-
-#if HAVE_LIBXML_PARSER_H
-#include <libxml/parser.h>
-#endif
-#if HAVE_LIBXML_HTMLPARSER_H
-#include <libxml/HTMLparser.h>
-#endif
-#if HAVE_LIBXML_HTMLTREE_H
-#include <libxml/HTMLtree.h>
-#endif
-
-#ifdef OLD_FREE
-#define free OLD_FREE
-#endif
-
-class ESILibxml2Parser : public ESIParser
-{
-
-public:
- ESILibxml2Parser(ESIParserClient *);
- ~ESILibxml2Parser() override;
- /* true on success */
- bool parse(char const *dataToParse, size_t const lengthOfData, bool const endOfStream) override;
- long int lineNumber() const override;
- char const * errorString() const override;
-
- ESIParserClient *getClient() { return theClient; }
-
- EsiParserDeclaration;
-
-private:
- mutable xmlParserCtxtPtr parser; /* our parser */
-
- ESIParserClient *theClient;
-};
-
-#endif /* USE_SQUID_ESI */
-
-#endif /* SQUID_SRC_ESI_LIBXML2PARSER_H */
-
+++ /dev/null
-/*
- * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
- *
- * Squid software is distributed under GPLv2+ license and includes
- * contributions from numerous individuals and organizations.
- * Please see the COPYING and CONTRIBUTORS files for details.
- */
-
-/* DEBUG: section 86 ESI processing */
-
-#ifndef SQUID_SRC_ESI_LITERAL_H
-#define SQUID_SRC_ESI_LITERAL_H
-
-#include "esi/Element.h"
-
-class ESIContext;
-
-class esiLiteral : public ESIElement
-{
- MEMPROXY_CLASS(esiLiteral);
-
-public:
- esiLiteral(ESISegment::Pointer);
- esiLiteral(ESIContext *, const char *s, int len);
- ~esiLiteral() override;
-
- void render(ESISegment::Pointer) override;
- esiProcessResult_t process (int dovars) override;
- Pointer makeCacheable() const override;
- Pointer makeUsable(esiTreeParentPtr, ESIVarState &) const override;
- /* optimise copies away later */
- ESISegment::Pointer buffer;
-
- struct {
- unsigned int donevars:1;
- } flags;
-
- ESIVarState *varState;
- void finish() override;
-
-private:
- esiLiteral(esiLiteral const &);
-};
-
-#endif /* SQUID_SRC_ESI_LITERAL_H */
-
+++ /dev/null
-## Copyright (C) 1996-2023 The Squid Software Foundation and contributors
-##
-## Squid software is distributed under GPLv2+ license and includes
-## contributions from numerous individuals and organizations.
-## Please see the COPYING and CONTRIBUTORS files for details.
-##
-
-include $(top_srcdir)/src/Common.am
-
-noinst_LTLIBRARIES = libesi.la
-
-ESI_PARSER_SOURCES =
-
-if ENABLE_LIBEXPAT
-ESI_PARSER_SOURCES += \
- ExpatParser.cc \
- ExpatParser.h
-endif
-
-if ENABLE_LIBXML2
-ESI_PARSER_SOURCES += \
- Libxml2Parser.cc \
- Libxml2Parser.h
-endif
-
-libesi_la_SOURCES = \
- $(ESI_PARSER_SOURCES) \
- Assign.cc \
- Assign.h \
- Attempt.h \
- Context.cc \
- Context.h \
- Element.h \
- Esi.cc \
- Esi.h \
- Except.h \
- Expression.cc \
- Expression.h \
- Include.cc \
- Include.h \
- Literal.h \
- Parser.cc \
- Parser.h \
- Segment.cc \
- Segment.h \
- Sequence.cc \
- Sequence.h \
- Var.h \
- VarState.cc \
- VarState.h
+++ /dev/null
-/*
- * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
- *
- * Squid software is distributed under GPLv2+ license and includes
- * contributions from numerous individuals and organizations.
- * Please see the COPYING and CONTRIBUTORS files for details.
- */
-
-/* DEBUG: section 86 ESI processing */
-
-#include "squid.h"
-#include "debug/Stream.h"
-#include "esi/Parser.h"
-#include "fatal.h"
-
-char *ESIParser::Type = nullptr;
-ESIParser::Register *ESIParser::Parser = nullptr;
-
-std::list<ESIParser::Register *> &
-ESIParser::GetRegistry()
-{
- static std::list<ESIParser::Register *> parsers;
- return parsers;
-}
-
-ESIParser::Pointer
-ESIParser::NewParser(ESIParserClient *aClient)
-{
- if (!Parser) {
- // if esi_parser is configured, use that
- const char *selectParserName = Type;
- if (!selectParserName || strcasecmp(selectParserName, "auto") == 0) {
-#if HAVE_LIBXML2
- // libxml2 is the more secure. prefer when possible
- selectParserName = "libxml2";
-#else
- // expat is more widely available
- selectParserName = "expat";
-#endif
- }
-
- for (auto *p : GetRegistry()) {
- if (p && strcasecmp(p->name, selectParserName) == 0)
- Parser = p;
- }
-
- if (!Parser)
- fatalf("Unknown ESI Parser type '%s'", selectParserName);
- debugs(86, 2, "selected ESI parser: " << Parser->name);
- }
-
- return (Parser->newParser)(aClient);
-}
-
-ESIParser::Register::Register(const char *_name, ESIParser::Pointer (*_newParser)(ESIParserClient *aClient)) : name(_name), newParser(_newParser)
-{
- ESIParser::GetRegistry().emplace_back(this);
-}
-
-ESIParser::Register::~Register()
-{
- ESIParser::GetRegistry().remove(this);
-}
-
+++ /dev/null
-/*
- * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
- *
- * Squid software is distributed under GPLv2+ license and includes
- * contributions from numerous individuals and organizations.
- * Please see the COPYING and CONTRIBUTORS files for details.
- */
-
-#ifndef SQUID_SRC_ESI_PARSER_H
-#define SQUID_SRC_ESI_PARSER_H
-
-#include "base/RefCount.h"
-
-#include <list>
-
-class ESIParserClient
-{
-public:
- virtual void start(const char *el, const char **attr, size_t attrCount) = 0;
- virtual void end(const char *el) = 0;
- virtual void parserDefault (const char *s, int len) =0;
- virtual void parserComment (const char *s) = 0;
- virtual ~ESIParserClient() {};
-};
-
-class ESIParser : public RefCountable
-{
-public:
- class Register;
- typedef RefCount<ESIParser> Pointer;
-
- static void registerParser(const char *name, Pointer (*new_func)(ESIParserClient *aClient));
- static Pointer NewParser(ESIParserClient *aClient);
- static char *Type;
-
- /**
- \retval true on success
- \retval false on what?
- */
- virtual bool parse(char const *dataToParse, size_t const lengthOfData, bool const endOfStream) = 0;
-
- virtual long int lineNumber() const =0;
- virtual char const * errorString() const =0;
-
-protected:
- ESIParser() {};
-
-private:
- static Register *Parser;
- static std::list<Register *> & GetRegistry();
-};
-
-class ESIParser::Register
-{
-
-public:
- Register(const char *_name, ESIParser::Pointer (*_newParser)(ESIParserClient *aClient));
- ~Register();
-
- const char *name;
- ESIParser::Pointer (*newParser)(ESIParserClient *aClient);
-};
-
-#define EsiParserDefinition(ThisClass) \
- ESIParser::Pointer ThisClass::NewParser(ESIParserClient *aClient) \
- { \
- return new ThisClass (aClient); \
- }
-
-#define EsiParserDeclaration \
- static ESIParser::Pointer NewParser(ESIParserClient *aClient)
-
-#endif /* SQUID_SRC_ESI_PARSER_H */
-
+++ /dev/null
-/*
- * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
- *
- * Squid software is distributed under GPLv2+ license and includes
- * contributions from numerous individuals and organizations.
- * Please see the COPYING and CONTRIBUTORS files for details.
- */
-
-/* DEBUG: section 86 ESI processing */
-
-#include "squid.h"
-#include "debug/Stream.h"
-#include "esi/Segment.h"
-#include "SquidString.h"
-
-CBDATA_CLASS_INIT(ESISegment);
-
-void
-ESISegmentFreeList (ESISegment::Pointer &head)
-{
- while (head.getRaw()) {
- ESISegment::Pointer temp = head;
- head = head->next;
- temp->next = nullptr;
- }
-}
-
-size_t
-ESISegment::space() const
-{
- assert (len <= sizeof(buf));
- return sizeof (buf) - len;
-}
-
-void
-ESISegment::adsorbList (ESISegment::Pointer from)
-{
- assert (next.getRaw() == nullptr);
- assert (from.getRaw() != nullptr);
- /* prevent worst case */
- assert (!(len == 0 && from->len == space() ));
- Pointer copyFrom = from;
-
- while (copyFrom.getRaw() && space() >= copyFrom->len) {
- assert (append (copyFrom) == copyFrom->len);
- copyFrom = copyFrom->next;
- }
-
- next = copyFrom;
-}
-
-void
-ESISegment::ListTransfer (ESISegment::Pointer &from, ESISegment::Pointer &to)
-{
- if (!to.getRaw()) {
- to = from;
- from = nullptr;
- return;
- }
-
- ESISegment::Pointer temp = to->tail();
- temp->adsorbList (from);
- from = nullptr;
-}
-
-size_t
-ESISegment::listLength() const
-{
- size_t result = 0;
- ESISegment const* temp = this;
-
- while (temp) {
- result += temp->len;
- temp = temp->next.getRaw();
- }
-
- return result;
-}
-
-char *
-ESISegment::listToChar() const
-{
- size_t length = listLength();
- char *rv = (char *)xmalloc (length + 1);
- assert (rv);
- rv [length] = '\0';
-
- ESISegment::Pointer temp = this;
- size_t pos = 0;
-
- while (temp.getRaw()) {
- memcpy(&rv[pos], temp->buf, temp->len);
- pos += temp->len;
- temp = temp->next;
- }
-
- return rv;
-}
-
-void
-ESISegment::listAppend (char const *s, size_t length)
-{
- assert (next.getRaw() == nullptr);
- ESISegment::Pointer output = this;
- /* copy the string to output */
- size_t pos=0;
-
- while (pos < length) {
- if (output->space() == 0) {
- assert (output->next.getRaw() == nullptr);
- output->next = new ESISegment;
- output = output->next;
- }
-
- pos += output->append(s + pos, length - pos);
- }
-}
-
-void
-ESISegment::ListAppend (ESISegment::Pointer &head, char const *s, size_t len)
-{
- if (!head.getRaw())
- head = new ESISegment;
-
- head->tail()->listAppend (s, len);
-}
-
-/* XXX: if needed, make this iterative */
-ESISegment::Pointer
-ESISegment::cloneList () const
-{
- ESISegment::Pointer result = new ESISegment (*this);
- result->next = next.getRaw() ? next->cloneList() : nullptr;
- return result;
-}
-
-size_t
-ESISegment::append(char const *appendBuffer, size_t appendLength)
-{
- size_t toCopy = min(appendLength, space());
- memcpy(&buf[len], appendBuffer, toCopy);
- len += toCopy;
- return toCopy;
-}
-
-size_t
-ESISegment::append(ESISegment::Pointer from)
-{
- return append (from->buf, from->len);
-}
-
-ESISegment const *
-ESISegment::tail() const
-{
- ESISegment const *result = this;
-
- while (result->next.getRaw())
- result = result->next.getRaw();
-
- return result;
-}
-
-ESISegment *
-ESISegment::tail()
-{
- ESISegment::Pointer result = this;
-
- while (result->next.getRaw())
- result = result->next;
-
- return result.getRaw();
-}
-
-ESISegment::ESISegment(ESISegment const &old) : len (0), next(nullptr)
-{
- append (old.buf, old.len);
-}
-
-void
-ESISegment::dumpToLog() const
-{
- ESISegment::Pointer temp = this;
-
- while (temp.getRaw()) {
- temp->dumpOne();
- temp = temp->next;
- }
-}
-
-void
-ESISegment::dumpOne() const
-{
- String temp;
- temp.assign(buf, len);
- debugs(86, 9, "ESISegment::dumpOne: \"" << temp << "\"");
-}
-
+++ /dev/null
-/*
- * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
- *
- * Squid software is distributed under GPLv2+ license and includes
- * contributions from numerous individuals and organizations.
- * Please see the COPYING and CONTRIBUTORS files for details.
- */
-
-#ifndef SQUID_SRC_ESI_SEGMENT_H
-#define SQUID_SRC_ESI_SEGMENT_H
-
-/* TODO: Factor the store memory segment management into a reusable code block
- * or perhaps use membuffers here?
- */
-
-#include "base/RefCount.h"
-#include "cbdata.h"
-#include "http/forward.h"
-#include "SquidString.h"
-
-class ESISegment : public RefCountable
-{
- CBDATA_CLASS(ESISegment);
-
-public:
- typedef RefCount<ESISegment> Pointer;
- static void ListAppend (Pointer &, char const *, size_t);
- static void ListTransfer (Pointer &from, Pointer &to);
-
- ESISegment() : len(0), next(nullptr) {*buf = 0;}
- ESISegment(ESISegment const &);
- ~ESISegment() override {}
-
- ESISegment::Pointer cloneList() const;
- char *listToChar() const;
- void listAppend (char const *s, size_t length);
- void adsorbList (ESISegment::Pointer from);
- size_t space() const;
-
- char buf[HTTP_REQBUF_SZ];
- size_t len; /* how much data has been pushed into this */
- Pointer next;
- size_t append(char const *, size_t);
- size_t append (Pointer);
- ESISegment const *tail() const;
- ESISegment *tail();
- void dumpToLog() const;
-
-private:
- size_t listLength()const;
- void dumpOne() const;
-};
-
-void ESISegmentFreeList (ESISegment::Pointer &head);
-
-#endif /* SQUID_SRC_ESI_SEGMENT_H */
-
+++ /dev/null
-/*
- * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
- *
- * Squid software is distributed under GPLv2+ license and includes
- * contributions from numerous individuals and organizations.
- * Please see the COPYING and CONTRIBUTORS files for details.
- */
-
-/* DEBUG: section 86 ESI processing */
-
-#include "squid.h"
-#include "debug/Stream.h"
-#include "fatal.h"
-
-/* MS Visual Studio Projects are monolithic, so we need the following
- * #if to exclude the ESI code from compile process when not needed.
- */
-#if (USE_SQUID_ESI == 1)
-
-#include "esi/Attempt.h"
-#include "esi/Except.h"
-#include "esi/Literal.h"
-#include "esi/Sequence.h"
-
-class esiExcept;
-
-esiSequence::~esiSequence ()
-{
- debugs(86, 5, "esiSequence::~esiSequence " << this);
- FinishAllElements(elements); // finish if not already done
-}
-
-esiSequence::esiSequence(esiTreeParentPtr aParent, bool incrementalFlag) :
- elements(),
- processedcount(0),
- parent(aParent),
- mayFail_(true),
- failed(false),
- provideIncrementalData(incrementalFlag),
- processing(false),
- processingResult(ESI_PROCESS_COMPLETE),
- nextElementToProcess_(0)
-{
- memset(&flags, 0, sizeof(flags));
-}
-
-size_t
-esiSequence::nextElementToProcess() const
-{
- return nextElementToProcess_;
-}
-
-void
-esiSequence::nextElementToProcess(size_t const &aSizeT)
-{
- nextElementToProcess_ = aSizeT;
-}
-
-bool
-esiSequence::finishedProcessing() const
-{
- return nextElementToProcess() >= elements.size();
-}
-
-bool
-esiSequence::mayFail () const
-{
- if (failed)
- return true;
-
- return mayFail_;
-}
-
-void
-esiSequence::wontFail()
-{
- assert (!failed);
- mayFail_ = false;
-}
-
-void
-esiSequence::render(ESISegment::Pointer output)
-{
- /* append all processed elements, and trim processed
- * and rendered elements
- */
- assert (output->next == nullptr);
- debugs (86,5, "esiSequenceRender: rendering " << processedcount << " elements");
-
- for (size_t i = 0; i < processedcount; ++i) {
- elements[i]->render(output);
- FinishAnElement(elements[i], i);
- // TODO: pass an "ESISegment **" ?
- output = output->tail();
- }
-
- // prune completed elements
- elements.erase(elements.begin(), elements.begin() + processedcount);
- processedcount = 0;
- assert (output->next == nullptr);
-}
-
-void
-esiSequence::finish()
-{
- debugs(86, 5, "esiSequence::finish: " << this << " is finished");
- FinishAllElements(elements);
- parent = nullptr;
-}
-
-void
-esiSequence::provideData (ESISegment::Pointer data, ESIElement *source)
-{
- ESIElement::Pointer lockthis = this;
-
- if (processing)
- debugs(86, 5, "esiSequence::provideData: " << this << " data provided during processing");
- debugs(86, 5, "esiSequence::provideData " << this << " " << data.getRaw() << " " << source);
-
- /* when data is provided, the element *must* be completed */
- /* XXX: when the callback model is complete,
- * we can introduce 'finished'. And then this rule can be
- * relaxed
- */
- /* find the index */
- int index = elementIndex (source);
-
- assert (index >= 0);
-
- /* remove the current node */
- FinishAnElement(elements[index], index);
-
- /* create a literal */
- esiLiteral *temp = new esiLiteral (data);
-
- /* insert the literal */
- elements[index] = temp;
-
- /* XXX: TODO push any pushable data upwards */
- /* fail() not done */
- if (processing)
- return;
-
- assert (process (flags.dovars) != ESI_PROCESS_FAILED);
-}
-
-bool
-esiSequence::addElement (ESIElement::Pointer element)
-{
- /* add an element to the output list */
- /* Some elements require specific parents */
-
- if (dynamic_cast<esiAttempt*>(element.getRaw()) ||
- dynamic_cast<esiExcept*>(element.getRaw())) {
- debugs(86, DBG_CRITICAL, "esiSequenceAdd: misparented Attempt or Except element (section 3.4)");
- return false;
- }
-
- /* Tie literals together for efficiency */
- if (elements.size() && dynamic_cast<esiLiteral*>(element.getRaw()) &&
- dynamic_cast<esiLiteral*>(elements[elements.size() - 1].getRaw())) {
- debugs(86, 5, "esiSequenceAdd: tying Literals " <<
- elements[elements.size() - 1].getRaw() << " and " <<
- element.getRaw() << " together");
-
- ESISegment::ListTransfer (((esiLiteral *)element.getRaw())->buffer,
- ((esiLiteral *)elements[elements.size() - 1].getRaw())->buffer);
- return true;
- }
-
- elements.push_back(element);
- debugs (86,3, "esiSequenceAdd: Added a new element, elements = " << elements.size());
- return true;
-}
-
-int
-esiSequence::elementIndex(ESIElement::Pointer anElement) const
-{
- for (size_t i = 0; i < elements.size(); ++i)
- if (elements[i] == anElement)
- return i;
-
- return -1;
-}
-
-void
-esiSequence::processStep(int dovars)
-{
- size_t elementToProcess = nextElementToProcess();
- nextElementToProcess(elementToProcess + 1);
- esiProcessResult_t tempResult = processOne(dovars, elementToProcess);
-
- if (processingResult < tempResult) {
- debugs(86, 5, "esiSequence::process: processingResult was " << processingResult << ", increasing to " << tempResult);
- processingResult = tempResult;
- }
-}
-
-esiProcessResult_t
-esiSequence::processOne(int dovars, size_t index)
-{
- debugs (86,5, "esiSequence::process " << this << " about to process element[" << index << "] " << elements[index].getRaw());
-
- switch (elements[index]->process(dovars)) {
-
- case ESI_PROCESS_COMPLETE:
- debugs(86, 5, "esiSequenceProcess: " << this << " element " << elements[index].getRaw() << " Processed OK");
-
- if (index == processedcount)
- /* another completely ready */
- ++processedcount;
-
- return ESI_PROCESS_COMPLETE;
-
- case ESI_PROCESS_PENDING_WONTFAIL:
- debugs(86, 5, "esiSequenceProcess: element Processed PENDING OK");
-
- return ESI_PROCESS_PENDING_WONTFAIL;
-
- case ESI_PROCESS_PENDING_MAYFAIL:
- debugs(86, 5, "eseSequenceProcess: element Processed PENDING UNKNOWN");
-
- return ESI_PROCESS_PENDING_MAYFAIL;
-
- case ESI_PROCESS_FAILED:
- debugs(86, 5, "esiSequenceProcess: element Processed FAILED");
-
- return ESI_PROCESS_FAILED;
-
- default:
- fatal ("unexpected code in esiSequence::processOne\n");
-
- return ESI_PROCESS_FAILED;
- }
-}
-
-esiProcessResult_t
-esiSequence::process (int inheritedVarsFlag)
-{
- debugs(86, 5, "esiSequence::process: " << this << " processing");
-
- if (processing) {
- debugs(86, 5, "esiSequence::process: " << this <<
- " reentry attempt during processing");
- }
-
- /* process as much of the list as we can, stopping only on
- * failures
- */
- if (!processing || processedcount == 0)
- processingResult = ESI_PROCESS_COMPLETE;
-
- int dovars = inheritedVarsFlag;
-
- if (flags.dovars)
- dovars = 1;
-
- debugs(86, 5, "esiSequence::process: Processing " << this << " with" <<
- (dovars ? "" : "out") << " variable processing");
-
- processing = true;
-
- nextElementToProcess(processedcount);
-
- while (!finishedProcessing()) {
- processStep(dovars);
-
- if (!processing)
- return processingResult;
-
- if (processingResult == ESI_PROCESS_FAILED) {
- FinishAllElements(elements);
- failed = true;
- parent = nullptr;
- processing = false;
- return processingResult;
- }
- }
-
- assert (processingResult != ESI_PROCESS_COMPLETE || processedcount == elements.size());
-
- if (processingResult == ESI_PROCESS_COMPLETE || processingResult == ESI_PROCESS_PENDING_WONTFAIL)
- wontFail();
-
- if (processedcount == elements.size() || provideIncrementalData) {
- ESISegment::Pointer temp(new ESISegment);
- render (temp);
-
- if (temp->next.getRaw() || temp->len)
- parent->provideData(temp, this);
- else
- ESISegmentFreeList (temp);
- }
-
- /* Depends on full parsing before processing */
- if (processedcount == elements.size())
- parent = nullptr;
-
- debugs(86, 5, "esiSequence::process: " << this << " completed");
-
- processing = false;
-
- return processingResult;
-}
-
-void
-esiSequence::fail(ESIElement * /* source */, char const *anError)
-{
- failed = true;
-
- if (processing) {
- debugs(86, 5, "esiSequence::fail: " << this << " failure callback during processing");
- return;
- }
-
- debugs(86, 5, "esiSequence::fail: " << this << " has failed.");
- parent->fail (this, anError);
- FinishAllElements(elements);
- parent = nullptr;
-}
-
-esiSequence::esiSequence(esiSequence const &old) :
- processedcount(0),
- parent(nullptr),
- mayFail_(old.mayFail_),
- failed(old.failed),
- provideIncrementalData(old.provideIncrementalData),
- processing(false),
- processingResult(ESI_PROCESS_COMPLETE),
- nextElementToProcess_(0)
-{
- flags.dovars = old.flags.dovars;
-}
-
-void
-esiSequence::makeCachableElements(esiSequence const &old)
-{
- for (size_t counter = 0; counter < old.elements.size(); ++counter) {
- ESIElement::Pointer newElement = old.elements[counter]->makeCacheable();
-
- if (newElement.getRaw())
- assert (addElement(newElement));
- }
-}
-
-void
-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);
-
- if (newElement.getRaw())
- assert (addElement(newElement));
- }
-}
-
-ESIElement::Pointer
-esiSequence::makeCacheable() const
-{
- debugs(86, 5, "esiSequence::makeCacheable: Making cachable sequence from " << this);
- assert (processedcount == 0);
- assert (!failed);
-
- if (elements.size() == 0) {
- debugs(86, 5, "esiSequence::makeCacheable: No elements in sequence " << this << ", returning NULL");
- return nullptr;
- }
-
- esiSequence * resultS = new esiSequence (*this);
- ESIElement::Pointer result = resultS;
- resultS->makeCachableElements(*this);
- debugs(86, 5, "esiSequence::makeCacheable: " << this << " created " << result.getRaw());
- return result;
-}
-
-ESIElement::Pointer
-esiSequence::makeUsable(esiTreeParentPtr newParent, ESIVarState &newVarState) const
-{
- debugs(86, 5, "esiSequence::makeUsable: Creating usable Sequence");
- assert (processedcount == 0);
- assert (!failed);
-
- if (elements.size() == 0) {
- debugs(86, 5, "esiSequence::makeUsable: No elements in sequence " << this << ", returning NULL");
- return nullptr;
- }
-
- esiSequence * resultS = new esiSequence (*this);
- ESIElement::Pointer result = resultS;
- resultS->parent = newParent;
- resultS->makeUsableElements(*this, newVarState);
- return result;
-}
-
-#endif /* USE_SQUID_ESI == 1 */
-
+++ /dev/null
-/*
- * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
- *
- * Squid software is distributed under GPLv2+ license and includes
- * contributions from numerous individuals and organizations.
- * Please see the COPYING and CONTRIBUTORS files for details.
- */
-
-/* DEBUG: section 86 ESI processing */
-
-#ifndef SQUID_SRC_ESI_SEQUENCE_H
-#define SQUID_SRC_ESI_SEQUENCE_H
-
-#include "esi/Element.h"
-#include "mem/forward.h"
-
-/* esiSequence */
-
-class esiSequence : public ESIElement
-{
- MEMPROXY_CLASS(esiSequence);
-
-public:
- esiSequence(esiTreeParentPtr, bool = false);
- ~esiSequence() override;
-
- void render(ESISegment::Pointer) override;
- bool addElement (ESIElement::Pointer) override;
- esiProcessResult_t process (int dovars) override;
- void provideData (ESISegment::Pointer, ESIElement*) override;
- bool mayFail () const override;
- void wontFail();
- void fail(ESIElement *, char const *anError = nullptr) override;
- void makeCachableElements(esiSequence const &old);
- Pointer makeCacheable() const override;
- void makeUsableElements(esiSequence const &old, ESIVarState &);
- Pointer makeUsable(esiTreeParentPtr, ESIVarState &) const override;
-
- Esi::Elements elements; /* unprocessed or rendered nodes */
- size_t processedcount;
-
- struct {
- unsigned int dovars:1; /* for esiVar */
- } flags;
- void finish() override;
-
-protected:
- esiSequence(esiSequence const &);
- esiTreeParentPtr parent;
-
-private:
- int elementIndex (ESIElement::Pointer anElement) const;
- bool mayFail_;
- bool failed;
- esiProcessResult_t processOne(int, size_t);
- bool const provideIncrementalData;
- bool processing;
- esiProcessResult_t processingResult;
- size_t nextElementToProcess_;
- size_t nextElementToProcess() const;
- void nextElementToProcess(size_t const &);
- bool finishedProcessing() const;
- void processStep(int dovars);
-};
-
-#endif /* SQUID_SRC_ESI_SEQUENCE_H */
-
+++ /dev/null
-/*
- * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
- *
- * Squid software is distributed under GPLv2+ license and includes
- * contributions from numerous individuals and organizations.
- * Please see the COPYING and CONTRIBUTORS files for details.
- */
-
-/* DEBUG: section 86 ESI processing */
-
-#ifndef SQUID_SRC_ESI_VAR_H
-#define SQUID_SRC_ESI_VAR_H
-
-#include "esi/Element.h"
-#include "esi/Sequence.h"
-
-/* esiVar */
-
-class ESIVar:public esiSequence
-{
-
-public:
- ESIVar(esiTreeParentPtr aParent) : esiSequence (aParent) {
- flags.dovars = 1;
- }
-};
-
-#endif /* SQUID_SRC_ESI_VAR_H */
-
+++ /dev/null
-/*
- * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
- *
- * Squid software is distributed under GPLv2+ license and includes
- * contributions from numerous individuals and organizations.
- * Please see the COPYING and CONTRIBUTORS files for details.
- */
-
-/* DEBUG: section 86 ESI processing */
-
-#include "squid.h"
-#include "esi/VarState.h"
-#include "fatal.h"
-#include "HttpReply.h"
-
-char const *ESIVariableUserAgent::esiUserOs[]= {
- "WIN",
- "MAC",
- "UNIX",
- "OTHER"
-};
-
-char const * esiBrowsers[]= {"MSIE",
- "MOZILLA",
- "OTHER"
- };
-
-CBDATA_CLASS_INIT(ESIVarState);
-
-void
-ESIVarState::Variable::eval(ESIVarState &state, char const *, 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 */
- debugs (86,6, "esiVarState::feedData: accepting " << len << " bytes");
- ESISegment::ListAppend (input, buf, len);
-}
-
-ESISegment::Pointer
-ESIVarState::extractList()
-{
- doIt();
- ESISegment::Pointer rv = output;
- output = nullptr;
- debugs(86, 6, "ESIVarStateExtractList: Extracted list");
- 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);
-
- debugs(86, 6, "ESIVarStateExtractList: Extracted char");
-
- return rv;
-}
-
-ESIVarState::~ESIVarState()
-{
- // freeResources
- input = nullptr;
- ESISegmentFreeList(output);
- hdr.clean();
-
- while (!variablesForCleanup.empty()) {
- delete variablesForCleanup.back();
- variablesForCleanup.pop_back();
- }
-
- delete defaultVariable;
-}
-
-char *
-ESIVariableUserAgent::getProductVersion (char const *s)
-{
- char const *t;
- int len;
- t = strchr(s, '/');
-
- if (!t || !*(++t))
- return xstrdup("");
-
- len = strcspn(t, " \r\n()<>@,;:\\\"/[]?={}");
-
- return xstrndup(t, len + 1);
-}
-
-ESIVariableQuery::ESIVariableQuery(char const *uri) : query (nullptr), query_sz (0), query_elements (0), query_string (nullptr)
-{
- /* 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 = static_cast<_query_elem *>(memAllocBuf(query_elements * sizeof(struct _query_elem), &query_sz));
- memset(query, 0, query_sz);
- query_pos = query_start + 1;
- n = 0;
-
- while (query_pos) {
- char const *next = strchr(query_pos, '&');
- char const *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;
- debugs(86, 6, "esiVarStateNew: Parsed Query string: '" << uri << "'");
-
- while (n < query_elements) {
- debugs(86, 6, "esiVarStateNew: Parsed Query element " << 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(nullptr),
- hdr(hoReply)
-{
- memset(&flags, 0, sizeof(flags));
-
- /* TODO: only grab the needed headers */
- /* Note that as we pass these through to included requests, we
- * cannot trim them */
- hdr.append(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.rawBuf(), 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.assign(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
- *
- * Using 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 (state.header().has(Http::HdrType::USER_AGENT)) {
- char const *s = state.header().getStr(Http::HdrType::USER_AGENT);
- UserOs = identifyOs(s);
- char const *t, *t1;
-
- /* Now the browser and version */
-
- if ((t = strstr (s, "MSIE"))) {
- browser = ESI_BROWSER_MSIE;
- t = strchr(t, ' ');
-
- if (!t)
- browserversion = xstrdup("");
- else {
- t1 = strchr(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 = nullptr;
- state.cookieUsed();
-
- if (state.header().has(Http::HdrType::COOKIE)) {
- if (!subref)
- s = state.header().getStr (Http::HdrType::COOKIE);
- else {
- const auto subCookie = state.header().getListMember(Http::HdrType::COOKIE, subref, ';');
-
- if (subCookie.length())
- ESISegment::ListAppend(state.getOutput(), subCookie.rawContent(), subCookie.length());
- 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 = nullptr;
- state.hostUsed();
-
- if (!subref && state.header().has(Http::HdrType::HOST)) {
- s = state.header().getStr (Http::HdrType::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 = nullptr;
- state.languageUsed();
-
- if (state.header().has(Http::HdrType::ACCEPT_LANGUAGE)) {
- if (!subref) {
- String S (state.header().getList (Http::HdrType::ACCEPT_LANGUAGE));
- ESISegment::ListAppend (state.getOutput(), S.rawBuf(), S.size());
- } else {
- if (state.header().hasListMember (Http::HdrType::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 = nullptr;
-
- 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 = nullptr;
- state.refererUsed();
-
- if (!subref && state.header().has(Http::HdrType::REFERER))
- s = state.header().getStr (Http::HdrType::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 = nullptr;
- state.useragentUsed();
-
- if (state.header().has(Http::HdrType::USER_AGENT)) {
- if (!subref)
- s = state.header().getStr (Http::HdrType::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 nullptr;
-}
-
-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 *foundDefault );
- 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 *foundDefault )
-{
- assert (var);
-
- if (!foundDefault)
- foundDefault = "";
-
- var->eval (*varState, subref, foundDefault);
-}
-
-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 (nullptr),
- found_default (nullptr), currentFunction(nullptr)
-{
- 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] == '{') {
- debugs(86, 6, "ESIVarStateDoIt: Subref of some sort");
- /* subreference of some sort */
- /* look for the entry name */
- var_pos = ++pos;
- state = 4;
- } else if (!found_default && string[pos] == '|') {
- debugs(86, 6, "esiVarStateDoIt: Default present");
- /* extract default value */
- state = 5;
- var_pos = ++pos;
- } else {
- /* unexpected char, not a variable after all */
- debugs(86, 6, "esiVarStateDoIt: unexpected char after varname");
- 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);
- debugs(86, 6, "esiVarStateDoIt: found end of variable subref '" << found_subref << "'");
- state = 3;
- ++pos;
- } else if (!validChar (string[pos])) {
- debugs(86, 6, "esiVarStateDoIt: found invalid char in variable subref");
- /* 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 */
- debugs(86, 6, "esiVarStateDoIt: found quoted default");
- state = 6;
- var_pos = ++pos;
- } else {
- /* doesn't */
- debugs(86, 6, "esiVarStateDoIt: found unquoted default");
- 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);
- debugs(86, 6, "esiVarStateDoIt: found end of quoted default '" << 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);
- debugs(86, 6, "esiVarStateDoIt: found end of variable (w/ unquoted default) '" << 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 == nullptr);
-
- 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: 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 (rep->header.getList (Http::HdrType::VARY));
-
- if (!strVary.size() || strVary[0] != '*') {
- rep->header.putStr (Http::HdrType::VARY, tempstr);
- }
-}
-
+++ /dev/null
-/*
- * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
- *
- * Squid software is distributed under GPLv2+ license and includes
- * contributions from numerous individuals and organizations.
- * Please see the COPYING and CONTRIBUTORS files for details.
- */
-
-#ifndef SQUID_SRC_ESI_VARSTATE_H
-#define SQUID_SRC_ESI_VARSTATE_H
-
-#include "esi/Segment.h"
-#include "HttpHeader.h"
-#include "libTrie/Trie.h"
-
-#include <vector>
-
-class HttpReply;
-
-/* 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
-{
- CBDATA_CLASS(ESIVarState);
-
-public:
- ESIVarState(HttpHeader const *hdr, char const *uri);
- ~ESIVarState();
-
- 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 &);
-
- /* 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 {
- unsigned int language:1;
- unsigned int cookie:1;
- unsigned int host:1;
- unsigned int referer:1;
- unsigned 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;
- std::vector<Variable*> variablesForCleanup;
- Variable *defaultVariable;
-};
-
-class ESIVariableCookie : public ESIVarState::Variable
-{
-
-public:
- void eval (ESIVarState &state, char const *, char const *) const override;
-};
-
-class ESIVariableHost : public ESIVarState::Variable
-{
-
-public:
- void eval (ESIVarState &state, char const *, char const *) const override;
-};
-
-class ESIVariableLanguage : public ESIVarState::Variable
-{
-
-public:
- void eval (ESIVarState &state, char const *, char const *) const override;
-};
-
-class ESIVariableQuery : public ESIVarState::Variable
-{
-
-public:
- ESIVariableQuery(char const *uri);
- ~ESIVariableQuery() override;
- void eval (ESIVarState &state, char const *, char const *) const override;
- 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:
- void eval (ESIVarState &state, char const *, char const *) const override;
-};
-
-class ESIVariableUserAgent : public ESIVarState::Variable
-{
-
-public:
- ~ESIVariableUserAgent() override;
- ESIVariableUserAgent (ESIVarState &state);
- void eval (ESIVarState &state, char const *, char const *) const override;
-
-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_SRC_ESI_VARSTATE_H */
-
if (request->flags.accelerated) {
/* Append Surrogate-Capabilities */
String strSurrogate(hdr_in->getList(Http::HdrType::SURROGATE_CAPABILITY));
-#if USE_SQUID_ESI
- snprintf(bbuf, BBUF_SZ, "%s=\"Surrogate/1.0 ESI/1.0\"", Config.Accel.surrogate_id);
-#else
snprintf(bbuf, BBUF_SZ, "%s=\"Surrogate/1.0\"", Config.Accel.surrogate_id);
-#endif
strListAdd(&strSurrogate, bbuf, ',');
hdr_out->putStr(Http::HdrType::SURROGATE_CAPABILITY, strSurrogate.termedBuf());
}
X_SQUID_ERROR, /**< Squid custom header on generated error responses */
HDR_X_ACCELERATOR_VARY, /**< obsolete Squid custom header. */
X_NEXT_SERVICES, /**< Squid custom ICAP header */
- SURROGATE_CAPABILITY, /**< Edge Side Includes (ESI) header */
- SURROGATE_CONTROL, /**< Edge Side Includes (ESI) header */
+ SURROGATE_CAPABILITY, /**< W3C Edge Architecture Specification */
+ SURROGATE_CONTROL, /**< W3C Edge Architecture Specification */
FRONT_END_HTTPS, /**< MS Exchange custom header we may have to add */
FTP_COMMAND, /**< Internal header for FTP command */
FTP_ARGUMENTS, /**< Internal header for FTP command arguments */
CallRunnerRegistrator(sslBumpCfgRr);
#endif
-#if USE_SQUID_ESI && HAVE_LIBEXPAT
- CallRunnerRegistratorIn(Esi, ExpatRr);
-#endif
-
-#if USE_SQUID_ESI && HAVE_LIBXML2
- CallRunnerRegistratorIn(Esi, Libxml2Rr);
-#endif
-
#if HAVE_FS_ROCK
CallRunnerRegistratorIn(Rock, SwapDirRr);
#endif
return new StoreEntry();
}
void StoreEntry::operator delete(void *) STUB
-//#if USE_SQUID_ESI
-//ESIElement::Pointer StoreEntry::cachedESITree STUB_RETVAL(nullptr)
-//#endif
void StoreEntry::buffer() STUB
void StoreEntry::flush() STUB
int StoreEntry::unlock(const char *) STUB_RETVAL(0)
+++ /dev/null
-/*
- * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
- *
- * Squid software is distributed under GPLv2+ license and includes
- * contributions from numerous individuals and organizations.
- * Please see the COPYING and CONTRIBUTORS files for details.
- */
-
-/* DEBUG: section 86 ESI Expressions */
-
-#include "squid.h"
-#include "esi/Expression.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 (result != results[i])
- return 1;
-
- ++i;
- }
-
- return 0;
-}
-
test-squid-conf.sh \
testHeader.cc.in
-ESI_ALL_TESTS = \
- ESIExpressions
-
-if ENABLE_ESI
- ESI_TESTS = $(ESI_ALL_TESTS)
-else
- ESI_TESTS =
-endif
-
## Sort by dependencies - test lowest layers first
TESTS += \
syntheticoperators \
VirtualDeleteOperator \
splay\
mem_node_test\
- mem_hdr_test\
- $(ESI_TESTS)
+ mem_hdr_test
## Sort by alpha - any build failures are significant.
check_PROGRAMS += \
- $(ESI_TESTS) \
mem_node_test\
mem_hdr_test \
splay \
STUB.h: $(top_srcdir)/src/tests/STUB.h
cp $(top_srcdir)/src/tests/STUB.h $@
-ESIExpressions_SOURCES = \
- $(DEBUG_SOURCE) \
- ESIExpressions.cc \
- stub_libmem.cc
-ESIExpressions_LDADD = $(top_builddir)/src/esi/Expression.o \
- $(top_builddir)/src/debug/libdebug.la \
- $(top_builddir)/src/comm/libminimal.la \
- $(LDADD)
-
mem_node_test_SOURCES = \
$(DEBUG_SOURCE) \
mem_node_test.cc
--disable-removal-policies \
--disable-icmp \
--disable-delay-pools \
- --disable-esi \
--disable-icap-client \
--disable-ecap \
--disable-useragent-log \
--without-aio \
--without-cap \
--without-dl \
- --without-expat \
--without-gnugss \
--without-heimdal-krb5 \
--without-large-files \
--without-psapi \
--without-systemd \
--without-tdb \
- --without-xml2 \
"
# Fix the distclean testing.
--enable-auto-locale \
--enable-translation \
--enable-zph-qos \
- --enable-esi \
--with-aio \
--with-build-environment=default \
--with-dl \
--enable-auto-locale \
--disable-translation \
--enable-zph-qos \
- --enable-esi \
--with-aio \
--with-build-environment=default \
--with-dl \
--enable-snmp \
--enable-htcp \
--enable-carp \
- --enable-esi \
--enable-useragent-log \
--enable-referer-log \
--disable-wccp \