Thu, 15 Jan 2015 21:03:48 +0100
Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 ts=8 et tw=80 : */
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "mozilla/net/FTPChannelParent.h"
9 #include "nsFTPChannel.h"
10 #include "nsNetUtil.h"
11 #include "nsFtpProtocolHandler.h"
12 #include "mozilla/ipc/InputStreamUtils.h"
13 #include "mozilla/ipc/URIUtils.h"
14 #include "mozilla/unused.h"
15 #include "SerializedLoadContext.h"
17 using namespace mozilla::ipc;
19 #undef LOG
20 #define LOG(args) PR_LOG(gFTPLog, PR_LOG_DEBUG, args)
22 namespace mozilla {
23 namespace net {
25 FTPChannelParent::FTPChannelParent(nsILoadContext* aLoadContext, PBOverrideStatus aOverrideStatus)
26 : mIPCClosed(false)
27 , mLoadContext(aLoadContext)
28 , mPBOverride(aOverrideStatus)
29 , mStatus(NS_OK)
30 , mDivertingFromChild(false)
31 , mDivertedOnStartRequest(false)
32 , mSuspendedForDiversion(false)
33 {
34 nsIProtocolHandler* handler;
35 CallGetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "ftp", &handler);
36 NS_ASSERTION(handler, "no ftp handler");
37 }
39 FTPChannelParent::~FTPChannelParent()
40 {
41 gFtpHandler->Release();
42 }
44 void
45 FTPChannelParent::ActorDestroy(ActorDestroyReason why)
46 {
47 // We may still have refcount>0 if the channel hasn't called OnStopRequest
48 // yet, but we must not send any more msgs to child.
49 mIPCClosed = true;
50 }
52 //-----------------------------------------------------------------------------
53 // FTPChannelParent::nsISupports
54 //-----------------------------------------------------------------------------
56 NS_IMPL_ISUPPORTS(FTPChannelParent,
57 nsIStreamListener,
58 nsIParentChannel,
59 nsIInterfaceRequestor,
60 nsIRequestObserver)
62 //-----------------------------------------------------------------------------
63 // FTPChannelParent::PFTPChannelParent
64 //-----------------------------------------------------------------------------
66 //-----------------------------------------------------------------------------
67 // FTPChannelParent methods
68 //-----------------------------------------------------------------------------
70 bool
71 FTPChannelParent::Init(const FTPChannelCreationArgs& aArgs)
72 {
73 switch (aArgs.type()) {
74 case FTPChannelCreationArgs::TFTPChannelOpenArgs:
75 {
76 const FTPChannelOpenArgs& a = aArgs.get_FTPChannelOpenArgs();
77 return DoAsyncOpen(a.uri(), a.startPos(), a.entityID(), a.uploadStream());
78 }
79 case FTPChannelCreationArgs::TFTPChannelConnectArgs:
80 {
81 const FTPChannelConnectArgs& cArgs = aArgs.get_FTPChannelConnectArgs();
82 return ConnectChannel(cArgs.channelId());
83 }
84 default:
85 NS_NOTREACHED("unknown open type");
86 return false;
87 }
88 }
90 bool
91 FTPChannelParent::DoAsyncOpen(const URIParams& aURI,
92 const uint64_t& aStartPos,
93 const nsCString& aEntityID,
94 const OptionalInputStreamParams& aUploadStream)
95 {
96 nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
97 if (!uri)
98 return false;
100 #ifdef DEBUG
101 nsCString uriSpec;
102 uri->GetSpec(uriSpec);
103 LOG(("FTPChannelParent DoAsyncOpen [this=%p uri=%s]\n",
104 this, uriSpec.get()));
105 #endif
107 nsresult rv;
108 nsCOMPtr<nsIIOService> ios(do_GetIOService(&rv));
109 if (NS_FAILED(rv))
110 return SendFailedAsyncOpen(rv);
112 nsCOMPtr<nsIChannel> chan;
113 rv = NS_NewChannel(getter_AddRefs(chan), uri, ios);
114 if (NS_FAILED(rv))
115 return SendFailedAsyncOpen(rv);
117 mChannel = static_cast<nsFtpChannel*>(chan.get());
119 if (mPBOverride != kPBOverride_Unset) {
120 mChannel->SetPrivate(mPBOverride == kPBOverride_Private ? true : false);
121 }
123 rv = mChannel->SetNotificationCallbacks(this);
124 if (NS_FAILED(rv))
125 return SendFailedAsyncOpen(rv);
127 nsTArray<mozilla::ipc::FileDescriptor> fds;
128 nsCOMPtr<nsIInputStream> upload = DeserializeInputStream(aUploadStream, fds);
129 if (upload) {
130 // contentType and contentLength are ignored
131 rv = mChannel->SetUploadStream(upload, EmptyCString(), 0);
132 if (NS_FAILED(rv))
133 return SendFailedAsyncOpen(rv);
134 }
136 rv = mChannel->ResumeAt(aStartPos, aEntityID);
137 if (NS_FAILED(rv))
138 return SendFailedAsyncOpen(rv);
140 rv = mChannel->AsyncOpen(this, nullptr);
141 if (NS_FAILED(rv))
142 return SendFailedAsyncOpen(rv);
144 return true;
145 }
147 bool
148 FTPChannelParent::ConnectChannel(const uint32_t& channelId)
149 {
150 nsresult rv;
152 LOG(("Looking for a registered channel [this=%p, id=%d]", this, channelId));
154 nsCOMPtr<nsIChannel> channel;
155 rv = NS_LinkRedirectChannels(channelId, this, getter_AddRefs(channel));
156 if (NS_SUCCEEDED(rv))
157 mChannel = static_cast<nsFtpChannel*>(channel.get());
159 LOG((" found channel %p, rv=%08x", mChannel.get(), rv));
161 return true;
162 }
164 bool
165 FTPChannelParent::RecvCancel(const nsresult& status)
166 {
167 if (mChannel)
168 mChannel->Cancel(status);
169 return true;
170 }
172 bool
173 FTPChannelParent::RecvSuspend()
174 {
175 if (mChannel)
176 mChannel->Suspend();
177 return true;
178 }
180 bool
181 FTPChannelParent::RecvResume()
182 {
183 if (mChannel)
184 mChannel->Resume();
185 return true;
186 }
188 bool
189 FTPChannelParent::RecvDivertOnDataAvailable(const nsCString& data,
190 const uint64_t& offset,
191 const uint32_t& count)
192 {
193 if (NS_WARN_IF(!mDivertingFromChild)) {
194 MOZ_ASSERT(mDivertingFromChild,
195 "Cannot RecvDivertOnDataAvailable if diverting is not set!");
196 FailDiversion(NS_ERROR_UNEXPECTED);
197 return false;
198 }
200 // Drop OnDataAvailables if the parent was canceled already.
201 if (NS_FAILED(mStatus)) {
202 return true;
203 }
205 nsCOMPtr<nsIInputStream> stringStream;
206 nsresult rv = NS_NewByteInputStream(getter_AddRefs(stringStream), data.get(),
207 count, NS_ASSIGNMENT_DEPEND);
208 if (NS_FAILED(rv)) {
209 if (mChannel) {
210 mChannel->Cancel(rv);
211 }
212 mStatus = rv;
213 return true;
214 }
216 rv = OnDataAvailable(mChannel, nullptr, stringStream, offset, count);
218 stringStream->Close();
219 if (NS_FAILED(rv)) {
220 if (mChannel) {
221 mChannel->Cancel(rv);
222 }
223 mStatus = rv;
224 }
225 return true;
226 }
228 bool
229 FTPChannelParent::RecvDivertOnStopRequest(const nsresult& statusCode)
230 {
231 if (NS_WARN_IF(!mDivertingFromChild)) {
232 MOZ_ASSERT(mDivertingFromChild,
233 "Cannot RecvDivertOnStopRequest if diverting is not set!");
234 FailDiversion(NS_ERROR_UNEXPECTED);
235 return false;
236 }
238 // Honor the channel's status even if the underlying transaction completed.
239 nsresult status = NS_FAILED(mStatus) ? mStatus : statusCode;
241 // Reset fake pending status in case OnStopRequest has already been called.
242 if (mChannel) {
243 mChannel->ForcePending(false);
244 }
246 OnStopRequest(mChannel, nullptr, status);
247 return true;
248 }
250 bool
251 FTPChannelParent::RecvDivertComplete()
252 {
253 if (NS_WARN_IF(!mDivertingFromChild)) {
254 MOZ_ASSERT(mDivertingFromChild,
255 "Cannot RecvDivertComplete if diverting is not set!");
256 FailDiversion(NS_ERROR_UNEXPECTED);
257 return false;
258 }
260 nsresult rv = ResumeForDiversion();
261 if (NS_WARN_IF(NS_FAILED(rv))) {
262 FailDiversion(NS_ERROR_UNEXPECTED);
263 return false;
264 }
266 return true;
267 }
269 //-----------------------------------------------------------------------------
270 // FTPChannelParent::nsIRequestObserver
271 //-----------------------------------------------------------------------------
273 NS_IMETHODIMP
274 FTPChannelParent::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
275 {
276 LOG(("FTPChannelParent::OnStartRequest [this=%p]\n", this));
278 if (mDivertingFromChild) {
279 MOZ_RELEASE_ASSERT(mDivertToListener,
280 "Cannot divert if listener is unset!");
281 return mDivertToListener->OnStartRequest(aRequest, aContext);
282 }
284 nsCOMPtr<nsIChannel> chan = do_QueryInterface(aRequest);
285 MOZ_ASSERT(chan);
286 NS_ENSURE_TRUE(chan, NS_ERROR_UNEXPECTED);
288 int64_t contentLength;
289 chan->GetContentLength(&contentLength);
290 nsCString contentType;
291 chan->GetContentType(contentType);
293 nsCString entityID;
294 nsCOMPtr<nsIResumableChannel> resChan = do_QueryInterface(aRequest);
295 MOZ_ASSERT(resChan); // both FTP and HTTP should implement nsIResumableChannel
296 if (resChan) {
297 resChan->GetEntityID(entityID);
298 }
300 nsCOMPtr<nsIFTPChannel> ftpChan = do_QueryInterface(aRequest);
301 PRTime lastModified = 0;
302 if (ftpChan) {
303 ftpChan->GetLastModifiedTime(&lastModified);
304 } else {
305 // Temporary hack: if we were redirected to use an HTTP channel (ie FTP is
306 // using an HTTP proxy), cancel, as we don't support those redirects yet.
307 aRequest->Cancel(NS_ERROR_NOT_IMPLEMENTED);
308 }
310 URIParams uriparam;
311 nsCOMPtr<nsIURI> uri;
312 chan->GetURI(getter_AddRefs(uri));
313 SerializeURI(uri, uriparam);
315 if (mIPCClosed || !SendOnStartRequest(mStatus, contentLength, contentType,
316 lastModified, entityID, uriparam)) {
317 return NS_ERROR_UNEXPECTED;
318 }
320 return NS_OK;
321 }
323 NS_IMETHODIMP
324 FTPChannelParent::OnStopRequest(nsIRequest* aRequest,
325 nsISupports* aContext,
326 nsresult aStatusCode)
327 {
328 LOG(("FTPChannelParent::OnStopRequest: [this=%p status=%ul]\n",
329 this, aStatusCode));
331 if (mDivertingFromChild) {
332 MOZ_RELEASE_ASSERT(mDivertToListener,
333 "Cannot divert if listener is unset!");
334 return mDivertToListener->OnStopRequest(aRequest, aContext, aStatusCode);
335 }
337 if (mIPCClosed || !SendOnStopRequest(aStatusCode)) {
338 return NS_ERROR_UNEXPECTED;
339 }
341 return NS_OK;
342 }
344 //-----------------------------------------------------------------------------
345 // FTPChannelParent::nsIStreamListener
346 //-----------------------------------------------------------------------------
348 NS_IMETHODIMP
349 FTPChannelParent::OnDataAvailable(nsIRequest* aRequest,
350 nsISupports* aContext,
351 nsIInputStream* aInputStream,
352 uint64_t aOffset,
353 uint32_t aCount)
354 {
355 LOG(("FTPChannelParent::OnDataAvailable [this=%p]\n", this));
357 if (mDivertingFromChild) {
358 MOZ_RELEASE_ASSERT(mDivertToListener,
359 "Cannot divert if listener is unset!");
360 return mDivertToListener->OnDataAvailable(aRequest, aContext, aInputStream,
361 aOffset, aCount);
362 }
364 nsCString data;
365 nsresult rv = NS_ReadInputStreamToString(aInputStream, data, aCount);
366 if (NS_FAILED(rv))
367 return rv;
369 if (mIPCClosed || !SendOnDataAvailable(mStatus, data, aOffset, aCount))
370 return NS_ERROR_UNEXPECTED;
372 return NS_OK;
373 }
375 //-----------------------------------------------------------------------------
376 // FTPChannelParent::nsIParentChannel
377 //-----------------------------------------------------------------------------
379 NS_IMETHODIMP
380 FTPChannelParent::SetParentListener(HttpChannelParentListener* aListener)
381 {
382 // Do not need ptr to HttpChannelParentListener.
383 return NS_OK;
384 }
386 NS_IMETHODIMP
387 FTPChannelParent::Delete()
388 {
389 if (mIPCClosed || !SendDeleteSelf())
390 return NS_ERROR_UNEXPECTED;
392 return NS_OK;
393 }
395 //-----------------------------------------------------------------------------
396 // FTPChannelParent::nsIInterfaceRequestor
397 //-----------------------------------------------------------------------------
399 NS_IMETHODIMP
400 FTPChannelParent::GetInterface(const nsIID& uuid, void** result)
401 {
402 // Only support nsILoadContext if child channel's callbacks did too
403 if (uuid.Equals(NS_GET_IID(nsILoadContext)) && mLoadContext) {
404 NS_ADDREF(mLoadContext);
405 *result = static_cast<nsILoadContext*>(mLoadContext);
406 return NS_OK;
407 }
409 return QueryInterface(uuid, result);
410 }
412 //-----------------------------------------------------------------------------
413 // FTPChannelParent::ADivertableParentChannel
414 //-----------------------------------------------------------------------------
415 nsresult
416 FTPChannelParent::SuspendForDiversion()
417 {
418 MOZ_ASSERT(mChannel);
419 if (NS_WARN_IF(mDivertingFromChild)) {
420 MOZ_ASSERT(!mDivertingFromChild, "Already suspended for diversion!");
421 return NS_ERROR_UNEXPECTED;
422 }
424 // Try suspending the channel. Allow it to fail, since OnStopRequest may have
425 // been called and thus the channel may not be pending.
426 nsresult rv = mChannel->Suspend();
427 MOZ_ASSERT(NS_SUCCEEDED(rv) || rv == NS_ERROR_NOT_AVAILABLE);
428 mSuspendedForDiversion = NS_SUCCEEDED(rv);
430 // Once this is set, no more OnStart/OnData/OnStop callbacks should be sent
431 // to the child.
432 mDivertingFromChild = true;
434 return NS_OK;
435 }
437 /* private, supporting function for ADivertableParentChannel */
438 nsresult
439 FTPChannelParent::ResumeForDiversion()
440 {
441 MOZ_ASSERT(mChannel);
442 MOZ_ASSERT(mDivertToListener);
443 if (NS_WARN_IF(!mDivertingFromChild)) {
444 MOZ_ASSERT(mDivertingFromChild,
445 "Cannot ResumeForDiversion if not diverting!");
446 return NS_ERROR_UNEXPECTED;
447 }
449 if (mSuspendedForDiversion) {
450 nsresult rv = mChannel->Resume();
451 if (NS_WARN_IF(NS_FAILED(rv))) {
452 FailDiversion(NS_ERROR_UNEXPECTED, true);
453 return rv;
454 }
455 mSuspendedForDiversion = false;
456 }
458 // Delete() will tear down IPDL, but ref from underlying nsFTPChannel will
459 // keep us alive if there's more data to be delivered to listener.
460 if (NS_WARN_IF(NS_FAILED(Delete()))) {
461 FailDiversion(NS_ERROR_UNEXPECTED);
462 return NS_ERROR_UNEXPECTED;
463 }
464 return NS_OK;
465 }
467 void
468 FTPChannelParent::DivertTo(nsIStreamListener *aListener)
469 {
470 MOZ_ASSERT(aListener);
471 if (NS_WARN_IF(!mDivertingFromChild)) {
472 MOZ_ASSERT(mDivertingFromChild,
473 "Cannot DivertTo new listener if diverting is not set!");
474 return;
475 }
477 if (NS_WARN_IF(mIPCClosed || !SendFlushedForDiversion())) {
478 FailDiversion(NS_ERROR_UNEXPECTED);
479 return;
480 }
482 mDivertToListener = aListener;
484 // Call OnStartRequest and SendDivertMessages asynchronously to avoid
485 // reentering client context.
486 NS_DispatchToCurrentThread(
487 NS_NewRunnableMethod(this, &FTPChannelParent::StartDiversion));
488 return;
489 }
491 void
492 FTPChannelParent::StartDiversion()
493 {
494 if (NS_WARN_IF(!mDivertingFromChild)) {
495 MOZ_ASSERT(mDivertingFromChild,
496 "Cannot StartDiversion if diverting is not set!");
497 return;
498 }
500 // Fake pending status in case OnStopRequest has already been called.
501 if (mChannel) {
502 mChannel->ForcePending(true);
503 }
505 // Call OnStartRequest for the "DivertTo" listener.
506 nsresult rv = OnStartRequest(mChannel, nullptr);
507 if (NS_FAILED(rv)) {
508 if (mChannel) {
509 mChannel->Cancel(rv);
510 }
511 mStatus = rv;
512 return;
513 }
515 // After OnStartRequest has been called, tell FTPChannelChild to divert the
516 // OnDataAvailables and OnStopRequest to this FTPChannelParent.
517 if (NS_WARN_IF(mIPCClosed || !SendDivertMessages())) {
518 FailDiversion(NS_ERROR_UNEXPECTED);
519 return;
520 }
521 }
523 class FTPFailDiversionEvent : public nsRunnable
524 {
525 public:
526 FTPFailDiversionEvent(FTPChannelParent *aChannelParent,
527 nsresult aErrorCode,
528 bool aSkipResume)
529 : mChannelParent(aChannelParent)
530 , mErrorCode(aErrorCode)
531 , mSkipResume(aSkipResume)
532 {
533 MOZ_RELEASE_ASSERT(aChannelParent);
534 MOZ_RELEASE_ASSERT(NS_FAILED(aErrorCode));
535 }
536 NS_IMETHOD Run()
537 {
538 mChannelParent->NotifyDiversionFailed(mErrorCode, mSkipResume);
539 return NS_OK;
540 }
541 private:
542 nsRefPtr<FTPChannelParent> mChannelParent;
543 nsresult mErrorCode;
544 bool mSkipResume;
545 };
547 void
548 FTPChannelParent::FailDiversion(nsresult aErrorCode,
549 bool aSkipResume)
550 {
551 MOZ_RELEASE_ASSERT(NS_FAILED(aErrorCode));
552 MOZ_RELEASE_ASSERT(mDivertingFromChild);
553 MOZ_RELEASE_ASSERT(mDivertToListener);
554 MOZ_RELEASE_ASSERT(mChannel);
556 NS_DispatchToCurrentThread(
557 new FTPFailDiversionEvent(this, aErrorCode, aSkipResume));
558 }
560 void
561 FTPChannelParent::NotifyDiversionFailed(nsresult aErrorCode,
562 bool aSkipResume)
563 {
564 MOZ_RELEASE_ASSERT(NS_FAILED(aErrorCode));
565 MOZ_RELEASE_ASSERT(mDivertingFromChild);
566 MOZ_RELEASE_ASSERT(mDivertToListener);
567 MOZ_RELEASE_ASSERT(mChannel);
569 mChannel->Cancel(aErrorCode);
571 mChannel->ForcePending(false);
573 bool isPending = false;
574 nsresult rv = mChannel->IsPending(&isPending);
575 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
577 // Resume only we suspended earlier.
578 if (mSuspendedForDiversion) {
579 mChannel->Resume();
580 }
581 // Channel has already sent OnStartRequest to the child, so ensure that we
582 // call it here if it hasn't already been called.
583 if (!mDivertedOnStartRequest) {
584 mChannel->ForcePending(true);
585 mDivertToListener->OnStartRequest(mChannel, nullptr);
586 mChannel->ForcePending(false);
587 }
588 // If the channel is pending, it will call OnStopRequest itself; otherwise, do
589 // it here.
590 if (!isPending) {
591 mDivertToListener->OnStopRequest(mChannel, nullptr, aErrorCode);
592 }
593 mDivertToListener = nullptr;
594 mChannel = nullptr;
596 if (!mIPCClosed) {
597 unused << SendDeleteSelf();
598 }
599 }
601 //---------------------
602 } // namespace net
603 } // namespace mozilla