24 #include <QStringList>
28 inline QString normalizeHeaderKey(
const QString &field);
29 inline QByteArray decodeBasicAuth(
const QString &auth);
30 inline std::pair<QString, QString> decodeBasicAuthPair(
const QString &auth);
38 return m_data.value(QStringLiteral(
"CONTENT_DISPOSITION"));
43 m_data.insert(QStringLiteral(
"CACHE_CONTROL"), value);
53 if (filename.isEmpty()) {
62 return m_data.value(QStringLiteral(
"CONTENT_ENCODING"));
67 m_data.insert(QStringLiteral(
"CONTENT_ENCODING"), encoding);
73 const auto it = m_data.constFind(QStringLiteral(
"CONTENT_TYPE"));
74 if (it != m_data.constEnd()) {
75 const QString &ct = it.value();
76 ret = ct.mid(0, ct.indexOf(QLatin1Char(
';'))).toLower();
83 m_data.insert(QStringLiteral(
"CONTENT_TYPE"),
contentType);
89 const auto it = m_data.constFind(QStringLiteral(
"CONTENT_TYPE"));
90 if (it != m_data.constEnd()) {
92 int pos =
contentType.indexOf(QLatin1String(
"charset="), 0, Qt::CaseInsensitive);
94 int endPos =
contentType.indexOf(QLatin1Char(
';'), pos);
95 ret =
contentType.mid(pos + 8, endPos).trimmed().toUpper();
104 const auto it = m_data.constFind(QStringLiteral(
"CONTENT_TYPE"));
105 if (it == m_data.constEnd() || (it.value().isEmpty() && !charset.isEmpty())) {
106 m_data.insert(QStringLiteral(
"CONTENT_TYPE"), QLatin1String(
"charset=") + charset);
111 int pos =
contentType.indexOf(QLatin1String(
"charset="), 0, Qt::CaseInsensitive);
113 int endPos =
contentType.indexOf(QLatin1Char(
';'), pos);
115 if (charset.isEmpty()) {
116 int lastPos =
contentType.lastIndexOf(QLatin1Char(
';'), pos);
118 m_data.remove(QStringLiteral(
"CONTENT_TYPE"));
129 }
else if (!charset.isEmpty()) {
130 contentType.append(QLatin1String(
"; charset=") + charset);
132 m_data.insert(QStringLiteral(
"CONTENT_TYPE"),
contentType);
137 return m_data.value(QStringLiteral(
"CONTENT_TYPE")).startsWith(QLatin1String(
"text/"));
143 return ct == QLatin1String(
"text/html") ||
144 ct == QLatin1String(
"application/xhtml+xml") ||
145 ct == QLatin1String(
"application/vnd.wap.xhtml+xml");
151 return ct == QLatin1String(
"application/xhtml+xml") ||
152 ct == QLatin1String(
"application/vnd.wap.xhtml+xml");
158 return ct == QLatin1String(
"text/xml") ||
159 ct == QLatin1String(
"application/xml") ||
160 ct.endsWith(QLatin1String(
"xml"));
165 const auto it = m_data.constFind(QStringLiteral(
"CONTENT_TYPE"));
166 if (it != m_data.constEnd()) {
167 return it.value() == QLatin1String(
"application/json");
174 auto it = m_data.constFind(QStringLiteral(
"CONTENT_LENGTH"));
175 if (it != m_data.constEnd()) {
176 return it.value().toLongLong();
183 m_data.insert(QStringLiteral(
"CONTENT_LENGTH"), QString::number(value));
190 const QString dt = QLocale::c().toString(
date.toUTC(),
191 QStringLiteral(
"ddd, dd MMM yyyy hh:mm:ss 'GMT"));
192 m_data.insert(QStringLiteral(
"DATE"), dt);
199 auto it = m_data.constFind(QStringLiteral(
"DATE"));
200 if (it != m_data.constEnd()) {
201 const QString &
date = it.value();
203 if (
date.endsWith(QLatin1String(
" GMT"))) {
204 ret = QLocale::c().toDateTime(
date.left(
date.size() - 4),
205 QStringLiteral(
"ddd, dd MMM yyyy hh:mm:ss"));
207 ret = QLocale::c().toDateTime(
date,
208 QStringLiteral(
"ddd, dd MMM yyyy hh:mm:ss"));
210 ret.setTimeSpec(Qt::UTC);
218 return m_data.value(QStringLiteral(
"IF_MODIFIED_SINCE"));
224 auto it = m_data.constFind(QStringLiteral(
"IF_MODIFIED_SINCE"));
225 if (it != m_data.constEnd()) {
226 const QString &ifModifiedStr = it.value();
228 if (ifModifiedStr.endsWith(QLatin1String(
" GMT"))) {
229 ret = QLocale::c().toDateTime(ifModifiedStr.left(ifModifiedStr.size() - 4),
230 QStringLiteral(
"ddd, dd MMM yyyy hh:mm:ss"));
232 ret = QLocale::c().toDateTime(ifModifiedStr,
233 QStringLiteral(
"ddd, dd MMM yyyy hh:mm:ss"));
235 ret.setTimeSpec(Qt::UTC);
243 auto it = m_data.constFind(QStringLiteral(
"IF_MODIFIED_SINCE"));
244 if (it != m_data.constEnd()) {
245 return it.value() != QLocale::c().toString(
lastModified.toUTC(),
246 QStringLiteral(
"ddd, dd MMM yyyy hh:mm:ss 'GMT"));
253 auto it = m_data.constFind(QStringLiteral(
"IF_MATCH"));
254 if (it != m_data.constEnd()) {
255 const QString &clientETag = it.value();
256 return clientETag.midRef(1, clientETag.size() - 2) == etag ||
257 clientETag.midRef(3, clientETag.size() - 4) == etag;
264 auto it = m_data.constFind(QStringLiteral(
"IF_NONE_MATCH"));
265 if (it != m_data.constEnd()) {
266 const QString &clientETag = it.value();
267 return clientETag.midRef(1, clientETag.size() - 2) == etag ||
268 clientETag.midRef(3, clientETag.size() - 4) == etag;
275 m_data.insert(QStringLiteral(
"ETAG"), QLatin1Char(
'"') + etag + QLatin1Char(
'"'));
280 return m_data.value(QStringLiteral(
"LAST_MODIFIED"));
285 m_data.insert(QStringLiteral(
"LAST_MODIFIED"), value);
292 const auto dt = QLocale::c().toString(
lastModified.toUTC(),
293 QStringLiteral(
"ddd, dd MMM yyyy hh:mm:ss 'GMT"));
300 return m_data.value(QStringLiteral(
"SERVER"));
305 m_data.insert(QStringLiteral(
"SERVER"), value);
310 return m_data.value(QStringLiteral(
"CONNECTION"));
315 return m_data.value(QStringLiteral(
"HOST"));
320 return m_data.value(QStringLiteral(
"USER_AGENT"));
325 return m_data.value(QStringLiteral(
"REFERER"));
330 int fragmentPos = uri.indexOf(QLatin1Char(
'#'));
331 if (fragmentPos != -1) {
333 m_data.insert(QStringLiteral(
"REFERER"), uri.mid(0, fragmentPos));
335 m_data.insert(QStringLiteral(
"REFERER"), uri);
341 m_data.insert(QStringLiteral(
"WWW_AUTHENTICATE"), value);
346 m_data.insert(QStringLiteral(
"PROXY_AUTHENTICATE"), value);
351 return m_data.value(QStringLiteral(
"AUTHORIZATION"));
356 return QString::fromLatin1(decodeBasicAuth(
authorization()));
367 if (username.contains(QLatin1Char(
':'))) {
368 qCWarning(CUTELYST_CORE) <<
"Headers::Basic authorization user name can't contain ':'";
372 const QString result = username + QLatin1Char(
':') + password;
373 ret = QStringLiteral(
"Basic ") + QString::fromLatin1(result.toLatin1().toBase64());
374 m_data.insert(QStringLiteral(
"AUTHORIZATION"), ret);
380 return m_data.value(QStringLiteral(
"PROXY_AUTHORIZATION"));
395 return m_data.value(normalizeHeaderKey(field));
400 return m_data.value(normalizeHeaderKey(field), defaultValue);
405 m_data.insert(normalizeHeaderKey(field), value);
410 setHeader(field, values.join(QStringLiteral(
", ")));
415 m_data.insertMulti(normalizeHeaderKey(field), value);
420 m_data.insertMulti(normalizeHeaderKey(field), values.join(QStringLiteral(
", ")));
425 m_data.remove(normalizeHeaderKey(field));
430 return m_data.contains(normalizeHeaderKey(field));
443 QString normalizeHeaderKey(
const QString &field)
447 while (i < key.size()) {
453 }
else if (c == QLatin1Char(
'-')) {
454 c = QLatin1Char(
'_');
461 QByteArray decodeBasicAuth(
const QString &auth)
464 if (!auth.isEmpty() && auth.startsWith(QLatin1String(
"Basic "))) {
465 int pos = auth.lastIndexOf(QLatin1Char(
' '));
467 ret = QByteArray::fromBase64(auth.mid(pos).toLatin1());
473 std::pair<QString, QString> decodeBasicAuthPair(
const QString &auth)
475 std::pair<QString, QString> ret;
476 const QByteArray authorization = decodeBasicAuth(auth);
477 if (!authorization.isEmpty()) {
478 int pos = authorization.indexOf(
':');
480 ret.first = QString::fromLatin1(authorization);
482 ret = { QString::fromLatin1(authorization.left(pos)),
483 QString::fromLatin1(authorization.mid(pos + 1)) };
489 QDebug operator<<(QDebug debug,
const Headers &headers)
491 const QHash<QString, QString> data = headers.
data();
492 const bool oldSetting = debug.autoInsertSpaces();
493 debug.nospace() <<
"Headers(";
494 for (
auto it = data.constBegin();
495 it != data.constEnd(); ++it) {
499 debug.setAutoInsertSpaces(oldSetting);
500 return debug.maybeSpace();