Merge pull request #344 from RangelReale/htmlformcontentlength

HTMLForm Content-Length calculation
This commit is contained in:
Aleksandar Fabijanic
2014-05-21 22:27:12 -05:00
12 changed files with 164 additions and 5 deletions

View File

@@ -69,6 +69,15 @@ public:
int getCurrentLineNumber() const; int getCurrentLineNumber() const;
/// Returns the current line number (same as lines()). /// Returns the current line number (same as lines()).
void addChars(int chars);
/// Add to the total number of characters.
void addLines(int lines);
/// Add to the total number of lines.
void addPos(int pos);
/// Add to the number of characters on the current line.
protected: protected:
int readFromDevice(); int readFromDevice();
int writeToDevice(char c); int writeToDevice(char c);
@@ -124,6 +133,15 @@ public:
int getCurrentLineNumber() const; int getCurrentLineNumber() const;
/// Returns the current line number (same as lines()). /// Returns the current line number (same as lines()).
void addChars(int chars);
/// Add to the total number of characters.
void addLines(int lines);
/// Add to the total number of lines.
void addPos(int pos);
/// Add to the number of characters on the current line.
CountingStreamBuf* rdbuf(); CountingStreamBuf* rdbuf();
/// Returns a pointer to the underlying streambuf. /// Returns a pointer to the underlying streambuf.

View File

@@ -96,6 +96,24 @@ void CountingStreamBuf::setCurrentLineNumber(int line)
} }
void CountingStreamBuf::addChars(int chars)
{
_chars += chars;
}
void CountingStreamBuf::addLines(int lines)
{
_lines += lines;
}
void CountingStreamBuf::addPos(int pos)
{
_pos += pos;
}
CountingIOS::CountingIOS() CountingIOS::CountingIOS()
{ {
poco_ios_init(&_buf); poco_ios_init(&_buf);
@@ -131,6 +149,24 @@ void CountingIOS::setCurrentLineNumber(int line)
} }
void CountingIOS::addChars(int chars)
{
_buf.addChars(chars);
}
void CountingIOS::addLines(int lines)
{
_buf.addLines(lines);
}
void CountingIOS::addPos(int pos)
{
_buf.addPos(pos);
}
CountingStreamBuf* CountingIOS::rdbuf() CountingStreamBuf* CountingIOS::rdbuf()
{ {
return &_buf; return &_buf;

View File

@@ -54,6 +54,16 @@ void CountingStreamTest::testInput()
assert (ci3.lines() == 2); assert (ci3.lines() == 2);
assert (ci3.chars() == 8); assert (ci3.chars() == 8);
assert (ci3.pos() == 0); assert (ci3.pos() == 0);
std::istringstream istr4("foo");
CountingInputStream ci4(istr4);
while (ci4.good()) ci4.get(c);
ci4.addChars(10);
ci4.addLines(2);
ci4.addPos(3);
assert (ci4.lines() == 1 + 2);
assert (ci4.chars() == 3 + 10);
assert (ci4.pos() == 3 + 3);
} }
@@ -80,6 +90,16 @@ void CountingStreamTest::testOutput()
assert (co3.lines() == 2); assert (co3.lines() == 2);
assert (co3.chars() == 8); assert (co3.chars() == 8);
assert (co3.pos() == 0); assert (co3.pos() == 0);
std::ostringstream ostr4;
CountingOutputStream co4(ostr4);
co4 << "foo";
co4.addChars(10);
co4.addLines(2);
co4.addPos(3);
assert (co4.lines() == 1 + 2);
assert (co4.chars() == 3 + 10);
assert (co4.pos() == 3 + 3);
} }

View File

@@ -63,7 +63,11 @@ public:
const std::string& filename() const; const std::string& filename() const;
/// Returns the filename portion of the path. /// Returns the filename portion of the path.
std::streamsize getContentLength() const;
/// Returns the file size.
private: private:
std::string _path;
std::string _filename; std::string _filename;
Poco::FileInputStream _istr; Poco::FileInputStream _istr;
}; };

View File

