diff --git a/CHANGELOG b/CHANGELOG index 1963807d9..4c11f039a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,7 +1,7 @@ This is the changelog file for the POCO C++ Libraries. -Release 1.5.0 (2012-07-30) +Release 1.5.0 (2012-08-??) ========================== - added JSON @@ -23,7 +23,7 @@ Release 1.5.0 (2012-07-30) - IPAddress force IPv6 always lowercase (RFC 5952) - fixed SF#3538785: SMTPClientSession::sendMessage() should take recipient list -Release 1.4.4 (2012-07-??) +Release 1.4.4 (2012-08-??) ========================== - ZipStream now builds correctly in unbundled build. @@ -53,6 +53,10 @@ Release 1.4.4 (2012-07-??) - Added Poco::ObjectPool class template. - Poco::Net::HTTPServer has a new stopAll() method allowing stopping/aborting of all currently active client connections. +- The HTTP server framework now actively prevents sending a message body in the + response to a HEAD request, or in case of a 204 No Content or 304 Not Modified + response status. +- fixed a DOM parser performance bug (patch by Peter Klotz) Release 1.4.3p1 (2012-01-23) ============================ diff --git a/Net/include/Poco/Net/HTTPFixedLengthStream.h b/Net/include/Poco/Net/HTTPFixedLengthStream.h index b7e3ec4e5..a7eaa2f01 100644 --- a/Net/include/Poco/Net/HTTPFixedLengthStream.h +++ b/Net/include/Poco/Net/HTTPFixedLengthStream.h @@ -63,7 +63,13 @@ class Net_API HTTPFixedLengthStreamBuf: public HTTPBasicStreamBuf public: typedef HTTPBasicStreamBuf::openmode openmode; - HTTPFixedLengthStreamBuf(HTTPSession& session, std::streamsize length, openmode mode); +#if defined(POCO_HAVE_INT64) + typedef Poco::Int64 ContentLength; +#else + typedef std::streamsize ContentLength; +#endif + + HTTPFixedLengthStreamBuf(HTTPSession& session, ContentLength length, openmode mode); ~HTTPFixedLengthStreamBuf(); protected: @@ -72,8 +78,8 @@ protected: private: HTTPSession& _session; - std::streamsize _length; - std::streamsize _count; + ContentLength _length; + ContentLength _count; }; @@ -81,7 +87,7 @@ class Net_API HTTPFixedLengthIOS: public virtual std::ios /// The base class for HTTPFixedLengthInputStream. { public: - HTTPFixedLengthIOS(HTTPSession& session, std::streamsize length, HTTPFixedLengthStreamBuf::openmode mode); + HTTPFixedLengthIOS(HTTPSession& session, HTTPFixedLengthStreamBuf::ContentLength length, HTTPFixedLengthStreamBuf::openmode mode); ~HTTPFixedLengthIOS(); HTTPFixedLengthStreamBuf* rdbuf(); @@ -94,7 +100,7 @@ class Net_API HTTPFixedLengthInputStream: public HTTPFixedLengthIOS, public std: /// This class is for internal use by HTTPSession only. { public: - HTTPFixedLengthInputStream(HTTPSession& session, std::streamsize length); + HTTPFixedLengthInputStream(HTTPSession& session, HTTPFixedLengthStreamBuf::ContentLength length); ~HTTPFixedLengthInputStream(); void* operator new(std::size_t size); @@ -109,7 +115,7 @@ class Net_API HTTPFixedLengthOutputStream: public HTTPFixedLengthIOS, public std /// This class is for internal use by HTTPSession only. { public: - HTTPFixedLengthOutputStream(HTTPSession& session, std::streamsize length); + HTTPFixedLengthOutputStream(HTTPSession& session, HTTPFixedLengthStreamBuf::ContentLength length); ~HTTPFixedLengthOutputStream(); void* operator new(std::size_t size); diff --git a/Net/include/Poco/Net/HTTPMessage.h b/Net/include/Poco/Net/HTTPMessage.h index f58a9acc9..c131feb6d 100644 --- a/Net/include/Poco/Net/HTTPMessage.h +++ b/Net/include/Poco/Net/HTTPMessage.h @@ -95,6 +95,9 @@ public: /// always returns a 64-bit integer for content length. #endif // defined(POCO_HAVE_INT64) + bool hasContentLength() const; + /// Returns true iff a Content-Length header is present. + void setTransferEncoding(const std::string& transferEncoding); /// Sets the transfer encoding for this message. /// @@ -192,6 +195,12 @@ inline const std::string& HTTPMessage::getVersion() const } +inline bool HTTPMessage::hasContentLength() const +{ + return has(CONTENT_LENGTH); +} + + } } // namespace Poco::Net diff --git a/Net/include/Poco/Net/HTTPServerRequestImpl.h b/Net/include/Poco/Net/HTTPServerRequestImpl.h index eba63e0d4..3444df5b9 100644 --- a/Net/include/Poco/Net/HTTPServerRequestImpl.h +++ b/Net/include/Poco/Net/HTTPServerRequestImpl.h @@ -42,6 +42,7 @@ #include "Poco/Net/Net.h" #include "Poco/Net/HTTPServerRequest.h" +#include "Poco/Net/HTTPServerResponseImpl.h" #include "Poco/Net/SocketAddress.h" #include "Poco/AutoPtr.h" #include @@ -64,7 +65,7 @@ class Net_API HTTPServerRequestImpl: public HTTPServerRequest /// handleRequest() method of HTTPRequestHandler. { public: - HTTPServerRequestImpl(HTTPServerResponse& response, HTTPServerSession& session, HTTPServerParams* pParams); + HTTPServerRequestImpl(HTTPServerResponseImpl& response, HTTPServerSession& session, HTTPServerParams* pParams); /// Creates the HTTPServerRequestImpl, using the /// given HTTPServerSession. @@ -105,7 +106,7 @@ protected: static const std::string EXPECT; private: - HTTPServerResponse& _response; + HTTPServerResponseImpl& _response; HTTPServerSession& _session; std::istream* _pStream; Poco::AutoPtr _pParams; diff --git a/Net/include/Poco/Net/HTTPServerResponseImpl.h b/Net/include/Poco/Net/HTTPServerResponseImpl.h index 025695cfa..34377eb9d 100644 --- a/Net/include/Poco/Net/HTTPServerResponseImpl.h +++ b/Net/include/Poco/Net/HTTPServerResponseImpl.h @@ -49,7 +49,7 @@ namespace Net { class HTTPServerSession; -class HTTPCookie; +class HTTPServerRequestImpl; class Net_API HTTPServerResponseImpl: public HTTPServerResponse @@ -128,9 +128,15 @@ public: bool sent() const; /// Returns true if the response (header) has been sent. +protected: + void attachRequest(HTTPServerRequestImpl* pRequest); + private: HTTPServerSession& _session; + HTTPServerRequestImpl* _pRequest; std::ostream* _pStream; + + friend class HTTPServerRequestImpl; }; @@ -143,6 +149,12 @@ inline bool HTTPServerResponseImpl::sent() const } +inline void HTTPServerResponseImpl::attachRequest(HTTPServerRequestImpl* pRequest) +{ + _pRequest = pRequest; +} + + } } // namespace Poco::Net diff --git a/Net/src/HTTPClientSession.cpp b/Net/src/HTTPClientSession.cpp index 8d8677633..b88459c41 100644 --- a/Net/src/HTTPClientSession.cpp +++ b/Net/src/HTTPClientSession.cpp @@ -223,11 +223,15 @@ std::ostream& HTTPClientSession::sendRequest(HTTPRequest& request) request.write(hos); _pRequestStream = new HTTPChunkedOutputStream(*this); } - else if (request.getContentLength() != HTTPMessage::UNKNOWN_CONTENT_LENGTH) + else if (request.hasContentLength()) { Poco::CountingOutputStream cs; request.write(cs); +#if POCO_HAVE_INT64 + _pRequestStream = new HTTPFixedLengthOutputStream(*this, request.getContentLength64() + cs.chars()); +#else _pRequestStream = new HTTPFixedLengthOutputStream(*this, request.getContentLength() + cs.chars()); +#endif request.write(*_pRequestStream); } else if (request.getMethod() != HTTPRequest::HTTP_PUT && request.getMethod() != HTTPRequest::HTTP_POST) @@ -284,12 +288,16 @@ std::istream& HTTPClientSession::receiveResponse(HTTPResponse& response) _mustReconnect = getKeepAlive() && !response.getKeepAlive(); - if (!_expectResponseBody) + if (!_expectResponseBody || response.getStatus() < 200 || response.getStatus() == HTTPResponse::HTTP_NO_CONTENT || response.getStatus() == HTTPResponse::HTTP_NOT_MODIFIED) _pResponseStream = new HTTPFixedLengthInputStream(*this, 0); else if (response.getChunkedTransferEncoding()) _pResponseStream = new HTTPChunkedInputStream(*this); - else if (response.getContentLength() != HTTPMessage::UNKNOWN_CONTENT_LENGTH) + else if (response.hasContentLength()) +#if defined(POCO_HAVE_INT64) + _pResponseStream = new HTTPFixedLengthInputStream(*this, response.getContentLength64()); +#else _pResponseStream = new HTTPFixedLengthInputStream(*this, response.getContentLength()); +#endif else _pResponseStream = new HTTPInputStream(*this); diff --git a/Net/src/HTTPFixedLengthStream.cpp b/Net/src/HTTPFixedLengthStream.cpp index f62aaf2ba..31533c3dc 100644 --- a/Net/src/HTTPFixedLengthStream.cpp +++ b/Net/src/HTTPFixedLengthStream.cpp @@ -50,7 +50,7 @@ namespace Net { // -HTTPFixedLengthStreamBuf::HTTPFixedLengthStreamBuf(HTTPSession& session, std::streamsize length, openmode mode): +HTTPFixedLengthStreamBuf::HTTPFixedLengthStreamBuf(HTTPSession& session, ContentLength length, openmode mode): HTTPBasicStreamBuf(HTTPBufferAllocator::BUFFER_SIZE, mode), _session(session), _length(length), @@ -70,7 +70,7 @@ int HTTPFixedLengthStreamBuf::readFromDevice(char* buffer, std::streamsize lengt if (_count < _length) { if (_count + length > _length) - length = _length - _count; + length = static_cast(_length - _count); n = _session.read(buffer, length); if (n > 0) _count += n; } @@ -84,7 +84,7 @@ int HTTPFixedLengthStreamBuf::writeToDevice(const char* buffer, std::streamsize if (_count < _length) { if (_count + length > _length) - length = _length - _count; + length = static_cast(_length - _count); n = _session.write(buffer, length); if (n > 0) _count += n; } @@ -97,7 +97,7 @@ int HTTPFixedLengthStreamBuf::writeToDevice(const char* buffer, std::streamsize // -HTTPFixedLengthIOS::HTTPFixedLengthIOS(HTTPSession& session, std::streamsize length, HTTPFixedLengthStreamBuf::openmode mode): +HTTPFixedLengthIOS::HTTPFixedLengthIOS(HTTPSession& session, HTTPFixedLengthStreamBuf::ContentLength length, HTTPFixedLengthStreamBuf::openmode mode): _buf(session, length, mode) { poco_ios_init(&_buf); @@ -130,7 +130,7 @@ HTTPFixedLengthStreamBuf* HTTPFixedLengthIOS::rdbuf() Poco::MemoryPool HTTPFixedLengthInputStream::_pool(sizeof(HTTPFixedLengthInputStream)); -HTTPFixedLengthInputStream::HTTPFixedLengthInputStream(HTTPSession& session, std::streamsize length): +HTTPFixedLengthInputStream::HTTPFixedLengthInputStream(HTTPSession& session, HTTPFixedLengthStreamBuf::ContentLength length): HTTPFixedLengthIOS(session, length, std::ios::in), std::istream(&_buf) { @@ -162,7 +162,7 @@ void HTTPFixedLengthInputStream::operator delete(void* ptr) Poco::MemoryPool HTTPFixedLengthOutputStream::_pool(sizeof(HTTPFixedLengthOutputStream)); -HTTPFixedLengthOutputStream::HTTPFixedLengthOutputStream(HTTPSession& session, std::streamsize length): +HTTPFixedLengthOutputStream::HTTPFixedLengthOutputStream(HTTPSession& session, HTTPFixedLengthStreamBuf::ContentLength length): HTTPFixedLengthIOS(session, length, std::ios::out), std::ostream(&_buf) { diff --git a/Net/src/HTTPServerConnection.cpp b/Net/src/HTTPServerConnection.cpp index 7dd13919e..3fd5392df 100644 --- a/Net/src/HTTPServerConnection.cpp +++ b/Net/src/HTTPServerConnection.cpp @@ -105,7 +105,15 @@ void HTTPServerConnection::run() catch (Poco::Exception&) { if (!response.sent()) - sendErrorResponse(session, HTTPResponse::HTTP_INTERNAL_SERVER_ERROR); + { + try + { + sendErrorResponse(session, HTTPResponse::HTTP_INTERNAL_SERVER_ERROR); + } + catch (...) + { + } + } throw; } } @@ -140,7 +148,14 @@ void HTTPServerConnection::onServerStopped(const bool& abortCurrent) { try { + // Note: On Windows, select() will not return if one of its socket is being + // shut down. Therefore we have to call close(), which works better. + // On other platforms, we do the more graceful thing. +#if defined(_WIN32) socket().close(); +#else + socket().shutdown(); +#endif } catch (...) { @@ -152,7 +167,11 @@ void HTTPServerConnection::onServerStopped(const bool& abortCurrent) try { +#if defined(_WIN32) socket().close(); +#else + socket().shutdown(); +#endif } catch (...) { diff --git a/Net/src/HTTPServerRequestImpl.cpp b/Net/src/HTTPServerRequestImpl.cpp index ee7416f2d..26c195c1a 100644 --- a/Net/src/HTTPServerRequestImpl.cpp +++ b/Net/src/HTTPServerRequestImpl.cpp @@ -35,6 +35,7 @@ #include "Poco/Net/HTTPServerRequestImpl.h" +#include "Poco/Net/HTTPServerResponseImpl.h" #include "Poco/Net/HTTPServerSession.h" #include "Poco/Net/HTTPHeaderStream.h" #include "Poco/Net/HTTPStream.h" @@ -54,12 +55,14 @@ namespace Net { const std::string HTTPServerRequestImpl::EXPECT("Expect"); -HTTPServerRequestImpl::HTTPServerRequestImpl(HTTPServerResponse& response, HTTPServerSession& session, HTTPServerParams* pParams): +HTTPServerRequestImpl::HTTPServerRequestImpl(HTTPServerResponseImpl& response, HTTPServerSession& session, HTTPServerParams* pParams): _response(response), _session(session), _pStream(0), _pParams(pParams, true) { + response.attachRequest(this); + HTTPHeaderInputStream hs(session); read(hs); @@ -69,8 +72,12 @@ HTTPServerRequestImpl::HTTPServerRequestImpl(HTTPServerResponse& response, HTTPS if (getChunkedTransferEncoding()) _pStream = new HTTPChunkedInputStream(session); - else if (getContentLength() != HTTPMessage::UNKNOWN_CONTENT_LENGTH) + else if (hasContentLength()) +#if defined(POCO_HAVE_INT64) + _pStream = new HTTPFixedLengthInputStream(session, getContentLength64()); +#else _pStream = new HTTPFixedLengthInputStream(session, getContentLength()); +#endif else if (getMethod() == HTTPRequest::HTTP_GET || getMethod() == HTTPRequest::HTTP_HEAD) _pStream = new HTTPFixedLengthInputStream(session, 0); else diff --git a/Net/src/HTTPServerResponseImpl.cpp b/Net/src/HTTPServerResponseImpl.cpp index 5adb1639f..046193625 100644 --- a/Net/src/HTTPServerResponseImpl.cpp +++ b/Net/src/HTTPServerResponseImpl.cpp @@ -35,6 +35,7 @@ #include "Poco/Net/HTTPServerResponseImpl.h" +#include "Poco/Net/HTTPServerRequestImpl.h" #include "Poco/Net/HTTPServerSession.h" #include "Poco/Net/HTTPHeaderStream.h" #include "Poco/Net/HTTPStream.h" @@ -66,6 +67,7 @@ namespace Net { HTTPServerResponseImpl::HTTPServerResponseImpl(HTTPServerSession& session): _session(session), + _pRequest(0), _pStream(0) { } @@ -88,17 +90,31 @@ std::ostream& HTTPServerResponseImpl::send() { poco_assert (!_pStream); - if (getChunkedTransferEncoding()) + if (_pRequest && _pRequest->getMethod() == HTTPRequest::HTTP_HEAD || + getStatus() < 200 || + getStatus() == HTTPResponse::HTTP_NO_CONTENT || + getStatus() == HTTPResponse::HTTP_NOT_MODIFIED) + { + Poco::CountingOutputStream cs; + write(cs); + _pStream = new HTTPFixedLengthOutputStream(_session, cs.chars()); + write(*_pStream); + } + else if (getChunkedTransferEncoding()) { HTTPHeaderOutputStream hs(_session); write(hs); _pStream = new HTTPChunkedOutputStream(_session); } - else if (getContentLength() != HTTPMessage::UNKNOWN_CONTENT_LENGTH) + else if (hasContentLength()) { Poco::CountingOutputStream cs; write(cs); +#if defined(POCO_HAVE_INT64) + _pStream = new HTTPFixedLengthOutputStream(_session, getContentLength64() + cs.chars()); +#else _pStream = new HTTPFixedLengthOutputStream(_session, getContentLength() + cs.chars()); +#endif write(*_pStream); } else @@ -132,7 +148,10 @@ void HTTPServerResponseImpl::sendFile(const std::string& path, const std::string { _pStream = new HTTPHeaderOutputStream(_session); write(*_pStream); - StreamCopier::copyStream(istr, *_pStream); + if (_pRequest && _pRequest->getMethod() != HTTPRequest::HTTP_HEAD) + { + StreamCopier::copyStream(istr, *_pStream); + } } else throw OpenFileException(path); } @@ -147,7 +166,10 @@ void HTTPServerResponseImpl::sendBuffer(const void* pBuffer, std::size_t length) _pStream = new HTTPHeaderOutputStream(_session); write(*_pStream); - _pStream->write(static_cast(pBuffer), static_cast(length)); + if (_pRequest && _pRequest->getMethod() != HTTPRequest::HTTP_HEAD) + { + _pStream->write(static_cast(pBuffer), static_cast(length)); + } } diff --git a/Net/src/SocketImpl.cpp b/Net/src/SocketImpl.cpp index 0e39a4e63..171d3b8c0 100644 --- a/Net/src/SocketImpl.cpp +++ b/Net/src/SocketImpl.cpp @@ -523,6 +523,7 @@ bool SocketImpl::poll(const Poco::Timespan& timeout, int mode) FD_SET(sockfd, &fdExcept); } Poco::Timespan remainingTime(timeout); + int errorCode; int rc; do { @@ -531,7 +532,7 @@ bool SocketImpl::poll(const Poco::Timespan& timeout, int mode) tv.tv_usec = (long) remainingTime.useconds(); Poco::Timestamp start; rc = ::select(int(sockfd) + 1, &fdRead, &fdWrite, &fdExcept, &tv); - if (rc < 0 && lastError() == POCO_EINTR) + if (rc < 0 && (errorCode = lastError()) == POCO_EINTR) { Poco::Timestamp end; Poco::Timespan waited = end - start; @@ -541,8 +542,8 @@ bool SocketImpl::poll(const Poco::Timespan& timeout, int mode) remainingTime = 0; } } - while (rc < 0 && lastError() == POCO_EINTR); - if (rc < 0) error(); + while (rc < 0 && errorCode == POCO_EINTR); + if (rc < 0) error(errorCode); return rc > 0; #endif // POCO_HAVE_FD_EPOLL diff --git a/PageCompiler/samples/HTTPTimeServer/Makefile b/PageCompiler/samples/HTTPTimeServer/Makefile index 70a50f6a3..ed45790d7 100644 --- a/PageCompiler/samples/HTTPTimeServer/Makefile +++ b/PageCompiler/samples/HTTPTimeServer/Makefile @@ -9,7 +9,7 @@ include $(POCO_BASE)/build/rules/global # Where to find the PageCompiler executable -PAGECOMPILER = $(POCO_BASE)/PageCompiler/bin/$(POCO_HOST_OSNAME)/$(POCO_HOST_OSARCH)/cpspc$(OSARCH_POSTFIX) +PAGECOMPILER = $(POCO_BASE)/PageCompiler/bin/$(POCO_HOST_OSNAME)/$(POCO_HOST_OSARCH)/cpspc objects = HTTPTimeServerApp TimeHandler diff --git a/XML/src/CharacterData.cpp b/XML/src/CharacterData.cpp index f4285a33e..026a1a05a 100644 --- a/XML/src/CharacterData.cpp +++ b/XML/src/CharacterData.cpp @@ -64,9 +64,16 @@ CharacterData::~CharacterData() void CharacterData::setData(const XMLString& data) { - XMLString oldData = getData(); - _data = data; - if (events()) dispatchCharacterDataModified(oldData, _data); + if (events()) + { + XMLString oldData = _data; + _data = data; + dispatchCharacterDataModified(oldData, _data); + } + else + { + _data = data; + } } @@ -81,9 +88,16 @@ XMLString CharacterData::substringData(unsigned long offset, unsigned long count void CharacterData::appendData(const XMLString& arg) { - XMLString oldData = _data; - _data.append(arg); - if (events()) dispatchCharacterDataModified(oldData, _data); + if (events()) + { + XMLString oldData = _data; + _data.append(arg); + dispatchCharacterDataModified(oldData, _data); + } + else + { + _data.append(arg); + } } @@ -92,9 +106,16 @@ void CharacterData::insertData(unsigned long offset, const XMLString& arg) if (offset > _data.length()) throw DOMException(DOMException::INDEX_SIZE_ERR); - XMLString oldData = _data; - _data.insert(offset, arg); - if (events()) dispatchCharacterDataModified(oldData, _data); + if (events()) + { + XMLString oldData = _data; + _data.insert(offset, arg); + dispatchCharacterDataModified(oldData, _data); + } + else + { + _data.insert(offset, arg); + } } @@ -103,9 +124,14 @@ void CharacterData::deleteData(unsigned long offset, unsigned long count) if (offset >= _data.length()) throw DOMException(DOMException::INDEX_SIZE_ERR); - XMLString oldData = _data; - _data.replace(offset, count, EMPTY_STRING); - if (events()) dispatchCharacterDataModified(oldData, _data); + if (events()) + { + XMLString oldData = _data; + _data.replace(offset, count, EMPTY_STRING); + dispatchCharacterDataModified(oldData, _data); + } + else + _data.replace(offset, count, EMPTY_STRING); } @@ -114,9 +140,16 @@ void CharacterData::replaceData(unsigned long offset, unsigned long count, const if (offset >= _data.length()) throw DOMException(DOMException::INDEX_SIZE_ERR); - XMLString oldData = _data; - _data.replace(offset, count, arg); - if (events()) dispatchCharacterDataModified(oldData, _data); + if (events()) + { + XMLString oldData = _data; + _data.replace(offset, count, arg); + dispatchCharacterDataModified(oldData, _data); + } + else + { + _data.replace(offset, count, arg); + } }