0
\$\begingroup\$

The Response object is how the user sends an HTTP reponse back from the server. This object is provided to them via the Pynt interface.

Basically once you have sent the HeaderResponse object via addHeaders() this will return a reference to a std::ostream that underneath is a StreamOutput object. You can simple write the body of the HTTP response object to this stream.

Response.h

#ifndef THORSANVIL_NISSE_NISSEHTTP_RESPONSE_H
#define THORSANVIL_NISSE_NISSEHTTP_RESPONSE_H
#include "NisseHTTPConfig.h"
#include "Util.h"
#include "HeaderResponse.h"
#include "StreamOutput.h"
#include <set>
#include <ostream>
#include <functional>
namespace ThorsAnvil::Nisse::NisseHTTP
{
struct StatusCode
{
 int code;
 std::string_view message;
 friend std::ostream& operator<<(std::ostream& stream, StatusCode const& statusCode)
 {
 return stream << statusCode.code << " " << statusCode.message;
 }
};
class StandardStatusCodeMap
{
 using StatusCodeMap = std::set<StatusCode, std::function<bool(StatusCode const& lhs, StatusCode const& rhs)>>;
 static StatusCodeMap const standardCodes;
 public:
 StatusCode const& operator[](int code);
};
class Response
{
 StatusCode statusCode;
 bool headerSent;
 std::ostream& baseStream;
 StreamOutput stream;
 public:
 Response(std::ostream& stream, int code = 200);
 ~Response();
 void setStatus(int code);
 std::ostream& addHeaders(HeaderResponse const& headers, Encoding type);
 std::ostream& addHeaders(HeaderResponse const& headers, std::size_t length);
 private:
 std::ostream& addHeaders(HeaderResponse const& headers, StreamBufOutput&& buffer, std::string_view extraHeader);
};
}
#endif

Response.cpp

#include "Response.h"
#include "ThorsLogging/ThorsLogging.h"
#include <iostream>
#include <string>
using namespace ThorsAnvil::Nisse::NisseHTTP;
using std::literals::string_literals::operator""s;
using std::literals::string_view_literals::operator""sv;
StandardStatusCodeMap::StatusCodeMap const StandardStatusCodeMap::standardCodes
{
 {
 {100, "Continue"sv}, {101, "Switching Protocols"sv}, {102, "Processing Deprecated"sv}, {103, "Early Hints"sv},
 {200, "OK"sv}, {201, "Created"sv}, {202, "Accepted"sv}, {203, "Non-Authoritative Information"sv},
 {204, "No Content"sv}, {205, "Reset Content"sv}, {206, "Partial Content"sv}, {207, "Multi-Status (WebDAV)"sv},
 {208, "Already Reported (WebDAV)"sv}, {226, "IM Used (HTTP Delta encoding)"sv},
 {300, "Multiple Choices"sv}, {301, "Moved Permanently"sv}, {302, "Found"sv}, {303, "See Other"sv},
 {304, "Not Modified"sv}, {305, "Use Proxy Deprecated"sv}, {306, "unused"sv}, {307, "Temporary Redirect"sv},
 {308, "Permanent Redirect"sv},
 {400, "Bad Request"sv}, {401, "Unauthorized"sv}, {402, "Payment Required"sv}, {403, "Forbidden"sv},
 {404, "Not Found"sv}, {405, "Method Not Allowed"sv}, {406, "Not Acceptable"sv},
 {407, "Proxy Authentication Required"sv}, {408, "Request Timeout"sv}, {409, "Conflict"sv}, {410, "Gone"sv},
 {411, "Length Required"sv}, {412, "Precondition Failed"sv}, {413, "Content Too Large"sv}, {414, "URI Too Long"sv},
 {415, "Unsupported Media Type"sv}, {416, "Range Not Satisfiable"sv}, {417, "Expectation Failed"sv},
 {418, "I'm a teapot"sv}, {421, "Misdirected Request"sv}, {422, "Unprocessable Content (WebDAV)"sv},
 {423, "Locked (WebDAV)"sv}, {424, "Failed Dependency (WebDAV)"sv}, {425, "Too Early Experimental"sv},
 {426, "Upgrade Required"sv}, {428, "Precondition Required"sv}, {429, "Too Many Requests"sv},
 {431, "Request Header Fields Too Large"sv}, {451, "Unavailable For Legal Reasons"sv},
 {500, "Internal Server Error"sv}, {501, "Not Implemented"sv}, {502, "Bad Gateway"sv},
 {503, "Service Unavailable"sv}, {504, "Gateway Timeout"sv}, {505, "HTTP Version Not Supported"sv},
 {506, "Variant Also Negotiates"sv}, {507, "Insufficient Storage (WebDAV)"sv}, {508, "Loop Detected (WebDAV)"sv},
 {510, "Not Extended"sv}, {511, "Network Authentication Required"sv}
 },
 [](StatusCode const& lhs, StatusCode const& rhs){return lhs.code < rhs.code;}
};
StatusCode const& StandardStatusCodeMap::operator[](int code)
{
 static StatusCode unknown{500, "Internal Server Error"sv};
 auto find = standardCodes.find({code, ""sv});
 return find == standardCodes.end() ? unknown : *find;
}
StandardStatusCodeMap standardCodes;
Response::Response(std::ostream& stream, int responseCode)
 : statusCode{standardCodes[responseCode]}
 , headerSent{false}
 , baseStream{stream}
{}
Response::~Response()
{
 if (stream.rdbuf() == nullptr)
 {
 std::cerr << "\tSending minimum required data\n";
 baseStream << "HTTP/1.1 " << statusCode << "\r\n"
 << "\r\n";
 }
}
void Response::setStatus(int newStatusCode)
{
 statusCode = standardCodes[newStatusCode];
}
std::ostream& Response::addHeaders(HeaderResponse const& headers, Encoding type)
{
 return addHeaders(headers, StreamBufOutput{baseStream, type}, "transfer-encoding: chunked\r\n");
}
std::ostream& Response::addHeaders(HeaderResponse const& headers, std::size_t length)
{
 return addHeaders(headers, StreamBufOutput{baseStream, length}, length == 0 ? "" : "content-length: "s + std::to_string(length) + "\r\n");
}
std::ostream& Response::addHeaders(HeaderResponse const& headers, StreamBufOutput&& buffer, std::string_view extraHeader)
{
 if (headerSent) {
 ThorsLogAndThrowLogical("ThorsAnvil::Nisse::Response", "addHeaders", "Headers have already been sent");
 }
 baseStream << "HTTP/1.1 " << statusCode << "\r\n"
 << headers
 << extraHeader
 << "\r\n"
 << std::flush;
 headerSent = true;
 stream.addBuffer(std::move(buffer));
 return stream;
}
asked Oct 20, 2024 at 21:08
\$\endgroup\$

0

Know someone who can answer? Share a link to this question via email, Twitter, or Facebook.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.