Bug 1351163: Part 2 - Add a nsSimpleChannel helper class for creating simple wrapper channels. r=bz r?mayhemer
This helper class allows us to create simple channels that open an underlying
input stream when the channel is first opened, using a closure function,
without having to expose the nsBaseChannel class to non-necko code.
MozReview-Commit-ID: 6jiImdMXUp0
--- a/netwerk/base/moz.build
+++ b/netwerk/base/moz.build
@@ -148,16 +148,17 @@ EXPORTS += [
'nsASocketHandler.h',
'nsAsyncRedirectVerifyHelper.h',
'nsFileStreams.h',
'nsInputStreamPump.h',
'nsMIMEInputStream.h',
'nsNetUtil.h',
'nsReadLine.h',
'nsSerializationHelper.h',
+ 'nsSimpleChannel.h',
'nsSimpleNestedURI.h',
'nsSimpleURI.h',
'nsStreamListenerWrapper.h',
'nsTemporaryFileInputStream.h',
'nsURIHashKey.h',
'nsURLHelper.h',
'nsURLParsers.h',
]
@@ -221,16 +222,17 @@ UNIFIED_SOURCES += [
'nsPACMan.cpp',
'nsPreloadedStream.cpp',
'nsProtocolProxyService.cpp',
'nsProxyInfo.cpp',
'nsRequestObserverProxy.cpp',
'nsSecCheckWrapChannel.cpp',
'nsSerializationHelper.cpp',
'nsServerSocket.cpp',
+ 'nsSimpleChannel.cpp',
'nsSimpleNestedURI.cpp',
'nsSimpleStreamListener.cpp',
'nsSimpleURI.cpp',
'nsSocketTransport2.cpp',
'nsSocketTransportService2.cpp',
'nsStandardURL.cpp',
'nsStreamListenerTee.cpp',
'nsStreamListenerWrapper.cpp',
new file mode 100644
--- /dev/null
+++ b/netwerk/base/nsSimpleChannel.cpp
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsBaseChannel.h"
+#include "nsIInputStream.h"
+#include "nsIRequest.h"
+#include "nsSimpleChannel.h"
+
+namespace mozilla {
+namespace net {
+
+// Like MOZ_TRY, but returns the unwrapped error value rather than a
+// GenericErrorResult on failure.
+#define TRY_VAR(target, expr) \
+ do { \
+ auto result = (expr); \
+ if (result.isErr()) { \
+ return result.unwrapErr(); \
+ } \
+ (target) = result.unwrap(); \
+ } while (0)
+
+class nsSimpleChannel final : public nsBaseChannel
+{
+public:
+ explicit nsSimpleChannel(UniquePtr<nsSimpleChannelCallbacks>&& aCallbacks);
+
+protected:
+ virtual ~nsSimpleChannel() {}
+
+ virtual nsresult OpenContentStream(bool async, nsIInputStream **streamOut,
+ nsIChannel** channel) override;
+
+ virtual nsresult BeginAsyncRead(nsIStreamListener* listener,
+ nsIRequest** request) override;
+
+private:
+ UniquePtr<nsSimpleChannelCallbacks> mCallbacks;;
+};
+
+nsSimpleChannel::nsSimpleChannel(UniquePtr<nsSimpleChannelCallbacks>&& aCallbacks)
+ : mCallbacks(Move(aCallbacks))
+{
+ EnableSynthesizedProgressEvents(true);
+}
+
+nsresult
+nsSimpleChannel::OpenContentStream(bool async, nsIInputStream **streamOut, nsIChannel** channel)
+{
+ nsCOMPtr<nsIInputStream> stream;
+ TRY_VAR(stream, mCallbacks->OpenContentStream(async, this));
+ MOZ_ASSERT(stream);
+
+ *channel = nullptr;
+ stream.forget(streamOut);
+ return NS_OK;
+}
+
+nsresult
+nsSimpleChannel::BeginAsyncRead(nsIStreamListener* listener, nsIRequest** request)
+{
+ nsCOMPtr<nsIRequest> req;
+ TRY_VAR(req, mCallbacks->StartAsyncRead(listener, this));
+
+ req.forget(request);
+ return NS_OK;
+}
+
+#undef TRY_VAR
+
+already_AddRefed<nsIChannel>
+NS_NewSimpleChannelInternal(nsIURI* aURI, nsILoadInfo* aLoadInfo, UniquePtr<nsSimpleChannelCallbacks>&& aCallbacks)
+{
+ RefPtr<nsSimpleChannel> chan = new nsSimpleChannel(Move(aCallbacks));
+
+ chan->SetURI(aURI);
+
+ MOZ_ALWAYS_SUCCEEDS(chan->SetLoadInfo(aLoadInfo));
+
+ return chan.forget();
+}
+
+} // namespace net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/base/nsSimpleChannel.h
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsSimpleChannel_h
+#define nsSimpleChannel_h
+
+#include "mozilla/Result.h"
+#include "mozilla/UniquePtr.h"
+#include "nsCOMPtr.h"
+
+class nsIChannel;
+class nsIInputStream;
+class nsILoadInfo;
+class nsIRequest;
+class nsIStreamListener;
+class nsIURI;
+
+//-----------------------------------------------------------------------------
+
+namespace mozilla {
+
+using InputStreamOrReason = Result<nsCOMPtr<nsIInputStream>, nsresult>;
+using RequestOrReason = Result<nsCOMPtr<nsIRequest>, nsresult>;
+
+namespace net {
+
+class nsSimpleChannelCallbacks
+{
+public:
+ virtual InputStreamOrReason OpenContentStream(bool async, nsIChannel* channel) = 0;
+
+ virtual RequestOrReason StartAsyncRead(nsIStreamListener* stream, nsIChannel* channel) = 0;
+
+ virtual ~nsSimpleChannelCallbacks() {}
+};
+
+template <typename F1, typename F2, typename T>
+class nsSimpleChannelCallbacksImpl final : public nsSimpleChannelCallbacks
+{
+public:
+ nsSimpleChannelCallbacksImpl(F1&& aStartAsyncRead, F2&& aOpenContentStream, T* context)
+ : mStartAsyncRead(aStartAsyncRead)
+ , mOpenContentStream(aOpenContentStream)
+ , mContext(context)
+ {}
+
+ virtual ~nsSimpleChannelCallbacksImpl() {}
+
+ virtual InputStreamOrReason OpenContentStream(bool async, nsIChannel* channel) override
+ {
+ return mOpenContentStream(async, channel, mContext);
+ }
+
+ virtual RequestOrReason StartAsyncRead(nsIStreamListener* listener, nsIChannel* channel) override
+ {
+ return mStartAsyncRead(listener, channel, mContext);
+ }
+
+private:
+ F1 mStartAsyncRead;
+ F2 mOpenContentStream;
+ RefPtr<T> mContext;
+};
+
+already_AddRefed<nsIChannel>
+NS_NewSimpleChannelInternal(nsIURI* aURI, nsILoadInfo* aLoadInfo, UniquePtr<nsSimpleChannelCallbacks>&& aCallbacks);
+
+} // namespace net
+} // namespace mozilla
+
+/**
+ * Creates a simple channel which wraps an input stream created by the given
+ * callbacks. The callbacks are not called until the underlying AsyncOpen2 or
+ * Open2 methods are called, and correspond to the nsBaseChannel::StartAsyncRead
+ * and nsBaseChannel::OpenContentStream methods of the same names.
+ *
+ * The last two arguments of each callback are the created channel instance,
+ * and the ref-counted context object passed to NS_NewSimpleChannel. A strong
+ * reference to that object is guaranteed to be kept alive until the channel
+ * is destroyed.
+ */
+template <typename T, typename F1, typename F2>
+inline already_AddRefed<nsIChannel>
+NS_NewSimpleChannel(nsIURI* aURI, nsILoadInfo* aLoadInfo, T* context, F1&& aStartAsyncRead, F2&& aOpenContentStream)
+{
+ using namespace mozilla;
+
+ auto callbacks = MakeUnique<net::nsSimpleChannelCallbacksImpl<F1, F2, T>>(
+ Move(aStartAsyncRead), Move(aOpenContentStream), context);
+
+ return net::NS_NewSimpleChannelInternal(aURI, aLoadInfo, Move(callbacks));
+}
+
+template <typename T, typename F1>
+inline already_AddRefed<nsIChannel>
+NS_NewSimpleChannel(nsIURI* aURI, nsILoadInfo* aLoadInfo, T* context, F1&& aStartAsyncRead)
+{
+ using namespace mozilla;
+
+ auto openContentStream = [] (bool async, nsIChannel* channel, T* context) {
+ return Err(NS_ERROR_NOT_IMPLEMENTED);
+ };
+
+ return NS_NewSimpleChannel(
+ aURI, aLoadInfo, context, Move(aStartAsyncRead), Move(openContentStream));
+}
+
+#endif // nsSimpleChannel_h
--- a/netwerk/base/nsSyncStreamListener.cpp
+++ b/netwerk/base/nsSyncStreamListener.cpp
@@ -3,16 +3,18 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsIOService.h"
#include "nsSyncStreamListener.h"
#include "nsIPipe.h"
#include "nsThreadUtils.h"
#include <algorithm>
+using namespace mozilla::net;
+
nsresult
nsSyncStreamListener::Init()
{
return NS_NewPipe(getter_AddRefs(mPipeIn),
getter_AddRefs(mPipeOut),
nsIOService::gDefaultSegmentSize,
UINT32_MAX, // no size limit
false,