|
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/. */ |
|
6 |
|
7 // HttpLog.h should generally be included first |
|
8 #include "HttpLog.h" |
|
9 |
|
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() |
|
15 |
|
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" |
|
29 |
|
30 #include <algorithm> |
|
31 |
|
32 #ifdef DEBUG |
|
33 // defined by the socket transport service while active |
|
34 extern PRThread *gSocketThread; |
|
35 #endif |
|
36 |
|
37 namespace mozilla { |
|
38 namespace net { |
|
39 |
|
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); |
|
75 |
|
76 LOG3(("SpdyStream3::SpdyStream3 %p", this)); |
|
77 |
|
78 mRemoteWindow = spdySession->GetServerInitialWindow(); |
|
79 mLocalWindow = spdySession->PushAllowance(); |
|
80 |
|
81 mTxInlineFrame = new uint8_t[mTxInlineFrameSize]; |
|
82 mDecompressBuffer = new char[mDecompressBufferSize]; |
|
83 } |
|
84 |
|
85 SpdyStream3::~SpdyStream3() |
|
86 { |
|
87 mStreamID = SpdySession3::kDeadStreamID; |
|
88 } |
|
89 |
|
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. |
|
94 |
|
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)); |
|
102 |
|
103 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); |
|
104 |
|
105 nsresult rv = NS_ERROR_UNEXPECTED; |
|
106 mRequestBlockedOnRead = 0; |
|
107 |
|
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; |
|
117 |
|
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); |
|
124 |
|
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. |
|
130 |
|
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; |
|
136 |
|
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; |
|
154 |
|
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 } |
|
172 |
|
173 *countRead = 0; |
|
174 |
|
175 // don't change OK to WOULD BLOCK. we are really done sending if OK |
|
176 break; |
|
177 |
|
178 case UPSTREAM_COMPLETE: |
|
179 *countRead = 0; |
|
180 rv = NS_OK; |
|
181 break; |
|
182 |
|
183 default: |
|
184 MOZ_ASSERT(false, "SpdyStream3::ReadSegments unknown state"); |
|
185 break; |
|
186 } |
|
187 |
|
188 return rv; |
|
189 } |
|
190 |
|
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. |
|
195 |
|
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"); |
|
203 |
|
204 LOG3(("SpdyStream3::WriteSegments %p count=%d state=%x", |
|
205 this, count, mUpstreamState)); |
|
206 |
|
207 mSegmentWriter = writer; |
|
208 nsresult rv = mTransaction->WriteSegments(this, count, countWritten); |
|
209 mSegmentWriter = nullptr; |
|
210 |
|
211 return rv; |
|
212 } |
|
213 |
|
214 PLDHashOperator |
|
215 SpdyStream3::hdrHashEnumerate(const nsACString &key, |
|
216 nsAutoPtr<nsCString> &value, |
|
217 void *closure) |
|
218 { |
|
219 SpdyStream3 *self = static_cast<SpdyStream3 *>(closure); |
|
220 |
|
221 self->CompressToFrame(key); |
|
222 self->CompressToFrame(value.get()); |
|
223 return PL_DHASH_NEXT; |
|
224 } |
|
225 |
|
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); |
|
237 |
|
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 } |
|
244 |
|
245 |
|
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 |
|
253 |
|
254 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); |
|
255 MOZ_ASSERT(mUpstreamState == GENERATING_SYN_STREAM); |
|
256 |
|
257 LOG3(("SpdyStream3::ParseHttpRequestHeaders %p avail=%d state=%x", |
|
258 this, avail, mUpstreamState)); |
|
259 |
|
260 mFlatHttpRequestHeaders.Append(buf, avail); |
|
261 |
|
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"); |
|
265 |
|
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 } |
|
274 |
|
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; |
|
282 |
|
283 nsCString hostHeader; |
|
284 nsCString hashkey; |
|
285 mTransaction->RequestHead()->GetHeader(nsHttp::Host, hostHeader); |
|
286 |
|
287 CreatePushHashKey(NS_LITERAL_CSTRING("https"), |
|
288 hostHeader, mSession->Serial(), |
|
289 mTransaction->RequestHead()->RequestURI(), |
|
290 mOrigin, hashkey); |
|
291 |
|
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); |
|
299 |
|
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); |
|
306 |
|
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; |
|
313 |
|
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); |
|
319 |
|
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 } |
|
326 |
|
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"); |
|
332 |
|
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 } |
|
345 |
|
346 // Now we need to convert the flat http headers into a set |
|
347 // of SPDY headers.. writing to mTxInlineFrame{sz} |
|
348 |
|
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 |
|
354 |
|
355 NetworkEndian::writeUint32(mTxInlineFrame + 8, mStreamID); |
|
356 |
|
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); |
|
360 |
|
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. |
|
364 |
|
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); |
|
374 |
|
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 } |
|
384 |
|
385 // The client cert "slot". Right now we don't send client certs |
|
386 mTxInlineFrame[17] = 0; |
|
387 |
|
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"); |
|
393 |
|
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)); |
|
399 |
|
400 const char *beginBuffer = mFlatHttpRequestHeaders.BeginReading(); |
|
401 |
|
402 // need to hash all the headers together to remove duplicates, special |
|
403 // headers, etc.. |
|
404 |
|
405 int32_t crlfIndex = mFlatHttpRequestHeaders.Find("\r\n"); |
|
406 while (true) { |
|
407 int32_t startIndex = crlfIndex + 2; |
|
408 |
|
409 crlfIndex = mFlatHttpRequestHeaders.Find("\r\n", false, startIndex); |
|
410 if (crlfIndex == -1) |
|
411 break; |
|
412 |
|
413 int32_t colonIndex = mFlatHttpRequestHeaders.Find(":", false, startIndex, |
|
414 crlfIndex - startIndex); |
|
415 if (colonIndex == -1) |
|
416 break; |
|
417 |
|
418 nsDependentCSubstring name = Substring(beginBuffer + startIndex, |
|
419 beginBuffer + colonIndex); |
|
420 // all header names are lower case in spdy |
|
421 ToLowerCase(name); |
|
422 |
|
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; |
|
431 |
|
432 nsCString *val = hdrHash.Get(name); |
|
433 if (!val) { |
|
434 val = new nsCString(); |
|
435 hdrHash.Put(name, val); |
|
436 } |
|
437 |
|
438 int32_t valueIndex = colonIndex + 1; |
|
439 while (valueIndex < crlfIndex && beginBuffer[valueIndex] == ' ') |
|
440 ++valueIndex; |
|
441 |
|
442 nsDependentCSubstring v = Substring(beginBuffer + valueIndex, |
|
443 beginBuffer + crlfIndex); |
|
444 if (!val->IsEmpty()) |
|
445 val->Append(static_cast<char>(0)); |
|
446 val->Append(v); |
|
447 |
|
448 if (name.Equals("content-length")) { |
|
449 int64_t len; |
|
450 if (nsHttp::ParseInt64(val->get(), nullptr, &len)) |
|
451 mRequestBodyLenRemaining = len; |
|
452 } |
|
453 } |
|
454 |
|
455 mTxInlineFrameUsed = 18; |
|
456 |
|
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. |
|
460 |
|
461 const char *methodHeader = mTransaction->RequestHead()->Method().get(); |
|
462 |
|
463 // The header block length |
|
464 uint16_t count = hdrHash.Count() + 5; /* method, path, version, host, scheme */ |
|
465 CompressToFrame(count); |
|
466 |
|
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); |
|
475 |
|
476 CompressToFrame(NS_LITERAL_CSTRING(":host")); |
|
477 CompressToFrame(hostHeader); |
|
478 CompressToFrame(NS_LITERAL_CSTRING(":scheme")); |
|
479 CompressToFrame(NS_LITERAL_CSTRING("https")); |
|
480 |
|
481 hdrHash.Enumerate(hdrHashEnumerate, this); |
|
482 CompressFlushFrame(); |
|
483 |
|
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); |
|
487 |
|
488 MOZ_ASSERT(!mTxInlineFrame[4], "Size greater than 24 bits"); |
|
489 |
|
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. |
|
492 |
|
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 |
|
498 |
|
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 } |
|
514 |
|
515 Telemetry::Accumulate(Telemetry::SPDY_SYN_SIZE, mTxInlineFrameUsed - 18); |
|
516 |
|
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()); |
|
522 |
|
523 Telemetry::Accumulate(Telemetry::SPDY_SYN_RATIO, ratio); |
|
524 return NS_OK; |
|
525 } |
|
526 |
|
527 void |
|
528 SpdyStream3::AdjustInitialWindow() |
|
529 { |
|
530 MOZ_ASSERT(mSession->PushAllowance() <= ASpdySession::kInitialRwin); |
|
531 |
|
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. |
|
535 |
|
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 |
|
547 |
|
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 } |
|
553 |
|
554 // For server pushes we also want to include in the ack any data that has been |
|
555 // buffered but unacknowledged. |
|
556 |
|
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); |
|
572 |
|
573 SpdySession3::EnsureBuffer(mTxInlineFrame, |
|
574 mTxInlineFrameUsed + 16, |
|
575 mTxInlineFrameUsed, |
|
576 mTxInlineFrameSize); |
|
577 |
|
578 unsigned char *packet = mTxInlineFrame.get() + mTxInlineFrameUsed; |
|
579 mTxInlineFrameUsed += 16; |
|
580 |
|
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 |
|
586 |
|
587 uint32_t id = PR_htonl(stream->mStreamID); |
|
588 memcpy(packet + 8, &id, 4); |
|
589 memcpy(packet + 12, &toack, 4); |
|
590 |
|
591 stream->mLocalWindow += PR_ntohl(toack); |
|
592 LOG3(("AdjustInitialwindow %p 0x%X %u\n", |
|
593 this, stream->mStreamID, PR_ntohl(toack))); |
|
594 } |
|
595 |
|
596 void |
|
597 SpdyStream3::UpdateTransportReadEvents(uint32_t count) |
|
598 { |
|
599 mTotalRead += count; |
|
600 |
|
601 mTransaction->OnTransportStatus(mSocketTransport, |
|
602 NS_NET_STATUS_RECEIVING_FROM, |
|
603 mTotalRead); |
|
604 } |
|
605 |
|
606 void |
|
607 SpdyStream3::UpdateTransportSendEvents(uint32_t count) |
|
608 { |
|
609 mTotalSent += count; |
|
610 |
|
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 } |
|
627 |
|
628 if (mUpstreamState != SENDING_FIN_STREAM) |
|
629 mTransaction->OnTransportStatus(mSocketTransport, |
|
630 NS_NET_STATUS_SENDING_TO, |
|
631 mTotalSent); |
|
632 |
|
633 if (!mSentWaitingFor && !mRequestBodyLenRemaining) { |
|
634 mSentWaitingFor = 1; |
|
635 mTransaction->OnTransportStatus(mSocketTransport, |
|
636 NS_NET_STATUS_WAITING_FOR, |
|
637 0); |
|
638 } |
|
639 } |
|
640 |
|
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. |
|
649 |
|
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. |
|
653 |
|
654 LOG3(("SpdyStream3::TransmitFrame %p inline=%d stream=%d", |
|
655 this, mTxInlineFrameUsed, mTxStreamFrameSize)); |
|
656 if (countUsed) |
|
657 *countUsed = 0; |
|
658 |
|
659 if (!mTxInlineFrameUsed) { |
|
660 MOZ_ASSERT(!buf); |
|
661 return NS_OK; |
|
662 } |
|
663 |
|
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"); |
|
668 |
|
669 uint32_t transmittedCount; |
|
670 nsresult rv; |
|
671 |
|
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 } |
|
687 |
|
688 rv = |
|
689 mSegmentReader->CommitToSegmentSize(mTxStreamFrameSize + mTxInlineFrameUsed, |
|
690 forceCommitment); |
|
691 |
|
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; |
|
698 |
|
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 |
|
703 |
|
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)); |
|
710 |
|
711 MOZ_ASSERT(rv != NS_BASE_STREAM_WOULD_BLOCK, |
|
712 "inconsistent inline commitment result"); |
|
713 |
|
714 if (NS_FAILED(rv)) |
|
715 return rv; |
|
716 |
|
717 MOZ_ASSERT(transmittedCount == mTxInlineFrameUsed, |
|
718 "inconsistent inline commitment count"); |
|
719 |
|
720 SpdySession3::LogIO(mSession, this, "Writing from Inline Buffer", |
|
721 reinterpret_cast<char*>(mTxInlineFrame.get()), |
|
722 transmittedCount); |
|
723 |
|
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 } |
|
732 |
|
733 rv = mSegmentReader->OnReadSegment(buf, mTxStreamFrameSize, |
|
734 &transmittedCount); |
|
735 |
|
736 LOG3(("SpdyStream3::TransmitFrame for regular session=%p " |
|
737 "stream=%p result %x len=%d", |
|
738 mSession, this, rv, transmittedCount)); |
|
739 |
|
740 MOZ_ASSERT(rv != NS_BASE_STREAM_WOULD_BLOCK, |
|
741 "inconsistent stream commitment result"); |
|
742 |
|
743 if (NS_FAILED(rv)) |
|
744 return rv; |
|
745 |
|
746 MOZ_ASSERT(transmittedCount == mTxStreamFrameSize, |
|
747 "inconsistent stream commitment count"); |
|
748 |
|
749 SpdySession3::LogIO(mSession, this, "Writing from Transaction Buffer", |
|
750 buf, transmittedCount); |
|
751 |
|
752 *countUsed += mTxStreamFrameSize; |
|
753 } |
|
754 |
|
755 // calling this will trigger waiting_for if mRequestBodyLenRemaining is 0 |
|
756 UpdateTransportSendEvents(mTxInlineFrameUsed + mTxStreamFrameSize); |
|
757 |
|
758 mTxInlineFrameUsed = 0; |
|
759 mTxStreamFrameSize = 0; |
|
760 |
|
761 return NS_OK; |
|
762 } |
|
763 |
|
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 } |
|
772 |
|
773 void |
|
774 SpdyStream3::GenerateDataFrameHeader(uint32_t dataLength, bool lastFrame) |
|
775 { |
|
776 LOG3(("SpdyStream3::GenerateDataFrameHeader %p len=%d last=%d", |
|
777 this, dataLength, lastFrame)); |
|
778 |
|
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"); |
|
783 |
|
784 NetworkEndian::writeUint32(mTxInlineFrame, mStreamID); |
|
785 NetworkEndian::writeUint32(mTxInlineFrame + 1 * sizeof(uint32_t), dataLength); |
|
786 |
|
787 MOZ_ASSERT(!(mTxInlineFrame[0] & 0x80), "control bit set unexpectedly"); |
|
788 MOZ_ASSERT(!mTxInlineFrame[4], "flag bits set unexpectedly"); |
|
789 |
|
790 mTxInlineFrameUsed = 8; |
|
791 mTxStreamFrameSize = dataLength; |
|
792 |
|
793 if (lastFrame) { |
|
794 mTxInlineFrame[4] |= SpdySession3::kFlag_Data_FIN; |
|
795 if (dataLength) |
|
796 mSentFinOnData = 1; |
|
797 } |
|
798 } |
|
799 |
|
800 void |
|
801 SpdyStream3::CompressToFrame(const nsACString &str) |
|
802 { |
|
803 CompressToFrame(str.BeginReading(), str.Length()); |
|
804 } |
|
805 |
|
806 void |
|
807 SpdyStream3::CompressToFrame(const nsACString *str) |
|
808 { |
|
809 CompressToFrame(str->BeginReading(), str->Length()); |
|
810 } |
|
811 |
|
812 // Dictionary taken from |
|
813 // http://dev.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3 |
|
814 |
|
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 }; |
|
995 |
|
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; |
|
1003 |
|
1004 context->avail_in = blockLen; |
|
1005 context->next_in = reinterpret_cast<unsigned char *>(blockStart); |
|
1006 bool triedDictionary = false; |
|
1007 |
|
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); |
|
1014 |
|
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 } |
|
1020 |
|
1021 triedDictionary = true; |
|
1022 inflateSetDictionary(context, kDictionary, sizeof(kDictionary)); |
|
1023 } |
|
1024 |
|
1025 if (zlib_rv == Z_DATA_ERROR) |
|
1026 return NS_ERROR_ILLEGAL_VALUE; |
|
1027 |
|
1028 if (zlib_rv == Z_MEM_ERROR) |
|
1029 return NS_ERROR_FAILURE; |
|
1030 |
|
1031 // zlib's inflate() decreases context->avail_out by the amount it places |
|
1032 // in the output buffer |
|
1033 |
|
1034 mDecompressBufferUsed += mDecompressBufferSize - mDecompressBufferUsed - |
|
1035 context->avail_out; |
|
1036 |
|
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 } |
|
1052 |
|
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; |
|
1064 |
|
1065 do { |
|
1066 uint32_t numPairs = NetworkEndian::readUint32(nvpair - 1 * sizeof(uint32_t)); |
|
1067 |
|
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 } |
|
1088 |
|
1089 // that pair didn't match - try the next one in this block |
|
1090 nvpair += 8 + nameLen + valueLen; |
|
1091 } |
|
1092 |
|
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); |
|
1097 |
|
1098 return NS_ERROR_NOT_AVAILABLE; |
|
1099 } |
|
1100 |
|
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; |
|
1113 |
|
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; |
|
1118 |
|
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 } |
|
1125 |
|
1126 aHeadersOut.Truncate(); |
|
1127 aHeadersOut.SetCapacity(mDecompressBufferUsed + 64); |
|
1128 |
|
1129 // Connection, Keep-Alive and chunked transfer encodings are to be |
|
1130 // removed. |
|
1131 |
|
1132 // Content-Length is 'advisory'.. we will not strip it because it can |
|
1133 // create UI feedback. |
|
1134 |
|
1135 aHeadersOut.Append(version); |
|
1136 aHeadersOut.Append(NS_LITERAL_CSTRING(" ")); |
|
1137 aHeadersOut.Append(status); |
|
1138 aHeadersOut.Append(NS_LITERAL_CSTRING("\r\n")); |
|
1139 |
|
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; |
|
1146 |
|
1147 do { |
|
1148 uint32_t numPairs = NetworkEndian::readUint32(nvpair - 1 * sizeof(uint32_t)); |
|
1149 |
|
1150 for (uint32_t index = 0; index < numPairs; ++index) { |
|
1151 if (lastHeaderByte < nvpair + 4) |
|
1152 return NS_ERROR_ILLEGAL_VALUE; |
|
1153 |
|
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; |
|
1158 |
|
1159 nsDependentCSubstring nameString = |
|
1160 Substring(reinterpret_cast<const char *>(nvpair) + 4, |
|
1161 reinterpret_cast<const char *>(nvpair) + 4 + nameLen); |
|
1162 |
|
1163 if (lastHeaderByte < nvpair + 8 + nameLen) |
|
1164 return NS_ERROR_ILLEGAL_VALUE; |
|
1165 |
|
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); |
|
1175 |
|
1176 LOG3(("SpdyStream3::ConvertHeaders session=%p stream=%p " |
|
1177 "upper case response header found. [%s]\n", |
|
1178 mSession, this, toLog.get())); |
|
1179 |
|
1180 return NS_ERROR_ILLEGAL_VALUE; |
|
1181 } |
|
1182 |
|
1183 // check for null characters |
|
1184 if (*cPtr == '\0') |
|
1185 return NS_ERROR_ILLEGAL_VALUE; |
|
1186 } |
|
1187 |
|
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)); |
|
1196 |
|
1197 return NS_ERROR_ILLEGAL_VALUE; |
|
1198 } |
|
1199 |
|
1200 uint32_t valueLen = |
|
1201 (nvpair[4 + nameLen] << 24) + (nvpair[5 + nameLen] << 16) + |
|
1202 (nvpair[6 + nameLen] << 8) + nvpair[7 + nameLen]; |
|
1203 |
|
1204 if (lastHeaderByte < nvpair + 8 + nameLen + valueLen) |
|
1205 return NS_ERROR_ILLEGAL_VALUE; |
|
1206 |
|
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); |
|
1215 |
|
1216 aHeadersOut.Append(nameString); |
|
1217 aHeadersOut.Append(NS_LITERAL_CSTRING(": ")); |
|
1218 |
|
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 } |
|
1227 |
|
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 } |
|
1233 |
|
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 } |
|
1239 |
|
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); |
|
1244 |
|
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())); |
|
1248 |
|
1249 // The spdy formatted buffer isnt needed anymore - free it up |
|
1250 mDecompressBuffer = nullptr; |
|
1251 mDecompressBufferSize = 0; |
|
1252 mDecompressBufferUsed = 0; |
|
1253 |
|
1254 return NS_OK; |
|
1255 } |
|
1256 |
|
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 |
|
1262 |
|
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 } |
|
1273 |
|
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 } |
|
1280 |
|
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); |
|
1288 |
|
1289 mZlib->next_in = databuf; |
|
1290 mZlib->avail_in = sizeof(databuf); |
|
1291 ExecuteCompress(Z_NO_FLUSH); |
|
1292 } |
|
1293 |
|
1294 |
|
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 |
|
1300 |
|
1301 unsigned char lenbuf[sizeof(len)]; |
|
1302 NetworkEndian::writeUint32(lenbuf, len); |
|
1303 |
|
1304 // write out the length |
|
1305 mZlib->next_in = lenbuf; |
|
1306 mZlib->avail_in = sizeof(lenbuf); |
|
1307 ExecuteCompress(Z_NO_FLUSH); |
|
1308 |
|
1309 // write out the data |
|
1310 mZlib->next_in = (unsigned char *)data; |
|
1311 mZlib->avail_in = len; |
|
1312 ExecuteCompress(Z_NO_FLUSH); |
|
1313 } |
|
1314 |
|
1315 void |
|
1316 SpdyStream3::CompressFlushFrame() |
|
1317 { |
|
1318 mZlib->next_in = (unsigned char *) ""; |
|
1319 mZlib->avail_in = 0; |
|
1320 ExecuteCompress(Z_SYNC_FLUSH); |
|
1321 } |
|
1322 |
|
1323 void |
|
1324 SpdyStream3::Close(nsresult reason) |
|
1325 { |
|
1326 mTransaction->Close(reason); |
|
1327 } |
|
1328 |
|
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 } |
|
1339 |
|
1340 //----------------------------------------------------------------------------- |
|
1341 // nsAHttpSegmentReader |
|
1342 //----------------------------------------------------------------------------- |
|
1343 |
|
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)); |
|
1351 |
|
1352 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); |
|
1353 MOZ_ASSERT(mSegmentReader, "OnReadSegment with null mSegmentReader"); |
|
1354 |
|
1355 nsresult rv = NS_ERROR_UNEXPECTED; |
|
1356 uint32_t dataLength; |
|
1357 |
|
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) |
|
1366 |
|
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 } |
|
1380 |
|
1381 ChangeState(GENERATING_REQUEST_BODY); |
|
1382 break; |
|
1383 } |
|
1384 MOZ_ASSERT(*countRead == count, "Header parsing not complete but unused data"); |
|
1385 break; |
|
1386 |
|
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; |
|
1396 |
|
1397 dataLength = std::min(count, mChunkSize); |
|
1398 |
|
1399 if (dataLength > mRemoteWindow) |
|
1400 dataLength = static_cast<uint32_t>(mRemoteWindow); |
|
1401 |
|
1402 LOG3(("SpdyStream3 this=%p id 0x%X remote window is %d. Chunk is %d\n", |
|
1403 this, mStreamID, mRemoteWindow, dataLength)); |
|
1404 mRemoteWindow -= dataLength; |
|
1405 |
|
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 |
|
1415 |
|
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"); |
|
1421 |
|
1422 LOG3(("TransmitFrame() rv=%x returning %d data bytes. " |
|
1423 "Header is %d Body is %d.", |
|
1424 rv, *countRead, mTxInlineFrameUsed, mTxStreamFrameSize)); |
|
1425 |
|
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; |
|
1431 |
|
1432 // If that frame was all sent, look for another one |
|
1433 if (!mTxInlineFrameUsed) |
|
1434 ChangeState(GENERATING_REQUEST_BODY); |
|
1435 break; |
|
1436 |
|
1437 case SENDING_FIN_STREAM: |
|
1438 MOZ_ASSERT(false, "resuming partial fin stream out of OnReadSegment"); |
|
1439 break; |
|
1440 |
|
1441 default: |
|
1442 MOZ_ASSERT(false, "SpdyStream3::OnReadSegment non-write state"); |
|
1443 break; |
|
1444 } |
|
1445 |
|
1446 return rv; |
|
1447 } |
|
1448 |
|
1449 //----------------------------------------------------------------------------- |
|
1450 // nsAHttpSegmentWriter |
|
1451 //----------------------------------------------------------------------------- |
|
1452 |
|
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)); |
|
1460 |
|
1461 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); |
|
1462 MOZ_ASSERT(mSegmentWriter); |
|
1463 |
|
1464 if (!mPushSource) |
|
1465 return mSegmentWriter->OnWriteSegment(buf, count, countWritten); |
|
1466 |
|
1467 nsresult rv; |
|
1468 rv = mPushSource->GetBufferedData(buf, count, countWritten); |
|
1469 if (NS_FAILED(rv)) |
|
1470 return rv; |
|
1471 |
|
1472 mSession->ConnectPushedStream(this); |
|
1473 return NS_OK; |
|
1474 } |
|
1475 |
|
1476 } // namespace mozilla::net |
|
1477 } // namespace mozilla |
|
1478 |