@@ -153,7 +153,14 @@ public:
/// 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
std::streamsize calculateContentLength();
/// Calculate the content length for the form.
/// May be UNKNOWN_CONTENT_LENGTH if not possible
/// to calculate
void write(std::ostream& ostr, const std::string& boundary); void write(std::ostream& ostr, const std::string& boundary);
/// Writes the form data to the given output stream, /// Writes the form data to the given output stream,
/// using the specified encoding. /// using the specified encoding.
@@ -183,6 +190,7 @@ public:
static const std::string ENCODING_URL; /// "application/x-www-form-urlencoded" static const std::string ENCODING_URL; /// "application/x-www-form-urlencoded"
static const std::string ENCODING_MULTIPART; /// "multipart/form-data" static const std::string ENCODING_MULTIPART; /// "multipart/form-data"
static const int UNKNOWN_CONTENT_LENGTH;
protected: protected:
void readUrl(std::istream& istr); void readUrl(std::istream& istr);
void readMultipart(std::istream& istr, PartHandler& handler); void readMultipart(std::istream& istr, PartHandler& handler);

View File

@@ -57,9 +57,15 @@ public:
/// Returns a MessageHeader containing additional header /// Returns a MessageHeader containing additional header
/// fields for the part. /// fields for the part.
virtual std::streamsize getContentLength() const;
/// Returns the content length for this part
/// which may be UNKNOWN_CONTENT_LENGTH if
/// not available.
virtual ~PartSource(); virtual ~PartSource();
/// Destroys the PartSource. /// Destroys the PartSource.
static const int UNKNOWN_CONTENT_LENGTH;
protected: protected:
PartSource(); PartSource();
/// Creates the PartSource, using /// Creates the PartSource, using

View File

@@ -55,6 +55,9 @@ public:
const std::string& filename() const; const std::string& filename() const;
/// Returns the filename portion of the path. /// Returns the filename portion of the path.
std::streamsize getContentLength() const;
/// Returns the string size.
private: private:
std::istringstream _istr; std::istringstream _istr;
std::string _filename; std::string _filename;

View File

