/*
* Message.cpp
*
* Copyright (C) 2009-12 by RStudio, PBC
*
* Unless you have received this program directly from RStudio pursuant
* to the terms of a commercial license agreement with RStudio, then
* this program is licensed to you under the terms of version 3 of the
* GNU Affero General Public License. This program is distributed WITHOUT
* ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
* AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
*
*/
#include
#include
#include
#include
#include
#include
#include
namespace rstudio {
namespace core {
namespace http {
// encodings
const char * const kGzipEncoding = "gzip";
const char * const kDeflateEncoding = "deflate";
// transfer encodings
const char * const kTransferEncoding = "Transfer-Encoding";
const char * const kChunkedTransferEncoding = "chunked";
void Message::setHttpVersion(int httpVersionMajor, int httpVersionMinor)
{
httpVersionMajor_ = httpVersionMajor ;
httpVersionMinor_ = httpVersionMinor ;
}
void Message::setContentType(const std::string& contentType)
{
setHeader("Content-Type", contentType) ;
}
std::string Message::contentType() const
{
return headerValue("Content-Type") ;
}
uintmax_t Message::contentLength() const
{
std::string value = headerValue("Content-Length");
if (value.empty())
return 0;
return safe_convert::stringTo(value, 0);
}
void Message::setContentLength(uintmax_t contentLength)
{
setHeader("Content-Length", contentLength);
}
void Message::addHeader(const std::string& name, const std::string& value)
{
Header header ;
header.name = name ;
header.value = value ;
addHeader(header);
}
void Message::addHeader(const Header& header)
{
headers_.push_back(header);
}
void Message::addHeaders(const std::vector& headers)
{
std::copy(headers.begin(), headers.end(), std::back_inserter(headers_));
}
std::string Message::headerValue(const std::string& name) const
{
return http::headerValue(headers_, name);
}
bool Message::containsHeader(const std::string& name) const
{
return http::containsHeader(headers_, name);
}
void Message::setHeaderLine(const std::string& line)
{
Header header;
if (http::parseHeader(line, &header))
setHeader(header);
}
void Message::setHeader(const Header& header)
{
setHeader(header.name, header.value);
}
void Message::setHeader(const std::string& name, const std::string& value)
{
Headers::iterator it = std::find_if(headers_.begin(),
headers_.end(),
HeaderNamePredicate(name));
if ( it != headers_.end() )
{
Header hdr ;
hdr.name = name ;
hdr.value = value ;
*it = hdr ;
}
else
{
addHeader(name, value) ;
}
}
void Message::setHeader(const std::string& name, int value)
{
setHeader(name, safe_convert::numberToString(value));
}
void Message::setHeader(const std::string& name, uintmax_t value)
{
setHeader(name, safe_convert::numberToString(value));
}
void Message::replaceHeader(const std::string& name, const std::string& value)
{
Header hdr ;
hdr.name = name ;
hdr.value = value ;
std::replace_if(headers_.begin(),
headers_.end(),
HeaderNamePredicate(name),
hdr) ;
}
void Message::removeHeader(const std::string& name)
{
headers_.erase(std::remove_if(headers_.begin(),
headers_.end(),
HeaderNamePredicate(name)),
headers_.end()) ;
}
void Message::reset()
{
setHttpVersion(1,1) ;
httpVersion_.clear() ;
headers_.clear() ;
body_.clear() ;
// allow additional reseting by subclasses
resetMembers() ;
}
namespace {
const char Space[] = { ' ' } ;
const char HeaderSeparator[] = { ':', ' ' } ;
const char CrLf[] = { '\r', '\n' } ;
void appendHeader(const Header& header,
std::vector* pBuffers)
{
pBuffers->push_back(boost::asio::buffer(header.name)) ;
pBuffers->push_back(boost::asio::buffer(HeaderSeparator)) ;
pBuffers->push_back(boost::asio::buffer(header.value)) ;
pBuffers->push_back(boost::asio::buffer(CrLf)) ;
}
}
std::vector Message::toBuffers(
const Header& overrideHeader) const
{
// buffers to return
std::vector buffers ;
// headers
std::vector headerBuffs =
headerBuffers(overrideHeader);
buffers.insert(buffers.end(), headerBuffs.begin(), headerBuffs.end());
// body
buffers.push_back(boost::asio::buffer(body_)) ;
// return the buffers
return buffers ;
}
std::vector Message::headerBuffers(
const Header& overrideHeader) const
{
// buffers to return
std::vector buffers ;
// call subclass to append first line
appendFirstLineBuffers(buffers) ;
buffers.push_back(boost::asio::buffer(CrLf)) ;
// copy override header (for stable storage)
overrideHeader_ = overrideHeader;
// headers
for (Headers::const_iterator
it = headers_.begin(); it != headers_.end(); ++it)
{
// add the header if it isn't being overriden
if (it->name != overrideHeader_.name)
appendHeader(*it, &buffers);
}
// add override header
if (!overrideHeader_.empty())
appendHeader(overrideHeader_, &buffers);
// empty line
buffers.push_back(boost::asio::buffer(CrLf)) ;
// return the buffers
return buffers ;
}
void Message::appendSpaceBuffer(
std::vector& buffers) const
{
buffers.push_back(boost::asio::buffer(Space)) ;
}
void Message::appendHttpVersionBuffers(
std::vector& buffers) const
{
std::ostringstream httpVersionStream;
httpVersionStream