|
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 <algorithm> |
|
17 |
|
18 #include "nsDependentString.h" |
|
19 #include "SpdyPush31.h" |
|
20 |
|
21 namespace mozilla { |
|
22 namespace net { |
|
23 |
|
24 ////////////////////////////////////////// |
|
25 // SpdyPushedStream31 |
|
26 ////////////////////////////////////////// |
|
27 |
|
28 SpdyPushedStream31::SpdyPushedStream31(SpdyPush31TransactionBuffer *aTransaction, |
|
29 SpdySession31 *aSession, |
|
30 SpdyStream31 *aAssociatedStream, |
|
31 uint32_t aID) |
|
32 :SpdyStream31(aTransaction, aSession, |
|
33 0 /* priority is only for sending, so ignore it on push */) |
|
34 , mConsumerStream(nullptr) |
|
35 , mBufferedPush(aTransaction) |
|
36 , mStatus(NS_OK) |
|
37 , mPushCompleted(false) |
|
38 , mDeferCleanupOnSuccess(true) |
|
39 { |
|
40 LOG3(("SpdyPushedStream31 ctor this=%p id=0x%X\n", this, aID)); |
|
41 mStreamID = aID; |
|
42 mBufferedPush->SetPushStream(this); |
|
43 mLoadGroupCI = aAssociatedStream->LoadGroupConnectionInfo(); |
|
44 mLastRead = TimeStamp::Now(); |
|
45 } |
|
46 |
|
47 bool |
|
48 SpdyPushedStream31::GetPushComplete() |
|
49 { |
|
50 return mPushCompleted; |
|
51 } |
|
52 |
|
53 nsresult |
|
54 SpdyPushedStream31::WriteSegments(nsAHttpSegmentWriter *writer, |
|
55 uint32_t count, |
|
56 uint32_t *countWritten) |
|
57 { |
|
58 nsresult rv = SpdyStream31::WriteSegments(writer, count, countWritten); |
|
59 if (NS_SUCCEEDED(rv) && *countWritten) { |
|
60 mLastRead = TimeStamp::Now(); |
|
61 } |
|
62 |
|
63 if (rv == NS_BASE_STREAM_CLOSED) { |
|
64 mPushCompleted = true; |
|
65 rv = NS_OK; // this is what a normal HTTP transaction would do |
|
66 } |
|
67 if (rv != NS_BASE_STREAM_WOULD_BLOCK && NS_FAILED(rv)) |
|
68 mStatus = rv; |
|
69 return rv; |
|
70 } |
|
71 |
|
72 nsresult |
|
73 SpdyPushedStream31::ReadSegments(nsAHttpSegmentReader *, uint32_t, uint32_t *count) |
|
74 { |
|
75 // The SYN_STREAM for this has been processed, so we need to verify |
|
76 // that :host, :scheme, and :path MUST be present |
|
77 nsDependentCSubstring host, scheme, path; |
|
78 nsresult rv; |
|
79 |
|
80 rv = SpdyStream31::FindHeader(NS_LITERAL_CSTRING(":host"), host); |
|
81 if (NS_FAILED(rv)) { |
|
82 LOG3(("SpdyPushedStream31::ReadSegments session=%p ID 0x%X " |
|
83 "push without required :host\n", mSession, mStreamID)); |
|
84 return rv; |
|
85 } |
|
86 |
|
87 rv = SpdyStream31::FindHeader(NS_LITERAL_CSTRING(":scheme"), scheme); |
|
88 if (NS_FAILED(rv)) { |
|
89 LOG3(("SpdyPushedStream31::ReadSegments session=%p ID 0x%X " |
|
90 "push without required :scheme\n", mSession, mStreamID)); |
|
91 return rv; |
|
92 } |
|
93 |
|
94 rv = SpdyStream31::FindHeader(NS_LITERAL_CSTRING(":path"), path); |
|
95 if (NS_FAILED(rv)) { |
|
96 LOG3(("SpdyPushedStream31::ReadSegments session=%p ID 0x%X " |
|
97 "push without required :host\n", mSession, mStreamID)); |
|
98 return rv; |
|
99 } |
|
100 |
|
101 CreatePushHashKey(nsCString(scheme), nsCString(host), |
|
102 mSession->Serial(), path, |
|
103 mOrigin, mHashKey); |
|
104 |
|
105 LOG3(("SpdyPushStream31 0x%X hash key %s\n", mStreamID, mHashKey.get())); |
|
106 |
|
107 // the write side of a pushed transaction just involves manipulating a little state |
|
108 SpdyStream31::mSentFinOnData = 1; |
|
109 SpdyStream31::mSynFrameComplete = 1; |
|
110 SpdyStream31::ChangeState(UPSTREAM_COMPLETE); |
|
111 *count = 0; |
|
112 return NS_OK; |
|
113 } |
|
114 |
|
115 bool |
|
116 SpdyPushedStream31::GetHashKey(nsCString &key) |
|
117 { |
|
118 if (mHashKey.IsEmpty()) |
|
119 return false; |
|
120 |
|
121 key = mHashKey; |
|
122 return true; |
|
123 } |
|
124 |
|
125 void |
|
126 SpdyPushedStream31::ConnectPushedStream(SpdyStream31 *stream) |
|
127 { |
|
128 mSession->ConnectPushedStream(stream); |
|
129 } |
|
130 |
|
131 bool |
|
132 SpdyPushedStream31::IsOrphaned(TimeStamp now) |
|
133 { |
|
134 MOZ_ASSERT(!now.IsNull()); |
|
135 |
|
136 // if spdy is not transmitting, and is also not connected to a consumer |
|
137 // stream, and its been like that for too long then it is oprhaned |
|
138 |
|
139 if (mConsumerStream) |
|
140 return false; |
|
141 |
|
142 bool rv = ((now - mLastRead).ToSeconds() > 30.0); |
|
143 if (rv) { |
|
144 LOG3(("SpdyPushedStream31::IsOrphaned 0x%X IsOrphaned %3.2f\n", |
|
145 mStreamID, (now - mLastRead).ToSeconds())); |
|
146 } |
|
147 return rv; |
|
148 } |
|
149 |
|
150 nsresult |
|
151 SpdyPushedStream31::GetBufferedData(char *buf, |
|
152 uint32_t count, |
|
153 uint32_t *countWritten) |
|
154 { |
|
155 if (NS_FAILED(mStatus)) |
|
156 return mStatus; |
|
157 |
|
158 nsresult rv = mBufferedPush->GetBufferedData(buf, count, countWritten); |
|
159 if (NS_FAILED(rv)) |
|
160 return rv; |
|
161 |
|
162 if (!*countWritten) |
|
163 rv = GetPushComplete() ? NS_BASE_STREAM_CLOSED : NS_BASE_STREAM_WOULD_BLOCK; |
|
164 |
|
165 return rv; |
|
166 } |
|
167 |
|
168 ////////////////////////////////////////// |
|
169 // SpdyPush31TransactionBuffer |
|
170 // This is the nsAHttpTransction owned by the stream when the pushed |
|
171 // stream has not yet been matched with a pull request |
|
172 ////////////////////////////////////////// |
|
173 |
|
174 NS_IMPL_ISUPPORTS0(SpdyPush31TransactionBuffer) |
|
175 |
|
176 SpdyPush31TransactionBuffer::SpdyPush31TransactionBuffer() |
|
177 : mStatus(NS_OK) |
|
178 , mRequestHead(nullptr) |
|
179 , mPushStream(nullptr) |
|
180 , mIsDone(false) |
|
181 , mBufferedHTTP1Size(kDefaultBufferSize) |
|
182 , mBufferedHTTP1Used(0) |
|
183 , mBufferedHTTP1Consumed(0) |
|
184 { |
|
185 mBufferedHTTP1 = new char[mBufferedHTTP1Size]; |
|
186 } |
|
187 |
|
188 SpdyPush31TransactionBuffer::~SpdyPush31TransactionBuffer() |
|
189 { |
|
190 delete mRequestHead; |
|
191 } |
|
192 |
|
193 void |
|
194 SpdyPush31TransactionBuffer::SetConnection(nsAHttpConnection *conn) |
|
195 { |
|
196 } |
|
197 |
|
198 nsAHttpConnection * |
|
199 SpdyPush31TransactionBuffer::Connection() |
|
200 { |
|
201 return nullptr; |
|
202 } |
|
203 |
|
204 void |
|
205 SpdyPush31TransactionBuffer::GetSecurityCallbacks(nsIInterfaceRequestor **outCB) |
|
206 { |
|
207 *outCB = nullptr; |
|
208 } |
|
209 |
|
210 void |
|
211 SpdyPush31TransactionBuffer::OnTransportStatus(nsITransport* transport, |
|
212 nsresult status, uint64_t progress) |
|
213 { |
|
214 } |
|
215 |
|
216 bool |
|
217 SpdyPush31TransactionBuffer::IsDone() |
|
218 { |
|
219 return mIsDone; |
|
220 } |
|
221 |
|
222 nsresult |
|
223 SpdyPush31TransactionBuffer::Status() |
|
224 { |
|
225 return mStatus; |
|
226 } |
|
227 |
|
228 uint32_t |
|
229 SpdyPush31TransactionBuffer::Caps() |
|
230 { |
|
231 return 0; |
|
232 } |
|
233 |
|
234 void |
|
235 SpdyPush31TransactionBuffer::SetDNSWasRefreshed() |
|
236 { |
|
237 } |
|
238 |
|
239 uint64_t |
|
240 SpdyPush31TransactionBuffer::Available() |
|
241 { |
|
242 return mBufferedHTTP1Used - mBufferedHTTP1Consumed; |
|
243 } |
|
244 |
|
245 nsresult |
|
246 SpdyPush31TransactionBuffer::ReadSegments(nsAHttpSegmentReader *reader, |
|
247 uint32_t count, uint32_t *countRead) |
|
248 { |
|
249 *countRead = 0; |
|
250 return NS_ERROR_NOT_IMPLEMENTED; |
|
251 } |
|
252 |
|
253 nsresult |
|
254 SpdyPush31TransactionBuffer::WriteSegments(nsAHttpSegmentWriter *writer, |
|
255 uint32_t count, uint32_t *countWritten) |
|
256 { |
|
257 if ((mBufferedHTTP1Size - mBufferedHTTP1Used) < 20480) { |
|
258 SpdySession31::EnsureBuffer(mBufferedHTTP1, |
|
259 mBufferedHTTP1Size + kDefaultBufferSize, |
|
260 mBufferedHTTP1Used, |
|
261 mBufferedHTTP1Size); |
|
262 } |
|
263 |
|
264 count = std::min(count, mBufferedHTTP1Size - mBufferedHTTP1Used); |
|
265 nsresult rv = writer->OnWriteSegment(mBufferedHTTP1 + mBufferedHTTP1Used, |
|
266 count, countWritten); |
|
267 if (NS_SUCCEEDED(rv)) { |
|
268 mBufferedHTTP1Used += *countWritten; |
|
269 } |
|
270 else if (rv == NS_BASE_STREAM_CLOSED) { |
|
271 mIsDone = true; |
|
272 } |
|
273 |
|
274 if (Available()) { |
|
275 SpdyStream31 *consumer = mPushStream->GetConsumerStream(); |
|
276 |
|
277 if (consumer) { |
|
278 LOG3(("SpdyPush31TransactionBuffer::WriteSegments notifying connection " |
|
279 "consumer data available 0x%X [%u]\n", |
|
280 mPushStream->StreamID(), Available())); |
|
281 mPushStream->ConnectPushedStream(consumer); |
|
282 } |
|
283 } |
|
284 |
|
285 return rv; |
|
286 } |
|
287 |
|
288 uint32_t |
|
289 SpdyPush31TransactionBuffer::Http1xTransactionCount() |
|
290 { |
|
291 return 0; |
|
292 } |
|
293 |
|
294 nsHttpRequestHead * |
|
295 SpdyPush31TransactionBuffer::RequestHead() |
|
296 { |
|
297 if (!mRequestHead) |
|
298 mRequestHead = new nsHttpRequestHead(); |
|
299 return mRequestHead; |
|
300 } |
|
301 |
|
302 nsresult |
|
303 SpdyPush31TransactionBuffer::TakeSubTransactions( |
|
304 nsTArray<nsRefPtr<nsAHttpTransaction> > &outTransactions) |
|
305 { |
|
306 return NS_ERROR_NOT_IMPLEMENTED; |
|
307 } |
|
308 |
|
309 void |
|
310 SpdyPush31TransactionBuffer::SetProxyConnectFailed() |
|
311 { |
|
312 } |
|
313 |
|
314 void |
|
315 SpdyPush31TransactionBuffer::Close(nsresult reason) |
|
316 { |
|
317 mStatus = reason; |
|
318 mIsDone = true; |
|
319 } |
|
320 |
|
321 nsresult |
|
322 SpdyPush31TransactionBuffer::AddTransaction(nsAHttpTransaction *trans) |
|
323 { |
|
324 return NS_ERROR_NOT_IMPLEMENTED; |
|
325 } |
|
326 |
|
327 uint32_t |
|
328 SpdyPush31TransactionBuffer::PipelineDepth() |
|
329 { |
|
330 return 0; |
|
331 } |
|
332 |
|
333 nsresult |
|
334 SpdyPush31TransactionBuffer::SetPipelinePosition(int32_t position) |
|
335 { |
|
336 return NS_OK; |
|
337 } |
|
338 |
|
339 int32_t |
|
340 SpdyPush31TransactionBuffer::PipelinePosition() |
|
341 { |
|
342 return 1; |
|
343 } |
|
344 |
|
345 nsresult |
|
346 SpdyPush31TransactionBuffer::GetBufferedData(char *buf, |
|
347 uint32_t count, |
|
348 uint32_t *countWritten) |
|
349 { |
|
350 *countWritten = std::min(count, static_cast<uint32_t>(Available())); |
|
351 if (*countWritten) { |
|
352 memcpy(buf, mBufferedHTTP1 + mBufferedHTTP1Consumed, *countWritten); |
|
353 mBufferedHTTP1Consumed += *countWritten; |
|
354 } |
|
355 |
|
356 // If all the data has been consumed then reset the buffer |
|
357 if (mBufferedHTTP1Consumed == mBufferedHTTP1Used) { |
|
358 mBufferedHTTP1Consumed = 0; |
|
359 mBufferedHTTP1Used = 0; |
|
360 } |
|
361 |
|
362 return NS_OK; |
|
363 } |
|
364 |
|
365 } // namespace mozilla::net |
|
366 } // namespace mozilla |