2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
42#include "web/http/server/Response.h"
45#include "core/file/FileReader.h"
46#include "core/socket/stream/SocketConnection.h"
47#include "web/http/MimeTypes.h"
48#include "web/http/StatusCodes.h"
49#include "web/http/server/SocketContextUpgradeFactorySelector.h"
51#ifndef DOXYGEN_SHOULD_SKIP_THIS
53#include "log/Logger.h"
54#include "utils/system/time.h"
55#include "web/http/CiStringMap.h"
56#include "web/http/http_utils.h"
63#include <system_error>
68#define to_hex_str(int_val) (static_cast<std::ostringstream const&>(std::ostringstream() << std::uppercase << std::hex << int_val)).str()
70namespace web::http::
server {
106 const std::map<std::string, std::string>::iterator it =
headers.find(field);
109 set(field
, it->second.append(
", ").append(value)
);
118 for (
const auto& [field, value] : headers) {
126 if (!value.empty()) {
128 headers.insert_or_assign(field, value);
130 headers.insert({field, value});
142 headers.erase(
"Transfer-Encoding");
146 headers.erase(
"Content-Length");
172 return set("Content-Type", type
);
175 Response&
Response::
cookie(
const std::string& name,
const std::string& value,
const std::map<std::string, std::string>& options) {
182 std::map<std::string, std::string> opts = options;
184 opts.erase(
"Max-Age");
185 const time_t time = 0;
192 if (!value.empty()) {
194 trailer.insert_or_assign(field, value);
196 trailer.insert({field, value});
198 if (!
headers.contains(
"Trailer")) {
201 headers[
"Trailer"].append(
"," + field);
212 set("Content-Type", "application/octet-stream", false);
214 set("Content-Length", std::to_string(chunkLen)
);
222 if (!chunk.empty()) {
223 set("Content-Type", "text/html; charset=utf-8", false);
226 send(chunk.data()
, chunk.size()
);
237
238
239
240
246 LOG(DEBUG) << connectionName <<
" HTTP: Initiating upgrade: " << request->
method <<
" " << request->
url
254 std::vector<
char>()
);
257 if (request !=
nullptr) {
262 if (socketContextUpgradeFactory !=
nullptr) {
263 name = socketContextUpgradeFactory
->name();
265 LOG(DEBUG) << connectionName <<
" HTTP upgrade: SocketContextUpgradeFactory create success for: " << name;
270 if (socketContextUpgrade !=
nullptr) {
271 LOG(DEBUG) << connectionName <<
" HTTP upgrade: SocketContextUpgrade create success for: " << name;
275 LOG(DEBUG) << connectionName <<
" HTTP upgrade: SocketContextUpgrade create failed for: " << name;
280 LOG(DEBUG) << connectionName
281 <<
" SocketContextUpgradeFactory create failed for all of: " << request->
get("upgrade");
286 LOG(DEBUG) << connectionName <<
" HTTP upgrade: No upgrade requested";
291 LOG(ERROR) << connectionName <<
" HTTP upgrade: Request has gone away";
296 LOG(ERROR) << connectionName <<
"HTTP upgrade: Unexpected disconnect";
299 LOG(DEBUG) << connectionName <<
" HTTP: Upgrade bootstrap " << (!name.empty() ?
"success" :
"failed");
300 LOG(DEBUG) <<
" Protocol selected: " << name;
301 LOG(DEBUG) <<
" requested: " << request->
get("upgrade");
302 LOG(DEBUG) <<
" Subprotocol selected: " <<
header("upgrade");
303 LOG(DEBUG) <<
" requested: " << request->
get("Sec-WebSocket-Protocol");
308 void Response::
sendFile(
const std::string& file,
const std::function<
void(
int)>& callback) {
310 std::string absolutFileName = file;
312 if (std::filesystem::exists(absolutFileName)) {
314 absolutFileName = std::filesystem::canonical(absolutFileName);
316 if (std::filesystem::is_regular_file(absolutFileName, ec) && !ec) {
325 set("Transfer-Encoding", "chunked");
327 set("Content-Length", std::to_string(std::filesystem::file_size(absolutFileName))
);
364 set("X-Powered-By", "snode.c");
366 for (
const auto& [field, value] :
headers) {
370 for (
const auto& [cookie, cookieValue] :
cookies) {
371 const std::string cookieString = std::accumulate(
375 [](
const std::string& str,
const std::pair<
const std::string&,
const std::string&> option) -> std::string {
376 return str +
"; " + option.first + (!option.second.empty() ?
"=" + option.second :
"");
414 for (
auto& [field, value] :
trailer) {
static FileReader * open(const std::string &path)
void pipe(Sink *sink, const std::function< void(int)> &callback)
void sendToPeer(const std::string &data) const
const std::string & getConnectionName() const
SocketConnection * getSocketConnection() const
virtual void switchSocketContext(SocketContext *newSocketContext)
void sendToPeer(const char *chunk, std::size_t chunkLen) const final
bool streamToPeer(core::pipe::Source *source) const
const std::map< std::string, std::string > & getOptions() const
CookieOptions(const std::string &value, const std::map< std::string, std::string > &options)
const std::string & getValue() const
static std::string contentType(const std::string &file)
core::socket::stream::SocketContext * create(core::socket::stream::SocketConnection *socketConnection) final
virtual std::string name()=0
static std::string reason(int status)
CiStringMap< std::string > cookies
CiStringMap< std::string > headers
CiStringMap< std::string > queries
const std::string & get(const std::string &key, int i=0) const
void onSourceData(const char *chunk, std::size_t chunkLen) override
Response(SocketContext *socketContext)
SocketContext * getSocketContext() const
void onSourceConnect(core::pipe::Source *source) override
web::http::CiStringMap< web::http::CookieOptions > cookies
Response & status(int statusCode)
void send(const std::string &chunk)
void send(const char *chunk, std::size_t chunkLen)
Response & setTrailer(const std::string &field, const std::string &value, bool overwrite=true)
Response & set(const std::map< std::string, std::string > &headers, bool overwrite=true)
web::http::CiStringMap< std::string > trailer
web::http::CiStringMap< std::string > headers
Response & set(const std::string &field, const std::string &value, bool overwrite=true)
TransferEncoding transferEncoding
void upgrade(const std::shared_ptr< Request > &request, const std::function< void(const std::string &)> &status)
Response & type(const std::string &type)
void onSourceError(int errnum) override
std::size_t contentLength
Response & sendFragment(const std::string &chunk)
Response & cookie(const std::string &name, const std::string &value, const std::map< std::string, std::string > &options={})
web::http::server::SocketContext * socketContext
Response & append(const std::string &field, const std::string &value)
void sendStatus(int statusCode)
Response & sendFragment(const char *chunk, std::size_t chunkLen)
void onSourceEof() override
void sendFile(const std::string &file, const std::function< void(int)> &callback)
Response & clearCookie(const std::string &name, const std::map< std::string, std::string > &options={})
ConnectionState connectionState
const std::string & header(const std::string &field)
SocketContextUpgradeFactory * select(Request &req, Response &res) override
static SocketContextUpgradeFactorySelector * instance()
void responseCompleted(bool success)
std::string to_http_date(struct tm *tm)
std::string file_mod_http_date(const std::string &filePath)
std::string toString(const std::string &method, const std::string &url, const std::string &version, const web::http::CiStringMap< std::string > &queries, const web::http::CiStringMap< std::string > &header, const web::http::CiStringMap< std::string > &cookies, const std::vector< char > &body)
struct tm * gmtime(const time_t *timep)
bool ciEquals(const std::string &str1, const std::string &str2)
bool ciContains(const std::string &str1, const std::string &str2)
#define to_hex_str(int_val)