summaryrefslogtreecommitdiffhomepage
path: root/response.cpp
blob: 70c93b14f329a13431b04e5c23bbe89197f86d36 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#include "response.h"
#include "file.h"

#include <functional>
#include <iostream>
#include <unordered_map>

using namespace std::placeholders;

namespace {

std::string extend_index_html(std::string path)
{
 if (path.size() && path.back() == '/')
  path.append("index.html");
 return path;
}

std::string GetServerParam(const std::string& key, Server& server)
{
 // following are the supported fields:
 // ...
 throw std::runtime_error("Unsupported server param: "s + key);
}

std::unordered_map<std::string, std::function<std::string(request_type&, Server&)>> GetRequestParamFunctions{
 // following are the supported fields:
 {"target", [](request_type& req, Server& server){return std::string{req.target()};}},

 {"rel_target", [](request_type& req, Server& server){
  std::string host{req["host"]};
  std::string target{req.target()};
  return server.GetConfig().GetRelativePath(server.GetSocket(), host, target);
 }},

 {"doc_root", [](request_type& req, Server& server) {
  std::string host{req["host"]};
  std::string target{req.target()};
  return server.GetConfig().DocRoot(server.GetSocket(), host, target);
 }},

 {"method", [](request_type& req, Server& server){
  if (req.method() == http::verb::get)
   return "GET";
  else if (req.method() == http::verb::post)
   return "POST";
  else if (req.method() == http::verb::head)
   return "HEAD";
  else
   return "";
 }},
};

std::string GetRequestParam(const std::string& key, request_type& req, Server& server)
{
 auto it = GetRequestParamFunctions.find(key);
 if (it != GetRequestParamFunctions.end())
  return it->second(req, server);
 throw std::runtime_error("Unsupported request param: "s + key);
}

void SetResponseHeader(const std::string& key, const std::string& value, response_type& res)
{
 // following are the supported fields:
 
 if (key == "status") { // HTTP Status, e.g. "200" (OK)
  res.result(unsigned(stoul(value)));
 } else if (key == "server") { // Server name/version string
  res.set(http::field::server, value);
 } else  if (key == "content_type") { // e.g. text/html
  res.set(http::field::content_type, value);
 } else
  throw std::runtime_error("Unsupported response field: "s + key);
}

// Return a reasonable mime type based on the extension of a file.
beast::string_view
mime_type(beast::string_view path)
{
    using beast::iequals;
    auto const ext = [&path]
    {
        auto const pos = path.rfind(".");
        if (pos == beast::string_view::npos)
            return beast::string_view{};
        return path.substr(pos);
    }();
    if(iequals(ext, ".htm"))  return "text/html";
    if(iequals(ext, ".html")) return "text/html";
    if(iequals(ext, ".php"))  return "text/html";
    if(iequals(ext, ".css"))  return "text/css";
    if(iequals(ext, ".txt"))  return "text/plain";
    if(iequals(ext, ".js"))   return "application/javascript";
    if(iequals(ext, ".json")) return "application/json";
    if(iequals(ext, ".xml"))  return "application/xml";
    if(iequals(ext, ".swf"))  return "application/x-shockwave-flash";
    if(iequals(ext, ".flv"))  return "video/x-flv";
    if(iequals(ext, ".png"))  return "image/png";
    if(iequals(ext, ".jpe"))  return "image/jpeg";
    if(iequals(ext, ".jpeg")) return "image/jpeg";
    if(iequals(ext, ".jpg"))  return "image/jpeg";
    if(iequals(ext, ".gif"))  return "image/gif";
    if(iequals(ext, ".bmp"))  return "image/bmp";
    if(iequals(ext, ".ico"))  return "image/vnd.microsoft.icon";
    if(iequals(ext, ".tiff")) return "image/tiff";
    if(iequals(ext, ".tif"))  return "image/tiff";
    if(iequals(ext, ".svg"))  return "image/svg+xml";
    if(iequals(ext, ".svgz")) return "image/svg+xml";
    return "application/text";
}

} // anonymous namespace

response_type generate_response(request_type& req, Server& server)
{
 response_type res{http::status::ok, req.version()};
 res.set(http::field::server, VersionString);
 res.set(http::field::content_type, mime_type(extend_index_html(std::string(req.target()))));
 res.keep_alive(req.keep_alive());

 std::string host{req["host"]};
 std::string target{req.target()};
 std::string plugin_name { server.GetConfig().GetPlugin(server.GetSocket(), host, target)};
 plugin_type plugin{server.GetPlugin(plugin_name)};

 auto GetServerParamFunction {std::function<std::string(const std::string& key)>(std::bind(GetServerParam, _1, std::ref(server)))};
 auto GetRequestParamFunction {std::function<std::string(const std::string& key)>(std::bind(GetRequestParam, _1, std::ref(req), std::ref(server)))};
 auto SetResponseHeaderFunction{std::function<void(const std::string& key, const std::string& value)>(std::bind(SetResponseHeader, _1, _2, std::ref(res)))};
 
 std::string res_data { plugin->generate_page(GetServerParamFunction, GetRequestParamFunction, SetResponseHeaderFunction)};
 if (req.method() == http::verb::head) {
  res.content_length(res_data.size());
 } else {
  res.body() = res_data;
  res.prepare_payload();
 }

 return res;
}