1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
|
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 2008 Andreas Hartmetz <ahartmetz@gmail.com>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "httpheadertokenizetest.h"
#include <QTest>
#include <QByteArray>
#include <QDebug>
#include <QHash>
#include <parsinghelpers.h>
#include <parsinghelpers.cpp>
// QT5 TODO QTEST_GUILESS_MAIN(HeaderTokenizeTest)
QTEST_MAIN(HeaderTokenizeTest)
/* some possible fields that can be used for test headers
{"accept-ranges", false},
{"cache-control", true},
{"connection", true},
{"content-disposition", false}, //is multi-valued in a way, but with ";" separator!
{"content-encoding", true},
{"content-language", true},
{"content-length", false},
{"content-location", false},
{"content-md5", false},
{"content-type", false},
{"date", false},
{"dav", true}, //RFC 2518
{"etag", false},
{"expires", false},
{"keep-alive", false}, //RFC 2068
{"last-modified", false},
{"link", false}, //RFC 2068, multi-valued with ";" separator
{"location", false},
*/
// no use testing many different headers, just a couple each of the multi-valued
// and the single-valued group to make sure that corner cases work both if there
// are already entries for the header and if there are no entries.
static const char messyHeader[] =
"\n"
"accept-ranges:foo\r\n"
"connection: one\r\n"
" t_\r\n"
" wo,\r\n"
"\tthree\r\n"
"accept-ranges:42\n"
"accept-Ranges:\tmaybe \r"
" or not\n"
"CoNNectIoN:four, , ,, , \r\n"
" :fi:ve\r\n"
":invalid stuff\r\n"
"\tinvalid: connection:close\t\r"
"connection: Six, seven ,, , eight\r" // one malformed newline...
"\n\r "; // two malformed newlines; end of header. also observe the trailing space.
// tab separates values, newline separates header lines. the first word is the key.
static const char messyResult[] =
"accept-ranges\tfoo\t42\tmaybe or not\n"
"connection\tone t_ wo\tthree\tfour\t:fi:ve\tSix\tseven\teight";
static const char redirectHeader[] =
//"HTTP/1.1 302 Moved Temporarily\r\n"
"Location: http://www.hertz.de/rentacar/index.jsp?bsc=t&targetPage=reservationOnHomepage.jsp\r\n"
"Connection:close\r\n"
"Cache-Control: no-cache\r\n"
"Pragma: no-cache\r\n"
"\r\n";
static const char redirectResult[] =
"cache-control\tno-cache\n"
"connection\tclose\n"
"location\thttp://www.hertz.de/rentacar/index.jsp?bsc=t&targetPage=reservationOnHomepage.jsp\n"
"pragma\tno-cache";
static const int bufSize = 4096;
char buffer[bufSize];
void HeaderTokenizeTest::testMessyHeader()
{
// Copy the header into a writable buffer
for (int i = 0; i < bufSize; i++) {
buffer[i] = 0;
}
strcpy(buffer, messyHeader);
HeaderTokenizer tokenizer(buffer);
int tokenizeEnd = tokenizer.tokenize(0, strlen(messyHeader));
QCOMPARE(tokenizeEnd, (int)(strlen(messyHeader) - 1));
// If the output of the tokenizer contains all the terms that should be there and
// exactly the number of terms that should be there then it's exactly correct.
// We are lax wrt trailing whitespace, by the way. It does neither explicitly matter
// nor not matter according to the standard. Internal whitespace similarly should not
// matter but we have to be exact because the tokenizer does not move strings around,
// it only overwrites \r and \n in case of line continuations.
int nValues = 0;
const QList<QByteArray> messyResultsList = QByteArray(messyResult).split('\n');
for (const QByteArray &ba : messyResultsList) {
QList<QByteArray> values = ba.split('\t');
QByteArray key = values.takeFirst();
nValues += values.count();
QList<QByteArray> comparisonValues;
for (const auto [startIdx, endIdx] : tokenizer.value(key).beginEnd) {
comparisonValues.append(QByteArray(buffer + startIdx, endIdx - startIdx));
}
QCOMPARE(comparisonValues.count(), values.count());
for (int i = 0; i < values.count(); i++) {
QVERIFY(comparisonValues[i].startsWith(values[i]));
}
}
int nValues2 = 0;
HeaderTokenizer::ConstIterator it = tokenizer.constBegin();
for (; it != tokenizer.constEnd(); ++it) {
nValues2 += it.value().beginEnd.count();
}
QCOMPARE(nValues2, nValues);
return; // comment out for parsed header dump to stdout
it = tokenizer.constBegin();
for (; it != tokenizer.constEnd(); ++it) {
if (!it.value().beginEnd.isEmpty()) {
qDebug() << it.key() << ":";
}
for (const auto [startIdx, endIdx] : it.value().beginEnd) {
qDebug() << " " << QByteArray(buffer + startIdx, endIdx - startIdx);
}
}
}
void HeaderTokenizeTest::testRedirectHeader()
{
// Copy the header into a writable buffer
for (int i = 0; i < bufSize; i++) {
buffer[i] = 0;
}
strcpy(buffer, redirectHeader);
HeaderTokenizer tokenizer(buffer);
int tokenizeEnd = tokenizer.tokenize(0, strlen(redirectHeader));
QCOMPARE(tokenizeEnd, (int)strlen(redirectHeader));
int nValues = 0;
const QList<QByteArray> redirectResults = QByteArray(redirectResult).split('\n');
for (const QByteArray &ba : redirectResults) {
QList<QByteArray> values = ba.split('\t');
QByteArray key = values.takeFirst();
nValues += values.count();
QList<QByteArray> comparisonValues;
for (const auto [startIdx, endIdx] : tokenizer.value(key).beginEnd) {
comparisonValues.append(QByteArray(buffer + startIdx, endIdx - startIdx));
}
QCOMPARE(comparisonValues.count(), values.count());
for (int i = 0; i < values.count(); i++) {
QVERIFY(comparisonValues[i].startsWith(values[i]));
}
}
int nValues2 = 0;
HeaderTokenizer::ConstIterator it = tokenizer.constBegin();
for (; it != tokenizer.constEnd(); ++it) {
nValues2 += it.value().beginEnd.count();
}
QCOMPARE(nValues2, nValues);
// Fix compiler warning
(void)contentDispositionParser;
}
#include "moc_httpheadertokenizetest.cpp"
|