#2565: HTMLForm: optional enforcement of Content-Length instead of Chunked Transfer-Encoding

This commit is contained in:
Günter Obiltschnig 2018-12-12 18:00:58 +01:00
parent e1aac5ff95
commit db86fec382
4 changed files with 73 additions and 39 deletions

View File

@ -46,6 +46,12 @@ class Net_API HTMLForm: public NameValueCollection
/// form fields programmatically. The default limit is 100. /// form fields programmatically. The default limit is 100.
{ {
public: public:
enum Options
{
OPT_USE_CONTENT_LENGTH = 0x01
/// Don't use Chunked Transfer-Encoding for multipart requests.
};
HTMLForm(); HTMLForm();
/// Creates an empty HTMLForm and sets the /// Creates an empty HTMLForm and sets the
/// encoding to "application/x-www-form-urlencoded". /// encoding to "application/x-www-form-urlencoded".
@ -137,7 +143,7 @@ public:
/// Note that read() does not clear the form before /// Note that read() does not clear the form before
/// reading the new values. /// reading the new values.
void prepareSubmit(HTTPRequest& request); void prepareSubmit(HTTPRequest& request, int options = 0);
/// Fills out the request object for submitting the form. /// Fills out the request object for submitting the form.
/// ///
/// If the request method is GET, the encoded form is appended to the /// If the request method is GET, the encoded form is appended to the
@ -150,7 +156,12 @@ public:
/// - the content transfer encoding is set to identity encoding /// - the content transfer encoding is set to identity encoding
/// Otherwise, if the request's HTTP version is HTTP/1.1: /// Otherwise, if the request's HTTP version is HTTP/1.1:
/// - the request's persistent connection state is left unchanged /// - the request's persistent connection state is left unchanged
/// - the content transfer encoding is set to chunked /// - the content transfer encoding is set to chunked, unless
/// the OPT_USE_CONTENT_LENGTH is given in options
///
/// Note: Not using chunked transfer encoding for multipart forms
/// degrades performance, as the request content must be generated
/// twice, first to determine its size, then to actually send it.
std::streamsize calculateContentLength(); std::streamsize calculateContentLength();
/// Calculate the content length for the form. /// Calculate the content length for the form.

View File

@ -203,7 +203,7 @@ void HTMLForm::read(const std::string& queryString)
} }
void HTMLForm::prepareSubmit(HTTPRequest& request) void HTMLForm::prepareSubmit(HTTPRequest& request, int options)
{ {
if (request.getMethod() == HTTPRequest::HTTP_POST || request.getMethod() == HTTPRequest::HTTP_PUT) if (request.getMethod() == HTTPRequest::HTTP_POST || request.getMethod() == HTTPRequest::HTTP_PUT)
{ {
@ -229,11 +229,11 @@ void HTMLForm::prepareSubmit(HTTPRequest& request)
request.setKeepAlive(false); request.setKeepAlive(false);
request.setChunkedTransferEncoding(false); request.setChunkedTransferEncoding(false);
} }
else if (_encoding != ENCODING_URL) else if (_encoding != ENCODING_URL && (options & OPT_USE_CONTENT_LENGTH) == 0)
{ {
request.setChunkedTransferEncoding(true); request.setChunkedTransferEncoding(true);
} }
if (!request.getChunkedTransferEncoding()) if (!request.getChunkedTransferEncoding() && !request.hasContentLength())
{ {
request.setContentLength(calculateContentLength()); request.setContentLength(calculateContentLength());
} }

View File

@ -19,6 +19,7 @@
#include "Poco/Net/NetException.h" #include "Poco/Net/NetException.h"
#include <sstream> #include <sstream>
#include <iostream>
using Poco::Net::HTMLForm; using Poco::Net::HTMLForm;
using Poco::Net::PartSource; using Poco::Net::PartSource;
@ -294,6 +295,7 @@ void HTMLFormTest::testSubmit2()
HTTPRequest req("POST", "/form.cgi"); HTTPRequest req("POST", "/form.cgi");
form.prepareSubmit(req); form.prepareSubmit(req);
assertTrue (req.getContentType() == HTMLForm::ENCODING_URL); assertTrue (req.getContentType() == HTMLForm::ENCODING_URL);
assertTrue (req.getContentLength() == 64);
} }
@ -331,6 +333,25 @@ void HTMLFormTest::testSubmit4()
} }
void HTMLFormTest::testSubmit5()
{
HTMLForm form(HTMLForm::ENCODING_MULTIPART);
form.set("field1", "value1");
form.set("field2", "value 2");
form.set("field3", "value=3");
form.set("field4", "value&4");
HTTPRequest req("POST", "/form.cgi", HTTPMessage::HTTP_1_1);
form.prepareSubmit(req, HTMLForm::OPT_USE_CONTENT_LENGTH);
std::string expCT(HTMLForm::ENCODING_MULTIPART);
expCT.append("; boundary=\"");
expCT.append(form.boundary());
expCT.append("\"");
assertTrue (req.getContentType() == expCT);
assertTrue (req.getContentLength() == 403);
}
void HTMLFormTest::testFieldLimitUrl() void HTMLFormTest::testFieldLimitUrl()
{ {
HTTPRequest req("GET", "/form.cgi?field1=value1&field2=value%202&field3=value%3D3&field4=value%264"); HTTPRequest req("GET", "/form.cgi?field1=value1&field2=value%202&field3=value%3D3&field4=value%264");
@ -416,6 +437,7 @@ CppUnit::Test* HTMLFormTest::suite()
CppUnit_addTest(pSuite, HTMLFormTest, testSubmit2); CppUnit_addTest(pSuite, HTMLFormTest, testSubmit2);
CppUnit_addTest(pSuite, HTMLFormTest, testSubmit3); CppUnit_addTest(pSuite, HTMLFormTest, testSubmit3);
CppUnit_addTest(pSuite, HTMLFormTest, testSubmit4); CppUnit_addTest(pSuite, HTMLFormTest, testSubmit4);
CppUnit_addTest(pSuite, HTMLFormTest, testSubmit5);
CppUnit_addTest(pSuite, HTMLFormTest, testFieldLimitUrl); CppUnit_addTest(pSuite, HTMLFormTest, testFieldLimitUrl);
CppUnit_addTest(pSuite, HTMLFormTest, testFieldLimitMultipart); CppUnit_addTest(pSuite, HTMLFormTest, testFieldLimitMultipart);

View File

@ -36,6 +36,7 @@ public:
void testSubmit2(); void testSubmit2();
void testSubmit3(); void testSubmit3();
void testSubmit4(); void testSubmit4();
void testSubmit5();
void testFieldLimitUrl(); void testFieldLimitUrl();
void testFieldLimitMultipart(); void testFieldLimitMultipart();