Bug 1046892 part 1 - HTTP/2 draft 14 client implementation r=mcmanus

This commit is contained in:
Nicholas Hurley 2014-08-05 08:41:09 -07:00
parent 7644bb7745
commit 8446acda81
6 changed files with 253 additions and 531 deletions

View File

@ -169,6 +169,12 @@ nvFIFO::VariableLength() const
return mTable.GetSize();
}
uint32_t
nvFIFO::StaticLength() const
{
return gStaticHeaders->GetSize();
}
void
nvFIFO::Clear()
{
@ -180,15 +186,17 @@ nvFIFO::Clear()
const nvPair *
nvFIFO::operator[] (int32_t index) const
{
// NWGH - ensure index > 0
// NWGH - subtract 1 from index here
if (index >= (mTable.GetSize() + gStaticHeaders->GetSize())) {
MOZ_ASSERT(false);
NS_WARNING("nvFIFO Table Out of Range");
return nullptr;
}
if (index >= mTable.GetSize()) {
return static_cast<nvPair *>(gStaticHeaders->ObjectAt(index - mTable.GetSize()));
if (index >= gStaticHeaders->GetSize()) {
return static_cast<nvPair *>(mTable.ObjectAt(index - gStaticHeaders->GetSize()));
}
return static_cast<nvPair *>(mTable.ObjectAt(index));
return static_cast<nvPair *>(gStaticHeaders->ObjectAt(index));
}
Http2BaseCompressor::Http2BaseCompressor()
@ -200,109 +208,35 @@ Http2BaseCompressor::Http2BaseCompressor()
void
Http2BaseCompressor::ClearHeaderTable()
{
uint32_t dynamicCount = mHeaderTable.VariableLength();
mHeaderTable.Clear();
for (int32_t i = mReferenceSet.Length() - 1; i >= 0; --i) {
if (mReferenceSet[i] < dynamicCount) {
mReferenceSet.RemoveElementAt(i);
} else {
mReferenceSet[i] -= dynamicCount;
}
}
for (int32_t i = mAlternateReferenceSet.Length() - 1; i >= 0; --i) {
if (mAlternateReferenceSet[i] < dynamicCount) {
mAlternateReferenceSet.RemoveElementAt(i);
} else {
mAlternateReferenceSet[i] -= dynamicCount;
}
}
}
void
Http2BaseCompressor::UpdateReferenceSet(int32_t delta)
Http2BaseCompressor::MakeRoom(uint32_t amount, const char *direction)
{
if (!delta)
return;
uint32_t headerTableSize = mHeaderTable.VariableLength();
uint32_t oldHeaderTableSize = headerTableSize + delta;
for (int32_t i = mReferenceSet.Length() - 1; i >= 0; --i) {
uint32_t indexRef = mReferenceSet[i];
if (indexRef >= headerTableSize) {
if (indexRef < oldHeaderTableSize) {
// This one got dropped
LOG(("HTTP base compressor reference to index %u removed.\n",
indexRef));
mReferenceSet.RemoveElementAt(i);
} else {
// This pointed to the static table, need to adjust
uint32_t newRef = indexRef - delta;
LOG(("HTTP base compressor reference to index %u changed to %d (%s %s)\n",
mReferenceSet[i], newRef, mHeaderTable[newRef]->mName.get(),
mHeaderTable[newRef]->mValue.get()));
mReferenceSet[i] = newRef;
}
}
}
for (int32_t i = mAlternateReferenceSet.Length() - 1; i >= 0; --i) {
uint32_t indexRef = mAlternateReferenceSet[i];
if (indexRef >= headerTableSize) {
if (indexRef < oldHeaderTableSize) {
// This one got dropped
LOG(("HTTP base compressor new reference to index %u removed.\n",
indexRef));
mAlternateReferenceSet.RemoveElementAt(i);
} else {
// This pointed to the static table, need to adjust
uint32_t newRef = indexRef - delta;
LOG(("HTTP base compressor new reference to index %u changed to %d (%s %s)\n",
mAlternateReferenceSet[i], newRef, mHeaderTable[newRef]->mName.get(),
mHeaderTable[newRef]->mValue.get()));
mAlternateReferenceSet[i] = newRef;
}
}
}
}
void
Http2BaseCompressor::IncrementReferenceSetIndices()
{
LOG(("Http2BaseCompressor::IncrementReferenceSetIndices"));
for (int32_t i = mReferenceSet.Length() - 1; i >= 0; --i) {
mReferenceSet[i] = mReferenceSet[i] + 1;
}
for (int32_t i = mAlternateReferenceSet.Length() - 1; i >= 0; --i) {
mAlternateReferenceSet[i] = mAlternateReferenceSet[i] + 1;
// make room in the header table
while (mHeaderTable.VariableLength() && ((mHeaderTable.ByteCount() + amount) > mMaxBuffer)) {
// NWGH - remove the "- 1" here
uint32_t index = mHeaderTable.Length() - 1;
LOG(("HTTP %s header table index %u %s %s removed for size.\n",
direction, index, mHeaderTable[index]->mName.get(),
mHeaderTable[index]->mValue.get()));
mHeaderTable.RemoveElement();
}
}
void
Http2BaseCompressor::DumpState()
{
LOG(("Alternate Reference Set"));
uint32_t i;
uint32_t length = mAlternateReferenceSet.Length();
for (i = 0; i < length; ++i) {
LOG(("index %u: %u", i, mAlternateReferenceSet[i]));
}
LOG(("Reference Set"));
length = mReferenceSet.Length();
for (i = 0; i < length; ++i) {
LOG(("index %u: %u", i, mReferenceSet[i]));
}
LOG(("Header Table"));
length = mHeaderTable.Length();
uint32_t variableLength = mHeaderTable.VariableLength();
uint32_t i;
uint32_t length = mHeaderTable.Length();
uint32_t staticLength = mHeaderTable.StaticLength();
// NWGH - make i = 1; i <= length; ++i
for (i = 0; i < length; ++i) {
const nvPair *pair = mHeaderTable[i];
LOG(("%sindex %u: %s %s", i < variableLength ? "" : "static ", i,
// NWGH - make this <= staticLength
LOG(("%sindex %u: %s %s", i < staticLength ? "static " : "", i,
pair->mName.get(), pair->mValue.get()));
}
}
@ -311,7 +245,6 @@ nsresult
Http2Decompressor::DecodeHeaderBlock(const uint8_t *data, uint32_t datalen,
nsACString &output)
{
mAlternateReferenceSet.Clear();
mOffset = 0;
mData = data;
mDataLen = datalen;
@ -322,6 +255,7 @@ Http2Decompressor::DecodeHeaderBlock(const uint8_t *data, uint32_t datalen,
mHeaderScheme.Truncate();
mHeaderPath.Truncate();
mHeaderMethod.Truncate();
mSeenNonColonHeader = false;
nsresult rv = NS_OK;
while (NS_SUCCEEDED(rv) && (mOffset < datalen)) {
@ -344,22 +278,6 @@ Http2Decompressor::DecodeHeaderBlock(const uint8_t *data, uint32_t datalen,
DumpState();
}
// after processing the input the decompressor comapres the alternate
// set to the inherited reference set and generates headers for
// anything implicit in reference - alternate.
uint32_t setLen = mReferenceSet.Length();
for (uint32_t index = 0; index < setLen; ++index) {
if (!mAlternateReferenceSet.Contains(mReferenceSet[index])) {
LOG(("HTTP decompressor carryover in reference set with index %u %s %s\n",
mReferenceSet[index],
mHeaderTable[mReferenceSet[index]]->mName.get(),
mHeaderTable[mReferenceSet[index]]->mValue.get()));
OutputHeader(mReferenceSet[index]);
}
}
mAlternateReferenceSet.Clear();
return rv;
}
@ -473,38 +391,34 @@ Http2Decompressor::OutputHeader(const nsACString &name, const nsACString &value)
}
// http/2 transport level headers shouldn't be gatewayed into http/1
if(*(name.BeginReading()) == ':') {
bool isColonHeader = false;
for (const char *cPtr = name.BeginReading();
cPtr && cPtr < name.EndReading();
++cPtr) {
if (*cPtr == ':') {
isColonHeader = true;
break;
} else if (*cPtr != ' ' && *cPtr != '\t') {
isColonHeader = false;
break;
}
}
if(isColonHeader) {
if (mSeenNonColonHeader) {
LOG(("HTTP Decompressor found illegal : header %s", name.BeginReading()));
return NS_ERROR_ILLEGAL_VALUE;
}
LOG(("HTTP Decompressor not gatewaying %s into http/1",
name.BeginReading()));
return NS_OK;
}
LOG(("Http2Decompressor::OutputHeader %s %s", name.BeginReading(),
value.BeginReading()));
mSeenNonColonHeader = true;
mOutput->Append(name);
mOutput->AppendLiteral(": ");
// Special handling for set-cookie according to the spec
bool isSetCookie = name.EqualsLiteral("set-cookie");
int32_t valueLen = value.Length();
nsAutoCString textValue;
for (int32_t i = 0; i < valueLen; ++i) {
if (value[i] == '\0') {
if (isSetCookie) {
LOG(("Http2Decompressor::OutputHeader %s %s", name.BeginReading(),
textValue.get()));
mOutput->Append(textValue);
textValue.Truncate(0);
mOutput->AppendLiteral("\r\n");
mOutput->Append(name);
mOutput->AppendLiteral(": ");
} else {
textValue.AppendLiteral(", ");
}
} else {
textValue.Append(value[i]);
}
}
LOG(("Http2Decompressor::OutputHeader %s %s", name.BeginReading(),
textValue.get()));
mOutput->Append(textValue);
mOutput->Append(value);
mOutput->AppendLiteral("\r\n");
return NS_OK;
}
@ -512,6 +426,7 @@ Http2Decompressor::OutputHeader(const nsACString &name, const nsACString &value)
nsresult
Http2Decompressor::OutputHeader(uint32_t index)
{
// NWGH - make this < index
// bounds check
if (mHeaderTable.Length() <= index) {
LOG(("Http2Decompressor::OutputHeader index too large %u", index));
@ -525,6 +440,7 @@ Http2Decompressor::OutputHeader(uint32_t index)
nsresult
Http2Decompressor::CopyHeaderString(uint32_t index, nsACString &name)
{
// NWGH - make this < index
// bounds check
if (mHeaderTable.Length() <= index)
return NS_ERROR_ILLEGAL_VALUE;
@ -715,31 +631,12 @@ Http2Decompressor::CopyHuffmanStringFromInput(uint32_t bytes, nsACString &val)
return NS_OK;
}
void
Http2Decompressor::MakeRoom(uint32_t amount)
{
// make room in the header table
uint32_t removedCount = 0;
while (mHeaderTable.VariableLength() && ((mHeaderTable.ByteCount() + amount) > mMaxBuffer)) {
uint32_t index = mHeaderTable.VariableLength() - 1;
LOG(("HTTP decompressor header table index %u %s %s removed for size.\n",
index, mHeaderTable[index]->mName.get(),
mHeaderTable[index]->mValue.get()));
mHeaderTable.RemoveElement();
++removedCount;
}
// adjust references to header table
UpdateReferenceSet(removedCount);
}
nsresult
Http2Decompressor::DoIndexed()
{
// this starts with a 1 bit pattern
MOZ_ASSERT(mData[mOffset] & 0x80);
// Indexed entries toggle the reference set
// This is a 7 bit prefix
uint32_t index;
@ -752,42 +649,10 @@ Http2Decompressor::DoIndexed()
if (index == 0) {
return NS_ERROR_ILLEGAL_VALUE;
}
// NWGH - remove this line, since we'll keep everything 1-indexed
index--; // Internally, we 0-index everything, since this is, y'know, C++
// Toggle this in the reference set..
// if its not currently in the reference set then add it and
// also emit it. If it is currently in the reference set then just
// remove it from there.
if (mReferenceSet.RemoveElement(index)) {
mAlternateReferenceSet.RemoveElement(index);
return NS_OK;
}
rv = OutputHeader(index);
if (index >= mHeaderTable.VariableLength()) {
const nvPair *pair = mHeaderTable[index];
uint32_t room = pair->Size();
if (room > mMaxBuffer) {
ClearHeaderTable();
LOG(("HTTP decompressor index not referenced due to size %u %s %s\n",
room, pair->mName.get(), pair->mValue.get()));
LOG(("Decompressor state after ClearHeaderTable"));
DumpState();
return rv;
}
LOG(("HTTP decompressor inserting static entry %u %s %s into dynamic table",
index, pair->mName.get(), pair->mValue.get()));
MakeRoom(room);
mHeaderTable.AddElement(pair->mName, pair->mValue);
IncrementReferenceSetIndices();
index = 0;
}
mReferenceSet.AppendElement(index);
mAlternateReferenceSet.AppendElement(index);
return rv;
return OutputHeader(index);
}
nsresult
@ -822,6 +687,7 @@ Http2Decompressor::DoLiteralInternal(nsACString &name, nsACString &value,
LOG(("Http2Decompressor::DoLiteralInternal literal name %s",
name.BeginReading()));
} else {
// NWGH - make this index, not index - 1
// name is from headertable
rv = CopyHeaderString(index - 1, name);
LOG(("Http2Decompressor::DoLiteralInternal indexed name %d %s",
@ -853,16 +719,12 @@ Http2Decompressor::DoLiteralWithoutIndex()
// this starts with 0000 bit pattern
MOZ_ASSERT((mData[mOffset] & 0xF0) == 0x00);
// This is not indexed so there is no adjustment to the
// persistent reference set
nsAutoCString name, value;
nsresult rv = DoLiteralInternal(name, value, 4);
LOG(("HTTP decompressor literal without index %s %s\n",
name.get(), value.get()));
// Output the header now because we don't keep void
// indicies in the reference set
if (NS_SUCCEEDED(rv))
rv = OutputHeader(name, value);
return rv;
@ -884,21 +746,17 @@ Http2Decompressor::DoLiteralWithIncremental()
uint32_t room = nvPair(name, value).Size();
if (room > mMaxBuffer) {
ClearHeaderTable();
LOG(("HTTP decompressor literal with index not referenced due to size %u %s %s\n",
LOG(("HTTP decompressor literal with index not inserted due to size %u %s %s\n",
room, name.get(), value.get()));
LOG(("Decompressor state after ClearHeaderTable"));
DumpState();
return NS_OK;
}
MakeRoom(room);
MakeRoom(room, "decompressor");
// Incremental Indexing implicitly adds a row to the header table.
// It also adds the new row to the Reference Set
mHeaderTable.AddElement(name, value);
IncrementReferenceSetIndices();
mReferenceSet.AppendElement(0);
mAlternateReferenceSet.AppendElement(0);
LOG(("HTTP decompressor literal with index 0 %s %s\n",
name.get(), value.get()));
@ -912,16 +770,12 @@ Http2Decompressor::DoLiteralNeverIndexed()
// This starts with 0001 bit pattern
MOZ_ASSERT((mData[mOffset] & 0xF0) == 0x10);
// This is not indexed so there is no adjustment to the
// persistent reference set
nsAutoCString name, value;
nsresult rv = DoLiteralInternal(name, value, 4);
LOG(("HTTP decompressor literal never indexed %s %s\n",
name.get(), value.get()));
// Output the header now because we don't keep void
// indicies in the reference set
if (NS_SUCCEEDED(rv))
rv = OutputHeader(name, value);
return rv;
@ -933,18 +787,9 @@ Http2Decompressor::DoContextUpdate()
// This starts with 001 bit pattern
MOZ_ASSERT((mData[mOffset] & 0xE0) == 0x20);
if (mData[mOffset] & 0x10) {
// This means we have to clear out the reference set
LOG(("Http2Decompressor::DoContextUpdate clearing reference set"));
mReferenceSet.Clear();
mAlternateReferenceSet.Clear();
++mOffset;
return NS_OK;
}
// Getting here means we have to adjust the max table size
uint32_t newMaxSize;
nsresult rv = DecodeInteger(4, newMaxSize);
nsresult rv = DecodeInteger(5, newMaxSize);
LOG(("Http2Decompressor::DoContextUpdate new maximum size %u", newMaxSize));
if (NS_FAILED(rv))
return rv;
@ -959,8 +804,6 @@ Http2Compressor::EncodeHeaderBlock(const nsCString &nvInput,
const nsACString &host, const nsACString &scheme,
bool connectForm, nsACString &output)
{
mAlternateReferenceSet.Clear();
mImpliedReferenceSet.Clear();
mOutput = &output;
output.SetCapacity(1024);
output.Truncate();
@ -1021,7 +864,19 @@ Http2Compressor::EncodeHeaderBlock(const nsCString &nvInput,
// colon headers are for http/2 and this is http/1 input, so that
// is probably a smuggling attack of some kind
if(*(name.BeginReading()) == ':') {
bool isColonHeader = false;
for (const char *cPtr = name.BeginReading();
cPtr && cPtr < name.EndReading();
++cPtr) {
if (*cPtr == ':') {
isColonHeader = true;
break;
} else if (*cPtr != ' ' && *cPtr != '\t') {
isColonHeader = false;
break;
}
}
if(isColonHeader) {
continue;
}
@ -1084,23 +939,6 @@ Http2Compressor::EncodeHeaderBlock(const nsCString &nvInput,
}
}
// iterate mreference set and if !alternate.contains(old[i])
// toggle off
uint32_t setLen = mReferenceSet.Length();
for (uint32_t index = 0; index < setLen; ++index) {
uint32_t indexRef = mReferenceSet[index];
if (!mAlternateReferenceSet.Contains(indexRef)) {
LOG(("Http2Compressor::EncodeHeaderBlock toggling off %s %s",
mHeaderTable[indexRef]->mName.get(),
mHeaderTable[indexRef]->mValue.get()));
DoOutput(kToggleOff, mHeaderTable[indexRef],
mReferenceSet[index]);
}
}
mReferenceSet = mAlternateReferenceSet;
mAlternateReferenceSet.Clear();
mImpliedReferenceSet.Clear();
mOutput = nullptr;
LOG(("Compressor state after EncodeHeaderBlock"));
DumpState();
@ -1169,11 +1007,10 @@ Http2Compressor::DoOutput(Http2Compressor::outputCode code,
HuffmanAppend(pair->mValue);
break;
case kToggleOff:
case kToggleOn:
LOG(("HTTP compressor %p toggle %s index %u %s %s\n",
this, (code == kToggleOff) ? "off" : "on",
index, pair->mName.get(), pair->mValue.get()));
case kIndex:
LOG(("HTTP compressor %p index %u %s %s\n",
this, index, pair->mName.get(), pair->mValue.get()));
// NWGH - make this plain old index instead of index + 1
// In this case, we are passed the raw 0-based C index, and need to
// increment to make it 1-based and comply with the spec
EncodeInteger(7, index + 1);
@ -1181,10 +1018,6 @@ Http2Compressor::DoOutput(Http2Compressor::outputCode code,
*startByte = *startByte | 0x80; // 1 1 bit prefix
break;
case kNop:
LOG(("HTTP compressor %p implied in reference set index %u %s %s\n",
this, index, pair->mName.get(), pair->mValue.get()));
break;
}
}
@ -1220,99 +1053,6 @@ Http2Compressor::EncodeInteger(uint32_t prefixLen, uint32_t val)
} while (q);
}
void
Http2Compressor::ClearHeaderTable()
{
uint32_t dynamicCount = mHeaderTable.VariableLength();
Http2BaseCompressor::ClearHeaderTable();
for (int32_t i = mImpliedReferenceSet.Length() - 1; i >= 0; --i) {
if (mImpliedReferenceSet[i] < dynamicCount) {
mImpliedReferenceSet.RemoveElementAt(i);
} else {
mImpliedReferenceSet[i] -= dynamicCount;
}
}
LOG(("Compressor state after ClearHeaderTable"));
DumpState();
}
void
Http2Compressor::UpdateReferenceSet(int32_t delta)
{
if (!delta)
return;
Http2BaseCompressor::UpdateReferenceSet(delta);
uint32_t headerTableSize = mHeaderTable.VariableLength();
uint32_t oldHeaderTableSize = headerTableSize + delta;
for (int32_t i = mImpliedReferenceSet.Length() - 1; i >= 0; --i) {
uint32_t indexRef = mImpliedReferenceSet[i];
if (indexRef >= headerTableSize) {
if (indexRef < oldHeaderTableSize) {
// This one got dropped
LOG(("HTTP compressor implied reference to index %u removed.\n",
indexRef));
mImpliedReferenceSet.RemoveElementAt(i);
} else {
// This pointed to the static table, need to adjust
uint32_t newRef = indexRef - delta;
LOG(("HTTP compressor implied reference to index %u changed to %d (%s %s)\n",
mImpliedReferenceSet[i], newRef, mHeaderTable[newRef]->mName.get(),
mHeaderTable[newRef]->mValue.get()));
mImpliedReferenceSet[i] = newRef;
}
}
}
}
void
Http2Compressor::IncrementReferenceSetIndices()
{
Http2BaseCompressor::IncrementReferenceSetIndices();
LOG(("Http2Compressor::IncrementReferenceSetIndices"));
for (int32_t i = mImpliedReferenceSet.Length() - 1; i >= 0; --i) {
mImpliedReferenceSet[i] = mImpliedReferenceSet[i] + 1;
}
}
void
Http2Compressor::MakeRoom(uint32_t amount)
{
// make room in the header table
uint32_t removedCount = 0;
while (mHeaderTable.VariableLength() && ((mHeaderTable.ByteCount() + amount) > mMaxBuffer)) {
// if there is a reference to removedCount (~0) in the implied reference set we need,
// to toggle it off/on so that the implied reference is not lost when the
// table is trimmed
uint32_t index = mHeaderTable.VariableLength() - 1;
if (mImpliedReferenceSet.Contains(index) ) {
LOG(("HTTP compressor header table index %u %s %s about to be "
"removed for size but has an implied reference. Will Toggle.\n",
index, mHeaderTable[index]->mName.get(),
mHeaderTable[index]->mValue.get()));
DoOutput(kToggleOff, mHeaderTable[index], index);
DoOutput(kToggleOn, mHeaderTable[index], index);
}
LOG(("HTTP compressor header table index %u %s %s removed for size.\n",
index, mHeaderTable[index]->mName.get(),
mHeaderTable[index]->mValue.get()));
mHeaderTable.RemoveElement();
++removedCount;
}
// adjust references to header table
UpdateReferenceSet(removedCount);
}
void
Http2Compressor::HuffmanAppend(const nsCString &value)
{
@ -1387,18 +1127,6 @@ Http2Compressor::HuffmanAppend(const nsCString &value)
"bytes.\n", this, length, bufLength));
}
void
Http2Compressor::DumpState()
{
LOG(("Implied Reference Set"));
uint32_t length = mImpliedReferenceSet.Length();
for (uint32_t i = 0; i < length; ++i) {
LOG(("index %u: %u", i, mImpliedReferenceSet[i]));
}
Http2BaseCompressor::DumpState();
}
void
Http2Compressor::ProcessHeader(const nvPair inputPair, bool noLocalIndex,
bool neverIndex)
@ -1412,8 +1140,10 @@ Http2Compressor::ProcessHeader(const nvPair inputPair, bool noLocalIndex,
LOG(("Http2Compressor::ProcessHeader %s %s", inputPair.mName.get(),
inputPair.mValue.get()));
// NWGH - make this index = 1; index <= headerTableSize; ++index
for (uint32_t index = 0; index < headerTableSize; ++index) {
if (mHeaderTable[index]->mName.Equals(inputPair.mName)) {
// NWGH - make this nameReference = index
nameReference = index + 1;
if (mHeaderTable[index]->mValue.Equals(inputPair.mValue)) {
match = true;
@ -1441,54 +1171,20 @@ Http2Compressor::ProcessHeader(const nvPair inputPair, bool noLocalIndex,
// make sure to makeroom() first so that any implied items
// get preserved.
MakeRoom(newSize);
MakeRoom(newSize, "compressor");
DoOutput(kIndexedLiteral, &inputPair, nameReference);
mHeaderTable.AddElement(inputPair.mName, inputPair.mValue);
IncrementReferenceSetIndices();
LOG(("HTTP compressor %p new literal placed at index 0\n",
this));
mAlternateReferenceSet.AppendElement(0);
LOG(("Compressor state after literal with index"));
DumpState();
return;
}
// It is in the reference set. just check to see if it is
// a duplicate for output purposes
if (mReferenceSet.Contains(matchedIndex)) {
if (mAlternateReferenceSet.Contains(matchedIndex)) {
DoOutput(kToggleOff, &inputPair, matchedIndex);
DoOutput(kToggleOn, &inputPair, matchedIndex);
LOG(("Compressor state after toggle off/on index"));
} else {
DoOutput(kNop, &inputPair, matchedIndex);
if (!mImpliedReferenceSet.Contains(matchedIndex))
mImpliedReferenceSet.AppendElement(matchedIndex);
mAlternateReferenceSet.AppendElement(matchedIndex);
LOG(("Compressor state after NOP index"));
}
DumpState();
return;
}
// emit an index
DoOutput(kIndex, &inputPair, matchedIndex);
// Need to ensure we have room for a new static entry before emitting
// anything, see bug 1019577
bool isStatic = (matchedIndex >= mHeaderTable.VariableLength());
if (isStatic) {
MakeRoom(newSize);
}
// emit an index to add to reference set
DoOutput(kToggleOn, &inputPair, matchedIndex);
if (isStatic) {
mHeaderTable.AddElement(inputPair.mName, inputPair.mValue);
IncrementReferenceSetIndices();
mAlternateReferenceSet.AppendElement(0);
} else {
mAlternateReferenceSet.AppendElement(matchedIndex);
}
LOG(("Compressor state after index"));
DumpState();
return;
@ -1531,7 +1227,6 @@ Http2Compressor::SetMaxBufferSizeInternal(uint32_t maxBufferSize)
mHeaderTable.RemoveElement();
++removedCount;
}
UpdateReferenceSet(removedCount);
mMaxBuffer = maxBufferSize;

View File

@ -45,6 +45,7 @@ public:
uint32_t ByteCount() const;
uint32_t Length() const;
uint32_t VariableLength() const;
uint32_t StaticLength() const;
void Clear();
const nvPair *operator[] (int32_t index) const;
@ -64,28 +65,9 @@ protected:
const static uint32_t kDefaultMaxBuffer = 4096;
virtual void ClearHeaderTable();
virtual void UpdateReferenceSet(int32_t delta);
virtual void IncrementReferenceSetIndices();
virtual void MakeRoom(uint32_t amount) = 0;
virtual void MakeRoom(uint32_t amount, const char *direction);
virtual void DumpState();
nsAutoTArray<uint32_t, 64> mReferenceSet; // list of indicies
// the alternate set is used to track the emitted headers when
// processing input for a header set. The input to the compressor
// is a series of nvpairs, the input to the decompressor is the
// series of op codes that make up the header block.
//
// after processing the input the compressor compares the alternate
// set to the inherited reference set and generates indicies to
// toggle off any members of alternate - inherited. the alternate
// then becomes the inherited set for the next header set.
//
// after processing the input the decompressor comapres the alternate
// set to the inherited reference set and generates headers for
// anything implicit in reference - alternate.
nsAutoTArray<uint32_t, 64> mAlternateReferenceSet; // list of indicies
nsACString *mOutput;
nvFIFO mHeaderTable;
@ -111,9 +93,6 @@ public:
void GetMethod(nsACString &hdr) { hdr = mHeaderMethod; }
void SetCompressor(Http2Compressor *compressor) { mCompressor = compressor; }
protected:
virtual void MakeRoom(uint32_t amount) MOZ_OVERRIDE;
private:
nsresult DoIndexed();
nsresult DoLiteralWithoutIndex();
@ -147,6 +126,7 @@ private:
uint32_t mOffset;
const uint8_t *mData;
uint32_t mDataLen;
bool mSeenNonColonHeader;
};
@ -172,21 +152,12 @@ public:
void SetMaxBufferSize(uint32_t maxBufferSize);
nsresult SetMaxBufferSizeInternal(uint32_t maxBufferSize);
protected:
virtual void ClearHeaderTable() MOZ_OVERRIDE;
virtual void UpdateReferenceSet(int32_t delta) MOZ_OVERRIDE;
virtual void IncrementReferenceSetIndices() MOZ_OVERRIDE;
virtual void MakeRoom(uint32_t amount) MOZ_OVERRIDE;
virtual void DumpState() MOZ_OVERRIDE;
private:
enum outputCode {
kNeverIndexedLiteral,
kPlainLiteral,
kIndexedLiteral,
kToggleOff,
kToggleOn,
kNop
kIndex
};
void DoOutput(Http2Compressor::outputCode code,
@ -201,8 +172,6 @@ private:
uint32_t mMaxBufferSetting;
bool mBufferSizeChangeWaiting;
uint32_t mLowestBufferSizeWaiting;
nsAutoTArray<uint32_t, 64> mImpliedReferenceSet;
};
} // namespace mozilla::net

View File

@ -600,8 +600,8 @@ Http2Session::ResetDownstreamState()
mInputFrameDataStream = nullptr;
}
// call with data length (i.e. 0 for 0 data bytes - ignore 8 byte header)
// dest must have 8 bytes of allocated space
// call with data length (i.e. 0 for 0 data bytes - ignore 9 byte header)
// dest must have 9 bytes of allocated space
template<typename charType> void
Http2Session::CreateFrameHeader(charType dest, uint16_t frameLength,
uint8_t frameType, uint8_t frameFlags,
@ -610,10 +610,11 @@ Http2Session::CreateFrameHeader(charType dest, uint16_t frameLength,
MOZ_ASSERT(frameLength <= kMaxFrameData, "framelength too large");
MOZ_ASSERT(!(streamID & 0x80000000));
CopyAsNetwork16(dest, frameLength);
dest[2] = frameType;
dest[3] = frameFlags;
CopyAsNetwork32(dest + 4, streamID);
dest[0] = 0x00;
CopyAsNetwork16(dest + 1, frameLength);
dest[3] = frameType;
dest[4] = frameFlags;
CopyAsNetwork32(dest + 5, streamID);
}
char *
@ -677,18 +678,19 @@ Http2Session::GeneratePing(bool isAck)
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
LOG3(("Http2Session::GeneratePing %p isAck=%d\n", this, isAck));
char *packet = EnsureOutputBuffer(16);
mOutputQueueUsed += 16;
char *packet = EnsureOutputBuffer(kFrameHeaderBytes + 8);
mOutputQueueUsed += kFrameHeaderBytes + 8;
if (isAck) {
CreateFrameHeader(packet, 8, FRAME_TYPE_PING, kFlag_ACK, 0);
memcpy(packet + 8, mInputFrameBuffer.get() + 8, 8);
memcpy(packet + kFrameHeaderBytes,
mInputFrameBuffer.get() + kFrameHeaderBytes, 8);
} else {
CreateFrameHeader(packet, 8, FRAME_TYPE_PING, 0, 0);
memset(packet + 8, 0, 8);
memset(packet + kFrameHeaderBytes, 0, 8);
}
LogIO(this, nullptr, "Generate Ping", packet, 16);
LogIO(this, nullptr, "Generate Ping", packet, kFrameHeaderBytes + 8);
FlushOutputQueue();
}
@ -699,10 +701,10 @@ Http2Session::GenerateSettingsAck()
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
LOG3(("Http2Session::GenerateSettingsAck %p\n", this));
char *packet = EnsureOutputBuffer(8);
mOutputQueueUsed += 8;
char *packet = EnsureOutputBuffer(kFrameHeaderBytes);
mOutputQueueUsed += kFrameHeaderBytes;
CreateFrameHeader(packet, 0, FRAME_TYPE_SETTINGS, kFlag_ACK, 0);
LogIO(this, nullptr, "Generate Settings ACK", packet, 8);
LogIO(this, nullptr, "Generate Settings ACK", packet, kFrameHeaderBytes);
FlushOutputQueue();
}
@ -713,13 +715,14 @@ Http2Session::GeneratePriority(uint32_t aID, uint8_t aPriorityWeight)
LOG3(("Http2Session::GeneratePriority %p %X %X\n",
this, aID, aPriorityWeight));
char *packet = EnsureOutputBuffer(13);
mOutputQueueUsed += 13;
uint32_t frameSize = kFrameHeaderBytes + 5;
char *packet = EnsureOutputBuffer(frameSize);
mOutputQueueUsed += frameSize;
CreateFrameHeader(packet, 5, FRAME_TYPE_PRIORITY, 0, aID);
CopyAsNetwork32(packet + 8, 0);
memcpy(packet + 12, &aPriorityWeight, 1);
LogIO(this, nullptr, "Generate Priority", packet, 13);
CopyAsNetwork32(packet + kFrameHeaderBytes, 0);
memcpy(packet + frameSize - 1, &aPriorityWeight, 1);
LogIO(this, nullptr, "Generate Priority", packet, frameSize);
FlushOutputQueue();
}
@ -739,13 +742,14 @@ Http2Session::GenerateRstStream(uint32_t aStatusCode, uint32_t aID)
LOG3(("Http2Session::GenerateRst %p 0x%X %d\n", this, aID, aStatusCode));
char *packet = EnsureOutputBuffer(12);
mOutputQueueUsed += 12;
uint32_t frameSize = kFrameHeaderBytes + 4;
char *packet = EnsureOutputBuffer(frameSize);
mOutputQueueUsed += frameSize;
CreateFrameHeader(packet, 4, FRAME_TYPE_RST_STREAM, 0, aID);
CopyAsNetwork32(packet + 8, aStatusCode);
CopyAsNetwork32(packet + kFrameHeaderBytes, aStatusCode);
LogIO(this, nullptr, "Generate Reset", packet, 12);
LogIO(this, nullptr, "Generate Reset", packet, frameSize);
FlushOutputQueue();
}
@ -755,18 +759,19 @@ Http2Session::GenerateGoAway(uint32_t aStatusCode)
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
LOG3(("Http2Session::GenerateGoAway %p code=%X\n", this, aStatusCode));
char *packet = EnsureOutputBuffer(16);
mOutputQueueUsed += 16;
uint32_t frameSize = kFrameHeaderBytes + 8;
char *packet = EnsureOutputBuffer(frameSize);
mOutputQueueUsed += frameSize;
CreateFrameHeader(packet, 8, FRAME_TYPE_GOAWAY, 0, 0);
// last-good-stream-id are bytes 8-11 reflecting pushes
CopyAsNetwork32(packet + 8, mOutgoingGoAwayID);
// last-good-stream-id are bytes 9-12 reflecting pushes
CopyAsNetwork32(packet + kFrameHeaderBytes, mOutgoingGoAwayID);
// bytes 12-15 are the status code.
CopyAsNetwork32(packet + 12, aStatusCode);
// bytes 13-16 are the status code.
CopyAsNetwork32(packet + frameSize - 4, aStatusCode);
LogIO(this, nullptr, "Generate GoAway", packet, 16);
LogIO(this, nullptr, "Generate GoAway", packet, frameSize);
FlushOutputQueue();
}
@ -780,10 +785,10 @@ Http2Session::SendHello()
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
LOG3(("Http2Session::SendHello %p\n", this));
// sized for magic + 3 settings and a session window update to follow
// 24 magic, 26 for settings (8 header + 3 settings @6), 12 for window update
static const uint32_t maxSettings = 3;
static const uint32_t maxDataLen = 24 + 8 + maxSettings * 6 + 12;
// sized for magic + 4 settings and a session window update to follow
// 24 magic, 33 for settings (9 header + 4 settings @6), 13 for window update
static const uint32_t maxSettings = 4;
static const uint32_t maxDataLen = 24 + kFrameHeaderBytes + maxSettings * 6 + 13;
char *packet = EnsureOutputBuffer(maxDataLen);
memcpy(packet, kMagicHello, 24);
mOutputQueueUsed += 24;
@ -796,18 +801,19 @@ Http2Session::SendHello()
uint8_t numberOfEntries = 0;
// entries need to be listed in order by ID
// 1st entry is bytes 8 to 13
// 2nd entry is bytes 14 to 19
// 3rd entry is bytes 20 to 25
// 1st entry is bytes 9 to 14
// 2nd entry is bytes 15 to 20
// 3rd entry is bytes 21 to 26
// 4th entry is bytes 27 to 32
if (!gHttpHandler->AllowPush()) {
// If we don't support push then set MAX_CONCURRENT to 0 and also
// set ENABLE_PUSH to 0
CopyAsNetwork16(packet + 8 + (6 * numberOfEntries), SETTINGS_TYPE_ENABLE_PUSH);
CopyAsNetwork16(packet + kFrameHeaderBytes + (6 * numberOfEntries), SETTINGS_TYPE_ENABLE_PUSH);
// The value portion of the setting pair is already initialized to 0
numberOfEntries++;
CopyAsNetwork16(packet + 8 + (6 * numberOfEntries), SETTINGS_TYPE_MAX_CONCURRENT);
CopyAsNetwork16(packet + kFrameHeaderBytes + (6 * numberOfEntries), SETTINGS_TYPE_MAX_CONCURRENT);
// The value portion of the setting pair is already initialized to 0
numberOfEntries++;
@ -816,16 +822,22 @@ Http2Session::SendHello()
// Advertise the Push RWIN for the session, and on each new pull stream
// send a window update with END_FLOW_CONTROL
CopyAsNetwork16(packet + 8 + (6 * numberOfEntries), SETTINGS_TYPE_INITIAL_WINDOW);
CopyAsNetwork32(packet + 8 + (6 * numberOfEntries) + 2, mPushAllowance);
CopyAsNetwork16(packet + kFrameHeaderBytes + (6 * numberOfEntries), SETTINGS_TYPE_INITIAL_WINDOW);
CopyAsNetwork32(packet + kFrameHeaderBytes + (6 * numberOfEntries) + 2, mPushAllowance);
numberOfEntries++;
// Make sure the other endpoint knows that we're sticking to the default max
// frame size
CopyAsNetwork16(packet + kFrameHeaderBytes + (6 * numberOfEntries), SETTINGS_TYPE_MAX_FRAME_SIZE);
CopyAsNetwork32(packet + kFrameHeaderBytes + (6 * numberOfEntries) + 2, kMaxFrameData);
numberOfEntries++;
MOZ_ASSERT(numberOfEntries <= maxSettings);
uint32_t dataLen = 6 * numberOfEntries;
CreateFrameHeader(packet, dataLen, FRAME_TYPE_SETTINGS, 0, 0);
mOutputQueueUsed += 8 + dataLen;
mOutputQueueUsed += kFrameHeaderBytes + dataLen;
LogIO(this, nullptr, "Generate Settings", packet, 8 + dataLen);
LogIO(this, nullptr, "Generate Settings", packet, kFrameHeaderBytes + dataLen);
// now bump the local session window from 64KB
uint32_t sessionWindowBump = ASpdySession::kInitialRwin - kDefaultRwin;
@ -837,12 +849,12 @@ Http2Session::SendHello()
packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
CreateFrameHeader(packet, 4, FRAME_TYPE_WINDOW_UPDATE, 0, 0);
mOutputQueueUsed += 12;
CopyAsNetwork32(packet + 8, sessionWindowBump);
mOutputQueueUsed += kFrameHeaderBytes + 4;
CopyAsNetwork32(packet + kFrameHeaderBytes, sessionWindowBump);
LOG3(("Session Window increase at start of session %p %u\n",
this, sessionWindowBump));
LogIO(this, nullptr, "Session Window Bump ", packet, 12);
LogIO(this, nullptr, "Session Window Bump ", packet, kFrameHeaderBytes + 4);
sendHello_complete:
FlushOutputQueue();
@ -1040,7 +1052,7 @@ nsresult
Http2Session::ParsePadding(uint8_t &paddingControlBytes, uint16_t &paddingLength)
{
if (mInputFrameFlags & kFlag_PADDED) {
paddingLength = *reinterpret_cast<uint8_t *>(mInputFrameBuffer + 8);
paddingLength = *reinterpret_cast<uint8_t *>(mInputFrameBuffer + kFrameHeaderBytes);
paddingControlBytes = 1;
}
@ -1112,7 +1124,7 @@ Http2Session::RecvHeaders(Http2Session *self)
if (self->mInputFrameID >= self->mNextStreamID)
self->GenerateRstStream(PROTOCOL_ERROR, self->mInputFrameID);
self->mDecompressBuffer.Append(self->mInputFrameBuffer + 8 + paddingControlBytes + priorityLen,
self->mDecompressBuffer.Append(self->mInputFrameBuffer + kFrameHeaderBytes + paddingControlBytes + priorityLen,
self->mInputFrameDataSize - paddingControlBytes - priorityLen - paddingLength);
if (self->mInputFrameFlags & kFlag_END_HEADERS) {
@ -1129,8 +1141,15 @@ Http2Session::RecvHeaders(Http2Session *self)
return NS_OK;
}
if (self->mInputFrameDataStream->AllHeadersReceived() &&
!(self->mInputFrameFlags & kFlag_END_STREAM)) {
// Any header block after the first that does *not* end the stream is
// illegal.
RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
}
// queue up any compression bytes
self->mDecompressBuffer.Append(self->mInputFrameBuffer + 8 + paddingControlBytes + priorityLen,
self->mDecompressBuffer.Append(self->mInputFrameBuffer + kFrameHeaderBytes + paddingControlBytes + priorityLen,
self->mInputFrameDataSize - paddingControlBytes - priorityLen - paddingLength);
self->mInputFrameDataStream->UpdateTransportReadEvents(self->mInputFrameDataSize);
@ -1164,6 +1183,7 @@ Http2Session::ResponseHeadersComplete()
// only interpret headers once, afterwards ignore trailers
if (mInputFrameDataStream->AllHeadersReceived()) {
LOG3(("Http2Session::ResponseHeadersComplete extra headers"));
MOZ_ASSERT(mInputFrameFlags & kFlag_END_STREAM);
nsresult rv = UncompressAndDiscard();
if (NS_FAILED(rv)) {
LOG3(("Http2Session::ResponseHeadersComplete extra uncompress failed\n"));
@ -1231,10 +1251,10 @@ Http2Session::RecvPriority(Http2Session *self)
return rv;
uint32_t newPriorityDependency =
PR_ntohl(reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get())[2]);
PR_ntohl(*reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get() + kFrameHeaderBytes));
bool exclusive = !!(newPriorityDependency & 0x80000000);
newPriorityDependency &= 0x7fffffff;
uint8_t newPriorityWeight = *(self->mInputFrameBuffer.get() + 12);
uint8_t newPriorityWeight = *(self->mInputFrameBuffer.get() + kFrameHeaderBytes + 4);
if (self->mInputFrameDataStream) {
self->mInputFrameDataStream->SetPriorityDependency(newPriorityDependency,
newPriorityWeight,
@ -1262,7 +1282,7 @@ Http2Session::RecvRstStream(Http2Session *self)
}
self->mDownstreamRstReason =
PR_ntohl(reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get())[2]);
PR_ntohl(*reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get() + kFrameHeaderBytes));
LOG3(("Http2Session::RecvRstStream %p RST_STREAM Reason Code %u ID %x\n",
self, self->mDownstreamRstReason, self->mInputFrameID));
@ -1321,7 +1341,7 @@ Http2Session::RecvSettings(Http2Session *self)
for (uint32_t index = 0; index < numEntries; ++index) {
uint8_t *setting = reinterpret_cast<uint8_t *>
(self->mInputFrameBuffer.get()) + 8 + index * 6;
(self->mInputFrameBuffer.get()) + kFrameHeaderBytes + index * 6;
uint16_t id = PR_ntohs(*reinterpret_cast<uint16_t *>(setting));
uint32_t value = PR_ntohl(*reinterpret_cast<uint32_t *>(setting + 2));
@ -1357,6 +1377,16 @@ Http2Session::RecvSettings(Http2Session *self)
}
break;
case SETTINGS_TYPE_MAX_FRAME_SIZE:
{
if ((value < kMaxFrameData) || (value >= 0x01000000)) {
LOG3(("Received invalid max frame size 0x%X", value));
RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
}
// We stick to the default for simplicity's sake, so nothing to change
}
break;
default:
break;
}
@ -1399,7 +1429,7 @@ Http2Session::RecvPushPromise(Http2Session *self)
}
promiseLen = 4;
promisedID =
PR_ntohl(*reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get() + 8 + paddingControlBytes));
PR_ntohl(*reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get() + kFrameHeaderBytes + paddingControlBytes));
promisedID &= 0x7fffffff;
}
@ -1490,7 +1520,7 @@ Http2Session::RecvPushPromise(Http2Session *self)
if (resetStream) {
// Need to decompress the headers even though we aren't using them yet in
// order to keep the compression context consistent for other frames
self->mDecompressBuffer.Append(self->mInputFrameBuffer + 8 + paddingControlBytes + promiseLen,
self->mDecompressBuffer.Append(self->mInputFrameBuffer + kFrameHeaderBytes + paddingControlBytes + promiseLen,
self->mInputFrameDataSize - paddingControlBytes - promiseLen - paddingLength);
if (self->mInputFrameFlags & kFlag_END_PUSH_PROMISE) {
rv = self->UncompressAndDiscard();
@ -1519,7 +1549,7 @@ Http2Session::RecvPushPromise(Http2Session *self)
self->mStreamTransactionHash.Put(transactionBuffer, pushedStream);
self->mPushedStreams.AppendElement(pushedStream);
self->mDecompressBuffer.Append(self->mInputFrameBuffer + 8 + paddingControlBytes + promiseLen,
self->mDecompressBuffer.Append(self->mInputFrameBuffer + kFrameHeaderBytes + paddingControlBytes + promiseLen,
self->mInputFrameDataSize - paddingControlBytes - promiseLen - paddingLength);
nsAutoCString requestHeaders;
@ -1634,11 +1664,11 @@ Http2Session::RecvGoAway(Http2Session *self)
self->mShouldGoAway = true;
self->mGoAwayID =
PR_ntohl(reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get())[2]);
PR_ntohl(*reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get() + kFrameHeaderBytes));
self->mGoAwayID &= 0x7fffffff;
self->mCleanShutdown = true;
uint32_t statusCode =
PR_ntohl(reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get())[3]);
PR_ntohl(*reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get() + kFrameHeaderBytes + 4));
// Find streams greater than the last-good ID and mark them for deletion
// in the mGoAwayStreamsToRestart queue with the GoAwayEnumerator. The
@ -1704,7 +1734,7 @@ Http2Session::RecvWindowUpdate(Http2Session *self)
}
uint32_t delta =
PR_ntohl(reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get())[2]);
PR_ntohl(*reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get() + kFrameHeaderBytes));
delta &= 0x7fffffff;
LOG3(("Http2Session::RecvWindowUpdate %p len=%d Stream 0x%X.\n",
@ -1725,6 +1755,15 @@ Http2Session::RecvWindowUpdate(Http2Session *self)
return NS_OK;
}
if (delta == 0) {
LOG3(("Http2Session::RecvWindowUpdate %p received 0 stream window update",
self));
self->CleanupStream(self->mInputFrameDataStream, NS_ERROR_ILLEGAL_VALUE,
PROTOCOL_ERROR);
self->ResetDownstreamState();
return NS_OK;
}
int64_t oldRemoteWindow = self->mInputFrameDataStream->ServerReceiveWindow();
self->mInputFrameDataStream->UpdateServerReceiveWindow(delta);
if (self->mInputFrameDataStream->ServerReceiveWindow() >= 0x80000000) {
@ -1743,6 +1782,12 @@ Http2Session::RecvWindowUpdate(Http2Session *self)
oldRemoteWindow, delta, oldRemoteWindow + delta));
} else { // session window update
if (delta == 0) {
LOG3(("Http2Session::RecvWindowUpdate %p received 0 session window update",
self));
RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
}
int64_t oldRemoteWindow = self->mServerSessionWindow;
self->mServerSessionWindow += delta;
@ -2090,15 +2135,15 @@ Http2Session::WriteSegments(nsAHttpSegmentWriter *writer,
if (mDownstreamState == BUFFERING_OPENING_SETTINGS ||
mDownstreamState == BUFFERING_FRAME_HEADER) {
// The first 8 bytes of every frame is header information that
// The first 9 bytes of every frame is header information that
// we are going to want to strip before passing to http. That is
// true of both control and data packets.
MOZ_ASSERT(mInputFrameBufferUsed < 8,
MOZ_ASSERT(mInputFrameBufferUsed < kFrameHeaderBytes,
"Frame Buffer Used Too Large for State");
rv = NetworkRead(writer, mInputFrameBuffer + mInputFrameBufferUsed,
8 - mInputFrameBufferUsed, countWritten);
kFrameHeaderBytes - mInputFrameBufferUsed, countWritten);
if (NS_FAILED(rv)) {
LOG3(("Http2Session %p buffering frame header read failure %x\n",
@ -2114,7 +2159,7 @@ Http2Session::WriteSegments(nsAHttpSegmentWriter *writer,
mInputFrameBufferUsed += *countWritten;
if (mInputFrameBufferUsed < 8)
if (mInputFrameBufferUsed < kFrameHeaderBytes)
{
LOG3(("Http2Session::WriteSegments %p "
"BUFFERING FRAME HEADER incomplete size=%d",
@ -2122,13 +2167,17 @@ Http2Session::WriteSegments(nsAHttpSegmentWriter *writer,
return rv;
}
// 2 bytes of length, 1 type byte, 1 flag byte, 1 unused bit, 31 bits of ID
mInputFrameDataSize =
PR_ntohs(reinterpret_cast<uint16_t *>(mInputFrameBuffer.get())[0]);
mInputFrameType = reinterpret_cast<uint8_t *>(mInputFrameBuffer.get())[2];
mInputFrameFlags = reinterpret_cast<uint8_t *>(mInputFrameBuffer.get())[3];
// 3 bytes of length, 1 type byte, 1 flag byte, 1 unused bit, 31 bits of ID
uint8_t totallyWastedByte = mInputFrameBuffer.get()[0];
mInputFrameDataSize = PR_ntohs(*reinterpret_cast<uint16_t *>(mInputFrameBuffer.get() + 1));
if (totallyWastedByte || (mInputFrameDataSize > kMaxFrameData)) {
LOG3(("Got frame too large 0x%02X%04X", totallyWastedByte, mInputFrameDataSize));
RETURN_SESSION_ERROR(this, PROTOCOL_ERROR);
}
mInputFrameType = *reinterpret_cast<uint8_t *>(mInputFrameBuffer.get() + kFrameLengthBytes);
mInputFrameFlags = *reinterpret_cast<uint8_t *>(mInputFrameBuffer.get() + kFrameLengthBytes + kFrameTypeBytes);
mInputFrameID =
PR_ntohl(reinterpret_cast<uint32_t *>(mInputFrameBuffer.get())[1]);
PR_ntohl(*reinterpret_cast<uint32_t *>(mInputFrameBuffer.get() + kFrameLengthBytes + kFrameTypeBytes + kFrameFlagBytes));
mInputFrameID &= 0x7fffffff;
mInputFrameDataRead = 0;
@ -2140,14 +2189,6 @@ Http2Session::WriteSegments(nsAHttpSegmentWriter *writer,
mPaddingLength = 0;
if (mInputFrameDataSize >= 0x4000) {
// Section 9.1 HTTP frames cannot exceed 2^14 - 1 but receviers must ignore
// those bits
LOG3(("Http2Session::WriteSegments %p WARNING Frame Length bits past 14 are not 0 %08X\n",
this, mInputFrameDataSize));
mInputFrameDataSize &= 0x3fff;
}
LOG3(("Http2Session::WriteSegments[%p::%x] Frame Header Read "
"type %X data len %u flags %x id 0x%X",
this, mSerial, mInputFrameType, mInputFrameDataSize, mInputFrameFlags,
@ -2179,8 +2220,8 @@ Http2Session::WriteSegments(nsAHttpSegmentWriter *writer,
}
if (mInputFrameType != FRAME_TYPE_DATA) { // control frame
EnsureBuffer(mInputFrameBuffer, mInputFrameDataSize + 8, 8,
mInputFrameBufferSize);
EnsureBuffer(mInputFrameBuffer, mInputFrameDataSize + kFrameHeaderBytes,
kFrameHeaderBytes, mInputFrameBufferSize);
ChangeDownstreamState(BUFFERING_CONTROL_FRAME);
} else if (mInputFrameFlags & kFlag_PADDED) {
ChangeDownstreamState(PROCESSING_DATA_FRAME_PADDING_CONTROL);
@ -2196,11 +2237,11 @@ Http2Session::WriteSegments(nsAHttpSegmentWriter *writer,
MOZ_ASSERT(mInputFrameFlags & kFlag_PADDED,
"Processing padding control on unpadded frame");
MOZ_ASSERT(mInputFrameBufferUsed < (8 + 1),
MOZ_ASSERT(mInputFrameBufferUsed < (kFrameHeaderBytes + 1),
"Frame buffer used too large for state");
rv = NetworkRead(writer, mInputFrameBuffer + mInputFrameBufferUsed,
(8 + 1) - mInputFrameBufferUsed,
(kFrameHeaderBytes + 1) - mInputFrameBufferUsed,
countWritten);
if (NS_FAILED(rv)) {
@ -2217,7 +2258,7 @@ Http2Session::WriteSegments(nsAHttpSegmentWriter *writer,
mInputFrameBufferUsed += *countWritten;
if (mInputFrameBufferUsed - 8 < 1) {
if (mInputFrameBufferUsed - kFrameHeaderBytes < 1) {
LOG3(("Http2Session::WriteSegments %p "
"BUFFERING DATA FRAME CONTROL PADDING incomplete size=%d",
this, mInputFrameBufferUsed - 8));
@ -2226,7 +2267,7 @@ Http2Session::WriteSegments(nsAHttpSegmentWriter *writer,
++mInputFrameDataRead;
char *control = mInputFrameBuffer + 8;
char *control = mInputFrameBuffer + kFrameHeaderBytes;
mPaddingLength = static_cast<uint8_t>(*control);
LOG3(("Http2Session::WriteSegments %p stream 0x%X mPaddingLength=%d", this,
@ -2379,11 +2420,11 @@ Http2Session::WriteSegments(nsAHttpSegmentWriter *writer,
return NS_ERROR_UNEXPECTED;
}
MOZ_ASSERT(mInputFrameBufferUsed == 8, "Frame Buffer Header Not Present");
MOZ_ASSERT(mInputFrameDataSize + 8 <= mInputFrameBufferSize,
MOZ_ASSERT(mInputFrameBufferUsed == kFrameHeaderBytes, "Frame Buffer Header Not Present");
MOZ_ASSERT(mInputFrameDataSize + kFrameHeaderBytes <= mInputFrameBufferSize,
"allocation for control frame insufficient");
rv = NetworkRead(writer, mInputFrameBuffer + 8 + mInputFrameDataRead,
rv = NetworkRead(writer, mInputFrameBuffer + kFrameHeaderBytes + mInputFrameDataRead,
mInputFrameDataSize - mInputFrameDataRead, countWritten);
if (NS_FAILED(rv)) {
@ -2396,7 +2437,7 @@ Http2Session::WriteSegments(nsAHttpSegmentWriter *writer,
}
LogIO(this, nullptr, "Reading Control Frame",
mInputFrameBuffer + 8 + mInputFrameDataRead, *countWritten);
mInputFrameBuffer + kFrameHeaderBytes + mInputFrameDataRead, *countWritten);
mInputFrameDataRead += *countWritten;
@ -2467,16 +2508,20 @@ Http2Session::UpdateLocalStreamWindow(Http2Stream *stream, uint32_t bytes)
LOG3(("Http2Session::UpdateLocalStreamWindow Ack this=%p id=0x%X acksize=%d\n",
this, stream->StreamID(), toack));
stream->IncrementClientReceiveWindow(toack);
if (toack == 0) {
// Ensure we never send an illegal 0 window update
return;
}
// room for this packet needs to be ensured before calling this function
char *packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
mOutputQueueUsed += 12;
mOutputQueueUsed += kFrameHeaderBytes + 4;
MOZ_ASSERT(mOutputQueueUsed <= mOutputQueueSize);
CreateFrameHeader(packet, 4, FRAME_TYPE_WINDOW_UPDATE, 0, stream->StreamID());
CopyAsNetwork32(packet + 8, toack);
CopyAsNetwork32(packet + kFrameHeaderBytes, toack);
LogIO(this, stream, "Stream Window Update", packet, 12);
LogIO(this, stream, "Stream Window Update", packet, kFrameHeaderBytes + 4);
// dont flush here, this write can commonly be coalesced with a
// session window update to immediately follow.
}
@ -2506,15 +2551,20 @@ Http2Session::UpdateLocalSessionWindow(uint32_t bytes)
this, toack));
mLocalSessionWindow += toack;
if (toack == 0) {
// Ensure we never send an illegal 0 window update
return;
}
// room for this packet needs to be ensured before calling this function
char *packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
mOutputQueueUsed += 12;
mOutputQueueUsed += kFrameHeaderBytes + 4;
MOZ_ASSERT(mOutputQueueUsed <= mOutputQueueSize);
CreateFrameHeader(packet, 4, FRAME_TYPE_WINDOW_UPDATE, 0, 0);
CopyAsNetwork32(packet + 8, toack);
CopyAsNetwork32(packet + kFrameHeaderBytes, toack);
LogIO(this, nullptr, "Session Window Update", packet, 12);
LogIO(this, nullptr, "Session Window Update", packet, kFrameHeaderBytes + 4);
// dont flush here, this write can commonly be coalesced with others
}
@ -2523,7 +2573,7 @@ Http2Session::UpdateLocalRwin(Http2Stream *stream, uint32_t bytes)
{
// make sure there is room for 2 window updates even though
// we may not generate any.
EnsureOutputBuffer(16 * 2);
EnsureOutputBuffer(2 * (kFrameHeaderBytes + 4));
UpdateLocalStreamWindow(stream, bytes);
UpdateLocalSessionWindow(bytes);

View File

@ -112,7 +112,6 @@ public:
const static uint8_t kFlag_END_HEADERS = 0x04; // headers, continuation
const static uint8_t kFlag_END_PUSH_PROMISE = 0x04; // push promise
const static uint8_t kFlag_ACK = 0x01; // ping and settings
const static uint8_t kFlag_END_SEGMENT = 0x02; // data
const static uint8_t kFlag_PADDED = 0x08; // data, headers, push promise, continuation
const static uint8_t kFlag_PRIORITY = 0x20; // headers
@ -120,7 +119,8 @@ public:
SETTINGS_TYPE_HEADER_TABLE_SIZE = 1, // compression table size
SETTINGS_TYPE_ENABLE_PUSH = 2, // can be used to disable push
SETTINGS_TYPE_MAX_CONCURRENT = 3, // streams recvr allowed to initiate
SETTINGS_TYPE_INITIAL_WINDOW = 4 // bytes for flow control default
SETTINGS_TYPE_INITIAL_WINDOW = 4, // bytes for flow control default
SETTINGS_TYPE_MAX_FRAME_SIZE = 5 // max frame size settings sender allows receipt of
};
// This should be big enough to hold all of your control packets,
@ -148,9 +148,16 @@ public:
// The default rwin is 64KB - 1 unless updated by a settings frame
const static uint32_t kDefaultRwin = 65535;
// Frames with HTTP semantics are limited to 2^14 - 1 bytes of length in
// order to preserve responsiveness
const static uint32_t kMaxFrameData = 16383;
// We limit frames to 2^14 bytes of length in order to preserve responsiveness
// This is the smallest allowed value for SETTINGS_MAX_FRAME_SIZE
const static uint32_t kMaxFrameData = 0x4000;
const static uint8_t kFrameLengthBytes = 3;
const static uint8_t kFrameStreamIDBytes = 4;
const static uint8_t kFrameFlagBytes = 1;
const static uint8_t kFrameTypeBytes = 1;
const static uint8_t kFrameHeaderBytes = kFrameLengthBytes + kFrameFlagBytes +
kFrameTypeBytes + kFrameStreamIDBytes;
static nsresult RecvHeaders(Http2Session *);
static nsresult RecvPriority(Http2Session *);

View File

@ -444,8 +444,8 @@ Http2Stream::ParseHttpRequestHeaders(const char *buf,
// note that we could still have 1 frame for 0 bytes of data. that's ok.
uint32_t messageSize = dataLength;
messageSize += 13; // frame header + priority overhead in HEADERS frame
messageSize += (numFrames - 1) * 8; // frame header overhead in CONTINUATION frames
messageSize += Http2Session::kFrameHeaderBytes + 5; // frame header + priority overhead in HEADERS frame
messageSize += (numFrames - 1) * Http2Session::kFrameHeaderBytes; // frame header overhead in CONTINUATION frames
EnsureBuffer(mTxInlineFrame, dataLength + messageSize,
mTxInlineFrameUsed, mTxInlineFrameSize);
@ -478,7 +478,7 @@ Http2Stream::ParseHttpRequestHeaders(const char *buf,
frameLen + (idx ? 0 : 5),
(idx) ? Http2Session::FRAME_TYPE_CONTINUATION : Http2Session::FRAME_TYPE_HEADERS,
flags, mStreamID);
outputOffset += 8;
outputOffset += Http2Session::kFrameHeaderBytes;
if (!idx) {
// Priority - Dependency is 0, weight is our gecko-calculated weight,
@ -570,9 +570,9 @@ Http2Stream::AdjustInitialWindow()
}
uint8_t *packet = mTxInlineFrame.get() + mTxInlineFrameUsed;
EnsureBuffer(mTxInlineFrame, mTxInlineFrameUsed + 12,
EnsureBuffer(mTxInlineFrame, mTxInlineFrameUsed + Http2Session::kFrameHeaderBytes + 4,
mTxInlineFrameUsed, mTxInlineFrameSize);
mTxInlineFrameUsed += 12;
mTxInlineFrameUsed += Http2Session::kFrameHeaderBytes + 4;
mSession->CreateFrameHeader(packet, 4,
Http2Session::FRAME_TYPE_WINDOW_UPDATE,
@ -582,7 +582,7 @@ Http2Stream::AdjustInitialWindow()
uint32_t bump = ASpdySession::kInitialRwin - mClientReceiveWindow;
mClientReceiveWindow += bump;
bump = PR_htonl(bump);
memcpy(packet + 8, &bump, 4);
memcpy(packet + Http2Session::kFrameHeaderBytes, &bump, 4);
LOG3(("AdjustInitialwindow increased flow control window %p 0x%X\n",
this, stream->mStreamID));
}
@ -604,9 +604,9 @@ Http2Stream::AdjustPushedPriority()
return;
uint8_t *packet = mTxInlineFrame.get() + mTxInlineFrameUsed;
EnsureBuffer(mTxInlineFrame, mTxInlineFrameUsed + 13,
EnsureBuffer(mTxInlineFrame, mTxInlineFrameUsed + Http2Session::kFrameHeaderBytes + 5,
mTxInlineFrameUsed, mTxInlineFrameSize);
mTxInlineFrameUsed += 13;
mTxInlineFrameUsed += Http2Session::kFrameHeaderBytes + 5;
mSession->CreateFrameHeader(packet, 5,
Http2Session::FRAME_TYPE_PRIORITY,
@ -614,8 +614,8 @@ Http2Stream::AdjustPushedPriority()
mPushSource->mStreamID);
mPushSource->SetPriority(mPriority);
memset(packet + 8, 0, 4);
memcpy(packet + 12, &mPriorityWeight, 1);
memset(packet + Http2Session::kFrameHeaderBytes, 0, 4);
memcpy(packet + Http2Session::kFrameHeaderBytes + 4, &mPriorityWeight, 1);
LOG3(("AdjustPushedPriority %p id 0x%X to weight %X\n", this, mPushSource->mStreamID,
mPriorityWeight));
@ -831,7 +831,7 @@ Http2Stream::GenerateDataFrameHeader(uint32_t dataLength, bool lastFrame)
Http2Session::FRAME_TYPE_DATA,
frameFlags, mStreamID);
mTxInlineFrameUsed = 8;
mTxInlineFrameUsed = Http2Session::kFrameHeaderBytes;
mTxStreamFrameSize = dataLength;
}

View File

@ -36,13 +36,14 @@ namespace net {
// 26 was http/2-draft08 and http/2-draft07 (they were the same)
// 27 was http/2-draft09, h2-10, and h2-11
// 28 was http/2-draft12
HTTP2_VERSION_DRAFT13 = 29
// 29 was http/2-draft13
HTTP2_VERSION_DRAFT14 = 30
};
typedef uint8_t nsHttpVersion;
#define NS_HTTP2_DRAFT_VERSION HTTP2_VERSION_DRAFT13
#define NS_HTTP2_DRAFT_TOKEN "h2-13"
#define NS_HTTP2_DRAFT_VERSION HTTP2_VERSION_DRAFT14
#define NS_HTTP2_DRAFT_TOKEN "h2-14"
//-----------------------------------------------------------------------------
// http connection capabilities