-
Notifications
You must be signed in to change notification settings - Fork 5.8k
HTTP PATCH requests do not send body #11384
Comments
+1 - the same problem. |
Can confirm this problem as well. Rails 4 now supports PATCH out of the box for resources so have been using it but cannot use PhantomJS to test and have had to revert to chrome driver. |
+1 same problem |
+1. Just ran into this problem as well |
Looks like QT4 doesn't support this built in, would have to do a little work to implement the custom request functionality: http://harmattan-dev.nokia.com/docs/library/html/qt4/qnetworkaccessmanager.html#Operation-enum |
+1 same problem |
+1 we have the same problem |
I ran into this yesterday and didn't have time to wait on an official fix (if one is even planned) so I went in and added sufficient support to get it working for my purposes running on linux. If anyone else would like to patch & build, you can use the following patch. It should apply cleanly to 1.9.1 diff --git src/networkaccessmanager.cpp src/networkaccessmanager.cpp
index fbe8af1..b04463b 100644
--- src/networkaccessmanager.cpp
+++ src/networkaccessmanager.cpp
@@ -59,6 +59,9 @@ static const char *toString(QNetworkAccessManager::Operation op)
case QNetworkAccessManager::PostOperation:
str = "POST";
break;
+ case QNetworkAccessManager::PatchOperation:
+ str = "PATCH";
+ break;
case QNetworkAccessManager::DeleteOperation:
str = "DELETE";
break;
diff --git src/qt/src/3rdparty/webkit/Source/WebCore/platform/network/qt/QNetworkReplyHandler.cpp src/qt/src/3rdparty/webkit/Source/WebCore/platform/network/qt/QNetworkReplyHandler.cpp
index 3cf6439..0921225 100644
--- src/qt/src/3rdparty/webkit/Source/WebCore/platform/network/qt/QNetworkReplyHandler.cpp
+++ src/qt/src/3rdparty/webkit/Source/WebCore/platform/network/qt/QNetworkReplyHandler.cpp
@@ -368,6 +368,8 @@ String QNetworkReplyHandler::httpMethod() const
return "POST";
case QNetworkAccessManager::PutOperation:
return "PUT";
+ case QNetworkAccessManager::PatchOperation:
+ return "PATCH";
case QNetworkAccessManager::DeleteOperation:
return "DELETE";
case QNetworkAccessManager::CustomOperation:
@@ -395,6 +397,8 @@ QNetworkReplyHandler::QNetworkReplyHandler(ResourceHandle* handle, LoadType load
m_method = QNetworkAccessManager::PostOperation;
else if (r.httpMethod() == "PUT")
m_method = QNetworkAccessManager::PutOperation;
+ else if (r.httpMethod() == "PATCH")
+ m_method = QNetworkAccessManager::PatchOperation;
else if (r.httpMethod() == "DELETE")
m_method = QNetworkAccessManager::DeleteOperation;
else
@@ -623,7 +627,7 @@ QNetworkReply* QNetworkReplyHandler::sendNetworkRequest(QNetworkAccessManager* m
&& (!url.toLocalFile().isEmpty() || url.scheme() == QLatin1String("data")))
m_method = QNetworkAccessManager::GetOperation;
- if (m_method != QNetworkAccessManager::PostOperation && m_method != QNetworkAccessManager::PutOperation) {
+ if (m_method != QNetworkAccessManager::PostOperation && m_method != QNetworkAccessManager::PutOperation && m_method != QNetworkAccessManager::PatchRequest) {
// clearing Contents-length and Contents-type of the requests that do not have contents.
m_request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant());
m_request.setHeader(QNetworkRequest::ContentLengthHeader, QVariant());
@@ -641,6 +645,15 @@ QNetworkReply* QNetworkReplyHandler::sendNetworkRequest(QNetworkAccessManager* m
postDevice->setParent(result);
return result;
}
+ case QNetworkAccessManager::PatchOperation: {
+ FormDataIODevice* patchDevice = new FormDataIODevice(request.httpBody());
+ // We may be uploading files so prevent QNR from buffering data
+ m_request.setHeader(QNetworkRequest::ContentLengthHeader, patchDevice->getFormDataSize());
+ m_request.setAttribute(QNetworkRequest::DoNotBufferUploadDataAttribute, QVariant(true));
+ QNetworkReply* result = manager->patch(m_request, patchDevice);
+ patchDevice->setParent(result);
+ return result;
+ }
case QNetworkAccessManager::HeadOperation:
return manager->head(m_request);
case QNetworkAccessManager::PutOperation: {
diff --git src/qt/src/3rdparty/webkit/Source/WebKit/qt/Api/qwebframe.cpp src/qt/src/3rdparty/webkit/Source/WebKit/qt/Api/qwebframe.cpp
index 04453b6..c517734 100644
--- src/qt/src/3rdparty/webkit/Source/WebKit/qt/Api/qwebframe.cpp
+++ src/qt/src/3rdparty/webkit/Source/WebKit/qt/Api/qwebframe.cpp
@@ -908,6 +908,9 @@ void QWebFrame::load(const QNetworkRequest &req,
case QNetworkAccessManager::PostOperation:
request.setHTTPMethod("POST");
break;
+ case QNetworkAccessManager::PatchOperation:
+ request.setHTTPMethod("PATCH");
+ break;
case QNetworkAccessManager::DeleteOperation:
request.setHTTPMethod("DELETE");
break;
diff --git src/qt/src/network/access/qhttp.cpp src/qt/src/network/access/qhttp.cpp
index dea23f9..984133d 100644
--- src/qt/src/network/access/qhttp.cpp
+++ src/qt/src/network/access/qhttp.cpp
@@ -2254,6 +2254,27 @@ int QHttp::post(const QString &path, const QByteArray &data, QIODevice *to)
return d->addRequest(new QHttpPGHRequest(header, new QByteArray(data), to));
}
+int QHttp::patch(const QString &path, QIODevice *data, QIODevice *to )
+{
+ Q_D(QHttp);
+ QHttpRequestHeader header(QLatin1String("PATCH"), path);
+ header.setValue(QLatin1String("Connection"), QLatin1String("Keep-Alive"));
+ return d->addRequest(new QHttpPGHRequest(header, data, to));
+}
+
+/*!
+ \overload
+
+ \a data is used as the content data of the HTTP request.
+*/
+int QHttp::patch(const QString &path, const QByteArray &data, QIODevice *to)
+{
+ Q_D(QHttp);
+ QHttpRequestHeader header(QLatin1String("PATCH"), path);
+ header.setValue(QLatin1String("Connection"), QLatin1String("Keep-Alive"));
+ return d->addRequest(new QHttpPGHRequest(header, new QByteArray(data), to));
+}
+
/*!
Sends a header request for \a path to the server set by setHost()
or as specified in the constructor.
diff --git src/qt/src/network/access/qhttp.h src/qt/src/network/access/qhttp.h
index 9700e6e..1590806 100644
--- src/qt/src/network/access/qhttp.h
+++ src/qt/src/network/access/qhttp.h
@@ -223,6 +223,8 @@ public:
int get(const QString &path, QIODevice *to=0);
int post(const QString &path, QIODevice *data, QIODevice *to=0 );
int post(const QString &path, const QByteArray &data, QIODevice *to=0);
+ int patch(const QString &path, QIODevice *data, QIODevice *to=0 );
+ int patch(const QString &path, const QByteArray &data, QIODevice *to=0);
int head(const QString &path);
int request(const QHttpRequestHeader &header, QIODevice *device=0, QIODevice *to=0);
int request(const QHttpRequestHeader &header, const QByteArray &data, QIODevice *to=0);
diff --git src/qt/src/network/access/qhttpnetworkrequest.cpp src/qt/src/network/access/qhttpnetworkrequest.cpp
index 617541f..78a2d82 100644
--- src/qt/src/network/access/qhttpnetworkrequest.cpp
+++ src/qt/src/network/access/qhttpnetworkrequest.cpp
@@ -90,6 +90,9 @@ QByteArray QHttpNetworkRequestPrivate::methodName() const
case QHttpNetworkRequest::Post:
return "POST";
break;
+ case QHttpNetworkRequest::Patch:
+ return "PATCH";
+ break;
case QHttpNetworkRequest::Options:
return "OPTIONS";
break;
diff --git src/qt/src/network/access/qhttpnetworkrequest_p.h src/qt/src/network/access/qhttpnetworkrequest_p.h
index 3b98342..4b6d654 100644
--- src/qt/src/network/access/qhttpnetworkrequest_p.h
+++ src/qt/src/network/access/qhttpnetworkrequest_p.h
@@ -69,6 +69,7 @@ public:
Get,
Head,
Post,
+ Patch,
Put,
Delete,
Trace,
diff --git src/qt/src/network/access/qnetworkaccesshttpbackend.cpp src/qt/src/network/access/qnetworkaccesshttpbackend.cpp
index 1d048ee..ffd6b32 100644
--- src/qt/src/network/access/qnetworkaccesshttpbackend.cpp
+++ src/qt/src/network/access/qnetworkaccesshttpbackend.cpp
@@ -171,6 +171,7 @@ QNetworkAccessHttpBackendFactory::create(QNetworkAccessManager::Operation op,
switch (op) {
case QNetworkAccessManager::GetOperation:
case QNetworkAccessManager::PostOperation:
+ case QNetworkAccessManager::PatchOperation:
case QNetworkAccessManager::HeadOperation:
case QNetworkAccessManager::PutOperation:
case QNetworkAccessManager::DeleteOperation:
@@ -453,6 +454,12 @@ void QNetworkAccessHttpBackend::postRequest()
createUploadByteDevice();
break;
+ case QNetworkAccessManager::PatchOperation:
+ invalidateCache();
+ httpRequest.setOperation(QHttpNetworkRequest::Patch);
+ createUploadByteDevice();
+ break;
+
case QNetworkAccessManager::PutOperation:
invalidateCache();
httpRequest.setOperation(QHttpNetworkRequest::Put);
diff --git src/qt/src/network/access/qnetworkaccessmanager.cpp src/qt/src/network/access/qnetworkaccessmanager.cpp
index 84b1b4b..6cc63d2 100644
--- src/qt/src/network/access/qnetworkaccessmanager.cpp
+++ src/qt/src/network/access/qnetworkaccessmanager.cpp
@@ -715,6 +715,56 @@ QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, const
return reply;
}
+QNetworkReply *QNetworkAccessManager::patch(const QNetworkRequest &request, QHttpMultiPart *multiPart)
+{
+ QNetworkRequest newRequest = d_func()->prepareMultipart(request, multiPart);
+ QIODevice *device = multiPart->d_func()->device;
+ QNetworkReply *reply = patch(newRequest, device);
+ return reply;
+}
+
+/*!
+ Uploads the contents of \a data to the destination \a request and
+ returnes a new QNetworkReply object that will be open for reply.
+
+ \a data must be opened for reading when this function is called
+ and must remain valid until the finished() signal is emitted for
+ this reply.
+
+ Whether anything will be available for reading from the returned
+ object is protocol dependent. For HTTP, the server may send a
+ small HTML page indicating the upload was successful (or not).
+ Other protocols will probably have content in their replies.
+
+ \note For HTTP, this request will send a PUT request, which most servers
+ do not allow. Form upload mechanisms, including that of uploading
+ files through HTML forms, use the POST mechanism.
+
+ \sa get(), post(), deleteResource(), sendCustomRequest()
+ */
+QNetworkReply *QNetworkAccessManager::patch(const QNetworkRequest &request, QIODevice *data)
+{
+ return d_func()->postProcess(createRequest(QNetworkAccessManager::PatchOperation, request, data));
+}
+
+/*!
+ \overload
+
+ Sends the contents of the \a data byte array to the destination
+ specified by \a request.
+ */
+QNetworkReply *QNetworkAccessManager::patch(const QNetworkRequest &request, const QByteArray &data)
+{
+ QBuffer *buffer = new QBuffer;
+ buffer->setData(data);
+ buffer->open(QIODevice::ReadOnly);
+
+ QNetworkReply *reply = patch(request, buffer);
+ buffer->setParent(reply);
+ return reply;
+}
+
+
/*!
\since 4.6
diff --git src/qt/src/network/access/qnetworkaccessmanager.h src/qt/src/network/access/qnetworkaccessmanager.h
index 26a28e1..52b8452 100644
--- src/qt/src/network/access/qnetworkaccessmanager.h
+++ src/qt/src/network/access/qnetworkaccessmanager.h
@@ -84,6 +84,7 @@ public:
PutOperation,
PostOperation,
DeleteOperation,
+ PatchOperation,
CustomOperation,
UnknownOperation = 0
@@ -121,6 +122,9 @@ public:
QNetworkReply *put(const QNetworkRequest &request, QIODevice *data);
QNetworkReply *put(const QNetworkRequest &request, const QByteArray &data);
QNetworkReply *put(const QNetworkRequest &request, QHttpMultiPart *multiPart);
+ QNetworkReply *patch(const QNetworkRequest &request, QIODevice *data);
+ QNetworkReply *patch(const QNetworkRequest &request, const QByteArray &data);
+ QNetworkReply *patch(const QNetworkRequest &request, QHttpMultiPart *multiPart);
QNetworkReply *deleteResource(const QNetworkRequest &request);
QNetworkReply *sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, QIODevice *data = 0);
diff --git src/webpage.cpp src/webpage.cpp
index c76a4b8..a56b46a 100644
--- src/webpage.cpp
+++ src/webpage.cpp
@@ -817,6 +817,8 @@ void WebPage::openUrl(const QString &address, const QVariant &op, const QVariant
networkOp = QNetworkAccessManager::PutOperation;
else if (operation == "post")
networkOp = QNetworkAccessManager::PostOperation;
+ else if (operation == "patch")
+ networkOp = QNetworkAccessManager::PatchOperation;
else if (operation == "delete")
networkOp = QNetworkAccessManager::DeleteOperation; |
+1 |
I think that Phantom may be already using a custom Qt build, but not sure, so not sure if we should actually customize Qt per that patch or add some other implementation for this. |
+1 |
2 similar comments
+1 |
+1 |
+1, we banged our head on this for a while |
+1 - also, patch requests have content type application/octet-stream, regardless of what you set in code (using jQuery's "ajax" method). |
+1 |
1 similar comment
+1 |
+42 |
+4 |
+1 |
1 similar comment
+1 |
Would love to see this fixed, as it is a big problem for tests in an app I'm working on. |
+1 Can't believe how long I spent figuring this out. Switched to PUT for the moment. |
+1 spent my morning on this one. Used PUT for now. |
Just a note, I had a talk with a colleague and he reminded me of what was wrong with this. It's generally assumed that browsers -won't- support PATCH/PUT/DELETE, so we went from using PUT to using POST and passing a form parameter defining what the method is (we're using Rails, so that was pretty easy - hidden form input named _method that has the value "PATCH"). That isn't to say PhantomJS shouldn't support PATCH, totally should, just a reminder for those who used the workaround I did. |
I had an issue with DELETE and jquery
the id param was empty in backend. Also changed method to PUT and it worked. ps: DELETE worked under Selenium |
Delete, like patch, doesn't send the message body in phantomjs. Hopefully this all gets fixed in a future rev. Sent from my iPad
|
PhantomJS doesn't seem to send request body for PATCH: ariya/phantomjs#11384
+1 experiencing the same problem! Anybody trying to get this integrated into the main branch? It's an issue for over a year, but still lots of people are searching crazy why tests are failing to find out this is a known issue. |
Hey! If someone has more info on that Qt WebKit issue, pls share it in the issue I created at QtWebKit issue tracker. |
+1 |
+1 - Just spent more time than I care to admit trying to figure out why my test wasn't passing. Using PUT for now. |
+1 |
+1 also spent a couple of hours on this problem. |
+3 hours wasted |
+10 DELETE not working... pls fix... |
Summary:
|
ariya/phantomjs#11384 Lack of bind() was already pretty bad, but silently failing on a perfectly normal type of HTTP request is more than I want to work around. Supposedly, version 2.0 will fix this issue (see the GitHub thread) and maybe bind() as well, so I'll give Phantom another shot then. Until then I'll just stick with Chrome/FF, and look into Sauce if I want continuous integration.
Just ran into this issue myself. Switching it to |
BTW PhantomJS 2.0 is going to be out soon. |
- NB: PATCH doesn't work in tests for reasons - ariya/phantomjs#11384 - thoughtbot/capybara-webkit#553
- NB: Using PUT now because PATCH doesn't work in tests for reasons - ariya/phantomjs#11384 - thoughtbot/capybara-webkit#553
this is fixed in 2.0, shouldn't this be closed? |
I don't think this was fixed in 2.0, sadface. |
How is this not resolved yet?! There are patch files and countless of people needing this. |
It is fixed in 2.0, did a quick test case to confirm it. It's just that 2.x cannot take off until binary are available for all platforms so, wait or help =) |
I am having this issue currently. PhantomJS 2.1.X. My BackboneJS PATCH requests are sending empty body requests to my rails api. |
I had this problem too, I just switched to PUT for now; but, it would be nice to have this. |
This bug is back in 2.1 ? |
On my local machine, I am properly able to perform a PATCH request with the body that I want, however I am using Semaphore for my CI. My same tests do not perform the PATCH requests. I am curious on what could not be sending them. |
@reidcooper semaphore defaults to PhantomJS 1.9.8 https://semaphoreci.com/docs/phantomjs.html |
@dholdren that seems to have fixed my problem, thanks a lot. However, I did not think about this before because in my Gemfile.lock, I do have version 2.1 of phantomjs? |
👍 PUT method working thanks @here |
Due to our very limited maintenance capacity (see #14541 for more details), we need to prioritize our development focus on other tasks. Therefore, this issue will be automatically closed. In the future, if we see the need to attend to this issue again, then it will be reopened. Thank you for your contribution! |
Much in the same vein as issues #11367 and #10873 , sending an AJAX PATCH request does not send the body, and instead just sends a blank body. PATCH requests are documented in RFC5789, and are supported by all the browsers I've tested in.
Arguably, it also exists in RFC2068, but I'm aware that that does label it an 'experimental' feature.
The text was updated successfully, but these errors were encountered: