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