Details
-
Bug
-
Resolution: Out of scope
-
P2: Important
-
None
-
6.5.1
-
None
-
-
21
-
22f67be5f (dev)
-
Foundation Sprint 104, Foundation Sprint 105, Foundation Sprint 106, Foundation Sprint 107, Foundation Sprint 108, Foundation Sprint 109
Description
Using QNetworkAccessManager::post() to send a large file using QHttpMultiPart;
If the HTTP server sends an HTTP response before the Qt app has uploaded the full request body, the QNetworkReply reports a QNetworkReply::RemoteHostClosedError error and neither the HTTP status code nor the response body are available.
Using wireshark it is possible to see that the response data was sent over the network with the header "Connection: close" before the connection was closed. And also that Qt retries 4 times before giving up and reporting the error.
An easy way to reproduce the issue is by using a go server that will no read the body:
func getRoot(w http.ResponseWriter, r *http.Request) { fmt.Printf("got / request\n") if r.Header.Get("Expect") == "100-continue" { w.WriteHeader(http.StatusContinue) } w.WriteHeader(http.StatusBadRequest) w.Header().Set("Content-Type", "application/json") io.WriteString(w, "{\"foo\": null}") } func main() { http.HandleFunc("/", getRoot) err := http.ListenAndServe(":3333", nil) if errors.Is(err, http.ErrServerClosed) { fmt.Printf("server closed\n") } else if err != nil { fmt.Printf("error starting server: %s\n", err) os.Exit(1) } }
With curl we get:
$ curl -F "file=@4GiB" http://localhost:3333 --verbose * Trying 127.0.0.1:3333... * Connected to localhost (127.0.0.1) port 3333 (#0) > POST / HTTP/1.1 > Host: localhost:3333 > User-Agent: curl/7.88.1 > Accept: */* > Content-Length: 4294967492 > Content-Type: multipart/form-data; boundary=------------------------d59b0074949dcd1e > Expect: 100-continue > < HTTP/1.1 100 Continue < HTTP/1.1 400 Bad Request < Date: Thu, 22 Jun 2023 14:52:04 GMT < Content-Length: 13 < Content-Type: text/plain; charset=utf-8 < Connection: close < * we are done reading and this is set to close, stop send * Closing connection 0 {"foo": null}⏎
With Qt:
QCoreApplication app(argc, argv); QNetworkRequest request; request.setUrl({"http://localhost:3333/"}); QByteArray data; data.resize(1024 * 1024 * 1024); auto multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); QHttpPart part; part.setHeader(QNetworkRequest::ContentDispositionHeader, QString("form-data; name=\"file\"; filename=\"4GiB\"")); part.setHeader(QNetworkRequest::ContentTypeHeader, QString("application/octet-stream")); part.setBody(data); multiPart->append(part); QNetworkAccessManager nam; auto reply = nam.post(request, multiPart); QObject::connect(reply, &QNetworkReply::finished, [reply]() { qDebug() << reply->error(); // QNetworkReply::RemoteHostClosedError qDebug() << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); // QVariant(Invalid) qDebug() << reply->readAll(); // "" qApp->exit(); }); return app.exec();
Attachments
For Gerrit Dashboard: QTBUG-114812 | ||||||
---|---|---|---|---|---|---|
# | Subject | Branch | Project | Status | CR | V |
570888,3 | Add a note to QNAM that RFC 2616 8.2.2. is not handled properly | dev | qt/qtbase | Status: MERGED | +2 | 0 |