@@ -16,6 +16,7 @@
#include "Poco/Net/FilePartSource.h" #include "Poco/Net/FilePartSource.h"
#include "Poco/Path.h" #include "Poco/Path.h"
#include "Poco/File.h"
#include "Poco/Exception.h" #include "Poco/Exception.h"
@@ -28,7 +29,7 @@ namespace Net {
FilePartSource::FilePartSource(const std::string& path): FilePartSource::FilePartSource(const std::string& path):
_istr(path) _path(path), _istr(path)
{ {
Path p(path); Path p(path);
_filename = p.getFileName(); _filename = p.getFileName();
@@ -39,6 +40,7 @@ FilePartSource::FilePartSource(const std::string& path):
FilePartSource::FilePartSource(const std::string& path, const std::string& mediaType): FilePartSource::FilePartSource(const std::string& path, const std::string& mediaType):
PartSource(mediaType), PartSource(mediaType),
_path(path),
_istr(path) _istr(path)
{ {
Path p(path); Path p(path);
@@ -50,6 +52,7 @@ FilePartSource::FilePartSource(const std::string& path, const std::string& media
FilePartSource::FilePartSource(const std::string& path, const std::string& filename, const std::string& mediaType): FilePartSource::FilePartSource(const std::string& path, const std::string& filename, const std::string& mediaType):
PartSource(mediaType), PartSource(mediaType),
_path(path),
_filename(filename), _filename(filename),
_istr(path) _istr(path)
{ {
@@ -76,4 +79,11 @@ const std::string& FilePartSource::filename() const
} }
std::streamsize FilePartSource::getContentLength() const
{
Poco::File p(_path);
return p.getSize();
}
} } // namespace Poco::Net } } // namespace Poco::Net

View File

@@ -27,6 +27,7 @@
#include "Poco/StreamCopier.h" #include "Poco/StreamCopier.h"
#include "Poco/URI.h" #include "Poco/URI.h"
#include "Poco/String.h" #include "Poco/String.h"
#include "Poco/CountingStream.h"
#include <sstream> #include <sstream>
@@ -41,8 +42,21 @@ namespace Poco {
namespace Net { namespace Net {
const std::string HTMLForm::ENCODING_URL = "application/x-www-form-urlencoded"; const std::string HTMLForm::ENCODING_URL = "application/x-www-form-urlencoded";
const std::string HTMLForm::ENCODING_MULTIPART = "multipart/form-data"; const std::string HTMLForm::ENCODING_MULTIPART = "multipart/form-data";
const int HTMLForm::UNKNOWN_CONTENT_LENGTH = -1;
class HTMLFormCountingOutputStream : public CountingOutputStream
{
public:
HTMLFormCountingOutputStream() : _isvalid(true) {}
bool getIsValid() const { return _isvalid; }
void setIsValid(bool v) { _isvalid = v; }
private:
bool _isvalid;
};
HTMLForm::HTMLForm(): HTMLForm::HTMLForm():
@@ -217,6 +231,20 @@ void HTMLForm::prepareSubmit(HTTPRequest& request)
} }
std::streamsize HTMLForm::calculateContentLength()
{
if (_boundary.empty())
throw HTMLFormException("Form must be prepared");
HTMLFormCountingOutputStream c;
write(c);
if (c.getIsValid())
return c.chars();
else
return UNKNOWN_CONTENT_LENGTH;
}
void HTMLForm::write(std::ostream& ostr, const std::string& boundary) void HTMLForm::write(std::ostream& ostr, const std::string& boundary)
{ {
if (_encoding == ENCODING_URL) if (_encoding == ENCODING_URL)
@@ -338,6 +366,8 @@ void HTMLForm::writeUrl(std::ostream& ostr)
void HTMLForm::writeMultipart(std::ostream& ostr) void HTMLForm::writeMultipart(std::ostream& ostr)
{ {
HTMLFormCountingOutputStream *costr(dynamic_cast<HTMLFormCountingOutputStream*>(&ostr));
MultipartWriter writer(ostr, _boundary); MultipartWriter writer(ostr, _boundary);
for (NameValueCollection::ConstIterator it = begin(); it != end(); ++it) for (NameValueCollection::ConstIterator it = begin(); it != end(); ++it)
{ {
@@ -365,7 +395,17 @@ void HTMLForm::writeMultipart(std::ostream& ostr)
header.set("Content-Disposition", disp); header.set("Content-Disposition", disp);
header.set("Content-Type", ita->pSource->mediaType()); header.set("Content-Type", ita->pSource->mediaType());
writer.nextPart(header); writer.nextPart(header);
StreamCopier::copyStream(ita->pSource->stream(), ostr); if (costr)
{
// count only, don't move stream position
std::streamsize partlen = ita->pSource->getContentLength();
if (partlen != PartSource::UNKNOWN_CONTENT_LENGTH)
costr->addChars(partlen);
else
costr->setIsValid(false);
}
else
StreamCopier::copyStream(ita->pSource->stream(), ostr);
} }
writer.close(); writer.close();
_boundary = writer.boundary(); _boundary = writer.boundary();

View File

@@ -21,6 +21,9 @@ namespace Poco {
namespace Net { namespace Net {
const int PartSource::UNKNOWN_CONTENT_LENGTH = -1;
PartSource::PartSource(): PartSource::PartSource():
_mediaType("application/octet-stream") _mediaType("application/octet-stream")
{ {
@@ -49,5 +52,9 @@ const std::string& PartSource::filename() const
return EMPTY; return EMPTY;
} }
std::streamsize PartSource::getContentLength() const
{
return UNKNOWN_CONTENT_LENGTH;
}
} } // namespace Poco::Net } } // namespace Poco::Net

View File

@@ -60,4 +60,10 @@ const std::string& StringPartSource::filename() const
} }
std::streamsize StringPartSource::getContentLength() const
{
return _istr.str().length();
}
} } // namespace Poco::Net } } // namespace Poco::Net

View File

@@ -147,6 +147,7 @@ void HTMLFormTest::testWriteMultipart()
"This is another attachment\r\n" "This is another attachment\r\n"
"--MIME_boundary_0123456789--\r\n" "--MIME_boundary_0123456789--\r\n"
); );
assert(s.length() == form.calculateContentLength());
} }