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