1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/protocol/http/SpdyStream3.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1478 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set sw=2 ts=8 et tw=80 : */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +// HttpLog.h should generally be included first 1.11 +#include "HttpLog.h" 1.12 + 1.13 +// Log on level :5, instead of default :4. 1.14 +#undef LOG 1.15 +#define LOG(args) LOG5(args) 1.16 +#undef LOG_ENABLED 1.17 +#define LOG_ENABLED() LOG5_ENABLED() 1.18 + 1.19 +#include "mozilla/Endian.h" 1.20 +#include "mozilla/Telemetry.h" 1.21 +#include "nsHttp.h" 1.22 +#include "nsHttpHandler.h" 1.23 +#include "nsHttpRequestHead.h" 1.24 +#include "nsISocketTransport.h" 1.25 +#include "nsISupportsPriority.h" 1.26 +#include "prnetdb.h" 1.27 +#include "SpdyPush3.h" 1.28 +#include "SpdySession3.h" 1.29 +#include "SpdyStream3.h" 1.30 +#include "PSpdyPush.h" 1.31 +#include "SpdyZlibReporter.h" 1.32 + 1.33 +#include <algorithm> 1.34 + 1.35 +#ifdef DEBUG 1.36 +// defined by the socket transport service while active 1.37 +extern PRThread *gSocketThread; 1.38 +#endif 1.39 + 1.40 +namespace mozilla { 1.41 +namespace net { 1.42 + 1.43 +SpdyStream3::SpdyStream3(nsAHttpTransaction *httpTransaction, 1.44 + SpdySession3 *spdySession, 1.45 + int32_t priority) 1.46 + : mStreamID(0), 1.47 + mSession(spdySession), 1.48 + mUpstreamState(GENERATING_SYN_STREAM), 1.49 + mSynFrameComplete(0), 1.50 + mSentFinOnData(0), 1.51 + mTransaction(httpTransaction), 1.52 + mSocketTransport(spdySession->SocketTransport()), 1.53 + mSegmentReader(nullptr), 1.54 + mSegmentWriter(nullptr), 1.55 + mChunkSize(spdySession->SendingChunkSize()), 1.56 + mRequestBlockedOnRead(0), 1.57 + mRecvdFin(0), 1.58 + mFullyOpen(0), 1.59 + mSentWaitingFor(0), 1.60 + mReceivedData(0), 1.61 + mSetTCPSocketBuffer(0), 1.62 + mTxInlineFrameSize(SpdySession3::kDefaultBufferSize), 1.63 + mTxInlineFrameUsed(0), 1.64 + mTxStreamFrameSize(0), 1.65 + mZlib(spdySession->UpstreamZlib()), 1.66 + mDecompressBufferSize(SpdySession3::kDefaultBufferSize), 1.67 + mDecompressBufferUsed(0), 1.68 + mDecompressedBytes(0), 1.69 + mRequestBodyLenRemaining(0), 1.70 + mPriority(priority), 1.71 + mLocalUnacked(0), 1.72 + mBlockedOnRwin(false), 1.73 + mTotalSent(0), 1.74 + mTotalRead(0), 1.75 + mPushSource(nullptr) 1.76 +{ 1.77 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.78 + 1.79 + LOG3(("SpdyStream3::SpdyStream3 %p", this)); 1.80 + 1.81 + mRemoteWindow = spdySession->GetServerInitialWindow(); 1.82 + mLocalWindow = spdySession->PushAllowance(); 1.83 + 1.84 + mTxInlineFrame = new uint8_t[mTxInlineFrameSize]; 1.85 + mDecompressBuffer = new char[mDecompressBufferSize]; 1.86 +} 1.87 + 1.88 +SpdyStream3::~SpdyStream3() 1.89 +{ 1.90 + mStreamID = SpdySession3::kDeadStreamID; 1.91 +} 1.92 + 1.93 +// ReadSegments() is used to write data down the socket. Generally, HTTP 1.94 +// request data is pulled from the approriate transaction and 1.95 +// converted to SPDY data. Sometimes control data like a window-update is 1.96 +// generated instead. 1.97 + 1.98 +nsresult 1.99 +SpdyStream3::ReadSegments(nsAHttpSegmentReader *reader, 1.100 + uint32_t count, 1.101 + uint32_t *countRead) 1.102 +{ 1.103 + LOG3(("SpdyStream3 %p ReadSegments reader=%p count=%d state=%x", 1.104 + this, reader, count, mUpstreamState)); 1.105 + 1.106 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.107 + 1.108 + nsresult rv = NS_ERROR_UNEXPECTED; 1.109 + mRequestBlockedOnRead = 0; 1.110 + 1.111 + switch (mUpstreamState) { 1.112 + case GENERATING_SYN_STREAM: 1.113 + case GENERATING_REQUEST_BODY: 1.114 + case SENDING_REQUEST_BODY: 1.115 + // Call into the HTTP Transaction to generate the HTTP request 1.116 + // stream. That stream will show up in OnReadSegment(). 1.117 + mSegmentReader = reader; 1.118 + rv = mTransaction->ReadSegments(this, count, countRead); 1.119 + mSegmentReader = nullptr; 1.120 + 1.121 + // Check to see if the transaction's request could be written out now. 1.122 + // If not, mark the stream for callback when writing can proceed. 1.123 + if (NS_SUCCEEDED(rv) && 1.124 + mUpstreamState == GENERATING_SYN_STREAM && 1.125 + !mSynFrameComplete) 1.126 + mSession->TransactionHasDataToWrite(this); 1.127 + 1.128 + // mTxinlineFrameUsed represents any queued un-sent frame. It might 1.129 + // be 0 if there is no such frame, which is not a gurantee that we 1.130 + // don't have more request body to send - just that any data that was 1.131 + // sent comprised a complete SPDY frame. Likewise, a non 0 value is 1.132 + // a queued, but complete, spdy frame length. 1.133 + 1.134 + // Mark that we are blocked on read if the http transaction needs to 1.135 + // provide more of the request message body and there is nothing queued 1.136 + // for writing 1.137 + if (rv == NS_BASE_STREAM_WOULD_BLOCK && !mTxInlineFrameUsed) 1.138 + mRequestBlockedOnRead = 1; 1.139 + 1.140 + // If the sending flow control window is open (!mBlockedOnRwin) then 1.141 + // continue sending the request 1.142 + if (!mBlockedOnRwin && 1.143 + !mTxInlineFrameUsed && NS_SUCCEEDED(rv) && (!*countRead)) { 1.144 + LOG3(("SpdyStream3::ReadSegments %p 0x%X: Sending request data complete, " 1.145 + "mUpstreamState=%x",this, mStreamID, mUpstreamState)); 1.146 + if (mSentFinOnData) { 1.147 + ChangeState(UPSTREAM_COMPLETE); 1.148 + } 1.149 + else { 1.150 + GenerateDataFrameHeader(0, true); 1.151 + ChangeState(SENDING_FIN_STREAM); 1.152 + mSession->TransactionHasDataToWrite(this); 1.153 + rv = NS_BASE_STREAM_WOULD_BLOCK; 1.154 + } 1.155 + } 1.156 + break; 1.157 + 1.158 + case SENDING_FIN_STREAM: 1.159 + // We were trying to send the FIN-STREAM but were blocked from 1.160 + // sending it out - try again. 1.161 + if (!mSentFinOnData) { 1.162 + mSegmentReader = reader; 1.163 + rv = TransmitFrame(nullptr, nullptr, false); 1.164 + mSegmentReader = nullptr; 1.165 + MOZ_ASSERT(NS_FAILED(rv) || !mTxInlineFrameUsed, 1.166 + "Transmit Frame should be all or nothing"); 1.167 + if (NS_SUCCEEDED(rv)) 1.168 + ChangeState(UPSTREAM_COMPLETE); 1.169 + } 1.170 + else { 1.171 + rv = NS_OK; 1.172 + mTxInlineFrameUsed = 0; // cancel fin data packet 1.173 + ChangeState(UPSTREAM_COMPLETE); 1.174 + } 1.175 + 1.176 + *countRead = 0; 1.177 + 1.178 + // don't change OK to WOULD BLOCK. we are really done sending if OK 1.179 + break; 1.180 + 1.181 + case UPSTREAM_COMPLETE: 1.182 + *countRead = 0; 1.183 + rv = NS_OK; 1.184 + break; 1.185 + 1.186 + default: 1.187 + MOZ_ASSERT(false, "SpdyStream3::ReadSegments unknown state"); 1.188 + break; 1.189 + } 1.190 + 1.191 + return rv; 1.192 +} 1.193 + 1.194 +// WriteSegments() is used to read data off the socket. Generally this is 1.195 +// just the SPDY frame header and from there the appropriate SPDYStream 1.196 +// is identified from the Stream-ID. The http transaction associated with 1.197 +// that read then pulls in the data directly. 1.198 + 1.199 +nsresult 1.200 +SpdyStream3::WriteSegments(nsAHttpSegmentWriter *writer, 1.201 + uint32_t count, 1.202 + uint32_t *countWritten) 1.203 +{ 1.204 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.205 + MOZ_ASSERT(!mSegmentWriter, "segment writer in progress"); 1.206 + 1.207 + LOG3(("SpdyStream3::WriteSegments %p count=%d state=%x", 1.208 + this, count, mUpstreamState)); 1.209 + 1.210 + mSegmentWriter = writer; 1.211 + nsresult rv = mTransaction->WriteSegments(this, count, countWritten); 1.212 + mSegmentWriter = nullptr; 1.213 + 1.214 + return rv; 1.215 +} 1.216 + 1.217 +PLDHashOperator 1.218 +SpdyStream3::hdrHashEnumerate(const nsACString &key, 1.219 + nsAutoPtr<nsCString> &value, 1.220 + void *closure) 1.221 +{ 1.222 + SpdyStream3 *self = static_cast<SpdyStream3 *>(closure); 1.223 + 1.224 + self->CompressToFrame(key); 1.225 + self->CompressToFrame(value.get()); 1.226 + return PL_DHASH_NEXT; 1.227 +} 1.228 + 1.229 +void 1.230 +SpdyStream3::CreatePushHashKey(const nsCString &scheme, 1.231 + const nsCString &hostHeader, 1.232 + uint64_t serial, 1.233 + const nsCSubstring &pathInfo, 1.234 + nsCString &outOrigin, 1.235 + nsCString &outKey) 1.236 +{ 1.237 + outOrigin = scheme; 1.238 + outOrigin.Append(NS_LITERAL_CSTRING("://")); 1.239 + outOrigin.Append(hostHeader); 1.240 + 1.241 + outKey = outOrigin; 1.242 + outKey.Append(NS_LITERAL_CSTRING("/[spdy3.")); 1.243 + outKey.AppendInt(serial); 1.244 + outKey.Append(NS_LITERAL_CSTRING("]")); 1.245 + outKey.Append(pathInfo); 1.246 +} 1.247 + 1.248 + 1.249 +nsresult 1.250 +SpdyStream3::ParseHttpRequestHeaders(const char *buf, 1.251 + uint32_t avail, 1.252 + uint32_t *countUsed) 1.253 +{ 1.254 + // Returns NS_OK even if the headers are incomplete 1.255 + // set mSynFrameComplete flag if they are complete 1.256 + 1.257 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.258 + MOZ_ASSERT(mUpstreamState == GENERATING_SYN_STREAM); 1.259 + 1.260 + LOG3(("SpdyStream3::ParseHttpRequestHeaders %p avail=%d state=%x", 1.261 + this, avail, mUpstreamState)); 1.262 + 1.263 + mFlatHttpRequestHeaders.Append(buf, avail); 1.264 + 1.265 + // We can use the simple double crlf because firefox is the 1.266 + // only client we are parsing 1.267 + int32_t endHeader = mFlatHttpRequestHeaders.Find("\r\n\r\n"); 1.268 + 1.269 + if (endHeader == kNotFound) { 1.270 + // We don't have all the headers yet 1.271 + LOG3(("SpdyStream3::ParseHttpRequestHeaders %p " 1.272 + "Need more header bytes. Len = %d", 1.273 + this, mFlatHttpRequestHeaders.Length())); 1.274 + *countUsed = avail; 1.275 + return NS_OK; 1.276 + } 1.277 + 1.278 + // We have recvd all the headers, trim the local 1.279 + // buffer of the final empty line, and set countUsed to reflect 1.280 + // the whole header has been consumed. 1.281 + uint32_t oldLen = mFlatHttpRequestHeaders.Length(); 1.282 + mFlatHttpRequestHeaders.SetLength(endHeader + 2); 1.283 + *countUsed = avail - (oldLen - endHeader) + 4; 1.284 + mSynFrameComplete = 1; 1.285 + 1.286 + nsCString hostHeader; 1.287 + nsCString hashkey; 1.288 + mTransaction->RequestHead()->GetHeader(nsHttp::Host, hostHeader); 1.289 + 1.290 + CreatePushHashKey(NS_LITERAL_CSTRING("https"), 1.291 + hostHeader, mSession->Serial(), 1.292 + mTransaction->RequestHead()->RequestURI(), 1.293 + mOrigin, hashkey); 1.294 + 1.295 + // check the push cache for GET 1.296 + if (mTransaction->RequestHead()->IsGet()) { 1.297 + // from :scheme, :host, :path 1.298 + nsILoadGroupConnectionInfo *loadGroupCI = mTransaction->LoadGroupConnectionInfo(); 1.299 + SpdyPushCache *cache = nullptr; 1.300 + if (loadGroupCI) 1.301 + loadGroupCI->GetSpdyPushCache(&cache); 1.302 + 1.303 + SpdyPushedStream3 *pushedStream = nullptr; 1.304 + // we remove the pushedstream from the push cache so that 1.305 + // it will not be used for another GET. This does not destroy the 1.306 + // stream itself - that is done when the transactionhash is done with it. 1.307 + if (cache) 1.308 + pushedStream = cache->RemovePushedStreamSpdy3(hashkey); 1.309 + 1.310 + if (pushedStream) { 1.311 + LOG3(("Pushed Stream Match located id=0x%X key=%s\n", 1.312 + pushedStream->StreamID(), hashkey.get())); 1.313 + pushedStream->SetConsumerStream(this); 1.314 + mPushSource = pushedStream; 1.315 + mSentFinOnData = 1; 1.316 + 1.317 + // This stream has been activated (and thus counts against the concurrency 1.318 + // limit intentionally), but will not be registered via 1.319 + // RegisterStreamID (below) because of the push match. Therefore the 1.320 + // concurrency sempahore needs to be balanced. 1.321 + mSession->DecrementConcurrent(this); 1.322 + 1.323 + // There is probably pushed data buffered so trigger a read manually 1.324 + // as we can't rely on future network events to do it 1.325 + mSession->ConnectPushedStream(this); 1.326 + return NS_OK; 1.327 + } 1.328 + } 1.329 + 1.330 + // It is now OK to assign a streamID that we are assured will 1.331 + // be monotonically increasing amongst syn-streams on this 1.332 + // session 1.333 + mStreamID = mSession->RegisterStreamID(this); 1.334 + MOZ_ASSERT(mStreamID & 1, "Spdy Stream Channel ID must be odd"); 1.335 + 1.336 + if (mStreamID >= 0x80000000) { 1.337 + // streamID must fit in 31 bits. This is theoretically possible 1.338 + // because stream ID assignment is asynchronous to stream creation 1.339 + // because of the protocol requirement that the ID in syn-stream 1.340 + // be monotonically increasing. In reality this is really not possible 1.341 + // because new streams stop being added to a session with 0x10000000 / 2 1.342 + // IDs still available and no race condition is going to bridge that gap, 1.343 + // so we can be comfortable on just erroring out for correctness in that 1.344 + // case. 1.345 + LOG3(("Stream assigned out of range ID: 0x%X", mStreamID)); 1.346 + return NS_ERROR_UNEXPECTED; 1.347 + } 1.348 + 1.349 + // Now we need to convert the flat http headers into a set 1.350 + // of SPDY headers.. writing to mTxInlineFrame{sz} 1.351 + 1.352 + mTxInlineFrame[0] = SpdySession3::kFlag_Control; 1.353 + mTxInlineFrame[1] = SpdySession3::kVersion; 1.354 + mTxInlineFrame[2] = 0; 1.355 + mTxInlineFrame[3] = SpdySession3::CONTROL_TYPE_SYN_STREAM; 1.356 + // 4 to 7 are length and flags, we'll fill that in later 1.357 + 1.358 + NetworkEndian::writeUint32(mTxInlineFrame + 8, mStreamID); 1.359 + 1.360 + // this is the associated-to field, which is not used sending 1.361 + // from the client in the http binding 1.362 + memset(mTxInlineFrame + 12, 0, 4); 1.363 + 1.364 + // Priority flags are the E0 mask of byte 16. 1.365 + // 0 is highest priority, 7 is lowest. 1.366 + // The other 5 bits of byte 16 are unused. 1.367 + 1.368 + if (mPriority >= nsISupportsPriority::PRIORITY_LOWEST) 1.369 + mTxInlineFrame[16] = 7 << 5; 1.370 + else if (mPriority <= nsISupportsPriority::PRIORITY_HIGHEST) 1.371 + mTxInlineFrame[16] = 0 << 5; 1.372 + else { 1.373 + // The priority mapping relies on the unfiltered ranged to be 1.374 + // between -20 .. +20 1.375 + PR_STATIC_ASSERT(nsISupportsPriority::PRIORITY_LOWEST == 20); 1.376 + PR_STATIC_ASSERT(nsISupportsPriority::PRIORITY_HIGHEST == -20); 1.377 + 1.378 + // Add one to the priority so that values such as -10 and -11 1.379 + // get different spdy priorities - this appears to be an important 1.380 + // breaking line in the priorities content assigns to 1.381 + // transactions. 1.382 + uint8_t calculatedPriority = 3 + ((mPriority + 1) / 5); 1.383 + MOZ_ASSERT (!(calculatedPriority & 0xf8), 1.384 + "Calculated Priority Out Of Range"); 1.385 + mTxInlineFrame[16] = calculatedPriority << 5; 1.386 + } 1.387 + 1.388 + // The client cert "slot". Right now we don't send client certs 1.389 + mTxInlineFrame[17] = 0; 1.390 + 1.391 + nsCString versionHeader; 1.392 + if (mTransaction->RequestHead()->Version() == NS_HTTP_VERSION_1_1) 1.393 + versionHeader = NS_LITERAL_CSTRING("HTTP/1.1"); 1.394 + else 1.395 + versionHeader = NS_LITERAL_CSTRING("HTTP/1.0"); 1.396 + 1.397 + // use mRequestHead() to get a sense of how big to make the hash, 1.398 + // even though we are parsing the actual text stream because 1.399 + // it is legit to append headers. 1.400 + nsClassHashtable<nsCStringHashKey, nsCString> 1.401 + hdrHash(1 + (mTransaction->RequestHead()->Headers().Count() * 2)); 1.402 + 1.403 + const char *beginBuffer = mFlatHttpRequestHeaders.BeginReading(); 1.404 + 1.405 + // need to hash all the headers together to remove duplicates, special 1.406 + // headers, etc.. 1.407 + 1.408 + int32_t crlfIndex = mFlatHttpRequestHeaders.Find("\r\n"); 1.409 + while (true) { 1.410 + int32_t startIndex = crlfIndex + 2; 1.411 + 1.412 + crlfIndex = mFlatHttpRequestHeaders.Find("\r\n", false, startIndex); 1.413 + if (crlfIndex == -1) 1.414 + break; 1.415 + 1.416 + int32_t colonIndex = mFlatHttpRequestHeaders.Find(":", false, startIndex, 1.417 + crlfIndex - startIndex); 1.418 + if (colonIndex == -1) 1.419 + break; 1.420 + 1.421 + nsDependentCSubstring name = Substring(beginBuffer + startIndex, 1.422 + beginBuffer + colonIndex); 1.423 + // all header names are lower case in spdy 1.424 + ToLowerCase(name); 1.425 + 1.426 + // exclusions.. mostly from 3.2.1 1.427 + if (name.Equals("connection") || 1.428 + name.Equals("keep-alive") || 1.429 + name.Equals("host") || 1.430 + name.Equals("accept-encoding") || 1.431 + name.Equals("te") || 1.432 + name.Equals("transfer-encoding")) 1.433 + continue; 1.434 + 1.435 + nsCString *val = hdrHash.Get(name); 1.436 + if (!val) { 1.437 + val = new nsCString(); 1.438 + hdrHash.Put(name, val); 1.439 + } 1.440 + 1.441 + int32_t valueIndex = colonIndex + 1; 1.442 + while (valueIndex < crlfIndex && beginBuffer[valueIndex] == ' ') 1.443 + ++valueIndex; 1.444 + 1.445 + nsDependentCSubstring v = Substring(beginBuffer + valueIndex, 1.446 + beginBuffer + crlfIndex); 1.447 + if (!val->IsEmpty()) 1.448 + val->Append(static_cast<char>(0)); 1.449 + val->Append(v); 1.450 + 1.451 + if (name.Equals("content-length")) { 1.452 + int64_t len; 1.453 + if (nsHttp::ParseInt64(val->get(), nullptr, &len)) 1.454 + mRequestBodyLenRemaining = len; 1.455 + } 1.456 + } 1.457 + 1.458 + mTxInlineFrameUsed = 18; 1.459 + 1.460 + // Do not naively log the request headers here beacuse they might 1.461 + // contain auth. The http transaction already logs the sanitized request 1.462 + // headers at this same level so it is not necessary to do so here. 1.463 + 1.464 + const char *methodHeader = mTransaction->RequestHead()->Method().get(); 1.465 + 1.466 + // The header block length 1.467 + uint16_t count = hdrHash.Count() + 5; /* method, path, version, host, scheme */ 1.468 + CompressToFrame(count); 1.469 + 1.470 + // :method, :path, :version comprise a HTTP/1 request line, so send those first 1.471 + // to make life easy for any gateways 1.472 + CompressToFrame(NS_LITERAL_CSTRING(":method")); 1.473 + CompressToFrame(methodHeader, strlen(methodHeader)); 1.474 + CompressToFrame(NS_LITERAL_CSTRING(":path")); 1.475 + CompressToFrame(mTransaction->RequestHead()->RequestURI()); 1.476 + CompressToFrame(NS_LITERAL_CSTRING(":version")); 1.477 + CompressToFrame(versionHeader); 1.478 + 1.479 + CompressToFrame(NS_LITERAL_CSTRING(":host")); 1.480 + CompressToFrame(hostHeader); 1.481 + CompressToFrame(NS_LITERAL_CSTRING(":scheme")); 1.482 + CompressToFrame(NS_LITERAL_CSTRING("https")); 1.483 + 1.484 + hdrHash.Enumerate(hdrHashEnumerate, this); 1.485 + CompressFlushFrame(); 1.486 + 1.487 + // 4 to 7 are length and flags, which we can now fill in 1.488 + NetworkEndian::writeUint32(mTxInlineFrame + 1 * sizeof(uint32_t), 1.489 + mTxInlineFrameUsed - 8); 1.490 + 1.491 + MOZ_ASSERT(!mTxInlineFrame[4], "Size greater than 24 bits"); 1.492 + 1.493 + // Determine whether to put the fin bit on the syn stream frame or whether 1.494 + // to wait for a data packet to put it on. 1.495 + 1.496 + if (mTransaction->RequestHead()->IsGet() || 1.497 + mTransaction->RequestHead()->IsConnect() || 1.498 + mTransaction->RequestHead()->IsHead()) { 1.499 + // for GET, CONNECT, and HEAD place the fin bit right on the 1.500 + // syn stream packet 1.501 + 1.502 + mSentFinOnData = 1; 1.503 + mTxInlineFrame[4] = SpdySession3::kFlag_Data_FIN; 1.504 + } 1.505 + else if (mTransaction->RequestHead()->IsPost() || 1.506 + mTransaction->RequestHead()->IsPut() || 1.507 + mTransaction->RequestHead()->IsOptions()) { 1.508 + // place fin in a data frame even for 0 length messages, I've seen 1.509 + // the google gateway be unhappy with fin-on-syn for 0 length POST 1.510 + } 1.511 + else if (!mRequestBodyLenRemaining) { 1.512 + // for other HTTP extension methods, rely on the content-length 1.513 + // to determine whether or not to put fin on syn 1.514 + mSentFinOnData = 1; 1.515 + mTxInlineFrame[4] = SpdySession3::kFlag_Data_FIN; 1.516 + } 1.517 + 1.518 + Telemetry::Accumulate(Telemetry::SPDY_SYN_SIZE, mTxInlineFrameUsed - 18); 1.519 + 1.520 + // The size of the input headers is approximate 1.521 + uint32_t ratio = 1.522 + (mTxInlineFrameUsed - 18) * 100 / 1.523 + (11 + mTransaction->RequestHead()->RequestURI().Length() + 1.524 + mFlatHttpRequestHeaders.Length()); 1.525 + 1.526 + Telemetry::Accumulate(Telemetry::SPDY_SYN_RATIO, ratio); 1.527 + return NS_OK; 1.528 +} 1.529 + 1.530 +void 1.531 +SpdyStream3::AdjustInitialWindow() 1.532 +{ 1.533 + MOZ_ASSERT(mSession->PushAllowance() <= ASpdySession::kInitialRwin); 1.534 + 1.535 + // The session initial_window is sized for serverpushed streams. When we 1.536 + // generate a client pulled stream we want to adjust the initial window 1.537 + // to a huge value in a pipeline with that SYN_STREAM. 1.538 + 1.539 + // >0 even numbered IDs are pushed streams. 1.540 + // odd numbered IDs are pulled streams. 1.541 + // 0 is the sink for a pushed stream. 1.542 + SpdyStream3 *stream = this; 1.543 + if (!mStreamID) { 1.544 + MOZ_ASSERT(mPushSource); 1.545 + if (!mPushSource) 1.546 + return; 1.547 + stream = mPushSource; 1.548 + MOZ_ASSERT(stream->mStreamID); 1.549 + MOZ_ASSERT(!(stream->mStreamID & 1)); // is a push stream 1.550 + 1.551 + // If the pushed stream has sent a FIN, there is no reason to update 1.552 + // the window 1.553 + if (stream->RecvdFin()) 1.554 + return; 1.555 + } 1.556 + 1.557 + // For server pushes we also want to include in the ack any data that has been 1.558 + // buffered but unacknowledged. 1.559 + 1.560 + // mLocalUnacked is technically 64 bits, but because it can never grow larger than 1.561 + // our window size (which is closer to 29bits max) we know it fits comfortably in 32. 1.562 + // However we don't really enforce that, and track it as a 64 so that broken senders 1.563 + // can still interoperate. That means we have to be careful with this calculation. 1.564 + uint64_t toack64 = (ASpdySession::kInitialRwin - mSession->PushAllowance()) + 1.565 + stream->mLocalUnacked; 1.566 + stream->mLocalUnacked = 0; 1.567 + if (toack64 > 0x7fffffff) { 1.568 + stream->mLocalUnacked = toack64 - 0x7fffffff; 1.569 + toack64 = 0x7fffffff; 1.570 + } 1.571 + uint32_t toack = static_cast<uint32_t>(toack64); 1.572 + if (!toack) 1.573 + return; 1.574 + toack = PR_htonl(toack); 1.575 + 1.576 + SpdySession3::EnsureBuffer(mTxInlineFrame, 1.577 + mTxInlineFrameUsed + 16, 1.578 + mTxInlineFrameUsed, 1.579 + mTxInlineFrameSize); 1.580 + 1.581 + unsigned char *packet = mTxInlineFrame.get() + mTxInlineFrameUsed; 1.582 + mTxInlineFrameUsed += 16; 1.583 + 1.584 + memset(packet, 0, 8); 1.585 + packet[0] = SpdySession3::kFlag_Control; 1.586 + packet[1] = SpdySession3::kVersion; 1.587 + packet[3] = SpdySession3::CONTROL_TYPE_WINDOW_UPDATE; 1.588 + packet[7] = 8; // 8 data bytes after 8 byte header 1.589 + 1.590 + uint32_t id = PR_htonl(stream->mStreamID); 1.591 + memcpy(packet + 8, &id, 4); 1.592 + memcpy(packet + 12, &toack, 4); 1.593 + 1.594 + stream->mLocalWindow += PR_ntohl(toack); 1.595 + LOG3(("AdjustInitialwindow %p 0x%X %u\n", 1.596 + this, stream->mStreamID, PR_ntohl(toack))); 1.597 +} 1.598 + 1.599 +void 1.600 +SpdyStream3::UpdateTransportReadEvents(uint32_t count) 1.601 +{ 1.602 + mTotalRead += count; 1.603 + 1.604 + mTransaction->OnTransportStatus(mSocketTransport, 1.605 + NS_NET_STATUS_RECEIVING_FROM, 1.606 + mTotalRead); 1.607 +} 1.608 + 1.609 +void 1.610 +SpdyStream3::UpdateTransportSendEvents(uint32_t count) 1.611 +{ 1.612 + mTotalSent += count; 1.613 + 1.614 + // normally on non-windows platform we use TCP autotuning for 1.615 + // the socket buffers, and this works well (managing enough 1.616 + // buffers for BDP while conserving memory) for HTTP even when 1.617 + // it creates really deep queues. However this 'buffer bloat' is 1.618 + // a problem for spdy because it ruins the low latency properties 1.619 + // necessary for PING and cancel to work meaningfully. 1.620 + // 1.621 + // If this stream represents a large upload, disable autotuning for 1.622 + // the session and cap the send buffers by default at 128KB. 1.623 + // (10Mbit/sec @ 100ms) 1.624 + // 1.625 + uint32_t bufferSize = gHttpHandler->SpdySendBufferSize(); 1.626 + if ((mTotalSent > bufferSize) && !mSetTCPSocketBuffer) { 1.627 + mSetTCPSocketBuffer = 1; 1.628 + mSocketTransport->SetSendBufferSize(bufferSize); 1.629 + } 1.630 + 1.631 + if (mUpstreamState != SENDING_FIN_STREAM) 1.632 + mTransaction->OnTransportStatus(mSocketTransport, 1.633 + NS_NET_STATUS_SENDING_TO, 1.634 + mTotalSent); 1.635 + 1.636 + if (!mSentWaitingFor && !mRequestBodyLenRemaining) { 1.637 + mSentWaitingFor = 1; 1.638 + mTransaction->OnTransportStatus(mSocketTransport, 1.639 + NS_NET_STATUS_WAITING_FOR, 1.640 + 0); 1.641 + } 1.642 +} 1.643 + 1.644 +nsresult 1.645 +SpdyStream3::TransmitFrame(const char *buf, 1.646 + uint32_t *countUsed, 1.647 + bool forceCommitment) 1.648 +{ 1.649 + // If TransmitFrame returns SUCCESS than all the data is sent (or at least 1.650 + // buffered at the session level), if it returns WOULD_BLOCK then none of 1.651 + // the data is sent. 1.652 + 1.653 + // You can call this function with no data and no out parameter in order to 1.654 + // flush internal buffers that were previously blocked on writing. You can 1.655 + // of course feed new data to it as well. 1.656 + 1.657 + LOG3(("SpdyStream3::TransmitFrame %p inline=%d stream=%d", 1.658 + this, mTxInlineFrameUsed, mTxStreamFrameSize)); 1.659 + if (countUsed) 1.660 + *countUsed = 0; 1.661 + 1.662 + if (!mTxInlineFrameUsed) { 1.663 + MOZ_ASSERT(!buf); 1.664 + return NS_OK; 1.665 + } 1.666 + 1.667 + MOZ_ASSERT(mTxInlineFrameUsed, "empty stream frame in transmit"); 1.668 + MOZ_ASSERT(mSegmentReader, "TransmitFrame with null mSegmentReader"); 1.669 + MOZ_ASSERT((buf && countUsed) || (!buf && !countUsed), 1.670 + "TransmitFrame arguments inconsistent"); 1.671 + 1.672 + uint32_t transmittedCount; 1.673 + nsresult rv; 1.674 + 1.675 + // In the (relatively common) event that we have a small amount of data 1.676 + // split between the inlineframe and the streamframe, then move the stream 1.677 + // data into the inlineframe via copy in order to coalesce into one write. 1.678 + // Given the interaction with ssl this is worth the small copy cost. 1.679 + if (mTxStreamFrameSize && mTxInlineFrameUsed && 1.680 + mTxStreamFrameSize < SpdySession3::kDefaultBufferSize && 1.681 + mTxInlineFrameUsed + mTxStreamFrameSize < mTxInlineFrameSize) { 1.682 + LOG3(("Coalesce Transmit")); 1.683 + memcpy (mTxInlineFrame + mTxInlineFrameUsed, 1.684 + buf, mTxStreamFrameSize); 1.685 + if (countUsed) 1.686 + *countUsed += mTxStreamFrameSize; 1.687 + mTxInlineFrameUsed += mTxStreamFrameSize; 1.688 + mTxStreamFrameSize = 0; 1.689 + } 1.690 + 1.691 + rv = 1.692 + mSegmentReader->CommitToSegmentSize(mTxStreamFrameSize + mTxInlineFrameUsed, 1.693 + forceCommitment); 1.694 + 1.695 + if (rv == NS_BASE_STREAM_WOULD_BLOCK) { 1.696 + MOZ_ASSERT(!forceCommitment, "forceCommitment with WOULD_BLOCK"); 1.697 + mSession->TransactionHasDataToWrite(this); 1.698 + } 1.699 + if (NS_FAILED(rv)) // this will include WOULD_BLOCK 1.700 + return rv; 1.701 + 1.702 + // This function calls mSegmentReader->OnReadSegment to report the actual SPDY 1.703 + // bytes through to the SpdySession3 and then the HttpConnection which calls 1.704 + // the socket write function. It will accept all of the inline and stream 1.705 + // data because of the above 'commitment' even if it has to buffer 1.706 + 1.707 + rv = mSegmentReader->OnReadSegment(reinterpret_cast<char*>(mTxInlineFrame.get()), 1.708 + mTxInlineFrameUsed, 1.709 + &transmittedCount); 1.710 + LOG3(("SpdyStream3::TransmitFrame for inline session=%p " 1.711 + "stream=%p result %x len=%d", 1.712 + mSession, this, rv, transmittedCount)); 1.713 + 1.714 + MOZ_ASSERT(rv != NS_BASE_STREAM_WOULD_BLOCK, 1.715 + "inconsistent inline commitment result"); 1.716 + 1.717 + if (NS_FAILED(rv)) 1.718 + return rv; 1.719 + 1.720 + MOZ_ASSERT(transmittedCount == mTxInlineFrameUsed, 1.721 + "inconsistent inline commitment count"); 1.722 + 1.723 + SpdySession3::LogIO(mSession, this, "Writing from Inline Buffer", 1.724 + reinterpret_cast<char*>(mTxInlineFrame.get()), 1.725 + transmittedCount); 1.726 + 1.727 + if (mTxStreamFrameSize) { 1.728 + if (!buf) { 1.729 + // this cannot happen 1.730 + MOZ_ASSERT(false, "Stream transmit with null buf argument to " 1.731 + "TransmitFrame()"); 1.732 + LOG(("Stream transmit with null buf argument to TransmitFrame()\n")); 1.733 + return NS_ERROR_UNEXPECTED; 1.734 + } 1.735 + 1.736 + rv = mSegmentReader->OnReadSegment(buf, mTxStreamFrameSize, 1.737 + &transmittedCount); 1.738 + 1.739 + LOG3(("SpdyStream3::TransmitFrame for regular session=%p " 1.740 + "stream=%p result %x len=%d", 1.741 + mSession, this, rv, transmittedCount)); 1.742 + 1.743 + MOZ_ASSERT(rv != NS_BASE_STREAM_WOULD_BLOCK, 1.744 + "inconsistent stream commitment result"); 1.745 + 1.746 + if (NS_FAILED(rv)) 1.747 + return rv; 1.748 + 1.749 + MOZ_ASSERT(transmittedCount == mTxStreamFrameSize, 1.750 + "inconsistent stream commitment count"); 1.751 + 1.752 + SpdySession3::LogIO(mSession, this, "Writing from Transaction Buffer", 1.753 + buf, transmittedCount); 1.754 + 1.755 + *countUsed += mTxStreamFrameSize; 1.756 + } 1.757 + 1.758 + // calling this will trigger waiting_for if mRequestBodyLenRemaining is 0 1.759 + UpdateTransportSendEvents(mTxInlineFrameUsed + mTxStreamFrameSize); 1.760 + 1.761 + mTxInlineFrameUsed = 0; 1.762 + mTxStreamFrameSize = 0; 1.763 + 1.764 + return NS_OK; 1.765 +} 1.766 + 1.767 +void 1.768 +SpdyStream3::ChangeState(enum stateType newState) 1.769 +{ 1.770 + LOG3(("SpdyStream3::ChangeState() %p from %X to %X", 1.771 + this, mUpstreamState, newState)); 1.772 + mUpstreamState = newState; 1.773 + return; 1.774 +} 1.775 + 1.776 +void 1.777 +SpdyStream3::GenerateDataFrameHeader(uint32_t dataLength, bool lastFrame) 1.778 +{ 1.779 + LOG3(("SpdyStream3::GenerateDataFrameHeader %p len=%d last=%d", 1.780 + this, dataLength, lastFrame)); 1.781 + 1.782 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.783 + MOZ_ASSERT(!mTxInlineFrameUsed, "inline frame not empty"); 1.784 + MOZ_ASSERT(!mTxStreamFrameSize, "stream frame not empty"); 1.785 + MOZ_ASSERT(!(dataLength & 0xff000000), "datalength > 24 bits"); 1.786 + 1.787 + NetworkEndian::writeUint32(mTxInlineFrame, mStreamID); 1.788 + NetworkEndian::writeUint32(mTxInlineFrame + 1 * sizeof(uint32_t), dataLength); 1.789 + 1.790 + MOZ_ASSERT(!(mTxInlineFrame[0] & 0x80), "control bit set unexpectedly"); 1.791 + MOZ_ASSERT(!mTxInlineFrame[4], "flag bits set unexpectedly"); 1.792 + 1.793 + mTxInlineFrameUsed = 8; 1.794 + mTxStreamFrameSize = dataLength; 1.795 + 1.796 + if (lastFrame) { 1.797 + mTxInlineFrame[4] |= SpdySession3::kFlag_Data_FIN; 1.798 + if (dataLength) 1.799 + mSentFinOnData = 1; 1.800 + } 1.801 +} 1.802 + 1.803 +void 1.804 +SpdyStream3::CompressToFrame(const nsACString &str) 1.805 +{ 1.806 + CompressToFrame(str.BeginReading(), str.Length()); 1.807 +} 1.808 + 1.809 +void 1.810 +SpdyStream3::CompressToFrame(const nsACString *str) 1.811 +{ 1.812 + CompressToFrame(str->BeginReading(), str->Length()); 1.813 +} 1.814 + 1.815 +// Dictionary taken from 1.816 +// http://dev.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3 1.817 + 1.818 +const unsigned char SpdyStream3::kDictionary[] = { 1.819 + 0x00, 0x00, 0x00, 0x07, 0x6f, 0x70, 0x74, 0x69, // - - - - o p t i 1.820 + 0x6f, 0x6e, 0x73, 0x00, 0x00, 0x00, 0x04, 0x68, // o n s - - - - h 1.821 + 0x65, 0x61, 0x64, 0x00, 0x00, 0x00, 0x04, 0x70, // e a d - - - - p 1.822 + 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x03, 0x70, // o s t - - - - p 1.823 + 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x64, 0x65, // u t - - - - d e 1.824 + 0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x00, 0x05, // l e t e - - - - 1.825 + 0x74, 0x72, 0x61, 0x63, 0x65, 0x00, 0x00, 0x00, // t r a c e - - - 1.826 + 0x06, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x00, // - a c c e p t - 1.827 + 0x00, 0x00, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70, // - - - a c c e p 1.828 + 0x74, 0x2d, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, // t - c h a r s e 1.829 + 0x74, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x63, 0x63, // t - - - - a c c 1.830 + 0x65, 0x70, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, // e p t - e n c o 1.831 + 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x0f, // d i n g - - - - 1.832 + 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x6c, // a c c e p t - l 1.833 + 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00, // a n g u a g e - 1.834 + 0x00, 0x00, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70, // - - - a c c e p 1.835 + 0x74, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, // t - r a n g e s 1.836 + 0x00, 0x00, 0x00, 0x03, 0x61, 0x67, 0x65, 0x00, // - - - - a g e - 1.837 + 0x00, 0x00, 0x05, 0x61, 0x6c, 0x6c, 0x6f, 0x77, // - - - a l l o w 1.838 + 0x00, 0x00, 0x00, 0x0d, 0x61, 0x75, 0x74, 0x68, // - - - - a u t h 1.839 + 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, // o r i z a t i o 1.840 + 0x6e, 0x00, 0x00, 0x00, 0x0d, 0x63, 0x61, 0x63, // n - - - - c a c 1.841 + 0x68, 0x65, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72, // h e - c o n t r 1.842 + 0x6f, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x63, 0x6f, // o l - - - - c o 1.843 + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, // n n e c t i o n 1.844 + 0x00, 0x00, 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, // - - - - c o n t 1.845 + 0x65, 0x6e, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65, // e n t - b a s e 1.846 + 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, 0x6e, 0x74, // - - - - c o n t 1.847 + 0x65, 0x6e, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, // e n t - e n c o 1.848 + 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, // d i n g - - - - 1.849 + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, // c o n t e n t - 1.850 + 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, // l a n g u a g e 1.851 + 0x00, 0x00, 0x00, 0x0e, 0x63, 0x6f, 0x6e, 0x74, // - - - - c o n t 1.852 + 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, // e n t - l e n g 1.853 + 0x74, 0x68, 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, // t h - - - - c o 1.854 + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x6f, // n t e n t - l o 1.855 + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, // c a t i o n - - 1.856 + 0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, // - - c o n t e n 1.857 + 0x74, 0x2d, 0x6d, 0x64, 0x35, 0x00, 0x00, 0x00, // t - m d 5 - - - 1.858 + 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, // - c o n t e n t 1.859 + 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, // - r a n g e - - 1.860 + 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, // - - c o n t e n 1.861 + 0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x00, 0x00, // t - t y p e - - 1.862 + 0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x00, 0x00, // - - d a t e - - 1.863 + 0x00, 0x04, 0x65, 0x74, 0x61, 0x67, 0x00, 0x00, // - - e t a g - - 1.864 + 0x00, 0x06, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, // - - e x p e c t 1.865 + 0x00, 0x00, 0x00, 0x07, 0x65, 0x78, 0x70, 0x69, // - - - - e x p i 1.866 + 0x72, 0x65, 0x73, 0x00, 0x00, 0x00, 0x04, 0x66, // r e s - - - - f 1.867 + 0x72, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x68, // r o m - - - - h 1.868 + 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x08, 0x69, // o s t - - - - i 1.869 + 0x66, 0x2d, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, // f - m a t c h - 1.870 + 0x00, 0x00, 0x11, 0x69, 0x66, 0x2d, 0x6d, 0x6f, // - - - i f - m o 1.871 + 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2d, 0x73, // d i f i e d - s 1.872 + 0x69, 0x6e, 0x63, 0x65, 0x00, 0x00, 0x00, 0x0d, // i n c e - - - - 1.873 + 0x69, 0x66, 0x2d, 0x6e, 0x6f, 0x6e, 0x65, 0x2d, // i f - n o n e - 1.874 + 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, 0x00, 0x00, // m a t c h - - - 1.875 + 0x08, 0x69, 0x66, 0x2d, 0x72, 0x61, 0x6e, 0x67, // - i f - r a n g 1.876 + 0x65, 0x00, 0x00, 0x00, 0x13, 0x69, 0x66, 0x2d, // e - - - - i f - 1.877 + 0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, // u n m o d i f i 1.878 + 0x65, 0x64, 0x2d, 0x73, 0x69, 0x6e, 0x63, 0x65, // e d - s i n c e 1.879 + 0x00, 0x00, 0x00, 0x0d, 0x6c, 0x61, 0x73, 0x74, // - - - - l a s t 1.880 + 0x2d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, // - m o d i f i e 1.881 + 0x64, 0x00, 0x00, 0x00, 0x08, 0x6c, 0x6f, 0x63, // d - - - - l o c 1.882 + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, // a t i o n - - - 1.883 + 0x0c, 0x6d, 0x61, 0x78, 0x2d, 0x66, 0x6f, 0x72, // - m a x - f o r 1.884 + 0x77, 0x61, 0x72, 0x64, 0x73, 0x00, 0x00, 0x00, // w a r d s - - - 1.885 + 0x06, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x00, // - p r a g m a - 1.886 + 0x00, 0x00, 0x12, 0x70, 0x72, 0x6f, 0x78, 0x79, // - - - p r o x y 1.887 + 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, // - a u t h e n t 1.888 + 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, // i c a t e - - - 1.889 + 0x13, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2d, 0x61, // - p r o x y - a 1.890 + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, // u t h o r i z a 1.891 + 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x05, // t i o n - - - - 1.892 + 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, 0x00, // r a n g e - - - 1.893 + 0x07, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x72, // - r e f e r e r 1.894 + 0x00, 0x00, 0x00, 0x0b, 0x72, 0x65, 0x74, 0x72, // - - - - r e t r 1.895 + 0x79, 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, 0x00, // y - a f t e r - 1.896 + 0x00, 0x00, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, // - - - s e r v e 1.897 + 0x72, 0x00, 0x00, 0x00, 0x02, 0x74, 0x65, 0x00, // r - - - - t e - 1.898 + 0x00, 0x00, 0x07, 0x74, 0x72, 0x61, 0x69, 0x6c, // - - - t r a i l 1.899 + 0x65, 0x72, 0x00, 0x00, 0x00, 0x11, 0x74, 0x72, // e r - - - - t r 1.900 + 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x2d, 0x65, // a n s f e r - e 1.901 + 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00, // n c o d i n g - 1.902 + 0x00, 0x00, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61, // - - - u p g r a 1.903 + 0x64, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x73, // d e - - - - u s 1.904 + 0x65, 0x72, 0x2d, 0x61, 0x67, 0x65, 0x6e, 0x74, // e r - a g e n t 1.905 + 0x00, 0x00, 0x00, 0x04, 0x76, 0x61, 0x72, 0x79, // - - - - v a r y 1.906 + 0x00, 0x00, 0x00, 0x03, 0x76, 0x69, 0x61, 0x00, // - - - - v i a - 1.907 + 0x00, 0x00, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69, // - - - w a r n i 1.908 + 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, 0x77, 0x77, // n g - - - - w w 1.909 + 0x77, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, // w - a u t h e n 1.910 + 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, // t i c a t e - - 1.911 + 0x00, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, // - - m e t h o d 1.912 + 0x00, 0x00, 0x00, 0x03, 0x67, 0x65, 0x74, 0x00, // - - - - g e t - 1.913 + 0x00, 0x00, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, // - - - s t a t u 1.914 + 0x73, 0x00, 0x00, 0x00, 0x06, 0x32, 0x30, 0x30, // s - - - - 2 0 0 1.915 + 0x20, 0x4f, 0x4b, 0x00, 0x00, 0x00, 0x07, 0x76, // - O K - - - - v 1.916 + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x00, // e r s i o n - - 1.917 + 0x00, 0x08, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, // - - H T T P - 1 1.918 + 0x2e, 0x31, 0x00, 0x00, 0x00, 0x03, 0x75, 0x72, // - 1 - - - - u r 1.919 + 0x6c, 0x00, 0x00, 0x00, 0x06, 0x70, 0x75, 0x62, // l - - - - p u b 1.920 + 0x6c, 0x69, 0x63, 0x00, 0x00, 0x00, 0x0a, 0x73, // l i c - - - - s 1.921 + 0x65, 0x74, 0x2d, 0x63, 0x6f, 0x6f, 0x6b, 0x69, // e t - c o o k i 1.922 + 0x65, 0x00, 0x00, 0x00, 0x0a, 0x6b, 0x65, 0x65, // e - - - - k e e 1.923 + 0x70, 0x2d, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x00, // p - a l i v e - 1.924 + 0x00, 0x00, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, // - - - o r i g i 1.925 + 0x6e, 0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x32, // n 1 0 0 1 0 1 2 1.926 + 0x30, 0x31, 0x32, 0x30, 0x32, 0x32, 0x30, 0x35, // 0 1 2 0 2 2 0 5 1.927 + 0x32, 0x30, 0x36, 0x33, 0x30, 0x30, 0x33, 0x30, // 2 0 6 3 0 0 3 0 1.928 + 0x32, 0x33, 0x30, 0x33, 0x33, 0x30, 0x34, 0x33, // 2 3 0 3 3 0 4 3 1.929 + 0x30, 0x35, 0x33, 0x30, 0x36, 0x33, 0x30, 0x37, // 0 5 3 0 6 3 0 7 1.930 + 0x34, 0x30, 0x32, 0x34, 0x30, 0x35, 0x34, 0x30, // 4 0 2 4 0 5 4 0 1.931 + 0x36, 0x34, 0x30, 0x37, 0x34, 0x30, 0x38, 0x34, // 6 4 0 7 4 0 8 4 1.932 + 0x30, 0x39, 0x34, 0x31, 0x30, 0x34, 0x31, 0x31, // 0 9 4 1 0 4 1 1 1.933 + 0x34, 0x31, 0x32, 0x34, 0x31, 0x33, 0x34, 0x31, // 4 1 2 4 1 3 4 1 1.934 + 0x34, 0x34, 0x31, 0x35, 0x34, 0x31, 0x36, 0x34, // 4 4 1 5 4 1 6 4 1.935 + 0x31, 0x37, 0x35, 0x30, 0x32, 0x35, 0x30, 0x34, // 1 7 5 0 2 5 0 4 1.936 + 0x35, 0x30, 0x35, 0x32, 0x30, 0x33, 0x20, 0x4e, // 5 0 5 2 0 3 - N 1.937 + 0x6f, 0x6e, 0x2d, 0x41, 0x75, 0x74, 0x68, 0x6f, // o n - A u t h o 1.938 + 0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65, // r i t a t i v e 1.939 + 0x20, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, // - I n f o r m a 1.940 + 0x74, 0x69, 0x6f, 0x6e, 0x32, 0x30, 0x34, 0x20, // t i o n 2 0 4 - 1.941 + 0x4e, 0x6f, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x65, // N o - C o n t e 1.942 + 0x6e, 0x74, 0x33, 0x30, 0x31, 0x20, 0x4d, 0x6f, // n t 3 0 1 - M o 1.943 + 0x76, 0x65, 0x64, 0x20, 0x50, 0x65, 0x72, 0x6d, // v e d - P e r m 1.944 + 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x34, // a n e n t l y 4 1.945 + 0x30, 0x30, 0x20, 0x42, 0x61, 0x64, 0x20, 0x52, // 0 0 - B a d - R 1.946 + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x34, 0x30, // e q u e s t 4 0 1.947 + 0x31, 0x20, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68, // 1 - U n a u t h 1.948 + 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x34, 0x30, // o r i z e d 4 0 1.949 + 0x33, 0x20, 0x46, 0x6f, 0x72, 0x62, 0x69, 0x64, // 3 - F o r b i d 1.950 + 0x64, 0x65, 0x6e, 0x34, 0x30, 0x34, 0x20, 0x4e, // d e n 4 0 4 - N 1.951 + 0x6f, 0x74, 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, // o t - F o u n d 1.952 + 0x35, 0x30, 0x30, 0x20, 0x49, 0x6e, 0x74, 0x65, // 5 0 0 - I n t e 1.953 + 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x53, 0x65, 0x72, // r n a l - S e r 1.954 + 0x76, 0x65, 0x72, 0x20, 0x45, 0x72, 0x72, 0x6f, // v e r - E r r o 1.955 + 0x72, 0x35, 0x30, 0x31, 0x20, 0x4e, 0x6f, 0x74, // r 5 0 1 - N o t 1.956 + 0x20, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, // - I m p l e m e 1.957 + 0x6e, 0x74, 0x65, 0x64, 0x35, 0x30, 0x33, 0x20, // n t e d 5 0 3 - 1.958 + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, // S e r v i c e - 1.959 + 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, // U n a v a i l a 1.960 + 0x62, 0x6c, 0x65, 0x4a, 0x61, 0x6e, 0x20, 0x46, // b l e J a n - F 1.961 + 0x65, 0x62, 0x20, 0x4d, 0x61, 0x72, 0x20, 0x41, // e b - M a r - A 1.962 + 0x70, 0x72, 0x20, 0x4d, 0x61, 0x79, 0x20, 0x4a, // p r - M a y - J 1.963 + 0x75, 0x6e, 0x20, 0x4a, 0x75, 0x6c, 0x20, 0x41, // u n - J u l - A 1.964 + 0x75, 0x67, 0x20, 0x53, 0x65, 0x70, 0x74, 0x20, // u g - S e p t - 1.965 + 0x4f, 0x63, 0x74, 0x20, 0x4e, 0x6f, 0x76, 0x20, // O c t - N o v - 1.966 + 0x44, 0x65, 0x63, 0x20, 0x30, 0x30, 0x3a, 0x30, // D e c - 0 0 - 0 1.967 + 0x30, 0x3a, 0x30, 0x30, 0x20, 0x4d, 0x6f, 0x6e, // 0 - 0 0 - M o n 1.968 + 0x2c, 0x20, 0x54, 0x75, 0x65, 0x2c, 0x20, 0x57, // - - T u e - - W 1.969 + 0x65, 0x64, 0x2c, 0x20, 0x54, 0x68, 0x75, 0x2c, // e d - - T h u - 1.970 + 0x20, 0x46, 0x72, 0x69, 0x2c, 0x20, 0x53, 0x61, // - F r i - - S a 1.971 + 0x74, 0x2c, 0x20, 0x53, 0x75, 0x6e, 0x2c, 0x20, // t - - S u n - - 1.972 + 0x47, 0x4d, 0x54, 0x63, 0x68, 0x75, 0x6e, 0x6b, // G M T c h u n k 1.973 + 0x65, 0x64, 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, // e d - t e x t - 1.974 + 0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x69, 0x6d, 0x61, // h t m l - i m a 1.975 + 0x67, 0x65, 0x2f, 0x70, 0x6e, 0x67, 0x2c, 0x69, // g e - p n g - i 1.976 + 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x6a, 0x70, 0x67, // m a g e - j p g 1.977 + 0x2c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, // - i m a g e - g 1.978 + 0x69, 0x66, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, // i f - a p p l i 1.979 + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, // c a t i o n - x 1.980 + 0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, // m l - a p p l i 1.981 + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, // c a t i o n - x 1.982 + 0x68, 0x74, 0x6d, 0x6c, 0x2b, 0x78, 0x6d, 0x6c, // h t m l - x m l 1.983 + 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, // - t e x t - p l 1.984 + 0x61, 0x69, 0x6e, 0x2c, 0x74, 0x65, 0x78, 0x74, // a i n - t e x t 1.985 + 0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, // - j a v a s c r 1.986 + 0x69, 0x70, 0x74, 0x2c, 0x70, 0x75, 0x62, 0x6c, // i p t - p u b l 1.987 + 0x69, 0x63, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, // i c p r i v a t 1.988 + 0x65, 0x6d, 0x61, 0x78, 0x2d, 0x61, 0x67, 0x65, // e m a x - a g e 1.989 + 0x3d, 0x67, 0x7a, 0x69, 0x70, 0x2c, 0x64, 0x65, // - g z i p - d e 1.990 + 0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64, // f l a t e - s d 1.991 + 0x63, 0x68, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, // c h c h a r s e 1.992 + 0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x63, // t - u t f - 8 c 1.993 + 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x69, // h a r s e t - i 1.994 + 0x73, 0x6f, 0x2d, 0x38, 0x38, 0x35, 0x39, 0x2d, // s o - 8 8 5 9 - 1.995 + 0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x2c, 0x2a, // 1 - u t f - - - 1.996 + 0x2c, 0x65, 0x6e, 0x71, 0x3d, 0x30, 0x2e // - e n q - 0 - 1.997 +}; 1.998 + 1.999 +// This can be called N times.. 1 for syn_reply and 0->N for headers 1.1000 +nsresult 1.1001 +SpdyStream3::Uncompress(z_stream *context, 1.1002 + char *blockStart, 1.1003 + uint32_t blockLen) 1.1004 +{ 1.1005 + mDecompressedBytes += blockLen; 1.1006 + 1.1007 + context->avail_in = blockLen; 1.1008 + context->next_in = reinterpret_cast<unsigned char *>(blockStart); 1.1009 + bool triedDictionary = false; 1.1010 + 1.1011 + do { 1.1012 + context->next_out = 1.1013 + reinterpret_cast<unsigned char *>(mDecompressBuffer.get()) + 1.1014 + mDecompressBufferUsed; 1.1015 + context->avail_out = mDecompressBufferSize - mDecompressBufferUsed; 1.1016 + int zlib_rv = inflate(context, Z_NO_FLUSH); 1.1017 + 1.1018 + if (zlib_rv == Z_NEED_DICT) { 1.1019 + if (triedDictionary) { 1.1020 + LOG3(("SpdySession3::Uncompress %p Dictionary Error\n", this)); 1.1021 + return NS_ERROR_ILLEGAL_VALUE; 1.1022 + } 1.1023 + 1.1024 + triedDictionary = true; 1.1025 + inflateSetDictionary(context, kDictionary, sizeof(kDictionary)); 1.1026 + } 1.1027 + 1.1028 + if (zlib_rv == Z_DATA_ERROR) 1.1029 + return NS_ERROR_ILLEGAL_VALUE; 1.1030 + 1.1031 + if (zlib_rv == Z_MEM_ERROR) 1.1032 + return NS_ERROR_FAILURE; 1.1033 + 1.1034 + // zlib's inflate() decreases context->avail_out by the amount it places 1.1035 + // in the output buffer 1.1036 + 1.1037 + mDecompressBufferUsed += mDecompressBufferSize - mDecompressBufferUsed - 1.1038 + context->avail_out; 1.1039 + 1.1040 + // When there is no more output room, but input still available then 1.1041 + // increase the output space 1.1042 + if (zlib_rv == Z_OK && 1.1043 + !context->avail_out && context->avail_in) { 1.1044 + LOG3(("SpdyStream3::Uncompress %p Large Headers - so far %d", 1.1045 + this, mDecompressBufferSize)); 1.1046 + SpdySession3::EnsureBuffer(mDecompressBuffer, 1.1047 + mDecompressBufferSize + 4096, 1.1048 + mDecompressBufferUsed, 1.1049 + mDecompressBufferSize); 1.1050 + } 1.1051 + } 1.1052 + while (context->avail_in); 1.1053 + return NS_OK; 1.1054 +} 1.1055 + 1.1056 +// mDecompressBuffer contains 0 to N uncompressed Name/Value Header blocks 1.1057 +nsresult 1.1058 +SpdyStream3::FindHeader(nsCString name, 1.1059 + nsDependentCSubstring &value) 1.1060 +{ 1.1061 + const unsigned char *nvpair = reinterpret_cast<unsigned char *> 1.1062 + (mDecompressBuffer.get()) + 4; 1.1063 + const unsigned char *lastHeaderByte = reinterpret_cast<unsigned char *> 1.1064 + (mDecompressBuffer.get()) + mDecompressBufferUsed; 1.1065 + if (lastHeaderByte < nvpair) 1.1066 + return NS_ERROR_ILLEGAL_VALUE; 1.1067 + 1.1068 + do { 1.1069 + uint32_t numPairs = NetworkEndian::readUint32(nvpair - 1 * sizeof(uint32_t)); 1.1070 + 1.1071 + for (uint32_t index = 0; index < numPairs; ++index) { 1.1072 + if (lastHeaderByte < nvpair + 4) 1.1073 + return NS_ERROR_ILLEGAL_VALUE; 1.1074 + uint32_t nameLen = (nvpair[0] << 24) + (nvpair[1] << 16) + 1.1075 + (nvpair[2] << 8) + nvpair[3]; 1.1076 + if (lastHeaderByte < nvpair + 4 + nameLen) 1.1077 + return NS_ERROR_ILLEGAL_VALUE; 1.1078 + nsDependentCSubstring nameString = 1.1079 + Substring(reinterpret_cast<const char *>(nvpair) + 4, 1.1080 + reinterpret_cast<const char *>(nvpair) + 4 + nameLen); 1.1081 + if (lastHeaderByte < nvpair + 8 + nameLen) 1.1082 + return NS_ERROR_ILLEGAL_VALUE; 1.1083 + uint32_t valueLen = (nvpair[4 + nameLen] << 24) + (nvpair[5 + nameLen] << 16) + 1.1084 + (nvpair[6 + nameLen] << 8) + nvpair[7 + nameLen]; 1.1085 + if (lastHeaderByte < nvpair + 8 + nameLen + valueLen) 1.1086 + return NS_ERROR_ILLEGAL_VALUE; 1.1087 + if (nameString.Equals(name)) { 1.1088 + value.Assign(((char *)nvpair) + 8 + nameLen, valueLen); 1.1089 + return NS_OK; 1.1090 + } 1.1091 + 1.1092 + // that pair didn't match - try the next one in this block 1.1093 + nvpair += 8 + nameLen + valueLen; 1.1094 + } 1.1095 + 1.1096 + // move to the next name/value header block (if there is one) - the 1.1097 + // first pair is offset 4 bytes into it 1.1098 + nvpair += 4; 1.1099 + } while (lastHeaderByte >= nvpair); 1.1100 + 1.1101 + return NS_ERROR_NOT_AVAILABLE; 1.1102 +} 1.1103 + 1.1104 +// ConvertHeaders is used to convert the response headers 1.1105 +// in a syn_reply or in 0..N headers frames that follow it into 1.1106 +// HTTP/1 format 1.1107 +nsresult 1.1108 +SpdyStream3::ConvertHeaders(nsACString &aHeadersOut) 1.1109 +{ 1.1110 + // :status and :version are required. 1.1111 + nsDependentCSubstring status, version; 1.1112 + nsresult rv = FindHeader(NS_LITERAL_CSTRING(":status"), 1.1113 + status); 1.1114 + if (NS_FAILED(rv)) 1.1115 + return (rv == NS_ERROR_NOT_AVAILABLE) ? NS_ERROR_ILLEGAL_VALUE : rv; 1.1116 + 1.1117 + rv = FindHeader(NS_LITERAL_CSTRING(":version"), 1.1118 + version); 1.1119 + if (NS_FAILED(rv)) 1.1120 + return (rv == NS_ERROR_NOT_AVAILABLE) ? NS_ERROR_ILLEGAL_VALUE : rv; 1.1121 + 1.1122 + if (mDecompressedBytes && mDecompressBufferUsed) { 1.1123 + Telemetry::Accumulate(Telemetry::SPDY_SYN_REPLY_SIZE, mDecompressedBytes); 1.1124 + uint32_t ratio = 1.1125 + mDecompressedBytes * 100 / mDecompressBufferUsed; 1.1126 + Telemetry::Accumulate(Telemetry::SPDY_SYN_REPLY_RATIO, ratio); 1.1127 + } 1.1128 + 1.1129 + aHeadersOut.Truncate(); 1.1130 + aHeadersOut.SetCapacity(mDecompressBufferUsed + 64); 1.1131 + 1.1132 + // Connection, Keep-Alive and chunked transfer encodings are to be 1.1133 + // removed. 1.1134 + 1.1135 + // Content-Length is 'advisory'.. we will not strip it because it can 1.1136 + // create UI feedback. 1.1137 + 1.1138 + aHeadersOut.Append(version); 1.1139 + aHeadersOut.Append(NS_LITERAL_CSTRING(" ")); 1.1140 + aHeadersOut.Append(status); 1.1141 + aHeadersOut.Append(NS_LITERAL_CSTRING("\r\n")); 1.1142 + 1.1143 + const unsigned char *nvpair = reinterpret_cast<unsigned char *> 1.1144 + (mDecompressBuffer.get()) + 4; 1.1145 + const unsigned char *lastHeaderByte = reinterpret_cast<unsigned char *> 1.1146 + (mDecompressBuffer.get()) + mDecompressBufferUsed; 1.1147 + if (lastHeaderByte < nvpair) 1.1148 + return NS_ERROR_ILLEGAL_VALUE; 1.1149 + 1.1150 + do { 1.1151 + uint32_t numPairs = NetworkEndian::readUint32(nvpair - 1 * sizeof(uint32_t)); 1.1152 + 1.1153 + for (uint32_t index = 0; index < numPairs; ++index) { 1.1154 + if (lastHeaderByte < nvpair + 4) 1.1155 + return NS_ERROR_ILLEGAL_VALUE; 1.1156 + 1.1157 + uint32_t nameLen = (nvpair[0] << 24) + (nvpair[1] << 16) + 1.1158 + (nvpair[2] << 8) + nvpair[3]; 1.1159 + if (lastHeaderByte < nvpair + 4 + nameLen) 1.1160 + return NS_ERROR_ILLEGAL_VALUE; 1.1161 + 1.1162 + nsDependentCSubstring nameString = 1.1163 + Substring(reinterpret_cast<const char *>(nvpair) + 4, 1.1164 + reinterpret_cast<const char *>(nvpair) + 4 + nameLen); 1.1165 + 1.1166 + if (lastHeaderByte < nvpair + 8 + nameLen) 1.1167 + return NS_ERROR_ILLEGAL_VALUE; 1.1168 + 1.1169 + // Look for illegal characters in the nameString. 1.1170 + // This includes upper case characters and nulls (as they will 1.1171 + // break the fixup-nulls-in-value-string algorithm) 1.1172 + // Look for upper case characters in the name. They are illegal. 1.1173 + for (char *cPtr = nameString.BeginWriting(); 1.1174 + cPtr && cPtr < nameString.EndWriting(); 1.1175 + ++cPtr) { 1.1176 + if (*cPtr <= 'Z' && *cPtr >= 'A') { 1.1177 + nsCString toLog(nameString); 1.1178 + 1.1179 + LOG3(("SpdyStream3::ConvertHeaders session=%p stream=%p " 1.1180 + "upper case response header found. [%s]\n", 1.1181 + mSession, this, toLog.get())); 1.1182 + 1.1183 + return NS_ERROR_ILLEGAL_VALUE; 1.1184 + } 1.1185 + 1.1186 + // check for null characters 1.1187 + if (*cPtr == '\0') 1.1188 + return NS_ERROR_ILLEGAL_VALUE; 1.1189 + } 1.1190 + 1.1191 + // HTTP Chunked responses are not legal over spdy. We do not need 1.1192 + // to look for chunked specifically because it is the only HTTP 1.1193 + // allowed default encoding and we did not negotiate further encodings 1.1194 + // via TE 1.1195 + if (nameString.Equals(NS_LITERAL_CSTRING("transfer-encoding"))) { 1.1196 + LOG3(("SpdyStream3::ConvertHeaders session=%p stream=%p " 1.1197 + "transfer-encoding found. Chunked is invalid and no TE sent.", 1.1198 + mSession, this)); 1.1199 + 1.1200 + return NS_ERROR_ILLEGAL_VALUE; 1.1201 + } 1.1202 + 1.1203 + uint32_t valueLen = 1.1204 + (nvpair[4 + nameLen] << 24) + (nvpair[5 + nameLen] << 16) + 1.1205 + (nvpair[6 + nameLen] << 8) + nvpair[7 + nameLen]; 1.1206 + 1.1207 + if (lastHeaderByte < nvpair + 8 + nameLen + valueLen) 1.1208 + return NS_ERROR_ILLEGAL_VALUE; 1.1209 + 1.1210 + // spdy transport level headers shouldn't be gatewayed into http/1 1.1211 + if (!nameString.IsEmpty() && nameString[0] != ':' && 1.1212 + !nameString.Equals(NS_LITERAL_CSTRING("connection")) && 1.1213 + !nameString.Equals(NS_LITERAL_CSTRING("keep-alive"))) { 1.1214 + nsDependentCSubstring valueString = 1.1215 + Substring(reinterpret_cast<const char *>(nvpair) + 8 + nameLen, 1.1216 + reinterpret_cast<const char *>(nvpair) + 8 + nameLen + 1.1217 + valueLen); 1.1218 + 1.1219 + aHeadersOut.Append(nameString); 1.1220 + aHeadersOut.Append(NS_LITERAL_CSTRING(": ")); 1.1221 + 1.1222 + // expand nullptr bytes in the value string 1.1223 + for (char *cPtr = valueString.BeginWriting(); 1.1224 + cPtr && cPtr < valueString.EndWriting(); 1.1225 + ++cPtr) { 1.1226 + if (*cPtr != 0) { 1.1227 + aHeadersOut.Append(*cPtr); 1.1228 + continue; 1.1229 + } 1.1230 + 1.1231 + // NULLs are really "\r\nhdr: " 1.1232 + aHeadersOut.Append(NS_LITERAL_CSTRING("\r\n")); 1.1233 + aHeadersOut.Append(nameString); 1.1234 + aHeadersOut.Append(NS_LITERAL_CSTRING(": ")); 1.1235 + } 1.1236 + 1.1237 + aHeadersOut.Append(NS_LITERAL_CSTRING("\r\n")); 1.1238 + } 1.1239 + // move to the next name/value pair in this block 1.1240 + nvpair += 8 + nameLen + valueLen; 1.1241 + } 1.1242 + 1.1243 + // move to the next name/value header block (if there is one) - the 1.1244 + // first pair is offset 4 bytes into it 1.1245 + nvpair += 4; 1.1246 + } while (lastHeaderByte >= nvpair); 1.1247 + 1.1248 + aHeadersOut.Append(NS_LITERAL_CSTRING("X-Firefox-Spdy: 3\r\n\r\n")); 1.1249 + LOG (("decoded response headers are:\n%s", 1.1250 + aHeadersOut.BeginReading())); 1.1251 + 1.1252 + // The spdy formatted buffer isnt needed anymore - free it up 1.1253 + mDecompressBuffer = nullptr; 1.1254 + mDecompressBufferSize = 0; 1.1255 + mDecompressBufferUsed = 0; 1.1256 + 1.1257 + return NS_OK; 1.1258 +} 1.1259 + 1.1260 +void 1.1261 +SpdyStream3::ExecuteCompress(uint32_t flushMode) 1.1262 +{ 1.1263 + // Expect mZlib->avail_in and mZlib->next_in to be set. 1.1264 + // Append the compressed version of next_in to mTxInlineFrame 1.1265 + 1.1266 + do 1.1267 + { 1.1268 + uint32_t avail = mTxInlineFrameSize - mTxInlineFrameUsed; 1.1269 + if (avail < 1) { 1.1270 + SpdySession3::EnsureBuffer(mTxInlineFrame, 1.1271 + mTxInlineFrameSize + 2000, 1.1272 + mTxInlineFrameUsed, 1.1273 + mTxInlineFrameSize); 1.1274 + avail = mTxInlineFrameSize - mTxInlineFrameUsed; 1.1275 + } 1.1276 + 1.1277 + mZlib->next_out = mTxInlineFrame + mTxInlineFrameUsed; 1.1278 + mZlib->avail_out = avail; 1.1279 + deflate(mZlib, flushMode); 1.1280 + mTxInlineFrameUsed += avail - mZlib->avail_out; 1.1281 + } while (mZlib->avail_in > 0 || !mZlib->avail_out); 1.1282 +} 1.1283 + 1.1284 +void 1.1285 +SpdyStream3::CompressToFrame(uint32_t data) 1.1286 +{ 1.1287 + // convert the data to 4 byte network byte order and write that 1.1288 + // to the compressed stream 1.1289 + unsigned char databuf[sizeof(data)]; 1.1290 + NetworkEndian::writeUint32(databuf, data); 1.1291 + 1.1292 + mZlib->next_in = databuf; 1.1293 + mZlib->avail_in = sizeof(databuf); 1.1294 + ExecuteCompress(Z_NO_FLUSH); 1.1295 +} 1.1296 + 1.1297 + 1.1298 +void 1.1299 +SpdyStream3::CompressToFrame(const char *data, uint32_t len) 1.1300 +{ 1.1301 + // Format calls for a network ordered 32 bit length 1.1302 + // followed by the utf8 string 1.1303 + 1.1304 + unsigned char lenbuf[sizeof(len)]; 1.1305 + NetworkEndian::writeUint32(lenbuf, len); 1.1306 + 1.1307 + // write out the length 1.1308 + mZlib->next_in = lenbuf; 1.1309 + mZlib->avail_in = sizeof(lenbuf); 1.1310 + ExecuteCompress(Z_NO_FLUSH); 1.1311 + 1.1312 + // write out the data 1.1313 + mZlib->next_in = (unsigned char *)data; 1.1314 + mZlib->avail_in = len; 1.1315 + ExecuteCompress(Z_NO_FLUSH); 1.1316 +} 1.1317 + 1.1318 +void 1.1319 +SpdyStream3::CompressFlushFrame() 1.1320 +{ 1.1321 + mZlib->next_in = (unsigned char *) ""; 1.1322 + mZlib->avail_in = 0; 1.1323 + ExecuteCompress(Z_SYNC_FLUSH); 1.1324 +} 1.1325 + 1.1326 +void 1.1327 +SpdyStream3::Close(nsresult reason) 1.1328 +{ 1.1329 + mTransaction->Close(reason); 1.1330 +} 1.1331 + 1.1332 +void 1.1333 +SpdyStream3::UpdateRemoteWindow(int32_t delta) 1.1334 +{ 1.1335 + int64_t oldRemoteWindow = mRemoteWindow; 1.1336 + mRemoteWindow += delta; 1.1337 + if (oldRemoteWindow <= 0 && mRemoteWindow > 0) { 1.1338 + // the window has been opened :) 1.1339 + mSession->TransactionHasDataToWrite(this); 1.1340 + } 1.1341 +} 1.1342 + 1.1343 +//----------------------------------------------------------------------------- 1.1344 +// nsAHttpSegmentReader 1.1345 +//----------------------------------------------------------------------------- 1.1346 + 1.1347 +nsresult 1.1348 +SpdyStream3::OnReadSegment(const char *buf, 1.1349 + uint32_t count, 1.1350 + uint32_t *countRead) 1.1351 +{ 1.1352 + LOG3(("SpdyStream3::OnReadSegment %p count=%d state=%x", 1.1353 + this, count, mUpstreamState)); 1.1354 + 1.1355 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.1356 + MOZ_ASSERT(mSegmentReader, "OnReadSegment with null mSegmentReader"); 1.1357 + 1.1358 + nsresult rv = NS_ERROR_UNEXPECTED; 1.1359 + uint32_t dataLength; 1.1360 + 1.1361 + switch (mUpstreamState) { 1.1362 + case GENERATING_SYN_STREAM: 1.1363 + // The buffer is the HTTP request stream, including at least part of the 1.1364 + // HTTP request header. This state's job is to build a SYN_STREAM frame 1.1365 + // from the header information. count is the number of http bytes available 1.1366 + // (which may include more than the header), and in countRead we return 1.1367 + // the number of those bytes that we consume (i.e. the portion that are 1.1368 + // header bytes) 1.1369 + 1.1370 + rv = ParseHttpRequestHeaders(buf, count, countRead); 1.1371 + if (NS_FAILED(rv)) 1.1372 + return rv; 1.1373 + LOG3(("ParseHttpRequestHeaders %p used %d of %d. complete = %d", 1.1374 + this, *countRead, count, mSynFrameComplete)); 1.1375 + if (mSynFrameComplete) { 1.1376 + AdjustInitialWindow(); 1.1377 + rv = TransmitFrame(nullptr, nullptr, true); 1.1378 + if (rv == NS_BASE_STREAM_WOULD_BLOCK) { 1.1379 + // this can't happen 1.1380 + MOZ_ASSERT(false, "Transmit Frame SYN_FRAME must at least buffer data"); 1.1381 + rv = NS_ERROR_UNEXPECTED; 1.1382 + } 1.1383 + 1.1384 + ChangeState(GENERATING_REQUEST_BODY); 1.1385 + break; 1.1386 + } 1.1387 + MOZ_ASSERT(*countRead == count, "Header parsing not complete but unused data"); 1.1388 + break; 1.1389 + 1.1390 + case GENERATING_REQUEST_BODY: 1.1391 + if (mRemoteWindow <= 0) { 1.1392 + *countRead = 0; 1.1393 + LOG3(("SpdyStream3 this=%p, id 0x%X request body suspended because " 1.1394 + "remote window is %d.\n", this, mStreamID, mRemoteWindow)); 1.1395 + mBlockedOnRwin = true; 1.1396 + return NS_BASE_STREAM_WOULD_BLOCK; 1.1397 + } 1.1398 + mBlockedOnRwin = false; 1.1399 + 1.1400 + dataLength = std::min(count, mChunkSize); 1.1401 + 1.1402 + if (dataLength > mRemoteWindow) 1.1403 + dataLength = static_cast<uint32_t>(mRemoteWindow); 1.1404 + 1.1405 + LOG3(("SpdyStream3 this=%p id 0x%X remote window is %d. Chunk is %d\n", 1.1406 + this, mStreamID, mRemoteWindow, dataLength)); 1.1407 + mRemoteWindow -= dataLength; 1.1408 + 1.1409 + LOG3(("SpdyStream3 %p id %x request len remaining %d, " 1.1410 + "count avail %d, chunk used %d", 1.1411 + this, mStreamID, mRequestBodyLenRemaining, count, dataLength)); 1.1412 + if (dataLength > mRequestBodyLenRemaining) 1.1413 + return NS_ERROR_UNEXPECTED; 1.1414 + mRequestBodyLenRemaining -= dataLength; 1.1415 + GenerateDataFrameHeader(dataLength, !mRequestBodyLenRemaining); 1.1416 + ChangeState(SENDING_REQUEST_BODY); 1.1417 + // NO BREAK 1.1418 + 1.1419 + case SENDING_REQUEST_BODY: 1.1420 + MOZ_ASSERT(mTxInlineFrameUsed, "OnReadSegment Send Data Header 0b"); 1.1421 + rv = TransmitFrame(buf, countRead, false); 1.1422 + MOZ_ASSERT(NS_FAILED(rv) || !mTxInlineFrameUsed, 1.1423 + "Transmit Frame should be all or nothing"); 1.1424 + 1.1425 + LOG3(("TransmitFrame() rv=%x returning %d data bytes. " 1.1426 + "Header is %d Body is %d.", 1.1427 + rv, *countRead, mTxInlineFrameUsed, mTxStreamFrameSize)); 1.1428 + 1.1429 + // normalize a partial write with a WOULD_BLOCK into just a partial write 1.1430 + // as some code will take WOULD_BLOCK to mean an error with nothing 1.1431 + // written (e.g. nsHttpTransaction::ReadRequestSegment() 1.1432 + if (rv == NS_BASE_STREAM_WOULD_BLOCK && *countRead) 1.1433 + rv = NS_OK; 1.1434 + 1.1435 + // If that frame was all sent, look for another one 1.1436 + if (!mTxInlineFrameUsed) 1.1437 + ChangeState(GENERATING_REQUEST_BODY); 1.1438 + break; 1.1439 + 1.1440 + case SENDING_FIN_STREAM: 1.1441 + MOZ_ASSERT(false, "resuming partial fin stream out of OnReadSegment"); 1.1442 + break; 1.1443 + 1.1444 + default: 1.1445 + MOZ_ASSERT(false, "SpdyStream3::OnReadSegment non-write state"); 1.1446 + break; 1.1447 + } 1.1448 + 1.1449 + return rv; 1.1450 +} 1.1451 + 1.1452 +//----------------------------------------------------------------------------- 1.1453 +// nsAHttpSegmentWriter 1.1454 +//----------------------------------------------------------------------------- 1.1455 + 1.1456 +nsresult 1.1457 +SpdyStream3::OnWriteSegment(char *buf, 1.1458 + uint32_t count, 1.1459 + uint32_t *countWritten) 1.1460 +{ 1.1461 + LOG3(("SpdyStream3::OnWriteSegment %p count=%d state=%x 0x%X\n", 1.1462 + this, count, mUpstreamState, mStreamID)); 1.1463 + 1.1464 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.1465 + MOZ_ASSERT(mSegmentWriter); 1.1466 + 1.1467 + if (!mPushSource) 1.1468 + return mSegmentWriter->OnWriteSegment(buf, count, countWritten); 1.1469 + 1.1470 + nsresult rv; 1.1471 + rv = mPushSource->GetBufferedData(buf, count, countWritten); 1.1472 + if (NS_FAILED(rv)) 1.1473 + return rv; 1.1474 + 1.1475 + mSession->ConnectPushedStream(this); 1.1476 + return NS_OK; 1.1477 +} 1.1478 + 1.1479 +} // namespace mozilla::net 1.1480 +} // namespace mozilla 1.1481 +