107 if (!value.empty()) {
109 headers.insert_or_assign(field, value);
111 headers.insert({field, value});
114 if (web::http::ciEquals(field,
"Connection")) {
115 if (web::http::ciContains(headers[field],
"keep-alive")) {
116 connectionState = ConnectionState::Keep;
117 }
else if (web::http::ciContains(headers[field],
"close")) {
118 connectionState = ConnectionState::Close;
120 }
else if (web::http::ciEquals(field,
"Content-Length")) {
121 contentLength = std::stoul(value);
122 transferEncoding = TransferEncoding::Identity;
123 headers.erase(
"Transfer-Encoding");
124 }
else if (web::http::ciEquals(field,
"Transfer-Encoding")) {
125 if (web::http::ciContains(headers[field],
"chunked")) {
126 transferEncoding = TransferEncoding::Chunked;
127 headers.erase(
"Content-Length");
129 if (web::http::ciContains(headers[field],
"compressed")) {
131 if (web::http::ciContains(headers[field],
"deflate")) {
133 if (web::http::ciContains(headers[field],
"gzip")) {
135 }
else if (web::http::ciEquals(field,
"Content-Encoding")) {
136 if (web::http::ciContains(headers[field],
"compressed")) {
138 if (web::http::ciContains(headers[field],
"deflate")) {
140 if (web::http::ciContains(headers[field],
"gzip")) {
142 if (web::http::ciContains(headers[field],
"br")) {
146 headers.erase(field);
225 if (socketContext !=
nullptr) {
226 if (request !=
nullptr) {
227 if (web::http::ciContains(request->get(
"connection"),
"Upgrade")) {
228 web::http::server::SocketContextUpgradeFactory* socketContextUpgradeFactory =
229 web::http::server::SocketContextUpgradeFactorySelector::instance()->select(*request, *
this);
231 if (socketContextUpgradeFactory !=
nullptr) {
232 name = socketContextUpgradeFactory->name();
233 LOG(DEBUG) << socketContext->getSocketConnection()->getConnectionName()
234 <<
" HTTP upgrade: SocketContextUpgradeFactory created successful: " << name;
236 socketContextUpgrade = socketContextUpgradeFactory->create(socketContext->getSocketConnection());
238 if (socketContextUpgrade !=
nullptr) {
239 LOG(DEBUG) << socketContext->getSocketConnection()->getConnectionName()
240 <<
" HTTP upgrade: SocketContextUpgrade created successful: " << name;
242 LOG(DEBUG) << socketContext->getSocketConnection()->getConnectionName()
243 <<
" HTTP upgrade: Create SocketContextUpgrade failed: " << name;
245 set(
"Connection",
"close").status(404);
248 LOG(DEBUG) << socketContext->getSocketConnection()->getConnectionName()
249 <<
" HTTP upgrade: SocketContextUpgradeFactory not supported: " << request->get(
"upgrade");
251 set(
"Connection",
"close").status(404);
254 LOG(DEBUG) << socketContext->getSocketConnection()->getConnectionName() <<
" HTTP upgrade: No upgrade requested";
256 set(
"Connection",
"close").status(400);
259 LOG(ERROR) << socketContext->getSocketConnection()->getConnectionName() <<
" HTTP upgrade: Request has gone away";
261 set(
"Connection",
"close").status(500);
264 LOG(ERROR) << socketContext->getSocketConnection()->getConnectionName() <<
" HTTP upgrade: SocketContext has gone away";
270 void Response::
sendFile(
const std::string& file,
const std::function<
void(
int)>& callback) {
271 if (socketContext !=
nullptr) {
272 std::string absolutFileName = file;
274 if (std::filesystem::exists(absolutFileName)) {
276 absolutFileName = std::filesystem::canonical(absolutFileName);
278 if (std::filesystem::is_regular_file(absolutFileName, ec) && !ec) {
279 core::file::FileReader::open(absolutFileName)->pipe(
this, [
this, &absolutFileName, &callback](
int errnum) {
283 set(
"Content-Type", web::http::MimeTypes::contentType(absolutFileName),
false);
284 set(
"Last-Modified", httputils::file_mod_http_date(absolutFileName),
false);
285 if (httpMajor == 1) {
286 if (httpMinor == 1) {
287 set(
"Transfer-Encoding",
"chunked");
289 set(
"Content-Length", std::to_string(std::filesystem::file_size(absolutFileName)));
310 if (socketContext !=
nullptr) {
311 socketContext->responseStarted();
313 socketContext->sendToPeer(
"HTTP/" + std::to_string(httpMajor)
315 .append(std::to_string(httpMinor))
317 .append(std::to_string(statusCode))
319 .append(StatusCode::reason(statusCode))
321 socketContext->sendToPeer(
"Date: " + httputils::to_http_date() +
"\r\n");
323 set(
"X-Powered-By",
"snode.c");
325 for (
const auto& [field, value] : headers) {
326 socketContext->sendToPeer(std::string(field).append(
": ").append(value).append(
"\r\n"));
329 for (
const auto& [cookie, cookieValue] : cookies) {
330 const std::string cookieString = std::accumulate(
331 cookieValue.getOptions().begin(),
332 cookieValue.getOptions().end(),
333 cookie +
"=" + cookieValue.getValue(),
334 [](
const std::string& str,
const std::pair<
const std::string&,
const std::string&> option) -> std::string {
335 return str +
"; " + option.first + (!option.second.empty() ?
"=" + option.second :
"");
337 socketContext->sendToPeer(
"Set-Cookie: " + cookieString +
"\r\n");
340 socketContext->sendToPeer(
"\r\n");
347 if (socketContext !=
nullptr) {
348 if (transferEncoding == TransferEncoding::Chunked) {
349 socketContext->sendToPeer(
to_hex_str(chunkLen).append(
"\r\n"));
352 socketContext->sendToPeer(chunk, chunkLen);
353 contentSent += chunkLen;
355 if (transferEncoding == TransferEncoding::Chunked) {
356 socketContext->sendToPeer(
"\r\n");
357 contentLength += chunkLen;
369 if (transferEncoding == TransferEncoding::Chunked) {
372 if (!trailer.empty()) {
373 for (
auto& [field, value] : trailer) {
374 socketContext->sendToPeer(std::string(field).append(
":").append(value).append(
"\r\n"));
377 socketContext->sendToPeer(
"\r\n");
381 if (socketContext !=
nullptr) {
382 socketContext->responseCompleted(contentSent == contentLength || (httpMajor == 1 && httpMinor == 0));
384 if (socketContextUpgrade !=
nullptr) {
385 socketContext->switchSocketContext(socketContextUpgrade);
386 socketContextUpgrade =
nullptr;