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