From 77d4ac7242296aaddee18f9da5a4fa7a3e3041c5 Mon Sep 17 00:00:00 2001 From: ed Date: Sat, 13 Mar 2021 12:37:31 +0000 Subject: [PATCH] URL: Fixed backwards compatibility of URL::createInputStream() when specifying a POST-like request with parameters and no POST data --- .../juce_core/native/juce_android_Network.cpp | 17 +++++++----- .../juce_core/native/juce_curl_Network.cpp | 25 ++++++++++-------- .../juce_core/native/juce_linux_Network.cpp | 16 +++++++----- modules/juce_core/native/juce_mac_Network.mm | 16 +++++++----- .../juce_core/native/juce_win32_Network.cpp | 16 +++++++----- modules/juce_core/network/juce_URL.cpp | 13 +++++++--- modules/juce_core/network/juce_URL.h | 26 +++++++++---------- .../juce_core/network/juce_WebInputStream.h | 8 +++--- 8 files changed, 82 insertions(+), 55 deletions(-) diff --git a/modules/juce_core/native/juce_android_Network.cpp b/modules/juce_core/native/juce_android_Network.cpp index 791c8dbbbc..b9de1e21d8 100644 --- a/modules/juce_core/native/juce_android_Network.cpp +++ b/modules/juce_core/native/juce_android_Network.cpp @@ -324,11 +324,12 @@ class WebInputStream::Pimpl public: enum { contentStreamCacheSize = 1024 }; - Pimpl (WebInputStream&, const URL& urlToCopy, bool isPOSTLike) + Pimpl (WebInputStream&, const URL& urlToCopy, bool addParametersToBody) : url (urlToCopy), isContentURL (urlToCopy.getScheme() == "content"), - addParametersToRequestBody (isPOSTLike), - httpRequest (isPOSTLike || url.hasPOSTData() ? "POST" : "GET") + addParametersToRequestBody (addParametersToBody), + hasBodyDataToSend (addParametersToRequestBody || url.hasBodyDataToSend()), + httpRequest (hasBodyDataToSend ? "POST" : "GET") { } @@ -380,8 +381,12 @@ public: address = "http://" + address; MemoryBlock postData; - if (url.hasPOSTData()) - WebInputStream::createHeadersAndPostData (url, headers, postData, addParametersToRequestBody); + + if (hasBodyDataToSend) + WebInputStream::createHeadersAndPostData (url, + headers, + postData, + addParametersToRequestBody); jbyteArray postDataArray = nullptr; @@ -536,7 +541,7 @@ public: private: const URL url; - const bool isContentURL, addParametersToRequestBody; + const bool isContentURL, addParametersToRequestBody, hasBodyDataToSend; bool eofStreamReached = false; int numRedirectsToFollow = 5, timeOutMs = 0; String httpRequest, headers; diff --git a/modules/juce_core/native/juce_curl_Network.cpp b/modules/juce_core/native/juce_curl_Network.cpp index 47e36e0135..f80b806abe 100644 --- a/modules/juce_core/native/juce_curl_Network.cpp +++ b/modules/juce_core/native/juce_curl_Network.cpp @@ -110,12 +110,12 @@ private: class WebInputStream::Pimpl { public: - Pimpl (WebInputStream& ownerStream, const URL& urlToCopy, bool isPOSTLike) + Pimpl (WebInputStream& ownerStream, const URL& urlToCopy, bool addParametersToBody) : owner (ownerStream), url (urlToCopy), - addParametersToRequestBody (isPOSTLike), - hasPOSTData (url.hasPOSTData()), - httpRequest (isPOSTLike || url.hasPOSTData() ? "POST" : "GET") + addParametersToRequestBody (addParametersToBody), + hasBodyDataToSend (url.hasBodyDataToSend() || addParametersToRequestBody), + httpRequest (hasBodyDataToSend ? "POST" : "GET") { jassert (symbols); // Unable to load libcurl! @@ -231,8 +231,11 @@ public: if (! requestHeaders.endsWithChar ('\n')) requestHeaders << "\r\n"; - if (hasPOSTData) - WebInputStream::createHeadersAndPostData (url, requestHeaders, headersAndPostData, addParametersToRequestBody); + if (hasBodyDataToSend) + WebInputStream::createHeadersAndPostData (url, + requestHeaders, + headersAndPostData, + addParametersToRequestBody); if (! requestHeaders.endsWithChar ('\n')) requestHeaders << "\r\n"; @@ -247,7 +250,7 @@ public: && symbols->curl_easy_setopt (curl, CURLOPT_USERAGENT, userAgent.toRawUTF8()) == CURLE_OK && symbols->curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, (maxRedirects > 0 ? 1 : 0)) == CURLE_OK) { - if (hasPOSTData) + if (hasBodyDataToSend) { if (symbols->curl_easy_setopt (curl, CURLOPT_READDATA, this) != CURLE_OK || symbols->curl_easy_setopt (curl, CURLOPT_READFUNCTION, StaticCurlRead) != CURLE_OK) @@ -259,7 +262,7 @@ public: } // handle special http request commands - const auto hasSpecialRequestCmd = hasPOSTData ? (httpRequest != "POST") : (httpRequest != "GET"); + const auto hasSpecialRequestCmd = hasBodyDataToSend ? (httpRequest != "POST") : (httpRequest != "GET"); if (hasSpecialRequestCmd) if (symbols->curl_easy_setopt (curl, CURLOPT_CUSTOMREQUEST, httpRequest.toRawUTF8()) != CURLE_OK) @@ -326,7 +329,7 @@ public: listener = webInputListener; - if (hasPOSTData) + if (hasBodyDataToSend) postBuffer = &headersAndPostData; size_t lastPos = static_cast (-1); @@ -345,7 +348,7 @@ public: singleStep(); // call callbacks if this is a post request - if (hasPOSTData && listener != nullptr && lastPos != postPosition) + if (hasBodyDataToSend && listener != nullptr && lastPos != postPosition) { lastPos = postPosition; @@ -616,7 +619,7 @@ public: // Options int timeOutMs = 0; int maxRedirects = 5; - const bool addParametersToRequestBody, hasPOSTData; + const bool addParametersToRequestBody, hasBodyDataToSend; String httpRequest; //============================================================================== diff --git a/modules/juce_core/native/juce_linux_Network.cpp b/modules/juce_core/native/juce_linux_Network.cpp index a786baa89d..c8949fd78c 100644 --- a/modules/juce_core/native/juce_linux_Network.cpp +++ b/modules/juce_core/native/juce_linux_Network.cpp @@ -70,11 +70,12 @@ bool JUCE_CALLTYPE Process::openEmailWithAttachments (const String& /* targetEma class WebInputStream::Pimpl { public: - Pimpl (WebInputStream& pimplOwner, const URL& urlToCopy, bool isPOSTLike) + Pimpl (WebInputStream& pimplOwner, const URL& urlToCopy, bool addParametersToBody) : owner (pimplOwner), url (urlToCopy), - addParametersToRequestBody (isPOSTLike), - httpRequestCmd (isPOSTLike || url.hasPOSTData() ? "POST" : "GET") + addParametersToRequestBody (addParametersToBody), + hasBodyDataToSend (addParametersToRequestBody || url.hasBodyDataToSend()), + httpRequestCmd (hasBodyDataToSend ? "POST" : "GET") { } @@ -259,7 +260,7 @@ private: MemoryBlock postData; int64 contentLength = -1, position = 0; bool finished = false; - const bool addParametersToRequestBody; + const bool addParametersToRequestBody, hasBodyDataToSend; int timeOutMs = 0; int numRedirectsToFollow = 5; String httpRequestCmd; @@ -288,8 +289,11 @@ private: { closeSocket (false); - if (url.hasPOSTData()) - WebInputStream::createHeadersAndPostData (url, headers, postData, addParametersToRequestBody); + if (hasBodyDataToSend) + WebInputStream::createHeadersAndPostData (url, + headers, + postData, + addParametersToRequestBody); auto timeOutTime = Time::getMillisecondCounter(); diff --git a/modules/juce_core/native/juce_mac_Network.mm b/modules/juce_core/native/juce_mac_Network.mm index 3a77615c1a..bbf61c3b0c 100644 --- a/modules/juce_core/native/juce_mac_Network.mm +++ b/modules/juce_core/native/juce_mac_Network.mm @@ -943,11 +943,12 @@ JUCE_END_IGNORE_WARNINGS_GCC_LIKE class WebInputStream::Pimpl { public: - Pimpl (WebInputStream& pimplOwner, const URL& urlToUse, bool isPOSTLike) + Pimpl (WebInputStream& pimplOwner, const URL& urlToUse, bool addParametersToBody) : owner (pimplOwner), url (urlToUse), - addParametersToRequestBody (isPOSTLike), - httpRequestCmd (isPOSTLike || url.hasPOSTData() ? "POST" : "GET") + addParametersToRequestBody (addParametersToBody), + hasBodyDataToSend (addParametersToRequestBody || url.hasBodyDataToSend()), + httpRequestCmd (hasBodyDataToSend ? "POST" : "GET") { } @@ -1091,7 +1092,7 @@ private: MemoryBlock postData; int64 position = 0; bool finished = false; - const bool addParametersToRequestBody; + const bool addParametersToRequestBody, hasBodyDataToSend; int timeOutMs = 0; int numRedirectsToFollow = 5; String httpRequestCmd; @@ -1113,9 +1114,12 @@ private: { [req setHTTPMethod: httpMethod]; - if (url.hasPOSTData()) + if (hasBodyDataToSend) { - WebInputStream::createHeadersAndPostData (url, headers, postData, addParametersToRequestBody); + WebInputStream::createHeadersAndPostData (url, + headers, + postData, + addParametersToRequestBody); if (postData.getSize() > 0) [req setHTTPBody: [NSData dataWithBytes: postData.getData() diff --git a/modules/juce_core/native/juce_win32_Network.cpp b/modules/juce_core/native/juce_win32_Network.cpp index eb3a533bed..5132648a95 100644 --- a/modules/juce_core/native/juce_win32_Network.cpp +++ b/modules/juce_core/native/juce_win32_Network.cpp @@ -35,11 +35,12 @@ namespace juce class WebInputStream::Pimpl { public: - Pimpl (WebInputStream& pimplOwner, const URL& urlToCopy, bool isPOSTLike) + Pimpl (WebInputStream& pimplOwner, const URL& urlToCopy, bool addParametersToBody) : owner (pimplOwner), url (urlToCopy), - addParametersToRequestBody (isPOSTLike), - httpRequestCmd (isPOSTLike || url.hasPOSTData() ? "POST" : "GET") + addParametersToRequestBody (addParametersToBody), + hasBodyDataToSend (addParametersToRequestBody || url.hasBodyDataToSend()), + httpRequestCmd (hasBodyDataToSend ? "POST" : "GET") { } @@ -240,7 +241,7 @@ private: MemoryBlock postData; int64 position = 0; bool finished = false; - const bool addParametersToRequestBody; + const bool addParametersToRequestBody, hasBodyDataToSend; int timeOutMs = 0; String httpRequestCmd; int numRedirectsToFollow = 5; @@ -291,8 +292,11 @@ private: uc.lpszPassword = password; uc.dwPasswordLength = passwordNumChars; - if (url.hasPOSTData()) - WebInputStream::createHeadersAndPostData (url, headers, postData, addParametersToRequestBody); + if (hasBodyDataToSend) + WebInputStream::createHeadersAndPostData (url, + headers, + postData, + addParametersToRequestBody); if (InternetCrackUrl (address.toWideCharPointer(), 0, 0, &uc)) openConnection (uc, sessionHandle, address, listener); diff --git a/modules/juce_core/network/juce_URL.cpp b/modules/juce_core/network/juce_URL.cpp index e9dd39ab83..8bcfb656ae 100644 --- a/modules/juce_core/network/juce_URL.cpp +++ b/modules/juce_core/network/juce_URL.cpp @@ -452,6 +452,11 @@ URL URL::getChildURL (const String& subPath) const return u; } +bool URL::hasBodyDataToSend() const +{ + return filesToUpload.size() > 0 || postData.getSize() > 0; +} + void URL::createHeadersAndPostData (String& headers, MemoryBlock& postDataToWrite, bool addParametersToBody) const @@ -499,9 +504,9 @@ void URL::createHeadersAndPostData (String& headers, else { if (addParametersToBody) - data << URLHelpers::getMangledParameters (*this) << postData; - else - data << postData; + data << URLHelpers::getMangledParameters (*this); + + data << postData; // if the user-supplied headers didn't contain a content-type, add one now.. if (! headers.containsIgnoreCase ("Content-Type")) @@ -886,7 +891,7 @@ URL URL::withUpload (Upload* const f) const auto u = *this; for (int i = u.filesToUpload.size(); --i >= 0;) - if (u.filesToUpload.getObjectPointerUnchecked(i)->parameterName == f->parameterName) + if (u.filesToUpload.getObjectPointerUnchecked (i)->parameterName == f->parameterName) u.filesToUpload.remove (i); u.filesToUpload.add (f); diff --git a/modules/juce_core/network/juce_URL.h b/modules/juce_core/network/juce_URL.h index 293903a8b4..367a0bb4d6 100644 --- a/modules/juce_core/network/juce_URL.h +++ b/modules/juce_core/network/juce_URL.h @@ -282,9 +282,6 @@ public: /** Returns the data that was set using withPOSTData() as a MemoryBlock. */ const MemoryBlock& getPostDataAsMemoryBlock() const noexcept { return postData; } - /** Returns true if this URL has POST data set using withPOSTData(). */ - bool hasPOSTData() const noexcept { return postData.getSize() > 0; } - //============================================================================== /** Tries to launch the system's default browser to open the URL. @@ -303,21 +300,22 @@ public: */ static bool isProbablyAnEmailAddress (const String& possibleEmailAddress); + //============================================================================== enum class ParameterHandling { inAddress, inPostData }; - //============================================================================== /** Class used to create a set of options to pass to the createInputStream() method. You can chain together a series of calls to this class's methods to create a set of whatever options you want to specify, e.g. @code if (auto inputStream = URL ("http://www.xyz.com/foobar") - .createInputStream (URL::InputStreamOptions (false).withConnectionTimeoutMs (1000) - .withNumRedirectsToFollow (0))) + .createInputStream (URL::InputStreamOptions (URL::ParameterHandling::inAddress) + .withConnectionTimeoutMs (1000) + .withNumRedirectsToFollow (0))) { ... } @@ -328,10 +326,9 @@ public: public: /** Constructor. - If parameterHandling is ParameterHandling::inPostData and the URL contains some - POST data to send set via one of its withPOSTData() methods, the URL parameters - will be transferred via the request body data. Otherwise the parameters will - be added to the URL address. + If parameterHandling is ParameterHandling::inPostData, any URL parameters + that have been set will be transferred via the request body data. Otherwise + the parameters will be added to the URL address. */ explicit InputStreamOptions (ParameterHandling parameterHandling); @@ -371,10 +368,12 @@ public: */ InputStreamOptions withNumRedirectsToFollow (int numRedirectsToFollow) const; - /** Specifies which HTTP request to use. + /** Specifies which HTTP request command to use. - If this is not set, then this will be determined by the value of `doPostLikeRequest` - or the presence of POST data set via URL::withPOSTData(). + If this is not set, then the command will be POST if parameterHandling is + set to ParameterHandling::inPostData or if any POST data has been specified + via withPOSTData(), withFileToUpload(), or withDataToUpload(). Otherwise it + will be GET. */ InputStreamOptions withHttpRequestCmd (const String& httpRequestCmd) const; @@ -666,6 +665,7 @@ private: URL (const String&, int); void init(); void addParameter (const String&, const String&); + bool hasBodyDataToSend() const; void createHeadersAndPostData (String&, MemoryBlock&, bool) const; URL withUpload (Upload*) const; diff --git a/modules/juce_core/network/juce_WebInputStream.h b/modules/juce_core/network/juce_WebInputStream.h index f7cf04292a..e8997f45f8 100644 --- a/modules/juce_core/network/juce_WebInputStream.h +++ b/modules/juce_core/network/juce_WebInputStream.h @@ -36,10 +36,12 @@ class JUCE_API WebInputStream : public InputStream @param url The URL that should be retrieved. This parameter may also contain POST data and/or parameters. - @param usePost Specifies whether a GET or a POST command should be used. This - parameter will also influence the way parameters are encoded. + @param addParametersToRequestBody Specifies whether any URL parameters that have + been set will be transferred via the request body data or added + to the URL address. This will also determine whether a POST or GET + command will be used if a custom command is not set. */ - WebInputStream (const URL& url, bool usePost); + WebInputStream (const URL& url, bool addParametersToRequestBody); /** Destructor. */ ~WebInputStream() override;