зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1413999 - Part1: Parse Serve-Timing header r=dragana
1. Add a helper function Tokenize() which is able to handle quoted-string and quoted-pair. 2. Modify ParsedHeaderValueListList - Use Tokenize() to split string by ','. 3. Modify ParsedHeaderValueList - Use Tokenize() to split string by ';'. - Function ParseNameAndValue() is for getting name and value from the string containing '='. 4. Handle backslash escapes for quoted string. 5. Reuse ParsedHeaderValueListList to parse server-timing header. --HG-- extra : rebase_source : 7802a1ae9a6410c4ea992b1197018c5a3f994fa0
This commit is contained in:
Родитель
511d112e2d
Коммит
39f697a80b
|
@ -19,6 +19,7 @@
|
|||
#include "nsICacheEntry.h"
|
||||
#include "nsIRequest.h"
|
||||
#include <errno.h>
|
||||
#include <functional>
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
@ -603,127 +604,235 @@ void EnsureBuffer(UniquePtr<uint8_t[]> &buf, uint32_t newSize,
|
|||
{
|
||||
localEnsureBuffer<uint8_t> (buf, newSize, preserve, objSize);
|
||||
}
|
||||
///
|
||||
|
||||
static bool
|
||||
IsTokenSymbol(signed char chr) {
|
||||
if (chr < 33 || chr == 127 ||
|
||||
chr == '(' || chr == ')' || chr == '<' || chr == '>' ||
|
||||
chr == '@' || chr == ',' || chr == ';' || chr == ':' ||
|
||||
chr == '"' || chr == '/' || chr == '[' || chr == ']' ||
|
||||
chr == '?' || chr == '=' || chr == '{' || chr == '}' || chr == '\\') {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ParsedHeaderPair::ParsedHeaderPair(const char *name, int32_t nameLen,
|
||||
const char *val, int32_t valLen,
|
||||
bool isQuotedValue)
|
||||
: mName(nsDependentCSubstring(nullptr, 0u))
|
||||
, mValue(nsDependentCSubstring(nullptr, 0u))
|
||||
, mIsQuotedValue(isQuotedValue)
|
||||
{
|
||||
if (nameLen > 0) {
|
||||
mName.Rebind(name, name + nameLen);
|
||||
}
|
||||
if (valLen > 0) {
|
||||
if (mIsQuotedValue) {
|
||||
RemoveQuotedStringEscapes(val, valLen);
|
||||
mValue.Rebind(mUnquotedValue.BeginWriting(), mUnquotedValue.Length());
|
||||
} else {
|
||||
mValue.Rebind(val, val + valLen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ParsedHeaderValueList::Tokenize(char *input, uint32_t inputLen, char **token,
|
||||
uint32_t *tokenLen, bool *foundEquals, char **next)
|
||||
ParsedHeaderPair::RemoveQuotedStringEscapes(const char *val, int32_t valLen)
|
||||
{
|
||||
if (foundEquals) {
|
||||
*foundEquals = false;
|
||||
}
|
||||
if (next) {
|
||||
*next = nullptr;
|
||||
}
|
||||
if (inputLen < 1 || !input || !token) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool foundFirst = false;
|
||||
bool inQuote = false;
|
||||
bool foundToken = false;
|
||||
*token = input;
|
||||
*tokenLen = inputLen;
|
||||
|
||||
for (uint32_t index = 0; !foundToken && index < inputLen; ++index) {
|
||||
// strip leading cruft
|
||||
if (!foundFirst &&
|
||||
(input[index] == ' ' || input[index] == '"' || input[index] == '\t')) {
|
||||
(*token)++;
|
||||
} else {
|
||||
foundFirst = true;
|
||||
mUnquotedValue.Truncate();
|
||||
const char *c = val;
|
||||
for (int32_t i = 0; i < valLen; ++i) {
|
||||
if (c[i] == '\\' && c[i + 1]) {
|
||||
++i;
|
||||
}
|
||||
mUnquotedValue.Append(c[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void Tokenize(const char *input, uint32_t inputLen, const char token,
|
||||
const std::function<void(const char *, uint32_t)>& consumer)
|
||||
{
|
||||
auto trimWhitespace =
|
||||
[] (const char *in, uint32_t inLen, const char **out, uint32_t *outLen) {
|
||||
*out = in;
|
||||
*outLen = inLen;
|
||||
if (inLen == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Trim leading space
|
||||
while (nsCRT::IsAsciiSpace(**out)) {
|
||||
(*out)++;
|
||||
--(*outLen);
|
||||
}
|
||||
|
||||
// Trim tailing space
|
||||
for (const char *i = *out + *outLen - 1; i >= *out; --i) {
|
||||
if (!nsCRT::IsAsciiSpace(*i)) {
|
||||
break;
|
||||
}
|
||||
--(*outLen);
|
||||
}
|
||||
};
|
||||
|
||||
const char *first = input;
|
||||
bool inQuote = false;
|
||||
const char *result = nullptr;
|
||||
uint32_t resultLen = 0;
|
||||
for (uint32_t index = 0; index < inputLen; ++index) {
|
||||
if (inQuote && input[index] == '\\' && input[index + 1]) {
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
if (input[index] == '"') {
|
||||
inQuote = !inQuote;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inQuote) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (input[index] == '=' || input[index] == ';') {
|
||||
*tokenLen = (input + index) - *token;
|
||||
if (next && ((index + 1) < inputLen)) {
|
||||
*next = input + index + 1;
|
||||
}
|
||||
foundToken = true;
|
||||
if (foundEquals && input[index] == '=') {
|
||||
*foundEquals = true;
|
||||
}
|
||||
break;
|
||||
if (input[index] == token) {
|
||||
trimWhitespace(first, (input + index) - first,
|
||||
&result, &resultLen);
|
||||
consumer(result, resultLen);
|
||||
first = input + index + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundToken) {
|
||||
*tokenLen = (input + inputLen) - *token;
|
||||
}
|
||||
|
||||
// strip trailing cruft
|
||||
for (char *index = *token + *tokenLen - 1; index >= *token; --index) {
|
||||
if (*index != ' ' && *index != '\t' && *index != '"') {
|
||||
break;
|
||||
}
|
||||
--(*tokenLen);
|
||||
if (*index == '"') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
trimWhitespace(first, (input + inputLen) - first,
|
||||
&result, &resultLen);
|
||||
consumer(result, resultLen);
|
||||
}
|
||||
|
||||
ParsedHeaderValueList::ParsedHeaderValueList(char *t, uint32_t len)
|
||||
ParsedHeaderValueList::ParsedHeaderValueList(const char *t,
|
||||
uint32_t len,
|
||||
bool allowInvalidValue)
|
||||
{
|
||||
char *name = nullptr;
|
||||
uint32_t nameLen = 0;
|
||||
char *value = nullptr;
|
||||
uint32_t valueLen = 0;
|
||||
char *next = nullptr;
|
||||
bool foundEquals;
|
||||
|
||||
while (t) {
|
||||
Tokenize(t, len, &name, &nameLen, &foundEquals, &next);
|
||||
if (next) {
|
||||
len -= next - t;
|
||||
}
|
||||
t = next;
|
||||
if (foundEquals && t) {
|
||||
Tokenize(t, len, &value, &valueLen, nullptr, &next);
|
||||
if (next) {
|
||||
len -= next - t;
|
||||
}
|
||||
t = next;
|
||||
}
|
||||
mValues.AppendElement(ParsedHeaderPair(name, nameLen, value, valueLen));
|
||||
value = name = nullptr;
|
||||
valueLen = nameLen = 0;
|
||||
next = nullptr;
|
||||
if (!len) {
|
||||
return;
|
||||
}
|
||||
|
||||
ParsedHeaderValueList *self = this;
|
||||
auto consumer = [=] (const char *output, uint32_t outputLength) {
|
||||
self->ParseNameAndValue(output, allowInvalidValue);
|
||||
};
|
||||
|
||||
Tokenize(t, len, ';', consumer);
|
||||
}
|
||||
|
||||
ParsedHeaderValueListList::ParsedHeaderValueListList(const nsCString &fullHeader)
|
||||
void
|
||||
ParsedHeaderValueList::ParseNameAndValue(const char *input, bool allowInvalidValue)
|
||||
{
|
||||
const char *nameStart = input;
|
||||
const char *nameEnd = nullptr;
|
||||
const char *valueStart = input;
|
||||
const char *valueEnd = nullptr;
|
||||
bool isQuotedString = false;
|
||||
bool invalidValue = false;
|
||||
|
||||
for (; *input && *input != ';' && *input != ',' &&
|
||||
!nsCRT::IsAsciiSpace(*input) && *input != '='; input++)
|
||||
;
|
||||
|
||||
nameEnd = input;
|
||||
|
||||
if (!(nameEnd - nameStart)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check whether param name is a valid token.
|
||||
for (const char *c = nameStart; c < nameEnd; c++) {
|
||||
if (!IsTokenSymbol(*c)) {
|
||||
nameEnd = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(nameEnd - nameStart)) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (nsCRT::IsAsciiSpace(*input)) {
|
||||
++input;
|
||||
}
|
||||
|
||||
if (!*input || *input++ != '=') {
|
||||
mValues.AppendElement(ParsedHeaderPair(nameStart, nameEnd - nameStart,
|
||||
nullptr, 0, false));
|
||||
return;
|
||||
}
|
||||
|
||||
while (nsCRT::IsAsciiSpace(*input)) {
|
||||
++input;
|
||||
}
|
||||
|
||||
if (*input != '"') {
|
||||
// The value is a token, not a quoted string.
|
||||
valueStart = input;
|
||||
for (valueEnd = input;
|
||||
*valueEnd && !nsCRT::IsAsciiSpace (*valueEnd) &&
|
||||
*valueEnd != ';' && *valueEnd != ',';
|
||||
valueEnd++)
|
||||
;
|
||||
input = valueEnd;
|
||||
if (!allowInvalidValue) {
|
||||
for (const char *c = valueStart; c < valueEnd; c++) {
|
||||
if (!IsTokenSymbol(*c)) {
|
||||
valueEnd = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bool foundQuotedEnd = false;
|
||||
isQuotedString = true;
|
||||
|
||||
++input;
|
||||
valueStart = input;
|
||||
for (valueEnd = input; *valueEnd; ++valueEnd) {
|
||||
if (*valueEnd == '\\' && *(valueEnd + 1)) {
|
||||
++valueEnd;
|
||||
}
|
||||
else if (*valueEnd == '"') {
|
||||
foundQuotedEnd = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!foundQuotedEnd) {
|
||||
invalidValue = true;
|
||||
}
|
||||
|
||||
input = valueEnd;
|
||||
// *valueEnd != null means that *valueEnd is quote character.
|
||||
if (*valueEnd) {
|
||||
input++;
|
||||
}
|
||||
}
|
||||
|
||||
if (invalidValue) {
|
||||
valueEnd = valueStart;
|
||||
}
|
||||
|
||||
mValues.AppendElement(ParsedHeaderPair(nameStart, nameEnd - nameStart,
|
||||
valueStart, valueEnd - valueStart,
|
||||
isQuotedString));
|
||||
}
|
||||
|
||||
ParsedHeaderValueListList::ParsedHeaderValueListList(const nsCString &fullHeader,
|
||||
bool allowInvalidValue)
|
||||
: mFull(fullHeader)
|
||||
{
|
||||
char *t = mFull.BeginWriting();
|
||||
uint32_t len = mFull.Length();
|
||||
char *last = t;
|
||||
bool inQuote = false;
|
||||
for (uint32_t index = 0; index < len; ++index) {
|
||||
if (t[index] == '"') {
|
||||
inQuote = !inQuote;
|
||||
continue;
|
||||
}
|
||||
if (inQuote) {
|
||||
continue;
|
||||
}
|
||||
if (t[index] == ',') {
|
||||
mValues.AppendElement(ParsedHeaderValueList(last, (t + index) - last));
|
||||
last = t + index + 1;
|
||||
}
|
||||
}
|
||||
if (!inQuote) {
|
||||
mValues.AppendElement(ParsedHeaderValueList(last, (t + len) - last));
|
||||
}
|
||||
auto &values = mValues;
|
||||
auto consumer =
|
||||
[&values, allowInvalidValue] (const char *output, uint32_t outputLength) {
|
||||
values.AppendElement(ParsedHeaderValueList(output,
|
||||
outputLength,
|
||||
allowInvalidValue));
|
||||
};
|
||||
|
||||
Tokenize(mFull.BeginReading(), mFull.Length(), ',', consumer);
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
|
|
|
@ -266,42 +266,50 @@ class ParsedHeaderPair
|
|||
{
|
||||
public:
|
||||
ParsedHeaderPair(const char *name, int32_t nameLen,
|
||||
const char *val, int32_t valLen)
|
||||
{
|
||||
if (nameLen > 0) {
|
||||
mName.Rebind(name, name + nameLen);
|
||||
}
|
||||
if (valLen > 0) {
|
||||
mValue.Rebind(val, val + valLen);
|
||||
}
|
||||
}
|
||||
const char *val, int32_t valLen, bool isQuotedValue);
|
||||
|
||||
ParsedHeaderPair(ParsedHeaderPair const ©)
|
||||
: mName(copy.mName)
|
||||
, mValue(copy.mValue)
|
||||
, mUnquotedValue(copy.mUnquotedValue)
|
||||
, mIsQuotedValue(copy.mIsQuotedValue)
|
||||
{
|
||||
if (mIsQuotedValue) {
|
||||
mValue.Rebind(mUnquotedValue.BeginReading(), mUnquotedValue.Length());
|
||||
}
|
||||
}
|
||||
|
||||
nsDependentCSubstring mName;
|
||||
nsDependentCSubstring mValue;
|
||||
|
||||
private:
|
||||
nsCString mUnquotedValue;
|
||||
bool mIsQuotedValue;
|
||||
|
||||
void RemoveQuotedStringEscapes(const char *val, int32_t valLen);
|
||||
};
|
||||
|
||||
class ParsedHeaderValueList
|
||||
{
|
||||
public:
|
||||
ParsedHeaderValueList(char *t, uint32_t len);
|
||||
ParsedHeaderValueList(const char *t, uint32_t len, bool allowInvalidValue);
|
||||
nsTArray<ParsedHeaderPair> mValues;
|
||||
|
||||
private:
|
||||
void ParsePair(char *t, uint32_t len);
|
||||
void Tokenize(char *input, uint32_t inputLen, char **token,
|
||||
uint32_t *tokenLen, bool *foundEquals, char **next);
|
||||
void ParseNameAndValue(const char *input, bool allowInvalidValue);
|
||||
};
|
||||
|
||||
class ParsedHeaderValueListList
|
||||
{
|
||||
public:
|
||||
explicit ParsedHeaderValueListList(const nsCString &txt);
|
||||
// RFC 7231 section 3.2.6 defines the syntax of the header field values.
|
||||
// |allowInvalidValue| indicates whether the rule will be used to check
|
||||
// the input text.
|
||||
// Note that ParsedHeaderValueListList is currently used to parse
|
||||
// Alt-Svc and Server-Timing header. |allowInvalidValue| is set to true
|
||||
// when parsing Alt-Svc for historical reasons.
|
||||
explicit ParsedHeaderValueListList(const nsCString &txt,
|
||||
bool allowInvalidValue = true);
|
||||
nsTArray<ParsedHeaderValueList> mValues;
|
||||
|
||||
private:
|
||||
|
|
Загрузка…
Ссылка в новой задаче