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