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