Thu, 15 Jan 2015 21:03:48 +0100
Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 ts=8 et tw=80 : */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 // HttpLog.h should generally be included first
8 #include "HttpLog.h"
10 // Log on level :5, instead of default :4.
11 #undef LOG
12 #define LOG(args) LOG5(args)
13 #undef LOG_ENABLED
14 #define LOG_ENABLED() LOG5_ENABLED()
16 #include "mozilla/Telemetry.h"
17 #include "nsAlgorithm.h"
18 #include "nsHttp.h"
19 #include "nsHttpHandler.h"
20 #include "nsHttpRequestHead.h"
21 #include "nsISocketTransport.h"
22 #include "nsISupportsPriority.h"
23 #include "prnetdb.h"
24 #include "SpdyPush31.h"
25 #include "SpdySession31.h"
26 #include "SpdyStream31.h"
28 #include <algorithm>
30 #ifdef DEBUG
31 // defined by the socket transport service while active
32 extern PRThread *gSocketThread;
33 #endif
35 namespace mozilla {
36 namespace net {
38 SpdyStream31::SpdyStream31(nsAHttpTransaction *httpTransaction,
39 SpdySession31 *spdySession,
40 int32_t priority)
41 : mStreamID(0),
42 mSession(spdySession),
43 mUpstreamState(GENERATING_SYN_STREAM),
44 mSynFrameComplete(0),
45 mSentFinOnData(0),
46 mTransaction(httpTransaction),
47 mSocketTransport(spdySession->SocketTransport()),
48 mSegmentReader(nullptr),
49 mSegmentWriter(nullptr),
50 mChunkSize(spdySession->SendingChunkSize()),
51 mRequestBlockedOnRead(0),
52 mRecvdFin(0),
53 mFullyOpen(0),
54 mSentWaitingFor(0),
55 mReceivedData(0),
56 mSetTCPSocketBuffer(0),
57 mTxInlineFrameSize(SpdySession31::kDefaultBufferSize),
58 mTxInlineFrameUsed(0),
59 mTxStreamFrameSize(0),
60 mZlib(spdySession->UpstreamZlib()),
61 mDecompressBufferSize(SpdySession31::kDefaultBufferSize),
62 mDecompressBufferUsed(0),
63 mDecompressedBytes(0),
64 mRequestBodyLenRemaining(0),
65 mPriority(priority),
66 mLocalUnacked(0),
67 mBlockedOnRwin(false),
68 mTotalSent(0),
69 mTotalRead(0),
70 mPushSource(nullptr)
71 {
72 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
74 LOG3(("SpdyStream31::SpdyStream31 %p", this));
76 mRemoteWindow = spdySession->GetServerInitialStreamWindow();
77 mLocalWindow = spdySession->PushAllowance();
79 mTxInlineFrame = new uint8_t[mTxInlineFrameSize];
80 mDecompressBuffer = new char[mDecompressBufferSize];
81 }
83 SpdyStream31::~SpdyStream31()
84 {
85 mStreamID = SpdySession31::kDeadStreamID;
86 }
88 // ReadSegments() is used to write data down the socket. Generally, HTTP
89 // request data is pulled from the approriate transaction and
90 // converted to SPDY data. Sometimes control data like a window-update is
91 // generated instead.
93 nsresult
94 SpdyStream31::ReadSegments(nsAHttpSegmentReader *reader,
95 uint32_t count,
96 uint32_t *countRead)
97 {
98 LOG3(("SpdyStream31 %p ReadSegments reader=%p count=%d state=%x",
99 this, reader, count, mUpstreamState));
101 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
103 nsresult rv = NS_ERROR_UNEXPECTED;
104 mRequestBlockedOnRead = 0;
106 // avoid runt chunks if possible by anticipating
107 // full data frames
108 if (count > (mChunkSize + 8)) {
109 uint32_t numchunks = count / (mChunkSize + 8);
110 count = numchunks * (mChunkSize + 8);
111 }
113 switch (mUpstreamState) {
114 case GENERATING_SYN_STREAM:
115 case GENERATING_REQUEST_BODY:
116 case SENDING_REQUEST_BODY:
117 // Call into the HTTP Transaction to generate the HTTP request
118 // stream. That stream will show up in OnReadSegment().
119 mSegmentReader = reader;
120 rv = mTransaction->ReadSegments(this, count, countRead);
121 mSegmentReader = nullptr;
123 // Check to see if the transaction's request could be written out now.
124 // If not, mark the stream for callback when writing can proceed.
125 if (NS_SUCCEEDED(rv) &&
126 mUpstreamState == GENERATING_SYN_STREAM &&
127 !mSynFrameComplete)
128 mSession->TransactionHasDataToWrite(this);
130 // mTxinlineFrameUsed represents any queued un-sent frame. It might
131 // be 0 if there is no such frame, which is not a gurantee that we
132 // don't have more request body to send - just that any data that was
133 // sent comprised a complete SPDY frame. Likewise, a non 0 value is
134 // a queued, but complete, spdy frame length.
136 // Mark that we are blocked on read if the http transaction needs to
137 // provide more of the request message body and there is nothing queued
138 // for writing
139 if (rv == NS_BASE_STREAM_WOULD_BLOCK && !mTxInlineFrameUsed)
140 mRequestBlockedOnRead = 1;
142 // If the sending flow control window is open (!mBlockedOnRwin) then
143 // continue sending the request
144 if (!mBlockedOnRwin &&
145 !mTxInlineFrameUsed && NS_SUCCEEDED(rv) && (!*countRead)) {
146 LOG3(("SpdyStream31::ReadSegments %p 0x%X: Sending request data complete, "
147 "mUpstreamState=%x",this, mStreamID, mUpstreamState));
148 if (mSentFinOnData) {
149 ChangeState(UPSTREAM_COMPLETE);
150 }
151 else {
152 GenerateDataFrameHeader(0, true);
153 ChangeState(SENDING_FIN_STREAM);
154 mSession->TransactionHasDataToWrite(this);
155 rv = NS_BASE_STREAM_WOULD_BLOCK;
156 }
157 }
158 break;
160 case SENDING_FIN_STREAM:
161 // We were trying to send the FIN-STREAM but were blocked from
162 // sending it out - try again.
163 if (!mSentFinOnData) {
164 mSegmentReader = reader;
165 rv = TransmitFrame(nullptr, nullptr, false);
166 mSegmentReader = nullptr;
167 MOZ_ASSERT(NS_FAILED(rv) || !mTxInlineFrameUsed,
168 "Transmit Frame should be all or nothing");
169 if (NS_SUCCEEDED(rv))
170 ChangeState(UPSTREAM_COMPLETE);
171 }
172 else {
173 rv = NS_OK;
174 mTxInlineFrameUsed = 0; // cancel fin data packet
175 ChangeState(UPSTREAM_COMPLETE);
176 }
178 *countRead = 0;
180 // don't change OK to WOULD BLOCK. we are really done sending if OK
181 break;
183 case UPSTREAM_COMPLETE:
184 *countRead = 0;
185 rv = NS_OK;
186 break;
188 default:
189 MOZ_ASSERT(false, "SpdyStream31::ReadSegments unknown state");
190 break;
191 }
193 return rv;
194 }
196 // WriteSegments() is used to read data off the socket. Generally this is
197 // just the SPDY frame header and from there the appropriate SPDYStream
198 // is identified from the Stream-ID. The http transaction associated with
199 // that read then pulls in the data directly.
201 nsresult
202 SpdyStream31::WriteSegments(nsAHttpSegmentWriter *writer,
203 uint32_t count,
204 uint32_t *countWritten)
205 {
206 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
207 MOZ_ASSERT(!mSegmentWriter, "segment writer in progress");
209 LOG3(("SpdyStream31::WriteSegments %p count=%d state=%x",
210 this, count, mUpstreamState));
212 mSegmentWriter = writer;
213 nsresult rv = mTransaction->WriteSegments(this, count, countWritten);
214 mSegmentWriter = nullptr;
216 return rv;
217 }
219 PLDHashOperator
220 SpdyStream31::hdrHashEnumerate(const nsACString &key,
221 nsAutoPtr<nsCString> &value,
222 void *closure)
223 {
224 SpdyStream31 *self = static_cast<SpdyStream31 *>(closure);
226 self->CompressToFrame(key);
227 self->CompressToFrame(value.get());
228 return PL_DHASH_NEXT;
229 }
231 void
232 SpdyStream31::CreatePushHashKey(const nsCString &scheme,
233 const nsCString &hostHeader,
234 uint64_t serial,
235 const nsCSubstring &pathInfo,
236 nsCString &outOrigin,
237 nsCString &outKey)
238 {
239 outOrigin = scheme;
240 outOrigin.Append(NS_LITERAL_CSTRING("://"));
241 outOrigin.Append(hostHeader);
243 outKey = outOrigin;
244 outKey.Append(NS_LITERAL_CSTRING("/[spdy3_1."));
245 outKey.AppendInt(serial);
246 outKey.Append(NS_LITERAL_CSTRING("]"));
247 outKey.Append(pathInfo);
248 }
251 nsresult
252 SpdyStream31::ParseHttpRequestHeaders(const char *buf,
253 uint32_t avail,
254 uint32_t *countUsed)
255 {
256 // Returns NS_OK even if the headers are incomplete
257 // set mSynFrameComplete flag if they are complete
259 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
260 MOZ_ASSERT(mUpstreamState == GENERATING_SYN_STREAM);
262 LOG3(("SpdyStream31::ParseHttpRequestHeaders %p avail=%d state=%x",
263 this, avail, mUpstreamState));
265 mFlatHttpRequestHeaders.Append(buf, avail);
267 // We can use the simple double crlf because firefox is the
268 // only client we are parsing
269 int32_t endHeader = mFlatHttpRequestHeaders.Find("\r\n\r\n");
271 if (endHeader == kNotFound) {
272 // We don't have all the headers yet
273 LOG3(("SpdyStream31::ParseHttpRequestHeaders %p "
274 "Need more header bytes. Len = %d",
275 this, mFlatHttpRequestHeaders.Length()));
276 *countUsed = avail;
277 return NS_OK;
278 }
280 // We have recvd all the headers, trim the local
281 // buffer of the final empty line, and set countUsed to reflect
282 // the whole header has been consumed.
283 uint32_t oldLen = mFlatHttpRequestHeaders.Length();
284 mFlatHttpRequestHeaders.SetLength(endHeader + 2);
285 *countUsed = avail - (oldLen - endHeader) + 4;
286 mSynFrameComplete = 1;
288 nsCString hostHeader;
289 nsCString hashkey;
290 mTransaction->RequestHead()->GetHeader(nsHttp::Host, hostHeader);
292 CreatePushHashKey(NS_LITERAL_CSTRING("https"),
293 hostHeader, mSession->Serial(),
294 mTransaction->RequestHead()->RequestURI(),
295 mOrigin, hashkey);
297 // check the push cache for GET
298 if (mTransaction->RequestHead()->IsGet()) {
299 // from :scheme, :host, :path
300 nsILoadGroupConnectionInfo *loadGroupCI = mTransaction->LoadGroupConnectionInfo();
301 SpdyPushCache *cache = nullptr;
302 if (loadGroupCI)
303 loadGroupCI->GetSpdyPushCache(&cache);
305 SpdyPushedStream31 *pushedStream = nullptr;
306 // we remove the pushedstream from the push cache so that
307 // it will not be used for another GET. This does not destroy the
308 // stream itself - that is done when the transactionhash is done with it.
309 if (cache)
310 pushedStream = cache->RemovePushedStreamSpdy31(hashkey);
312 if (pushedStream) {
313 LOG3(("Pushed Stream Match located id=0x%X key=%s\n",
314 pushedStream->StreamID(), hashkey.get()));
315 pushedStream->SetConsumerStream(this);
316 mPushSource = pushedStream;
317 mSentFinOnData = 1;
319 // This stream has been activated (and thus counts against the concurrency
320 // limit intentionally), but will not be registered via
321 // RegisterStreamID (below) because of the push match. Therefore the
322 // concurrency sempahore needs to be balanced.
323 mSession->DecrementConcurrent(this);
325 // There is probably pushed data buffered so trigger a read manually
326 // as we can't rely on future network events to do it
327 mSession->ConnectPushedStream(this);
328 return NS_OK;
329 }
330 }
332 // It is now OK to assign a streamID that we are assured will
333 // be monotonically increasing amongst syn-streams on this
334 // session
335 mStreamID = mSession->RegisterStreamID(this);
336 MOZ_ASSERT(mStreamID & 1, "Spdy Stream Channel ID must be odd");
338 if (mStreamID >= 0x80000000) {
339 // streamID must fit in 31 bits. This is theoretically possible
340 // because stream ID assignment is asynchronous to stream creation
341 // because of the protocol requirement that the ID in syn-stream
342 // be monotonically increasing. In reality this is really not possible
343 // because new streams stop being added to a session with 0x10000000 / 2
344 // IDs still available and no race condition is going to bridge that gap,
345 // so we can be comfortable on just erroring out for correctness in that
346 // case.
347 LOG3(("Stream assigned out of range ID: 0x%X", mStreamID));
348 return NS_ERROR_UNEXPECTED;
349 }
351 // Now we need to convert the flat http headers into a set
352 // of SPDY headers.. writing to mTxInlineFrame{sz}
354 mTxInlineFrame[0] = SpdySession31::kFlag_Control;
355 mTxInlineFrame[1] = SpdySession31::kVersion;
356 mTxInlineFrame[2] = 0;
357 mTxInlineFrame[3] = SpdySession31::CONTROL_TYPE_SYN_STREAM;
358 // 4 to 7 are length and flags, we'll fill that in later
360 uint32_t networkOrderID = PR_htonl(mStreamID);
361 memcpy(mTxInlineFrame + 8, &networkOrderID, 4);
363 // this is the associated-to field, which is not used sending
364 // from the client in the http binding
365 memset (mTxInlineFrame + 12, 0, 4);
367 // Priority flags are the E0 mask of byte 16.
368 // 0 is highest priority, 7 is lowest.
369 // The other 5 bits of byte 16 are unused.
371 if (mPriority >= nsISupportsPriority::PRIORITY_LOWEST)
372 mTxInlineFrame[16] = 7 << 5;
373 else if (mPriority <= nsISupportsPriority::PRIORITY_HIGHEST)
374 mTxInlineFrame[16] = 0 << 5;
375 else {
376 // The priority mapping relies on the unfiltered ranged to be
377 // between -20 .. +20
378 PR_STATIC_ASSERT(nsISupportsPriority::PRIORITY_LOWEST == 20);
379 PR_STATIC_ASSERT(nsISupportsPriority::PRIORITY_HIGHEST == -20);
381 // Add one to the priority so that values such as -10 and -11
382 // get different spdy priorities - this appears to be an important
383 // breaking line in the priorities content assigns to
384 // transactions.
385 uint8_t calculatedPriority = 3 + ((mPriority + 1) / 5);
386 MOZ_ASSERT (!(calculatedPriority & 0xf8),
387 "Calculated Priority Out Of Range");
388 mTxInlineFrame[16] = calculatedPriority << 5;
389 }
391 // The client cert "slot". Right now we don't send client certs
392 mTxInlineFrame[17] = 0;
394 nsCString versionHeader;
395 if (mTransaction->RequestHead()->Version() == NS_HTTP_VERSION_1_1)
396 versionHeader = NS_LITERAL_CSTRING("HTTP/1.1");
397 else
398 versionHeader = NS_LITERAL_CSTRING("HTTP/1.0");
400 // use mRequestHead() to get a sense of how big to make the hash,
401 // even though we are parsing the actual text stream because
402 // it is legit to append headers.
403 nsClassHashtable<nsCStringHashKey, nsCString>
404 hdrHash(1 + (mTransaction->RequestHead()->Headers().Count() * 2));
406 const char *beginBuffer = mFlatHttpRequestHeaders.BeginReading();
408 // need to hash all the headers together to remove duplicates, special
409 // headers, etc..
411 int32_t crlfIndex = mFlatHttpRequestHeaders.Find("\r\n");
412 while (true) {
413 int32_t startIndex = crlfIndex + 2;
415 crlfIndex = mFlatHttpRequestHeaders.Find("\r\n", false, startIndex);
416 if (crlfIndex == -1)
417 break;
419 int32_t colonIndex = mFlatHttpRequestHeaders.Find(":", false, startIndex,
420 crlfIndex - startIndex);
421 if (colonIndex == -1)
422 break;
424 nsDependentCSubstring name = Substring(beginBuffer + startIndex,
425 beginBuffer + colonIndex);
426 // all header names are lower case in spdy
427 ToLowerCase(name);
429 // exclusions.. mostly from 3.2.1
430 if (name.Equals("connection") ||
431 name.Equals("keep-alive") ||
432 name.Equals("host") ||
433 name.Equals("accept-encoding") ||
434 name.Equals("te") ||
435 name.Equals("transfer-encoding"))
436 continue;
438 nsCString *val = hdrHash.Get(name);
439 if (!val) {
440 val = new nsCString();
441 hdrHash.Put(name, val);
442 }
444 int32_t valueIndex = colonIndex + 1;
445 while (valueIndex < crlfIndex && beginBuffer[valueIndex] == ' ')
446 ++valueIndex;
448 nsDependentCSubstring v = Substring(beginBuffer + valueIndex,
449 beginBuffer + crlfIndex);
450 if (!val->IsEmpty())
451 val->Append(static_cast<char>(0));
452 val->Append(v);
454 if (name.Equals("content-length")) {
455 int64_t len;
456 if (nsHttp::ParseInt64(val->get(), nullptr, &len))
457 mRequestBodyLenRemaining = len;
458 }
459 }
461 mTxInlineFrameUsed = 18;
463 // Do not naively log the request headers here beacuse they might
464 // contain auth. The http transaction already logs the sanitized request
465 // headers at this same level so it is not necessary to do so here.
467 const char *methodHeader = mTransaction->RequestHead()->Method().get();
469 // The header block length
470 uint16_t count = hdrHash.Count() + 5; /* method, path, version, host, scheme */
471 CompressToFrame(count);
473 // :method, :path, :version comprise a HTTP/1 request line, so send those first
474 // to make life easy for any gateways
475 CompressToFrame(NS_LITERAL_CSTRING(":method"));
476 CompressToFrame(methodHeader, strlen(methodHeader));
477 CompressToFrame(NS_LITERAL_CSTRING(":path"));
478 CompressToFrame(mTransaction->RequestHead()->RequestURI());
479 CompressToFrame(NS_LITERAL_CSTRING(":version"));
480 CompressToFrame(versionHeader);
482 CompressToFrame(NS_LITERAL_CSTRING(":host"));
483 CompressToFrame(hostHeader);
484 CompressToFrame(NS_LITERAL_CSTRING(":scheme"));
485 CompressToFrame(NS_LITERAL_CSTRING("https"));
487 hdrHash.Enumerate(hdrHashEnumerate, this);
488 CompressFlushFrame();
490 // 4 to 7 are length and flags, which we can now fill in
491 (reinterpret_cast<uint32_t *>(mTxInlineFrame.get()))[1] =
492 PR_htonl(mTxInlineFrameUsed - 8);
494 MOZ_ASSERT(!mTxInlineFrame[4], "Size greater than 24 bits");
496 // Determine whether to put the fin bit on the syn stream frame or whether
497 // to wait for a data packet to put it on.
499 if (mTransaction->RequestHead()->IsGet() ||
500 mTransaction->RequestHead()->IsConnect() ||
501 mTransaction->RequestHead()->IsHead()) {
502 // for GET, CONNECT, and HEAD place the fin bit right on the
503 // syn stream packet
505 mSentFinOnData = 1;
506 mTxInlineFrame[4] = SpdySession31::kFlag_Data_FIN;
507 }
508 else if (mTransaction->RequestHead()->IsPost() ||
509 mTransaction->RequestHead()->IsPut() ||
510 mTransaction->RequestHead()->IsOptions()) {
511 // place fin in a data frame even for 0 length messages, I've seen
512 // the google gateway be unhappy with fin-on-syn for 0 length POST
513 }
514 else if (!mRequestBodyLenRemaining) {
515 // for other HTTP extension methods, rely on the content-length
516 // to determine whether or not to put fin on syn
517 mSentFinOnData = 1;
518 mTxInlineFrame[4] = SpdySession31::kFlag_Data_FIN;
519 }
521 Telemetry::Accumulate(Telemetry::SPDY_SYN_SIZE, mTxInlineFrameUsed - 18);
523 // The size of the input headers is approximate
524 uint32_t ratio =
525 (mTxInlineFrameUsed - 18) * 100 /
526 (11 + mTransaction->RequestHead()->RequestURI().Length() +
527 mFlatHttpRequestHeaders.Length());
529 Telemetry::Accumulate(Telemetry::SPDY_SYN_RATIO, ratio);
530 return NS_OK;
531 }
533 void
534 SpdyStream31::AdjustInitialWindow()
535 {
536 MOZ_ASSERT(mSession->PushAllowance() <= ASpdySession::kInitialRwin);
538 // The session initial_window is sized for serverpushed streams. When we
539 // generate a client pulled stream we want to adjust the initial window
540 // to a huge value in a pipeline with that SYN_STREAM.
542 // >0 even numbered IDs are pushed streams.
543 // odd numbered IDs are pulled streams.
544 // 0 is the sink for a pushed stream.
545 SpdyStream31 *stream = this;
546 if (!mStreamID) {
547 MOZ_ASSERT(mPushSource);
548 if (!mPushSource)
549 return;
550 stream = mPushSource;
551 MOZ_ASSERT(stream->mStreamID);
552 MOZ_ASSERT(!(stream->mStreamID & 1)); // is a push stream
554 // If the pushed stream has sent a FIN, there is no reason to update
555 // the window
556 if (stream->RecvdFin())
557 return;
558 }
560 // For server pushes we also want to include in the ack any data that has been
561 // buffered but unacknowledged.
563 // mLocalUnacked is technically 64 bits, but because it can never grow larger than
564 // our window size (which is closer to 29bits max) we know it fits comfortably in 32.
565 // However we don't really enforce that, and track it as a 64 so that broken senders
566 // can still interoperate. That means we have to be careful with this calculation.
567 uint64_t toack64 = (ASpdySession::kInitialRwin - mSession->PushAllowance()) +
568 stream->mLocalUnacked;
569 stream->mLocalUnacked = 0;
570 if (toack64 > 0x7fffffff) {
571 stream->mLocalUnacked = toack64 - 0x7fffffff;
572 toack64 = 0x7fffffff;
573 }
574 uint32_t toack = static_cast<uint32_t>(toack64);
575 if (!toack)
576 return;
577 toack = PR_htonl(toack);
579 SpdySession31::EnsureBuffer(mTxInlineFrame,
580 mTxInlineFrameUsed + 16,
581 mTxInlineFrameUsed,
582 mTxInlineFrameSize);
584 unsigned char *packet = mTxInlineFrame.get() + mTxInlineFrameUsed;
585 mTxInlineFrameUsed += 16;
587 memset(packet, 0, 8);
588 packet[0] = SpdySession31::kFlag_Control;
589 packet[1] = SpdySession31::kVersion;
590 packet[3] = SpdySession31::CONTROL_TYPE_WINDOW_UPDATE;
591 packet[7] = 8; // 8 data bytes after 8 byte header
593 uint32_t id = PR_htonl(stream->mStreamID);
594 memcpy(packet + 8, &id, 4);
595 memcpy(packet + 12, &toack, 4);
597 stream->mLocalWindow += PR_ntohl(toack);
598 LOG3(("AdjustInitialwindow %p 0x%X %u\n",
599 this, stream->mStreamID, PR_ntohl(toack)));
600 }
602 void
603 SpdyStream31::UpdateTransportReadEvents(uint32_t count)
604 {
605 mTotalRead += count;
607 mTransaction->OnTransportStatus(mSocketTransport,
608 NS_NET_STATUS_RECEIVING_FROM,
609 mTotalRead);
610 }
612 void
613 SpdyStream31::UpdateTransportSendEvents(uint32_t count)
614 {
615 mTotalSent += count;
617 // normally on non-windows platform we use TCP autotuning for
618 // the socket buffers, and this works well (managing enough
619 // buffers for BDP while conserving memory) for HTTP even when
620 // it creates really deep queues. However this 'buffer bloat' is
621 // a problem for spdy because it ruins the low latency properties
622 // necessary for PING and cancel to work meaningfully.
623 //
624 // If this stream represents a large upload, disable autotuning for
625 // the session and cap the send buffers by default at 128KB.
626 // (10Mbit/sec @ 100ms)
627 //
628 uint32_t bufferSize = gHttpHandler->SpdySendBufferSize();
629 if ((mTotalSent > bufferSize) && !mSetTCPSocketBuffer) {
630 mSetTCPSocketBuffer = 1;
631 mSocketTransport->SetSendBufferSize(bufferSize);
632 }
634 if (mUpstreamState != SENDING_FIN_STREAM)
635 mTransaction->OnTransportStatus(mSocketTransport,
636 NS_NET_STATUS_SENDING_TO,
637 mTotalSent);
639 if (!mSentWaitingFor && !mRequestBodyLenRemaining) {
640 mSentWaitingFor = 1;
641 mTransaction->OnTransportStatus(mSocketTransport,
642 NS_NET_STATUS_WAITING_FOR,
643 0);
644 }
645 }
647 nsresult
648 SpdyStream31::TransmitFrame(const char *buf,
649 uint32_t *countUsed,
650 bool forceCommitment)
651 {
652 // If TransmitFrame returns SUCCESS than all the data is sent (or at least
653 // buffered at the session level), if it returns WOULD_BLOCK then none of
654 // the data is sent.
656 // You can call this function with no data and no out parameter in order to
657 // flush internal buffers that were previously blocked on writing. You can
658 // of course feed new data to it as well.
660 LOG3(("SpdyStream31::TransmitFrame %p inline=%d stream=%d",
661 this, mTxInlineFrameUsed, mTxStreamFrameSize));
662 if (countUsed)
663 *countUsed = 0;
665 if (!mTxInlineFrameUsed) {
666 MOZ_ASSERT(!buf);
667 return NS_OK;
668 }
670 MOZ_ASSERT(mTxInlineFrameUsed, "empty stream frame in transmit");
671 MOZ_ASSERT(mSegmentReader, "TransmitFrame with null mSegmentReader");
672 MOZ_ASSERT((buf && countUsed) || (!buf && !countUsed),
673 "TransmitFrame arguments inconsistent");
675 uint32_t transmittedCount;
676 nsresult rv;
678 // In the (relatively common) event that we have a small amount of data
679 // split between the inlineframe and the streamframe, then move the stream
680 // data into the inlineframe via copy in order to coalesce into one write.
681 // Given the interaction with ssl this is worth the small copy cost.
682 if (mTxStreamFrameSize && mTxInlineFrameUsed &&
683 mTxStreamFrameSize < SpdySession31::kDefaultBufferSize &&
684 mTxInlineFrameUsed + mTxStreamFrameSize < mTxInlineFrameSize) {
685 LOG3(("Coalesce Transmit"));
686 memcpy (mTxInlineFrame + mTxInlineFrameUsed,
687 buf, mTxStreamFrameSize);
688 if (countUsed)
689 *countUsed += mTxStreamFrameSize;
690 mTxInlineFrameUsed += mTxStreamFrameSize;
691 mTxStreamFrameSize = 0;
692 }
694 rv =
695 mSegmentReader->CommitToSegmentSize(mTxStreamFrameSize + mTxInlineFrameUsed,
696 forceCommitment);
698 if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
699 MOZ_ASSERT(!forceCommitment, "forceCommitment with WOULD_BLOCK");
700 mSession->TransactionHasDataToWrite(this);
701 }
702 if (NS_FAILED(rv)) // this will include WOULD_BLOCK
703 return rv;
705 // This function calls mSegmentReader->OnReadSegment to report the actual SPDY
706 // bytes through to the SpdySession31 and then the HttpConnection which calls
707 // the socket write function. It will accept all of the inline and stream
708 // data because of the above 'commitment' even if it has to buffer
710 rv = mSession->BufferOutput(reinterpret_cast<char*>(mTxInlineFrame.get()),
711 mTxInlineFrameUsed,
712 &transmittedCount);
713 LOG3(("SpdyStream31::TransmitFrame for inline BufferOutput session=%p "
714 "stream=%p result %x len=%d",
715 mSession, this, rv, transmittedCount));
717 MOZ_ASSERT(rv != NS_BASE_STREAM_WOULD_BLOCK,
718 "inconsistent inline commitment result");
720 if (NS_FAILED(rv))
721 return rv;
723 MOZ_ASSERT(transmittedCount == mTxInlineFrameUsed,
724 "inconsistent inline commitment count");
726 SpdySession31::LogIO(mSession, this, "Writing from Inline Buffer",
727 reinterpret_cast<char*>(mTxInlineFrame.get()),
728 transmittedCount);
730 if (mTxStreamFrameSize) {
731 if (!buf) {
732 // this cannot happen
733 MOZ_ASSERT(false, "Stream transmit with null buf argument to "
734 "TransmitFrame()");
735 LOG(("Stream transmit with null buf argument to TransmitFrame()\n"));
736 return NS_ERROR_UNEXPECTED;
737 }
739 // If there is already data buffered, just add to that to form
740 // a single TLS Application Data Record - otherwise skip the memcpy
741 if (mSession->AmountOfOutputBuffered()) {
742 rv = mSession->BufferOutput(buf, mTxStreamFrameSize,
743 &transmittedCount);
744 } else {
745 rv = mSession->OnReadSegment(buf, mTxStreamFrameSize,
746 &transmittedCount);
747 }
749 LOG3(("SpdyStream31::TransmitFrame for regular session=%p "
750 "stream=%p result %x len=%d",
751 mSession, this, rv, transmittedCount));
753 MOZ_ASSERT(rv != NS_BASE_STREAM_WOULD_BLOCK,
754 "inconsistent stream commitment result");
756 if (NS_FAILED(rv))
757 return rv;
759 MOZ_ASSERT(transmittedCount == mTxStreamFrameSize,
760 "inconsistent stream commitment count");
762 SpdySession31::LogIO(mSession, this, "Writing from Transaction Buffer",
763 buf, transmittedCount);
765 *countUsed += mTxStreamFrameSize;
766 }
768 mSession->FlushOutputQueue();
770 // calling this will trigger waiting_for if mRequestBodyLenRemaining is 0
771 UpdateTransportSendEvents(mTxInlineFrameUsed + mTxStreamFrameSize);
773 mTxInlineFrameUsed = 0;
774 mTxStreamFrameSize = 0;
776 return NS_OK;
777 }
779 void
780 SpdyStream31::ChangeState(enum stateType newState)
781 {
782 LOG3(("SpdyStream31::ChangeState() %p from %X to %X",
783 this, mUpstreamState, newState));
784 mUpstreamState = newState;
785 return;
786 }
788 void
789 SpdyStream31::GenerateDataFrameHeader(uint32_t dataLength, bool lastFrame)
790 {
791 LOG3(("SpdyStream31::GenerateDataFrameHeader %p len=%d last=%d",
792 this, dataLength, lastFrame));
794 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
795 MOZ_ASSERT(!mTxInlineFrameUsed, "inline frame not empty");
796 MOZ_ASSERT(!mTxStreamFrameSize, "stream frame not empty");
797 MOZ_ASSERT(!(dataLength & 0xff000000), "datalength > 24 bits");
799 (reinterpret_cast<uint32_t *>(mTxInlineFrame.get()))[0] = PR_htonl(mStreamID);
800 (reinterpret_cast<uint32_t *>(mTxInlineFrame.get()))[1] =
801 PR_htonl(dataLength);
803 MOZ_ASSERT(!(mTxInlineFrame[0] & 0x80), "control bit set unexpectedly");
804 MOZ_ASSERT(!mTxInlineFrame[4], "flag bits set unexpectedly");
806 mTxInlineFrameUsed = 8;
807 mTxStreamFrameSize = dataLength;
809 if (lastFrame) {
810 mTxInlineFrame[4] |= SpdySession31::kFlag_Data_FIN;
811 if (dataLength)
812 mSentFinOnData = 1;
813 }
814 }
816 void
817 SpdyStream31::CompressToFrame(const nsACString &str)
818 {
819 CompressToFrame(str.BeginReading(), str.Length());
820 }
822 void
823 SpdyStream31::CompressToFrame(const nsACString *str)
824 {
825 CompressToFrame(str->BeginReading(), str->Length());
826 }
828 // Dictionary taken from
829 // http://dev.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3
831 const unsigned char SpdyStream31::kDictionary[] = {
832 0x00, 0x00, 0x00, 0x07, 0x6f, 0x70, 0x74, 0x69, // - - - - o p t i
833 0x6f, 0x6e, 0x73, 0x00, 0x00, 0x00, 0x04, 0x68, // o n s - - - - h
834 0x65, 0x61, 0x64, 0x00, 0x00, 0x00, 0x04, 0x70, // e a d - - - - p
835 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x03, 0x70, // o s t - - - - p
836 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x64, 0x65, // u t - - - - d e
837 0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x00, 0x05, // l e t e - - - -
838 0x74, 0x72, 0x61, 0x63, 0x65, 0x00, 0x00, 0x00, // t r a c e - - -
839 0x06, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x00, // - a c c e p t -
840 0x00, 0x00, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70, // - - - a c c e p
841 0x74, 0x2d, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, // t - c h a r s e
842 0x74, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x63, 0x63, // t - - - - a c c
843 0x65, 0x70, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, // e p t - e n c o
844 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x0f, // d i n g - - - -
845 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x6c, // a c c e p t - l
846 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00, // a n g u a g e -
847 0x00, 0x00, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70, // - - - a c c e p
848 0x74, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, // t - r a n g e s
849 0x00, 0x00, 0x00, 0x03, 0x61, 0x67, 0x65, 0x00, // - - - - a g e -
850 0x00, 0x00, 0x05, 0x61, 0x6c, 0x6c, 0x6f, 0x77, // - - - a l l o w
851 0x00, 0x00, 0x00, 0x0d, 0x61, 0x75, 0x74, 0x68, // - - - - a u t h
852 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, // o r i z a t i o
853 0x6e, 0x00, 0x00, 0x00, 0x0d, 0x63, 0x61, 0x63, // n - - - - c a c
854 0x68, 0x65, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72, // h e - c o n t r
855 0x6f, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x63, 0x6f, // o l - - - - c o
856 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, // n n e c t i o n
857 0x00, 0x00, 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, // - - - - c o n t
858 0x65, 0x6e, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65, // e n t - b a s e
859 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, 0x6e, 0x74, // - - - - c o n t
860 0x65, 0x6e, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, // e n t - e n c o
861 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, // d i n g - - - -
862 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, // c o n t e n t -
863 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, // l a n g u a g e
864 0x00, 0x00, 0x00, 0x0e, 0x63, 0x6f, 0x6e, 0x74, // - - - - c o n t
865 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, // e n t - l e n g
866 0x74, 0x68, 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, // t h - - - - c o
867 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x6f, // n t e n t - l o
868 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, // c a t i o n - -
869 0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, // - - c o n t e n
870 0x74, 0x2d, 0x6d, 0x64, 0x35, 0x00, 0x00, 0x00, // t - m d 5 - - -
871 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, // - c o n t e n t
872 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, // - r a n g e - -
873 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, // - - c o n t e n
874 0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x00, 0x00, // t - t y p e - -
875 0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x00, 0x00, // - - d a t e - -
876 0x00, 0x04, 0x65, 0x74, 0x61, 0x67, 0x00, 0x00, // - - e t a g - -
877 0x00, 0x06, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, // - - e x p e c t
878 0x00, 0x00, 0x00, 0x07, 0x65, 0x78, 0x70, 0x69, // - - - - e x p i
879 0x72, 0x65, 0x73, 0x00, 0x00, 0x00, 0x04, 0x66, // r e s - - - - f
880 0x72, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x68, // r o m - - - - h
881 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x08, 0x69, // o s t - - - - i
882 0x66, 0x2d, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, // f - m a t c h -
883 0x00, 0x00, 0x11, 0x69, 0x66, 0x2d, 0x6d, 0x6f, // - - - i f - m o
884 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2d, 0x73, // d i f i e d - s
885 0x69, 0x6e, 0x63, 0x65, 0x00, 0x00, 0x00, 0x0d, // i n c e - - - -
886 0x69, 0x66, 0x2d, 0x6e, 0x6f, 0x6e, 0x65, 0x2d, // i f - n o n e -
887 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, 0x00, 0x00, // m a t c h - - -
888 0x08, 0x69, 0x66, 0x2d, 0x72, 0x61, 0x6e, 0x67, // - i f - r a n g
889 0x65, 0x00, 0x00, 0x00, 0x13, 0x69, 0x66, 0x2d, // e - - - - i f -
890 0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, // u n m o d i f i
891 0x65, 0x64, 0x2d, 0x73, 0x69, 0x6e, 0x63, 0x65, // e d - s i n c e
892 0x00, 0x00, 0x00, 0x0d, 0x6c, 0x61, 0x73, 0x74, // - - - - l a s t
893 0x2d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, // - m o d i f i e
894 0x64, 0x00, 0x00, 0x00, 0x08, 0x6c, 0x6f, 0x63, // d - - - - l o c
895 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, // a t i o n - - -
896 0x0c, 0x6d, 0x61, 0x78, 0x2d, 0x66, 0x6f, 0x72, // - m a x - f o r
897 0x77, 0x61, 0x72, 0x64, 0x73, 0x00, 0x00, 0x00, // w a r d s - - -
898 0x06, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x00, // - p r a g m a -
899 0x00, 0x00, 0x12, 0x70, 0x72, 0x6f, 0x78, 0x79, // - - - p r o x y
900 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, // - a u t h e n t
901 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, // i c a t e - - -
902 0x13, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2d, 0x61, // - p r o x y - a
903 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, // u t h o r i z a
904 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x05, // t i o n - - - -
905 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, 0x00, // r a n g e - - -
906 0x07, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x72, // - r e f e r e r
907 0x00, 0x00, 0x00, 0x0b, 0x72, 0x65, 0x74, 0x72, // - - - - r e t r
908 0x79, 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, 0x00, // y - a f t e r -
909 0x00, 0x00, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, // - - - s e r v e
910 0x72, 0x00, 0x00, 0x00, 0x02, 0x74, 0x65, 0x00, // r - - - - t e -
911 0x00, 0x00, 0x07, 0x74, 0x72, 0x61, 0x69, 0x6c, // - - - t r a i l
912 0x65, 0x72, 0x00, 0x00, 0x00, 0x11, 0x74, 0x72, // e r - - - - t r
913 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x2d, 0x65, // a n s f e r - e
914 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00, // n c o d i n g -
915 0x00, 0x00, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61, // - - - u p g r a
916 0x64, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x73, // d e - - - - u s
917 0x65, 0x72, 0x2d, 0x61, 0x67, 0x65, 0x6e, 0x74, // e r - a g e n t
918 0x00, 0x00, 0x00, 0x04, 0x76, 0x61, 0x72, 0x79, // - - - - v a r y
919 0x00, 0x00, 0x00, 0x03, 0x76, 0x69, 0x61, 0x00, // - - - - v i a -
920 0x00, 0x00, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69, // - - - w a r n i
921 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, 0x77, 0x77, // n g - - - - w w
922 0x77, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, // w - a u t h e n
923 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, // t i c a t e - -
924 0x00, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, // - - m e t h o d
925 0x00, 0x00, 0x00, 0x03, 0x67, 0x65, 0x74, 0x00, // - - - - g e t -
926 0x00, 0x00, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, // - - - s t a t u
927 0x73, 0x00, 0x00, 0x00, 0x06, 0x32, 0x30, 0x30, // s - - - - 2 0 0
928 0x20, 0x4f, 0x4b, 0x00, 0x00, 0x00, 0x07, 0x76, // - O K - - - - v
929 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x00, // e r s i o n - -
930 0x00, 0x08, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, // - - H T T P - 1
931 0x2e, 0x31, 0x00, 0x00, 0x00, 0x03, 0x75, 0x72, // - 1 - - - - u r
932 0x6c, 0x00, 0x00, 0x00, 0x06, 0x70, 0x75, 0x62, // l - - - - p u b
933 0x6c, 0x69, 0x63, 0x00, 0x00, 0x00, 0x0a, 0x73, // l i c - - - - s
934 0x65, 0x74, 0x2d, 0x63, 0x6f, 0x6f, 0x6b, 0x69, // e t - c o o k i
935 0x65, 0x00, 0x00, 0x00, 0x0a, 0x6b, 0x65, 0x65, // e - - - - k e e
936 0x70, 0x2d, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x00, // p - a l i v e -
937 0x00, 0x00, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, // - - - o r i g i
938 0x6e, 0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x32, // n 1 0 0 1 0 1 2
939 0x30, 0x31, 0x32, 0x30, 0x32, 0x32, 0x30, 0x35, // 0 1 2 0 2 2 0 5
940 0x32, 0x30, 0x36, 0x33, 0x30, 0x30, 0x33, 0x30, // 2 0 6 3 0 0 3 0
941 0x32, 0x33, 0x30, 0x33, 0x33, 0x30, 0x34, 0x33, // 2 3 0 3 3 0 4 3
942 0x30, 0x35, 0x33, 0x30, 0x36, 0x33, 0x30, 0x37, // 0 5 3 0 6 3 0 7
943 0x34, 0x30, 0x32, 0x34, 0x30, 0x35, 0x34, 0x30, // 4 0 2 4 0 5 4 0
944 0x36, 0x34, 0x30, 0x37, 0x34, 0x30, 0x38, 0x34, // 6 4 0 7 4 0 8 4
945 0x30, 0x39, 0x34, 0x31, 0x30, 0x34, 0x31, 0x31, // 0 9 4 1 0 4 1 1
946 0x34, 0x31, 0x32, 0x34, 0x31, 0x33, 0x34, 0x31, // 4 1 2 4 1 3 4 1
947 0x34, 0x34, 0x31, 0x35, 0x34, 0x31, 0x36, 0x34, // 4 4 1 5 4 1 6 4
948 0x31, 0x37, 0x35, 0x30, 0x32, 0x35, 0x30, 0x34, // 1 7 5 0 2 5 0 4
949 0x35, 0x30, 0x35, 0x32, 0x30, 0x33, 0x20, 0x4e, // 5 0 5 2 0 3 - N
950 0x6f, 0x6e, 0x2d, 0x41, 0x75, 0x74, 0x68, 0x6f, // o n - A u t h o
951 0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65, // r i t a t i v e
952 0x20, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, // - I n f o r m a
953 0x74, 0x69, 0x6f, 0x6e, 0x32, 0x30, 0x34, 0x20, // t i o n 2 0 4 -
954 0x4e, 0x6f, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x65, // N o - C o n t e
955 0x6e, 0x74, 0x33, 0x30, 0x31, 0x20, 0x4d, 0x6f, // n t 3 0 1 - M o
956 0x76, 0x65, 0x64, 0x20, 0x50, 0x65, 0x72, 0x6d, // v e d - P e r m
957 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x34, // a n e n t l y 4
958 0x30, 0x30, 0x20, 0x42, 0x61, 0x64, 0x20, 0x52, // 0 0 - B a d - R
959 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x34, 0x30, // e q u e s t 4 0
960 0x31, 0x20, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68, // 1 - U n a u t h
961 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x34, 0x30, // o r i z e d 4 0
962 0x33, 0x20, 0x46, 0x6f, 0x72, 0x62, 0x69, 0x64, // 3 - F o r b i d
963 0x64, 0x65, 0x6e, 0x34, 0x30, 0x34, 0x20, 0x4e, // d e n 4 0 4 - N
964 0x6f, 0x74, 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, // o t - F o u n d
965 0x35, 0x30, 0x30, 0x20, 0x49, 0x6e, 0x74, 0x65, // 5 0 0 - I n t e
966 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x53, 0x65, 0x72, // r n a l - S e r
967 0x76, 0x65, 0x72, 0x20, 0x45, 0x72, 0x72, 0x6f, // v e r - E r r o
968 0x72, 0x35, 0x30, 0x31, 0x20, 0x4e, 0x6f, 0x74, // r 5 0 1 - N o t
969 0x20, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, // - I m p l e m e
970 0x6e, 0x74, 0x65, 0x64, 0x35, 0x30, 0x33, 0x20, // n t e d 5 0 3 -
971 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, // S e r v i c e -
972 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, // U n a v a i l a
973 0x62, 0x6c, 0x65, 0x4a, 0x61, 0x6e, 0x20, 0x46, // b l e J a n - F
974 0x65, 0x62, 0x20, 0x4d, 0x61, 0x72, 0x20, 0x41, // e b - M a r - A
975 0x70, 0x72, 0x20, 0x4d, 0x61, 0x79, 0x20, 0x4a, // p r - M a y - J
976 0x75, 0x6e, 0x20, 0x4a, 0x75, 0x6c, 0x20, 0x41, // u n - J u l - A
977 0x75, 0x67, 0x20, 0x53, 0x65, 0x70, 0x74, 0x20, // u g - S e p t -
978 0x4f, 0x63, 0x74, 0x20, 0x4e, 0x6f, 0x76, 0x20, // O c t - N o v -
979 0x44, 0x65, 0x63, 0x20, 0x30, 0x30, 0x3a, 0x30, // D e c - 0 0 - 0
980 0x30, 0x3a, 0x30, 0x30, 0x20, 0x4d, 0x6f, 0x6e, // 0 - 0 0 - M o n
981 0x2c, 0x20, 0x54, 0x75, 0x65, 0x2c, 0x20, 0x57, // - - T u e - - W
982 0x65, 0x64, 0x2c, 0x20, 0x54, 0x68, 0x75, 0x2c, // e d - - T h u -
983 0x20, 0x46, 0x72, 0x69, 0x2c, 0x20, 0x53, 0x61, // - F r i - - S a
984 0x74, 0x2c, 0x20, 0x53, 0x75, 0x6e, 0x2c, 0x20, // t - - S u n - -
985 0x47, 0x4d, 0x54, 0x63, 0x68, 0x75, 0x6e, 0x6b, // G M T c h u n k
986 0x65, 0x64, 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, // e d - t e x t -
987 0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x69, 0x6d, 0x61, // h t m l - i m a
988 0x67, 0x65, 0x2f, 0x70, 0x6e, 0x67, 0x2c, 0x69, // g e - p n g - i
989 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x6a, 0x70, 0x67, // m a g e - j p g
990 0x2c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, // - i m a g e - g
991 0x69, 0x66, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, // i f - a p p l i
992 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, // c a t i o n - x
993 0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, // m l - a p p l i
994 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, // c a t i o n - x
995 0x68, 0x74, 0x6d, 0x6c, 0x2b, 0x78, 0x6d, 0x6c, // h t m l - x m l
996 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, // - t e x t - p l
997 0x61, 0x69, 0x6e, 0x2c, 0x74, 0x65, 0x78, 0x74, // a i n - t e x t
998 0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, // - j a v a s c r
999 0x69, 0x70, 0x74, 0x2c, 0x70, 0x75, 0x62, 0x6c, // i p t - p u b l
1000 0x69, 0x63, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, // i c p r i v a t
1001 0x65, 0x6d, 0x61, 0x78, 0x2d, 0x61, 0x67, 0x65, // e m a x - a g e
1002 0x3d, 0x67, 0x7a, 0x69, 0x70, 0x2c, 0x64, 0x65, // - g z i p - d e
1003 0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64, // f l a t e - s d
1004 0x63, 0x68, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, // c h c h a r s e
1005 0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x63, // t - u t f - 8 c
1006 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x69, // h a r s e t - i
1007 0x73, 0x6f, 0x2d, 0x38, 0x38, 0x35, 0x39, 0x2d, // s o - 8 8 5 9 -
1008 0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x2c, 0x2a, // 1 - u t f - - -
1009 0x2c, 0x65, 0x6e, 0x71, 0x3d, 0x30, 0x2e // - e n q - 0 -
1010 };
1012 // This can be called N times.. 1 for syn_reply and 0->N for headers
1013 nsresult
1014 SpdyStream31::Uncompress(z_stream *context,
1015 char *blockStart,
1016 uint32_t blockLen)
1017 {
1018 mDecompressedBytes += blockLen;
1020 context->avail_in = blockLen;
1021 context->next_in = reinterpret_cast<unsigned char *>(blockStart);
1022 bool triedDictionary = false;
1024 do {
1025 context->next_out =
1026 reinterpret_cast<unsigned char *>(mDecompressBuffer.get()) +
1027 mDecompressBufferUsed;
1028 context->avail_out = mDecompressBufferSize - mDecompressBufferUsed;
1029 int zlib_rv = inflate(context, Z_NO_FLUSH);
1031 if (zlib_rv == Z_NEED_DICT) {
1032 if (triedDictionary) {
1033 LOG3(("SpdySession31::Uncompress %p Dictionary Error\n", this));
1034 return NS_ERROR_ILLEGAL_VALUE;
1035 }
1037 triedDictionary = true;
1038 inflateSetDictionary(context, kDictionary, sizeof(kDictionary));
1039 }
1041 if (zlib_rv == Z_DATA_ERROR)
1042 return NS_ERROR_ILLEGAL_VALUE;
1044 if (zlib_rv == Z_MEM_ERROR)
1045 return NS_ERROR_FAILURE;
1047 // zlib's inflate() decreases context->avail_out by the amount it places
1048 // in the output buffer
1050 mDecompressBufferUsed += mDecompressBufferSize - mDecompressBufferUsed -
1051 context->avail_out;
1053 // When there is no more output room, but input still available then
1054 // increase the output space
1055 if (zlib_rv == Z_OK &&
1056 !context->avail_out && context->avail_in) {
1057 LOG3(("SpdyStream31::Uncompress %p Large Headers - so far %d",
1058 this, mDecompressBufferSize));
1059 SpdySession31::EnsureBuffer(mDecompressBuffer,
1060 mDecompressBufferSize + 4096,
1061 mDecompressBufferUsed,
1062 mDecompressBufferSize);
1063 }
1064 }
1065 while (context->avail_in);
1066 return NS_OK;
1067 }
1069 // mDecompressBuffer contains 0 to N uncompressed Name/Value Header blocks
1070 nsresult
1071 SpdyStream31::FindHeader(nsCString name,
1072 nsDependentCSubstring &value)
1073 {
1074 const unsigned char *nvpair = reinterpret_cast<unsigned char *>
1075 (mDecompressBuffer.get()) + 4;
1076 const unsigned char *lastHeaderByte = reinterpret_cast<unsigned char *>
1077 (mDecompressBuffer.get()) + mDecompressBufferUsed;
1078 if (lastHeaderByte < nvpair)
1079 return NS_ERROR_ILLEGAL_VALUE;
1081 do {
1082 uint32_t numPairs = PR_ntohl(reinterpret_cast<const uint32_t *>(nvpair)[-1]);
1084 for (uint32_t index = 0; index < numPairs; ++index) {
1085 if (lastHeaderByte < nvpair + 4)
1086 return NS_ERROR_ILLEGAL_VALUE;
1087 uint32_t nameLen = (nvpair[0] << 24) + (nvpair[1] << 16) +
1088 (nvpair[2] << 8) + nvpair[3];
1089 if (lastHeaderByte < nvpair + 4 + nameLen)
1090 return NS_ERROR_ILLEGAL_VALUE;
1091 nsDependentCSubstring nameString =
1092 Substring(reinterpret_cast<const char *>(nvpair) + 4,
1093 reinterpret_cast<const char *>(nvpair) + 4 + nameLen);
1094 if (lastHeaderByte < nvpair + 8 + nameLen)
1095 return NS_ERROR_ILLEGAL_VALUE;
1096 uint32_t valueLen = (nvpair[4 + nameLen] << 24) + (nvpair[5 + nameLen] << 16) +
1097 (nvpair[6 + nameLen] << 8) + nvpair[7 + nameLen];
1098 if (lastHeaderByte < nvpair + 8 + nameLen + valueLen)
1099 return NS_ERROR_ILLEGAL_VALUE;
1100 if (nameString.Equals(name)) {
1101 value.Assign(((char *)nvpair) + 8 + nameLen, valueLen);
1102 return NS_OK;
1103 }
1105 // that pair didn't match - try the next one in this block
1106 nvpair += 8 + nameLen + valueLen;
1107 }
1109 // move to the next name/value header block (if there is one) - the
1110 // first pair is offset 4 bytes into it
1111 nvpair += 4;
1112 } while (lastHeaderByte >= nvpair);
1114 return NS_ERROR_NOT_AVAILABLE;
1115 }
1117 // ConvertHeaders is used to convert the response headers
1118 // in a syn_reply or in 0..N headers frames that follow it into
1119 // HTTP/1 format
1120 nsresult
1121 SpdyStream31::ConvertHeaders(nsACString &aHeadersOut)
1122 {
1123 // :status and :version are required.
1124 nsDependentCSubstring status, version;
1125 nsresult rv = FindHeader(NS_LITERAL_CSTRING(":status"),
1126 status);
1127 if (NS_FAILED(rv))
1128 return (rv == NS_ERROR_NOT_AVAILABLE) ? NS_ERROR_ILLEGAL_VALUE : rv;
1130 rv = FindHeader(NS_LITERAL_CSTRING(":version"),
1131 version);
1132 if (NS_FAILED(rv))
1133 return (rv == NS_ERROR_NOT_AVAILABLE) ? NS_ERROR_ILLEGAL_VALUE : rv;
1135 if (mDecompressedBytes && mDecompressBufferUsed) {
1136 Telemetry::Accumulate(Telemetry::SPDY_SYN_REPLY_SIZE, mDecompressedBytes);
1137 uint32_t ratio =
1138 mDecompressedBytes * 100 / mDecompressBufferUsed;
1139 Telemetry::Accumulate(Telemetry::SPDY_SYN_REPLY_RATIO, ratio);
1140 }
1142 aHeadersOut.Truncate();
1143 aHeadersOut.SetCapacity(mDecompressBufferUsed + 64);
1145 // Connection, Keep-Alive and chunked transfer encodings are to be
1146 // removed.
1148 // Content-Length is 'advisory'.. we will not strip it because it can
1149 // create UI feedback.
1151 aHeadersOut.Append(version);
1152 aHeadersOut.Append(NS_LITERAL_CSTRING(" "));
1153 aHeadersOut.Append(status);
1154 aHeadersOut.Append(NS_LITERAL_CSTRING("\r\n"));
1156 const unsigned char *nvpair = reinterpret_cast<unsigned char *>
1157 (mDecompressBuffer.get()) + 4;
1158 const unsigned char *lastHeaderByte = reinterpret_cast<unsigned char *>
1159 (mDecompressBuffer.get()) + mDecompressBufferUsed;
1160 if (lastHeaderByte < nvpair)
1161 return NS_ERROR_ILLEGAL_VALUE;
1163 do {
1164 uint32_t numPairs = PR_ntohl(reinterpret_cast<const uint32_t *>(nvpair)[-1]);
1166 for (uint32_t index = 0; index < numPairs; ++index) {
1167 if (lastHeaderByte < nvpair + 4)
1168 return NS_ERROR_ILLEGAL_VALUE;
1170 uint32_t nameLen = (nvpair[0] << 24) + (nvpair[1] << 16) +
1171 (nvpair[2] << 8) + nvpair[3];
1172 if (lastHeaderByte < nvpair + 4 + nameLen)
1173 return NS_ERROR_ILLEGAL_VALUE;
1175 nsDependentCSubstring nameString =
1176 Substring(reinterpret_cast<const char *>(nvpair) + 4,
1177 reinterpret_cast<const char *>(nvpair) + 4 + nameLen);
1179 if (lastHeaderByte < nvpair + 8 + nameLen)
1180 return NS_ERROR_ILLEGAL_VALUE;
1182 // Look for illegal characters in the nameString.
1183 // This includes upper case characters and nulls (as they will
1184 // break the fixup-nulls-in-value-string algorithm)
1185 // Look for upper case characters in the name. They are illegal.
1186 for (char *cPtr = nameString.BeginWriting();
1187 cPtr && cPtr < nameString.EndWriting();
1188 ++cPtr) {
1189 if (*cPtr <= 'Z' && *cPtr >= 'A') {
1190 nsCString toLog(nameString);
1192 LOG3(("SpdyStream31::ConvertHeaders session=%p stream=%p "
1193 "upper case response header found. [%s]\n",
1194 mSession, this, toLog.get()));
1196 return NS_ERROR_ILLEGAL_VALUE;
1197 }
1199 // check for null characters
1200 if (*cPtr == '\0')
1201 return NS_ERROR_ILLEGAL_VALUE;
1202 }
1204 // HTTP Chunked responses are not legal over spdy. We do not need
1205 // to look for chunked specifically because it is the only HTTP
1206 // allowed default encoding and we did not negotiate further encodings
1207 // via TE
1208 if (nameString.Equals(NS_LITERAL_CSTRING("transfer-encoding"))) {
1209 LOG3(("SpdyStream31::ConvertHeaders session=%p stream=%p "
1210 "transfer-encoding found. Chunked is invalid and no TE sent.",
1211 mSession, this));
1213 return NS_ERROR_ILLEGAL_VALUE;
1214 }
1216 uint32_t valueLen =
1217 (nvpair[4 + nameLen] << 24) + (nvpair[5 + nameLen] << 16) +
1218 (nvpair[6 + nameLen] << 8) + nvpair[7 + nameLen];
1220 if (lastHeaderByte < nvpair + 8 + nameLen + valueLen)
1221 return NS_ERROR_ILLEGAL_VALUE;
1223 // spdy transport level headers shouldn't be gatewayed into http/1
1224 if (!nameString.IsEmpty() && nameString[0] != ':' &&
1225 !nameString.Equals(NS_LITERAL_CSTRING("connection")) &&
1226 !nameString.Equals(NS_LITERAL_CSTRING("keep-alive"))) {
1227 nsDependentCSubstring valueString =
1228 Substring(reinterpret_cast<const char *>(nvpair) + 8 + nameLen,
1229 reinterpret_cast<const char *>(nvpair) + 8 + nameLen +
1230 valueLen);
1232 aHeadersOut.Append(nameString);
1233 aHeadersOut.Append(NS_LITERAL_CSTRING(": "));
1235 // expand NULL bytes in the value string
1236 for (char *cPtr = valueString.BeginWriting();
1237 cPtr && cPtr < valueString.EndWriting();
1238 ++cPtr) {
1239 if (*cPtr != 0) {
1240 aHeadersOut.Append(*cPtr);
1241 continue;
1242 }
1244 // NULLs are really "\r\nhdr: "
1245 aHeadersOut.Append(NS_LITERAL_CSTRING("\r\n"));
1246 aHeadersOut.Append(nameString);
1247 aHeadersOut.Append(NS_LITERAL_CSTRING(": "));
1248 }
1250 aHeadersOut.Append(NS_LITERAL_CSTRING("\r\n"));
1251 }
1252 // move to the next name/value pair in this block
1253 nvpair += 8 + nameLen + valueLen;
1254 }
1256 // move to the next name/value header block (if there is one) - the
1257 // first pair is offset 4 bytes into it
1258 nvpair += 4;
1259 } while (lastHeaderByte >= nvpair);
1261 aHeadersOut.Append(NS_LITERAL_CSTRING("X-Firefox-Spdy: 3.1\r\n\r\n"));
1262 LOG (("decoded response headers are:\n%s",
1263 aHeadersOut.BeginReading()));
1265 // The spdy formatted buffer isnt needed anymore - free it up
1266 mDecompressBuffer = nullptr;
1267 mDecompressBufferSize = 0;
1268 mDecompressBufferUsed = 0;
1270 return NS_OK;
1271 }
1273 void
1274 SpdyStream31::ExecuteCompress(uint32_t flushMode)
1275 {
1276 // Expect mZlib->avail_in and mZlib->next_in to be set.
1277 // Append the compressed version of next_in to mTxInlineFrame
1279 do
1280 {
1281 uint32_t avail = mTxInlineFrameSize - mTxInlineFrameUsed;
1282 if (avail < 1) {
1283 SpdySession31::EnsureBuffer(mTxInlineFrame,
1284 mTxInlineFrameSize + 2000,
1285 mTxInlineFrameUsed,
1286 mTxInlineFrameSize);
1287 avail = mTxInlineFrameSize - mTxInlineFrameUsed;
1288 }
1290 mZlib->next_out = mTxInlineFrame + mTxInlineFrameUsed;
1291 mZlib->avail_out = avail;
1292 deflate(mZlib, flushMode);
1293 mTxInlineFrameUsed += avail - mZlib->avail_out;
1294 } while (mZlib->avail_in > 0 || !mZlib->avail_out);
1295 }
1297 void
1298 SpdyStream31::CompressToFrame(uint32_t data)
1299 {
1300 // convert the data to 4 byte network byte order and write that
1301 // to the compressed stream
1302 data = PR_htonl(data);
1304 mZlib->next_in = reinterpret_cast<unsigned char *> (&data);
1305 mZlib->avail_in = 4;
1306 ExecuteCompress(Z_NO_FLUSH);
1307 }
1310 void
1311 SpdyStream31::CompressToFrame(const char *data, uint32_t len)
1312 {
1313 // Format calls for a network ordered 32 bit length
1314 // followed by the utf8 string
1316 uint32_t networkLen = PR_htonl(len);
1318 // write out the length
1319 mZlib->next_in = reinterpret_cast<unsigned char *> (&networkLen);
1320 mZlib->avail_in = 4;
1321 ExecuteCompress(Z_NO_FLUSH);
1323 // write out the data
1324 mZlib->next_in = (unsigned char *)data;
1325 mZlib->avail_in = len;
1326 ExecuteCompress(Z_NO_FLUSH);
1327 }
1329 void
1330 SpdyStream31::CompressFlushFrame()
1331 {
1332 mZlib->next_in = (unsigned char *) "";
1333 mZlib->avail_in = 0;
1334 ExecuteCompress(Z_SYNC_FLUSH);
1335 }
1337 void
1338 SpdyStream31::Close(nsresult reason)
1339 {
1340 mTransaction->Close(reason);
1341 }
1343 void
1344 SpdyStream31::UpdateRemoteWindow(int32_t delta)
1345 {
1346 mRemoteWindow += delta;
1348 // If the stream had a <=0 window, that has now opened
1349 // schedule it for writing again
1350 if (mBlockedOnRwin && mSession->RemoteSessionWindow() > 0 &&
1351 mRemoteWindow > 0) {
1352 // the window has been opened :)
1353 mSession->TransactionHasDataToWrite(this);
1354 }
1355 }
1357 //-----------------------------------------------------------------------------
1358 // nsAHttpSegmentReader
1359 //-----------------------------------------------------------------------------
1361 nsresult
1362 SpdyStream31::OnReadSegment(const char *buf,
1363 uint32_t count,
1364 uint32_t *countRead)
1365 {
1366 LOG3(("SpdyStream31::OnReadSegment %p count=%d state=%x",
1367 this, count, mUpstreamState));
1369 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
1370 MOZ_ASSERT(mSegmentReader, "OnReadSegment with null mSegmentReader");
1372 nsresult rv = NS_ERROR_UNEXPECTED;
1373 uint32_t dataLength;
1375 switch (mUpstreamState) {
1376 case GENERATING_SYN_STREAM:
1377 // The buffer is the HTTP request stream, including at least part of the
1378 // HTTP request header. This state's job is to build a SYN_STREAM frame
1379 // from the header information. count is the number of http bytes available
1380 // (which may include more than the header), and in countRead we return
1381 // the number of those bytes that we consume (i.e. the portion that are
1382 // header bytes)
1384 rv = ParseHttpRequestHeaders(buf, count, countRead);
1385 if (NS_FAILED(rv))
1386 return rv;
1387 LOG3(("ParseHttpRequestHeaders %p used %d of %d. complete = %d",
1388 this, *countRead, count, mSynFrameComplete));
1389 if (mSynFrameComplete) {
1390 AdjustInitialWindow();
1391 rv = TransmitFrame(nullptr, nullptr, true);
1392 if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
1393 // this can't happen
1394 MOZ_ASSERT(false, "Transmit Frame SYN_FRAME must at least buffer data");
1395 rv = NS_ERROR_UNEXPECTED;
1396 }
1398 ChangeState(GENERATING_REQUEST_BODY);
1399 break;
1400 }
1401 MOZ_ASSERT(*countRead == count, "Header parsing not complete but unused data");
1402 break;
1404 case GENERATING_REQUEST_BODY:
1405 if ((mRemoteWindow <= 0) || (mSession->RemoteSessionWindow() <= 0)) {
1406 *countRead = 0;
1407 LOG3(("SpdyStream31 this=%p, id 0x%X request body suspended because "
1408 "remote window is stream=%ld session=%ld.\n", this, mStreamID,
1409 mRemoteWindow, mSession->RemoteSessionWindow()));
1410 mBlockedOnRwin = true;
1411 return NS_BASE_STREAM_WOULD_BLOCK;
1412 }
1413 mBlockedOnRwin = false;
1415 dataLength = std::min(count, mChunkSize);
1417 if (dataLength > mRemoteWindow)
1418 dataLength = static_cast<uint32_t>(mRemoteWindow);
1420 if (dataLength > mSession->RemoteSessionWindow())
1421 dataLength = static_cast<uint32_t>(mSession->RemoteSessionWindow());
1423 LOG3(("SpdyStream31 this=%p id 0x%X remote window is stream %ld and "
1424 "session %ld. Chunk is %d\n",
1425 this, mStreamID, mRemoteWindow, mSession->RemoteSessionWindow(),
1426 dataLength));
1427 mRemoteWindow -= dataLength;
1428 mSession->DecrementRemoteSessionWindow(dataLength);
1430 LOG3(("SpdyStream31 %p id %x request len remaining %d, "
1431 "count avail %d, chunk used %d",
1432 this, mStreamID, mRequestBodyLenRemaining, count, dataLength));
1433 if (dataLength > mRequestBodyLenRemaining)
1434 return NS_ERROR_UNEXPECTED;
1435 mRequestBodyLenRemaining -= dataLength;
1436 GenerateDataFrameHeader(dataLength, !mRequestBodyLenRemaining);
1437 ChangeState(SENDING_REQUEST_BODY);
1438 // NO BREAK
1440 case SENDING_REQUEST_BODY:
1441 MOZ_ASSERT(mTxInlineFrameUsed, "OnReadSegment Send Data Header 0b");
1442 rv = TransmitFrame(buf, countRead, false);
1443 MOZ_ASSERT(NS_FAILED(rv) || !mTxInlineFrameUsed,
1444 "Transmit Frame should be all or nothing");
1446 LOG3(("TransmitFrame() rv=%x returning %d data bytes. "
1447 "Header is %d Body is %d.",
1448 rv, *countRead, mTxInlineFrameUsed, mTxStreamFrameSize));
1450 // normalize a partial write with a WOULD_BLOCK into just a partial write
1451 // as some code will take WOULD_BLOCK to mean an error with nothing
1452 // written (e.g. nsHttpTransaction::ReadRequestSegment()
1453 if (rv == NS_BASE_STREAM_WOULD_BLOCK && *countRead)
1454 rv = NS_OK;
1456 // If that frame was all sent, look for another one
1457 if (!mTxInlineFrameUsed)
1458 ChangeState(GENERATING_REQUEST_BODY);
1459 break;
1461 case SENDING_FIN_STREAM:
1462 MOZ_ASSERT(false, "resuming partial fin stream out of OnReadSegment");
1463 break;
1465 default:
1466 MOZ_ASSERT(false, "SpdyStream31::OnReadSegment non-write state");
1467 break;
1468 }
1470 return rv;
1471 }
1473 //-----------------------------------------------------------------------------
1474 // nsAHttpSegmentWriter
1475 //-----------------------------------------------------------------------------
1477 nsresult
1478 SpdyStream31::OnWriteSegment(char *buf,
1479 uint32_t count,
1480 uint32_t *countWritten)
1481 {
1482 LOG3(("SpdyStream31::OnWriteSegment %p count=%d state=%x 0x%X\n",
1483 this, count, mUpstreamState, mStreamID));
1485 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
1486 MOZ_ASSERT(mSegmentWriter);
1488 if (!mPushSource)
1489 return mSegmentWriter->OnWriteSegment(buf, count, countWritten);
1491 nsresult rv;
1492 rv = mPushSource->GetBufferedData(buf, count, countWritten);
1493 if (NS_FAILED(rv))
1494 return rv;
1496 mSession->ConnectPushedStream(this);
1497 return NS_OK;
1498 }
1500 } // namespace mozilla::net
1501 } // namespace mozilla