Details
Description
When using QNAM to send a POST request, if there's an unexpected EOF or an error in the server's reply, QNAM sends the request again.
For instance:
QNetworkAccessManager qnam; QNetworkRequest req(QUrl("http://localhost:12345/service")); auto reply = qnam.post(req, "hello\n");
Run a local faux server with something like nc -l 12345 -k. When the request arrives, type something into nc and press return – QNAM will re-send the POST.
A quick analysis of the problem:
QHttpProtocolHandler::_q_receiveReply will call m_channel->handleUnexpectedEOF(); in a number of cases. The common case seems to be if the server side times out and closes the connection after having sent a partial reply.¹
QHttpNetworkConnectionChannel::handleUnexpectedEOF will then try again, for a maximum of 4 times, before giving up. It will however not check if it is allowed to re-send the request (i.e. the request is idempotent). This will cause a POST to be sent again, causing data losses.
¹ But also in case of garbage, like the demo above shows. For instance if you just type nonsense, QHttpHeaderParser::parseStatus returns false. That causes QHttpNetworkReplyPrivate::readStatus to return -1:
bool ok = parseStatus(fragment); state = ReadingHeaderState; fragment.clear(); if (!ok) { return -1; }
but -1 is for EOF, not for parsing errors! This will also end up in QHttpNetworkConnectionChannel::handleUnexpectedEOF, and eventually cause it to emit the wrong error (RemoteHostClosedError, instead of a protocol-level error – "server sent garbage").
Attachments
For Gerrit Dashboard: QTBUG-134694 | ||||||
---|---|---|---|---|---|---|
# | Subject | Branch | Project | Status | CR | V |
632222,4 | QNetworkAccessManager: don't resend non-idempotent requests | dev | qt/qtbase | Status: MERGED | +2 | 0 |
633298,2 | QNetworkAccessManager: don't resend non-idempotent requests | 6.9 | qt/qtbase | Status: MERGED | +2 | 0 |
633353,2 | QNetworkAccessManager: don't resend non-idempotent requests | 6.8 | qt/qtbase | Status: MERGED | +2 | 0 |
633530,5 | QNetworkAccessManager: don't resend non-idempotent requests | tqtc/lts-6.5 | qt/tqtc-qtbase | Status: MERGED | +2 | +1 |