mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 22:01:30 +00:00
Bug 1046892 part 1 - HTTP/2 draft 14 client implementation r=mcmanus
This commit is contained in:
parent
7644bb7745
commit
8446acda81
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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 *);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user