]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | ||
4 | #include <string.h> | |
5 | ||
6 | #include <boost/algorithm/string/predicate.hpp> | |
7 | #include <boost/utility/string_ref.hpp> | |
8 | ||
9 | #include "civetweb/civetweb.h" | |
10 | #include "rgw_civetweb.h" | |
11 | ||
12 | ||
13 | #define dout_subsys ceph_subsys_rgw | |
14 | ||
15 | size_t RGWCivetWeb::write_data(const char *buf, const size_t len) | |
16 | { | |
17 | auto to_sent = len; | |
18 | while (to_sent) { | |
19 | const int ret = mg_write(conn, buf, len); | |
20 | if (ret < 0 || ! ret) { | |
21 | /* According to the documentation of mg_write() it always returns -1 on | |
22 | * error. The details aren't available, so we will just throw EIO. Same | |
23 | * goes to 0 that is associated with writing to a closed connection. */ | |
24 | throw rgw::io::Exception(EIO, std::system_category()); | |
25 | } else { | |
26 | to_sent -= static_cast<size_t>(ret); | |
27 | } | |
28 | } | |
29 | return len; | |
30 | } | |
31 | ||
32 | RGWCivetWeb::RGWCivetWeb(mg_connection* const conn) | |
33 | : conn(conn), | |
34 | explicit_keepalive(false), | |
35 | explicit_conn_close(false), | |
36 | txbuf(*this) | |
37 | { | |
38 | sockaddr *lsa = mg_get_local_addr(conn); | |
39 | switch(lsa->sa_family) { | |
40 | case AF_INET: | |
41 | port = ntohs(((struct sockaddr_in*)lsa)->sin_port); | |
42 | break; | |
43 | case AF_INET6: | |
44 | port = ntohs(((struct sockaddr_in6*)lsa)->sin6_port); | |
45 | break; | |
46 | default: | |
47 | port = -1; | |
48 | } | |
49 | } | |
50 | ||
51 | size_t RGWCivetWeb::read_data(char *buf, size_t len) | |
52 | { | |
53 | const int ret = mg_read(conn, buf, len); | |
54 | if (ret < 0) { | |
55 | throw rgw::io::Exception(EIO, std::system_category()); | |
56 | } | |
57 | return ret; | |
58 | } | |
59 | ||
60 | void RGWCivetWeb::flush() | |
61 | { | |
62 | txbuf.pubsync(); | |
63 | } | |
64 | ||
65 | size_t RGWCivetWeb::complete_request() | |
66 | { | |
67 | return 0; | |
68 | } | |
69 | ||
70 | void RGWCivetWeb::init_env(CephContext *cct) | |
71 | { | |
72 | env.init(cct); | |
73 | const struct mg_request_info* info = mg_get_request_info(conn); | |
74 | ||
75 | if (! info) { | |
76 | return; | |
77 | } | |
78 | ||
79 | for (int i = 0; i < info->num_headers; i++) { | |
80 | const struct mg_request_info::mg_header* header = &info->http_headers[i]; | |
81 | const boost::string_ref name(header->name); | |
82 | const auto& value = header->value; | |
83 | ||
84 | if (boost::algorithm::iequals(name, "content-length")) { | |
85 | env.set("CONTENT_LENGTH", value); | |
86 | continue; | |
87 | } | |
88 | if (boost::algorithm::iequals(name, "content-type")) { | |
89 | env.set("CONTENT_TYPE", value); | |
90 | continue; | |
91 | } | |
92 | if (boost::algorithm::iequals(name, "connection")) { | |
93 | explicit_keepalive = boost::algorithm::iequals(value, "keep-alive"); | |
94 | explicit_conn_close = boost::algorithm::iequals(value, "close"); | |
95 | } | |
96 | ||
97 | static const boost::string_ref HTTP_{"HTTP_"}; | |
98 | ||
99 | char buf[name.size() + HTTP_.size() + 1]; | |
100 | auto dest = std::copy(std::begin(HTTP_), std::end(HTTP_), buf); | |
101 | for (auto src = name.begin(); src != name.end(); ++src, ++dest) { | |
102 | if (*src == '-') { | |
103 | *dest = '_'; | |
104 | } else { | |
105 | *dest = std::toupper(*src); | |
106 | } | |
107 | } | |
108 | *dest = '\0'; | |
109 | ||
110 | env.set(buf, value); | |
111 | } | |
112 | ||
113 | env.set("REQUEST_METHOD", info->request_method); | |
31f18b77 | 114 | env.set("REQUEST_URI", info->request_uri); // get the full uri, we anyway handle abs uris later |
7c673cae FG |
115 | env.set("SCRIPT_URI", info->uri); /* FIXME */ |
116 | if (info->query_string) { | |
117 | env.set("QUERY_STRING", info->query_string); | |
118 | } | |
119 | if (info->remote_user) { | |
120 | env.set("REMOTE_USER", info->remote_user); | |
121 | } | |
122 | ||
123 | if (port <= 0) | |
124 | lderr(cct) << "init_env: bug: invalid port number" << dendl; | |
125 | char port_buf[16]; | |
126 | snprintf(port_buf, sizeof(port_buf), "%d", port); | |
127 | env.set("SERVER_PORT", port_buf); | |
128 | if (info->is_ssl) { | |
129 | env.set("SERVER_PORT_SECURE", port_buf); | |
130 | } | |
131 | } | |
132 | ||
133 | size_t RGWCivetWeb::send_status(int status, const char *status_name) | |
134 | { | |
135 | mg_set_http_status(conn, status); | |
136 | ||
137 | static constexpr size_t STATUS_BUF_SIZE = 128; | |
138 | ||
139 | char statusbuf[STATUS_BUF_SIZE]; | |
140 | const auto statuslen = snprintf(statusbuf, sizeof(statusbuf), | |
141 | "HTTP/1.1 %d %s\r\n", status, status_name); | |
142 | ||
143 | return txbuf.sputn(statusbuf, statuslen); | |
144 | } | |
145 | ||
146 | size_t RGWCivetWeb::send_100_continue() | |
147 | { | |
148 | const char HTTTP_100_CONTINUE[] = "HTTP/1.1 100 CONTINUE\r\n\r\n"; | |
149 | const size_t sent = txbuf.sputn(HTTTP_100_CONTINUE, | |
150 | sizeof(HTTTP_100_CONTINUE) - 1); | |
151 | flush(); | |
152 | return sent; | |
153 | } | |
154 | ||
155 | size_t RGWCivetWeb::send_header(const boost::string_ref& name, | |
156 | const boost::string_ref& value) | |
157 | { | |
158 | static constexpr char HEADER_SEP[] = ": "; | |
159 | static constexpr char HEADER_END[] = "\r\n"; | |
160 | ||
161 | size_t sent = 0; | |
162 | ||
163 | sent += txbuf.sputn(name.data(), name.length()); | |
164 | sent += txbuf.sputn(HEADER_SEP, sizeof(HEADER_SEP) - 1); | |
165 | sent += txbuf.sputn(value.data(), value.length()); | |
166 | sent += txbuf.sputn(HEADER_END, sizeof(HEADER_END) - 1); | |
167 | ||
168 | return sent; | |
169 | } | |
170 | ||
171 | size_t RGWCivetWeb::dump_date_header() | |
172 | { | |
173 | char timestr[TIME_BUF_SIZE]; | |
174 | ||
175 | const time_t gtime = time(nullptr); | |
176 | struct tm result; | |
177 | struct tm const* const tmp = gmtime_r(>ime, &result); | |
178 | ||
179 | if (nullptr == tmp) { | |
180 | return 0; | |
181 | } | |
182 | ||
183 | if (! strftime(timestr, sizeof(timestr), | |
184 | "Date: %a, %d %b %Y %H:%M:%S %Z\r\n", tmp)) { | |
185 | return 0; | |
186 | } | |
187 | ||
188 | return txbuf.sputn(timestr, strlen(timestr)); | |
189 | } | |
190 | ||
191 | size_t RGWCivetWeb::complete_header() | |
192 | { | |
193 | size_t sent = dump_date_header(); | |
194 | ||
195 | if (explicit_keepalive) { | |
196 | constexpr char CONN_KEEP_ALIVE[] = "Connection: Keep-Alive\r\n"; | |
197 | sent += txbuf.sputn(CONN_KEEP_ALIVE, sizeof(CONN_KEEP_ALIVE) - 1); | |
198 | } else if (explicit_conn_close) { | |
199 | constexpr char CONN_KEEP_CLOSE[] = "Connection: close\r\n"; | |
200 | sent += txbuf.sputn(CONN_KEEP_CLOSE, sizeof(CONN_KEEP_CLOSE) - 1); | |
201 | } | |
202 | ||
203 | static constexpr char HEADER_END[] = "\r\n"; | |
204 | sent += txbuf.sputn(HEADER_END, sizeof(HEADER_END) - 1); | |
205 | ||
206 | flush(); | |
207 | return sent; | |
208 | } | |
209 | ||
210 | size_t RGWCivetWeb::send_content_length(uint64_t len) | |
211 | { | |
212 | static constexpr size_t CONLEN_BUF_SIZE = 128; | |
213 | ||
214 | char sizebuf[CONLEN_BUF_SIZE]; | |
215 | const auto sizelen = snprintf(sizebuf, sizeof(sizebuf), | |
216 | "Content-Length: %" PRIu64 "\r\n", len); | |
217 | return txbuf.sputn(sizebuf, sizelen); | |
218 | } |