Just the basics for some needed right now. This needs to be expanded.
Also, required to break some of the mime header parsing calls out into
mime_header.cc in preparation for splitting the icon handling from the
header handling and reduce dependencies on the new tests.
MemObject.cc \
MemObject.h \
mime.cc \
+ mime_header.cc \
multicast.cc \
neighbors.cc \
Packer.cc \
tests/testEvent \
tests/testEventLoop \
tests/test_http_range \
+ tests/testHttpReply \
tests/testHttpRequest \
tests/testStore \
tests/testString \
#tests_testX_DEPENDENCIES= @SQUID_CPPUNIT_LA@ \
# $(top_builddir)/lib/libmiscutil.a
+
+# - add other component .(h|cc) files needed to link and run tests
+tests_testHttpReply_SOURCES=\
+ tests/testHttpReply.h \
+ tests/testHttpReply.cc \
+ tests/testMain.cc \
+ cbdata.h \
+ cbdata.cc \
+ ETag.cc \
+ HttpBody.cc \
+ HttpHdrCc.cc \
+ HttpHdrContRange.h \
+ HttpHdrContRange.cc \
+ HttpHdrRange.cc \
+ HttpHdrSc.h \
+ HttpHdrSc.cc \
+ HttpHdrScTarget.h \
+ HttpHdrScTarget.cc \
+ HttpHeader.h \
+ HttpHeader.cc \
+ HttpHeaderMask.h \
+ HttpHeaderTools.cc \
+ HttpMsg.h \
+ HttpMsg.cc \
+ HttpReply.h \
+ HttpReply.cc \
+ HttpStatusLine.h \
+ HttpStatusLine.cc \
+ mem.cc \
+ MemBuf.h \
+ MemBuf.cc \
+ mime_header.cc \
+ Packer.h \
+ Packer.cc \
+ tests/stub_cache_manager.cc \
+ tests/stub_StatHist.cc \
+ tests/stub_store.cc \
+ SquidString.h \
+ String.cc \
+ SquidTime.h \
+ time.cc
+nodist_tests_testHttpReply_SOURCES=\
+ $(TESTSOURCES)
+tests_testHttpReply_LDFLAGS = $(LIBADD_DL)
+tests_testHttpReply_LDADD=\
+ acl/libapi.la \
+ acl/libstate.la \
+ auth/libauth.la \
+ ip/libip.la \
+ @SQUID_CPPUNIT_LIBS@ \
+ @SQUID_CPPUNIT_LA@ \
+ -L../lib -lmiscutil
+tests_testHttpReply_DEPENDENCIES= @SQUID_CPPUNIT_LA@ \
+ $(top_builddir)/lib/libmiscutil.a
+
tests_testAuth_SOURCES = \
tests/testAuth.cc tests/testMain.cc tests/testAuth.h \
ConfigParser.cc \
MemBuf.cc \
MemObject.cc \
mime.cc \
+ mime_header.cc \
neighbors.cc \
Packer.cc \
Parsing.cc \
MemBuf.cc \
MemObject.cc \
mime.cc \
+ mime_header.cc \
neighbors.cc \
Packer.cc \
Parsing.cc \
MemBuf.cc \
MemObject.cc \
mime.cc \
+ mime_header.cc \
neighbors.cc \
Packer.cc \
Parsing.cc \
mem_node.cc \
MemObject.cc \
mime.cc \
+ mime_header.cc \
multicast.cc \
neighbors.cc \
Parsing.cc \
MemBuf.cc \
MemObject.cc \
mime.cc \
+ mime_header.cc \
neighbors.cc \
Packer.cc \
Parsing.cc \
MemBuf.cc \
MemObject.cc \
mime.cc \
+ mime_header.cc \
neighbors.cc \
Packer.cc \
Parsing.cc \
/*
* $Id$
*
- * DEBUG: section 25 MIME Parsing
+ * DEBUG: section 25 MIME Parsing and Internal Icons
* AUTHOR: Harvest Derived
*
* SQUID Web Proxy Cache http://www.squid-cache.org/
safe_free (address);
}
-/* returns a pointer to a field-value of the first matching field-name */
-char *
-mime_get_header(const char *mime, const char *name)
-{
- return mime_get_header_field(mime, name, NULL);
-}
-
-/*
- * returns a pointer to a field-value of the first matching field-name where
- * field-value matches prefix if any
- */
-char *
-mime_get_header_field(const char *mime, const char *name, const char *prefix)
-{
- LOCAL_ARRAY(char, header, GET_HDR_SZ);
- const char *p = NULL;
- char *q = NULL;
- char got = 0;
- const int namelen = name ? strlen(name) : 0;
- const int preflen = prefix ? strlen(prefix) : 0;
- int l;
-
- if (NULL == mime)
- return NULL;
-
- assert(NULL != name);
-
- debugs(25, 5, "mime_get_header: looking for '" << name << "'");
-
- for (p = mime; *p; p += strcspn(p, "\n\r")) {
- if (strcmp(p, "\r\n\r\n") == 0 || strcmp(p, "\n\n") == 0)
- return NULL;
-
- while (xisspace(*p))
- p++;
-
- if (strncasecmp(p, name, namelen))
- continue;
-
- if (!xisspace(p[namelen]) && p[namelen] != ':')
- continue;
-
- l = strcspn(p, "\n\r") + 1;
-
- if (l > GET_HDR_SZ)
- l = GET_HDR_SZ;
-
- xstrncpy(header, p, l);
-
- debugs(25, 5, "mime_get_header: checking '" << header << "'");
-
- q = header;
-
- q += namelen;
-
- if (*q == ':')
- q++, got = 1;
-
- while (xisspace(*q))
- q++, got = 1;
-
- if (got && prefix) {
- /* we could process list entries here if we had strcasestr(). */
- /* make sure we did not match a part of another field-value */
- got = !strncasecmp(q, prefix, preflen) && !xisalpha(q[preflen]);
- }
-
- if (got) {
- debugs(25, 5, "mime_get_header: returning '" << q << "'");
- return q;
- }
- }
-
- return NULL;
-}
-
-size_t
-headersEnd(const char *mime, size_t l)
-{
- size_t e = 0;
- int state = 1;
-
- PROF_start(headersEnd);
-
- while (e < l && state < 3) {
- switch (state) {
-
- case 0:
-
- if ('\n' == mime[e])
- state = 1;
-
- break;
-
- case 1:
- if ('\r' == mime[e])
- state = 2;
- else if ('\n' == mime[e])
- state = 3;
- else
- state = 0;
-
- break;
-
- case 2:
- if ('\n' == mime[e])
- state = 3;
- else
- state = 0;
-
- break;
-
- default:
- break;
- }
-
- e++;
- }
- PROF_stop(headersEnd);
-
- if (3 == state)
- return e;
-
- return 0;
-}
-
static mimeEntry *
mimeGetEntry(const char *fn, int skip_encodings)
{
--- /dev/null
+
+/*
+ * $Id$
+ *
+ * DEBUG: section 25 MiME Header Parsing
+ * AUTHOR: Harvest Derived
+ *
+ * SQUID Web Proxy Cache http://www.squid-cache.org/
+ * ----------------------------------------------------------
+ *
+ * Squid is the result of efforts by numerous individuals from
+ * the Internet community; see the CONTRIBUTORS file for full
+ * details. Many organizations have provided support for Squid's
+ * development; see the SPONSORS file for full details. Squid is
+ * Copyrighted (C) 2001 by the Regents of the University of
+ * California; see the COPYRIGHT file for full details. Squid
+ * incorporates software developed and/or copyrighted by other
+ * sources; see the CREDITS file for full details.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ */
+
+#include "squid.h"
+
+#define GET_HDR_SZ 1024
+
+/* returns a pointer to a field-value of the first matching field-name */
+char *
+mime_get_header(const char *mime, const char *name)
+{
+ return mime_get_header_field(mime, name, NULL);
+}
+
+/*
+ * returns a pointer to a field-value of the first matching field-name where
+ * field-value matches prefix if any
+ */
+char *
+mime_get_header_field(const char *mime, const char *name, const char *prefix)
+{
+ LOCAL_ARRAY(char, header, GET_HDR_SZ);
+ const char *p = NULL;
+ char *q = NULL;
+ char got = 0;
+ const int namelen = name ? strlen(name) : 0;
+ const int preflen = prefix ? strlen(prefix) : 0;
+ int l;
+
+ if (NULL == mime)
+ return NULL;
+
+ assert(NULL != name);
+
+ debugs(25, 5, "mime_get_header: looking for '" << name << "'");
+
+ for (p = mime; *p; p += strcspn(p, "\n\r")) {
+ if (strcmp(p, "\r\n\r\n") == 0 || strcmp(p, "\n\n") == 0)
+ return NULL;
+
+ while (xisspace(*p))
+ p++;
+
+ if (strncasecmp(p, name, namelen))
+ continue;
+
+ if (!xisspace(p[namelen]) && p[namelen] != ':')
+ continue;
+
+ l = strcspn(p, "\n\r") + 1;
+
+ if (l > GET_HDR_SZ)
+ l = GET_HDR_SZ;
+
+ xstrncpy(header, p, l);
+
+ debugs(25, 5, "mime_get_header: checking '" << header << "'");
+
+ q = header;
+
+ q += namelen;
+
+ if (*q == ':')
+ q++, got = 1;
+
+ while (xisspace(*q))
+ q++, got = 1;
+
+ if (got && prefix) {
+ /* we could process list entries here if we had strcasestr(). */
+ /* make sure we did not match a part of another field-value */
+ got = !strncasecmp(q, prefix, preflen) && !xisalpha(q[preflen]);
+ }
+
+ if (got) {
+ debugs(25, 5, "mime_get_header: returning '" << q << "'");
+ return q;
+ }
+ }
+
+ return NULL;
+}
+
+size_t
+headersEnd(const char *mime, size_t l)
+{
+ size_t e = 0;
+ int state = 1;
+
+ PROF_start(headersEnd);
+
+ while (e < l && state < 3) {
+ switch (state) {
+
+ case 0:
+
+ if ('\n' == mime[e])
+ state = 1;
+
+ break;
+
+ case 1:
+ if ('\r' == mime[e])
+ state = 2;
+ else if ('\n' == mime[e])
+ state = 3;
+ else
+ state = 0;
+
+ break;
+
+ case 2:
+ if ('\n' == mime[e])
+ state = 3;
+ else
+ state = 0;
+
+ break;
+
+ default:
+ break;
+ }
+
+ e++;
+ }
+ PROF_stop(headersEnd);
+
+ if (3 == state)
+ return e;
+
+ return 0;
+}
--- /dev/null
+#include "squid.h"
+
+// for StatHist definitions
+#include "protos.h"
+
+void
+statHistDump(const StatHist * H, StoreEntry * sentry, StatHistBinDumper * bd)
+{
+ fatal("statHistDump: Not implemented");
+}
+
+void
+statHistCount(StatHist * H, double val)
+{
+ fatal("statHistCount: Not implemented");
+}
+
+void
+statHistEnumInit(StatHist * H, int last_enum)
+{
+//NO-OP fatal("statHistEnumInit: Not implemented");
+}
extern "C" void
storeAppendPrintf(StoreEntry * e, const char *fmt,...)
{
- fatal("Not implemented");
+ fatal("storeAppendPrintf: Not implemented");
}
extern "C" void
storeAppendVPrintf(StoreEntry * e, const char *fmt, va_list vargs)
{
- fatal("Not implemented");
+ fatal("storeAppendVPrintf: Not implemented");
}
#ifndef _USE_INLINE_
--- /dev/null
+#include "config.h"
+#include <cppunit/TestAssert.h>
+
+#include "testHttpReply.h"
+#include "HttpReply.h"
+#include "Mem.h"
+
+/* to avoid libsquid.la and its comm stuff */
+#include "TextException.cc"
+
+CPPUNIT_TEST_SUITE_REGISTRATION( testHttpReply );
+
+struct SquidConfig Config;
+
+/* stub functions to link successfully */
+
+#include "Store.h"
+void
+StoreEntry::timestampsSet()
+{
+ fatal("StoreEntry::timestampsSet. Not implemented.");
+}
+
+void
+StoreEntry::setPublicKey()
+{
+ fatal("StoreEntry::setPulicKey. Not implemented.");
+}
+
+#include "MemObject.h"
+int64_t
+MemObject::endOffset() const
+{
+ return 0;
+}
+
+#include "ConfigParser.h"
+void
+ConfigParser::destruct()
+{
+// CALLED as shutdown no-op
+// fatal("ConfigParser::destruct. Not implemented.");
+}
+
+void
+eventAdd(const char *name, EVH * func, void *arg, double when, int, bool cbdata)
+{
+// CALLED as setUp no-op
+// fatal("eventAdd. Not implemented.");
+}
+
+/* end */
+
+void
+testHttpReply::setUp()
+{
+ Mem::Init();
+ httpHeaderInitModule();
+}
+
+void
+testHttpReply::testSanityCheckFirstLine()
+{
+ MemBuf input;
+ HttpReply engine;
+ http_status error = HTTP_STATUS_NONE;
+ size_t hdr_len;
+ input.init();
+
+ // a valid status line
+ input.append("HTTP/1.1 200 Okay\n\n", 19);
+ hdr_len = headersEnd(input.content(),input.contentSize());
+ CPPUNIT_ASSERT( 1 && engine.sanityCheckStartLine(&input, hdr_len, &error) );
+ CPPUNIT_ASSERT_EQUAL(error, HTTP_STATUS_NONE);
+ input.reset();
+ error = HTTP_STATUS_NONE;
+
+ input.append("HTTP/1.1 200 Okay \n\n", 28);
+ hdr_len = headersEnd(input.content(),input.contentSize());
+ CPPUNIT_ASSERT( 2 && engine.sanityCheckStartLine(&input, hdr_len, &error) );
+ CPPUNIT_ASSERT_EQUAL(error, HTTP_STATUS_NONE);
+ input.reset();
+ error = HTTP_STATUS_NONE;
+
+#if TODO // these cases are only checked after parse...
+ // invalid status line
+ input.append("HTTP/1.1 999 Okay\n\n", 19);
+ hdr_len = headersEnd(input.content(),input.contentSize());
+ CPPUNIT_ASSERT( 3 && !engine.sanityCheckStartLine(&input, hdr_len, &error) );
+ CPPUNIT_ASSERT_EQUAL(error, HTTP_INVALID_HEADER);
+ input.reset();
+ error = HTTP_STATUS_NONE;
+
+ input.append("HTTP/1.1 2000 Okay \n\n", 29);
+ hdr_len = headersEnd(input.content(),input.contentSize());
+ CPPUNIT_ASSERT( 4 && engine.sanityCheckStartLine(&input, hdr_len, &error) );
+ CPPUNIT_ASSERT_EQUAL(error, HTTP_STATUS_NONE);
+ input.reset();
+ error = HTTP_STATUS_NONE;
+#endif
+
+ // empty status line
+ input.append("\n\n", 2);
+ hdr_len = headersEnd(input.content(),input.contentSize());
+ CPPUNIT_ASSERT( 5 && !engine.sanityCheckStartLine(&input, hdr_len, &error) );
+ CPPUNIT_ASSERT_EQUAL(error, HTTP_INVALID_HEADER);
+ input.reset();
+ error = HTTP_STATUS_NONE;
+
+ input.append(" \n\n", 8);
+ hdr_len = headersEnd(input.content(),input.contentSize());
+ CPPUNIT_ASSERT( 6 && !engine.sanityCheckStartLine(&input, hdr_len, &error) );
+ CPPUNIT_ASSERT_EQUAL(error, HTTP_INVALID_HEADER);
+ input.reset();
+ error = HTTP_STATUS_NONE;
+
+ // status line with no message
+ input.append("HTTP/1.1 200\n\n", 14); /* real case seen */
+ hdr_len = headersEnd(input.content(),input.contentSize());
+ CPPUNIT_ASSERT(engine.sanityCheckStartLine(&input, hdr_len, &error) );
+ CPPUNIT_ASSERT_EQUAL(error, HTTP_STATUS_NONE);
+ input.reset();
+ error = HTTP_STATUS_NONE;
+
+ input.append("HTTP/1.1 200 \n\n", 15); /* real case seen */
+ hdr_len = headersEnd(input.content(),input.contentSize());
+ CPPUNIT_ASSERT(engine.sanityCheckStartLine(&input, hdr_len, &error) );
+ CPPUNIT_ASSERT_EQUAL(error, HTTP_STATUS_NONE);
+ input.reset();
+ error = HTTP_STATUS_NONE;
+
+#if FUTURE
+
+ // status line with no status
+ input.append("HTTP/1.1 \n\n", 11);
+ hdr_len = headersEnd(input.content(),input.contentSize());
+ CPPUNIT_ASSERT(!engine.sanityCheckStartLine(&input, hdr_len, &error) );
+ CPPUNIT_ASSERT_EQUAL(error, HTTP_INVALID_HEADER);
+ input.reset();
+ error = HTTP_STATUS_NONE;
+
+ input.append("HTTP/1.1 \n\n", 15);
+ hdr_len = headersEnd(input.content(),input.contentSize());
+ CPPUNIT_ASSERT(!engine.sanityCheckStartLine(&input, hdr_len, &error) );
+ CPPUNIT_ASSERT_EQUAL(error, HTTP_INVALID_HEADER);
+ input.reset();
+ error = HTTP_STATUS_NONE;
+
+ input.append("HTTP/1.1 Okay\n\n", 16); /* real case seen */
+ hdr_len = headersEnd(input.content(),input.contentSize());
+ CPPUNIT_ASSERT(!engine.sanityCheckStartLine(&input, hdr_len, &error) );
+ CPPUNIT_ASSERT_EQUAL(error, HTTP_INVALID_HEADER);
+ input.reset();
+ error = HTTP_STATUS_NONE;
+
+ // status line with nul-byte
+ input.append("HTTP/1.1\0200 Okay\n\n", 19); /* real case seen */
+ hdr_len = headersEnd(input.content(),input.contentSize());
+ CPPUNIT_ASSERT(!engine.sanityCheckStartLine(&input, hdr_len, &error) );
+ CPPUNIT_ASSERT_EQUAL(error, HTTP_INVALID_HEADER);
+ input.reset();
+ error = HTTP_STATUS_NONE;
+
+ // status line with negative status
+ input.append("HTTP/1.1 -000\n\n", 15); /* real case seen */
+ hdr_len = headersEnd(input.content(),input.contentSize());
+ CPPUNIT_ASSERT(!engine.sanityCheckStartLine(&input, hdr_len, &error) );
+ CPPUNIT_ASSERT_EQUAL(error, HTTP_INVALID_HEADER);
+ input.reset();
+ error = HTTP_STATUS_NONE;
+
+ // status line with non-HTTP protocol
+ input.append("ICY/1.1 200 Okay\n\n", 18); /* real case seen */
+ hdr_len = headersEnd(input.content(),input.contentSize());
+ /* NP: for nw ICY is handled as a pass-thru */
+ /* Squid-3 will ignore it (and mangle the headers as per HTTP). */
+ CPPUNIT_ASSERT(!engine.sanityCheckStartLine(&input, hdr_len, &error) );
+ CPPUNIT_ASSERT_EQUAL(error, HTTP_INVALID_HEADER);
+ input.reset();
+ error = HTTP_STATUS_NONE;
+#endif
+
+}
--- /dev/null
+
+#ifndef SQUID_SRC_TEST_HTTP_REPLY_H
+#define SQUID_SRC_TEST_HTTP_REPLY_H
+
+#include <cppunit/extensions/HelperMacros.h>
+
+/*
+ * test HttpReply
+ */
+
+class testHttpReply : public CPPUNIT_NS::TestFixture
+{
+ CPPUNIT_TEST_SUITE( testHttpReply );
+ CPPUNIT_TEST( testSanityCheckFirstLine );
+ CPPUNIT_TEST_SUITE_END();
+
+public:
+ void setUp();
+
+protected:
+ void testSanityCheckFirstLine();
+};
+
+#endif
+
CPPUNIT_TEST_SUITE_REGISTRATION( testHttpRequest );
+/** wrapper for testing HttpRequest object private and protected functions */
+class PrivateHttpRequest : public HttpRequest
+{
+public:
+ bool doSanityCheckStartLine(MemBuf *b, const size_t h, http_status *e) { return sanityCheckStartLine(b,h,e); };
+};
+
/* stub functions to link successfully */
void
shut_down(int)
testHttpRequest::setUp()
{
Mem::Init();
+ httpHeaderInitModule();
}
/*
CPPUNIT_ASSERT_EQUAL(String("http://2000:800::45/foo"), String(url));
xfree(url);
}
+
+void
+testHttpRequest::testSanityCheckStartLine()
+{
+ MemBuf input;
+ PrivateHttpRequest engine;
+ http_status error = HTTP_STATUS_NONE;
+ size_t hdr_len;
+ input.init();
+
+ // a valid request line
+ input.append("GET / HTTP/1.1\n\n", 16);
+ hdr_len = headersEnd(input.content(), input.contentSize());
+ CPPUNIT_ASSERT(engine.doSanityCheckStartLine(&input, hdr_len, &error) );
+ CPPUNIT_ASSERT_EQUAL(error, HTTP_STATUS_NONE);
+ input.reset();
+ error = HTTP_STATUS_NONE;
+
+ input.append("GET / HTTP/1.1\n\n", 18);
+ hdr_len = headersEnd(input.content(), input.contentSize());
+ CPPUNIT_ASSERT(engine.doSanityCheckStartLine(&input, hdr_len, &error) );
+ CPPUNIT_ASSERT_EQUAL(error, HTTP_STATUS_NONE);
+ input.reset();
+ error = HTTP_STATUS_NONE;
+
+ // strange but valid methods
+ input.append(". / HTTP/1.1\n\n", 14);
+ hdr_len = headersEnd(input.content(), input.contentSize());
+ CPPUNIT_ASSERT(engine.doSanityCheckStartLine(&input, hdr_len, &error) );
+ CPPUNIT_ASSERT_EQUAL(error, HTTP_STATUS_NONE);
+ input.reset();
+ error = HTTP_STATUS_NONE;
+
+ input.append("OPTIONS * HTTP/1.1\n\n", 20);
+ hdr_len = headersEnd(input.content(), input.contentSize());
+ CPPUNIT_ASSERT(engine.doSanityCheckStartLine(&input, hdr_len, &error) );
+ CPPUNIT_ASSERT_EQUAL(error, HTTP_STATUS_NONE);
+ input.reset();
+ error = HTTP_STATUS_NONE;
+
+// TODO no method
+
+// TODO binary code in method
+
+// TODO no URL
+
+// TODO no status (okay)
+
+// TODO non-HTTP protocol
+
+ input.append(" \n\n", 8);
+ hdr_len = headersEnd(input.content(), input.contentSize());
+ CPPUNIT_ASSERT(!engine.doSanityCheckStartLine(&input, hdr_len, &error) );
+ CPPUNIT_ASSERT_EQUAL(error, HTTP_INVALID_HEADER);
+ input.reset();
+ error = HTTP_STATUS_NONE;
+}
CPPUNIT_TEST( testCreateFromUrlAndMethod );
CPPUNIT_TEST( testCreateFromUrl );
CPPUNIT_TEST( testIPv6HostColonBug );
+ CPPUNIT_TEST( testSanityCheckStartLine );
CPPUNIT_TEST_SUITE_END();
public:
void testCreateFromUrlAndMethod();
void testCreateFromUrl();
void testIPv6HostColonBug();
+ void testSanityCheckStartLine();
};
#endif