Bug 1943650 - Command-line --help output misformatted after --dbus-service. r=emilio
[gecko.git] / toolkit / components / viaduct / ViaductRequest.cpp
blob2ee7cb8eeaf8b56ed19c76b641b824d0c22d66a2
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
5 #include "mozilla/ViaductRequest.h"
7 #include "mozilla/ClearOnShutdown.h"
8 #include "mozilla/ErrorNames.h"
9 #include "mozilla/ResultExtensions.h"
10 #include "mozilla/ScopeExit.h"
11 #include "mozilla/Services.h"
12 #include "mozilla/Try.h"
14 #include "nsComponentManagerUtils.h"
15 #include "nsContentUtils.h"
16 #include "nsIAsyncVerifyRedirectCallback.h"
17 #include "nsIHttpChannel.h"
18 #include "nsIHttpHeaderVisitor.h"
19 #include "nsIInputStream.h"
20 #include "nsIUploadChannel2.h"
21 #include "nsIURI.h"
22 #include "nsNetUtil.h"
23 #include "nsPrintfCString.h"
24 #include "nsString.h"
25 #include "nsStringStream.h"
26 #include "nsThreadUtils.h"
28 namespace mozilla {
30 namespace {
32 extern "C" {
33 ViaductByteBuffer viaduct_alloc_bytebuffer(int32_t);
34 void viaduct_destroy_bytebuffer(ViaductByteBuffer);
37 } // namespace
39 class HeaderVisitor final : public nsIHttpHeaderVisitor {
40 public:
41 NS_DECL_ISUPPORTS
42 NS_DECL_NSIHTTPHEADERVISITOR
44 explicit HeaderVisitor(
45 google::protobuf::Map<std::string, std::string>* aHeaders)
46 : mHeaders(aHeaders) {}
48 private:
49 google::protobuf::Map<std::string, std::string>* mHeaders;
50 ~HeaderVisitor() = default;
53 NS_IMETHODIMP
54 HeaderVisitor::VisitHeader(const nsACString& aHeader,
55 const nsACString& aValue) {
56 (*mHeaders)[aHeader.BeginReading()] = aValue.BeginReading();
57 return NS_OK;
59 NS_IMPL_ISUPPORTS(HeaderVisitor, nsIHttpHeaderVisitor)
61 nsCString ConvertMethod(
62 appservices::httpconfig::protobuf::Request_Method method);
64 ///////////////////////////////////////////////////////////////////////////////
65 // ViaductRequest implementation
67 ViaductByteBuffer ViaductRequest::MakeRequest(ViaductByteBuffer reqBuf) {
68 MOZ_ASSERT(!NS_IsMainThread(), "Background thread only!");
69 auto clearBuf = MakeScopeExit([&] { viaduct_destroy_bytebuffer(reqBuf); });
70 // We keep the protobuf parsing/serializing in the background thread.
71 appservices::httpconfig::protobuf::Request request;
72 if (!request.ParseFromArray(static_cast<const void*>(reqBuf.data),
73 reqBuf.len)) {
74 // We still need to return something!
75 return ViaductByteBuffer{.len = 0, .data = nullptr};
77 MonitorAutoLock lock(mMonitor);
78 NS_DispatchToMainThread(NS_NewRunnableFunction(
79 "ViaductRequest::LaunchRequest", [this, &request]() {
80 nsresult rv = LaunchRequest(request);
81 if (NS_WARN_IF(NS_FAILED(rv))) {
82 // Something went very very wrong, but we still have to unblock
83 // the calling thread.
84 NotifyMonitor();
86 }));
87 while (!mDone) {
88 mMonitor.Wait();
90 ViaductByteBuffer respBuf =
91 viaduct_alloc_bytebuffer(mResponse.ByteSizeLong());
92 if (!mResponse.SerializeToArray(respBuf.data, respBuf.len)) {
93 viaduct_destroy_bytebuffer(respBuf);
94 return ViaductByteBuffer{.len = 0, .data = nullptr};
96 return respBuf;
99 nsresult ViaductRequest::LaunchRequest(
100 appservices::httpconfig::protobuf::Request& request) {
101 if (PastShutdownPhase(ShutdownPhase::AppShutdownNetTeardown)) {
102 return NS_ERROR_FAILURE;
104 nsCOMPtr<nsIURI> uri;
105 nsresult rv = NS_NewURI(getter_AddRefs(uri), request.url().c_str());
106 NS_ENSURE_SUCCESS(rv, rv);
108 nsSecurityFlags secFlags =
109 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL |
110 nsILoadInfo::SEC_COOKIES_OMIT;
111 uint32_t loadFlags = 0;
113 if (!request.use_caches()) {
114 loadFlags |= nsIRequest::LOAD_BYPASS_CACHE;
117 if (!request.follow_redirects()) {
118 secFlags |= nsILoadInfo::SEC_DONT_FOLLOW_REDIRECTS;
121 rv = NS_NewChannel(getter_AddRefs(mChannel), uri,
122 nsContentUtils::GetSystemPrincipal(), secFlags,
123 nsIContentPolicy::TYPE_OTHER,
124 nullptr, // nsICookieJarSettings
125 nullptr, // aPerformanceStorage
126 nullptr, // aLoadGroup
127 nullptr, loadFlags);
128 NS_ENSURE_SUCCESS(rv, rv);
130 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
131 nsCString method = ConvertMethod(request.method());
132 rv = httpChannel->SetRequestMethod(method);
133 NS_ENSURE_SUCCESS(rv, rv);
135 for (auto& header : request.headers()) {
136 rv = httpChannel->SetRequestHeader(
137 nsDependentCString(header.first.c_str(), header.first.size()),
138 nsDependentCString(header.second.c_str(), header.second.size()),
139 false /* merge */);
140 NS_ENSURE_SUCCESS(rv, rv);
143 // Body
144 if (request.has_body()) {
145 const std::string& body = request.body();
146 nsCOMPtr<nsIStringInputStream> stream(
147 do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID));
148 rv = stream->CopyData(body.data(), body.size());
149 NS_ENSURE_SUCCESS(rv, rv);
150 nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(mChannel);
151 uploadChannel->ExplicitSetUploadStream(stream, VoidCString(), -1, method,
152 false /* aStreamHasHeaders */);
155 MOZ_TRY_VAR(
156 mConnectTimeoutTimer,
157 NS_NewTimerWithCallback(this, request.connect_timeout_secs() * 1000,
158 nsITimer::TYPE_ONE_SHOT));
159 MOZ_TRY_VAR(mReadTimeoutTimer,
160 NS_NewTimerWithCallback(this, request.read_timeout_secs() * 1000,
161 nsITimer::TYPE_ONE_SHOT));
163 rv = httpChannel->AsyncOpen(this);
164 NS_ENSURE_SUCCESS(rv, rv);
166 return rv;
169 nsCString ConvertMethod(
170 appservices::httpconfig::protobuf::Request_Method method) {
171 using appservices::httpconfig::protobuf::Request_Method;
172 switch (method) {
173 case Request_Method::Request_Method_GET:
174 return "GET"_ns;
175 case Request_Method::Request_Method_HEAD:
176 return "HEAD"_ns;
177 case Request_Method::Request_Method_POST:
178 return "POST"_ns;
179 case Request_Method::Request_Method_PUT:
180 return "PUT"_ns;
181 case Request_Method::Request_Method_DELETE:
182 return "DELETE"_ns;
183 case Request_Method::Request_Method_CONNECT:
184 return "CONNECT"_ns;
185 case Request_Method::Request_Method_OPTIONS:
186 return "OPTIONS"_ns;
187 case Request_Method::Request_Method_TRACE:
188 return "TRACE"_ns;
189 case Request_Method::Request_Method_PATCH:
190 return "PATCH"_ns;
192 return "UNKNOWN"_ns;
195 void ViaductRequest::ClearTimers() {
196 if (mConnectTimeoutTimer) {
197 mConnectTimeoutTimer->Cancel();
198 mConnectTimeoutTimer = nullptr;
200 if (mReadTimeoutTimer) {
201 mReadTimeoutTimer->Cancel();
202 mReadTimeoutTimer = nullptr;
206 void ViaductRequest::NotifyMonitor() {
207 MonitorAutoLock lock(mMonitor);
208 mDone = true;
209 mMonitor.Notify();
212 ViaductRequest::~ViaductRequest() {
213 ClearTimers();
214 if (mChannel) {
215 mChannel->Cancel(NS_ERROR_ABORT);
216 mChannel = nullptr;
218 NotifyMonitor();
221 NS_IMPL_ISUPPORTS(ViaductRequest, nsIStreamListener, nsITimerCallback, nsINamed,
222 nsIChannelEventSink)
224 ///////////////////////////////////////////////////////////////////////////////
225 // nsIStreamListener implementation
227 NS_IMETHODIMP
228 ViaductRequest::OnStartRequest(nsIRequest* aRequest) {
229 if (mConnectTimeoutTimer) {
230 mConnectTimeoutTimer->Cancel();
231 mConnectTimeoutTimer = nullptr;
233 return NS_OK;
236 static nsresult AssignResponseToBuffer(nsIInputStream* aIn, void* aClosure,
237 const char* aFromRawSegment,
238 uint32_t aToOffset, uint32_t aCount,
239 uint32_t* aWriteCount) {
240 nsCString* buf = static_cast<nsCString*>(aClosure);
241 buf->Append(aFromRawSegment, aCount);
242 *aWriteCount = aCount;
243 return NS_OK;
246 NS_IMETHODIMP
247 ViaductRequest::OnDataAvailable(nsIRequest* aRequest,
248 nsIInputStream* aInputStream, uint64_t aOffset,
249 uint32_t aCount) {
250 nsresult rv;
251 uint32_t readCount;
252 rv = aInputStream->ReadSegments(AssignResponseToBuffer, &mBodyBuffer, aCount,
253 &readCount);
254 NS_ENSURE_SUCCESS(rv, rv);
255 return NS_OK;
258 NS_IMETHODIMP
259 ViaductRequest::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) {
260 ClearTimers();
261 auto defer = MakeScopeExit([&] {
262 mChannel = nullptr;
263 NotifyMonitor();
266 if (NS_FAILED(aStatusCode)) {
267 nsCString errorName;
268 GetErrorName(aStatusCode, errorName);
269 nsPrintfCString msg("Request error: %s", errorName.get());
270 mResponse.set_exception_message(msg.BeginReading());
271 } else {
272 nsresult rv;
273 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest, &rv);
274 // Early return is OK because MakeScopeExit will call Notify()
275 // and unblock the original calling thread.
276 NS_ENSURE_SUCCESS(rv, rv);
278 uint32_t httpStatus;
279 rv = httpChannel->GetResponseStatus(&httpStatus);
280 NS_ENSURE_SUCCESS(rv, rv);
281 mResponse.set_status(httpStatus);
283 nsCOMPtr<nsIURI> uri;
284 httpChannel->GetURI(getter_AddRefs(uri));
285 nsAutoCString uriStr;
286 uri->GetSpec(uriStr);
287 mResponse.set_url(uriStr.BeginReading());
289 auto* headers = mResponse.mutable_headers();
290 nsCOMPtr<nsIHttpHeaderVisitor> visitor = new HeaderVisitor(headers);
291 rv = httpChannel->VisitResponseHeaders(visitor);
292 NS_ENSURE_SUCCESS(rv, rv);
294 mResponse.set_body(mBodyBuffer.BeginReading(), mBodyBuffer.Length());
297 return NS_OK;
300 ///////////////////////////////////////////////////////////////////////////////
301 // nsIChannelEventSink implementation
303 NS_IMETHODIMP
304 ViaductRequest::AsyncOnChannelRedirect(
305 nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t flags,
306 nsIAsyncVerifyRedirectCallback* callback) {
307 mChannel = aNewChannel;
308 callback->OnRedirectVerifyCallback(NS_OK);
309 return NS_OK;
312 ///////////////////////////////////////////////////////////////////////////////
313 // nsITimerCallback implementation
315 NS_IMETHODIMP
316 ViaductRequest::Notify(nsITimer* timer) {
317 ClearTimers();
318 // Cancelling the channel will trigger OnStopRequest.
319 if (mChannel) {
320 mChannel->Cancel(NS_ERROR_ABORT);
321 mChannel = nullptr;
323 return NS_OK;
326 ///////////////////////////////////////////////////////////////////////////////
327 // nsINamed implementation
329 NS_IMETHODIMP
330 ViaductRequest::GetName(nsACString& aName) {
331 aName.AssignLiteral("ViaductRequest");
332 return NS_OK;
335 }; // namespace mozilla