• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <https/HTTPServer.h>
18 
19 #include <https/ClientSocket.h>
20 #include <https/HTTPRequestResponse.h>
21 #include <https/Support.h>
22 
23 #include <glog/logging.h>
24 
25 #include <iostream>
26 #include <map>
27 #include <string>
28 
29 #include <openssl/sha.h>
30 
31 #define CC_SHA1_CTX     SHA_CTX
32 #define CC_SHA1_Init    SHA1_Init
33 #define CC_SHA1_Update  SHA1_Update
34 #define CC_SHA1_Final   SHA1_Final
35 #define CC_LONG         size_t
36 
HTTPServer(std::shared_ptr<RunLoop> runLoop,const char * iface,uint16_t port,ServerSocket::TransportType transportType,const std::optional<std::string> & certificate_pem_path,const std::optional<std::string> & private_key_pem_path)37 HTTPServer::HTTPServer(
38         std::shared_ptr<RunLoop> runLoop,
39         const char *iface,
40         uint16_t port,
41         ServerSocket::TransportType transportType,
42         const std::optional<std::string> &certificate_pem_path,
43         const std::optional<std::string> &private_key_pem_path)
44     : mRunLoop(runLoop),
45       mLocalPort(port),
46       mSocketTLS(
47               std::make_shared<ServerSocket>(
48                   this,
49                   transportType,
50                   iface ? iface : "0.0.0.0",
51                   port,
52                   certificate_pem_path,
53                   private_key_pem_path)) {
54     CHECK(mSocketTLS->initCheck() == 0);
55 }
56 
getLocalPort() const57 uint16_t HTTPServer::getLocalPort() const {
58     return mLocalPort;
59 }
60 
run()61 void HTTPServer::run() {
62     mSocketTLS->run(mRunLoop);
63 }
64 
handleSingleRequest(ClientSocket * clientSocket,const uint8_t * data,size_t size,bool isEOS)65 bool HTTPServer::handleSingleRequest(
66         ClientSocket *clientSocket,
67         const uint8_t *data,
68         size_t size,
69         bool isEOS) {
70     (void)isEOS;
71 
72     static const std::unordered_map<int32_t, std::string> kStatusMessage {
73         { 101, "Switching Protocols" },
74         { 200, "OK" },
75         { 400, "Bad Request" },
76         { 404, "Not Found" },
77         { 405, "Method Not Allowed" },
78         { 503, "Service Unavailable" },
79         { 505, "HTTP Version Not Supported" },
80     };
81 
82     HTTPRequest request;
83     request.setTo(data, size);
84 
85     int32_t httpResultCode;
86     std::string body;
87     std::unordered_map<std::string, std::string> responseHeaders;
88 
89     if (request.initCheck() < 0) {
90         httpResultCode = 400;  // Bad Request
91     } else if (request.getMethod() != "GET") {
92         httpResultCode = 405;  // Method Not Allowed
93     } else if (request.getVersion() != "HTTP/1.1") {
94         httpResultCode = 505;  // HTTP Version Not Supported
95     } else {
96         httpResultCode = 404;
97 
98         auto path = request.getPath();
99 
100         std::string query;
101 
102         auto separatorPos = path.find('?');
103         if (separatorPos != std::string::npos) {
104             query = path.substr(separatorPos);
105             path.erase(separatorPos);
106         }
107 
108         if (path == "/") { path = "/index.html"; }
109 
110         bool done = false;
111 
112         {
113             std::lock_guard autoLock(mContentLock);
114 
115             auto it = mStaticFiles.find(path);
116 
117             if (it != mStaticFiles.end()) {
118                 handleStaticFileRequest(
119                         it->second,
120                         request,
121                         &httpResultCode,
122                         &responseHeaders,
123                         &body);
124 
125                 done = true;
126             }
127         }
128 
129         if (!done) {
130             std::lock_guard autoLock(mContentLock);
131 
132             auto it = mWebSocketHandlerFactories.find(path);
133 
134             if (it != mWebSocketHandlerFactories.end()) {
135                 handleWebSocketRequest(
136                         clientSocket,
137                         it->second,
138                         request,
139                         &httpResultCode,
140                         &responseHeaders,
141                         &body);
142 
143                 done = true;
144             }
145         }
146 
147         const auto remoteAddr = clientSocket->remoteAddr();
148         uint32_t ip = ntohl(remoteAddr.sin_addr.s_addr);
149 
150         LOG(INFO)
151             << (ip >> 24)
152             << "."
153             << ((ip >> 16) & 0xff)
154             << "."
155             << ((ip >> 8) & 0xff)
156             << "."
157             << (ip & 0xff)
158             << ":"
159             << ntohs(remoteAddr.sin_port)
160             << " "
161             << httpResultCode << " \"" << path << "\"";
162     }
163 
164     const std::string status =
165         std::to_string(httpResultCode)
166             + " "
167             + kStatusMessage.find(httpResultCode)->second;
168 
169     bool closeConnection = false;
170 
171     if (httpResultCode != 200 && httpResultCode != 101) {
172         body = "<h1>" + status + "</h1>";
173 
174         responseHeaders["Connection"] = "close";
175         responseHeaders["Content-Type"] = "text/html";
176 
177         closeConnection = true;
178     }
179 
180     std::string value;
181     if (request.getHeaderField("Connection", &value) && value == "close") {
182         LOG(VERBOSE) << "Closing connection per client's request.";
183         closeConnection = true;
184     }
185 
186     responseHeaders["Content-Length"] = std::to_string(body.size());
187 
188     if (closeConnection) {
189         responseHeaders["Connection"] = "close";
190     }
191 
192     std::string response;
193     response = "HTTP/1.1 " + status + "\r\n";
194 
195     for (const auto &pair : responseHeaders) {
196         response += pair.first + ": " + pair.second + "\r\n";
197     }
198 
199     response += "\r\n";
200 
201     clientSocket->queueResponse(response, body);
202 
203     return closeConnection;
204 }
205 
addStaticFile(const char * at,const char * path,std::optional<std::string> mimeType)206 void HTTPServer::addStaticFile(
207         const char *at, const char *path, std::optional<std::string> mimeType) {
208     std::lock_guard autoLock(mContentLock);
209     mStaticFiles[at] = { path, mimeType };
210 }
211 
addStaticContent(const char * at,const void * _data,size_t size,std::optional<std::string> mimeType)212 void HTTPServer::addStaticContent(
213         const char *at,
214         const void *_data,
215         size_t size,
216         std::optional<std::string> mimeType) {
217     if (!mimeType) {
218         // Note: unlike for static, file-based content, we guess the mime type
219         // based on the path we're mapping the content at, not the path it's
220         // originating from (since we don't know that for memory based content).
221         mimeType = GuessMimeType(at);
222     }
223 
224     auto data = static_cast<const uint8_t *>(_data);
225 
226     std::lock_guard autoLock(mContentLock);
227     mStaticFiles[at] = { std::vector<uint8_t>(data, data + size), mimeType };
228 }
229 
addWebSocketHandlerFactory(const char * at,WebSocketHandlerFactory factory)230 void HTTPServer::addWebSocketHandlerFactory(
231         const char *at, WebSocketHandlerFactory factory) {
232     std::lock_guard autoLock(mContentLock);
233     mWebSocketHandlerFactories[at] = factory;
234 }
235 
handleWebSocketRequest(ClientSocket * clientSocket,WebSocketHandlerFactory factory,const HTTPRequest & request,int32_t * httpResultCode,std::unordered_map<std::string,std::string> * responseHeaders,std::string * body)236 void HTTPServer::handleWebSocketRequest(
237         ClientSocket *clientSocket,
238         WebSocketHandlerFactory factory,
239         const HTTPRequest &request,
240         int32_t *httpResultCode,
241         std::unordered_map<std::string, std::string> *responseHeaders,
242         std::string *body) {
243     (void)body;
244 
245     auto [status, handler] = factory();
246 
247     if (status != 0 || !handler) {
248         *httpResultCode = 503;  // Service unavailable.
249         return;
250     }
251 
252     *httpResultCode = 400;
253 
254     std::string value;
255     if (!request.getHeaderField("Connection", &value)
256             || (value != "Upgrade" && value != "keep-alive, Upgrade")) {
257         return;
258     }
259 
260     if (!request.getHeaderField("Upgrade", &value) || value != "websocket") {
261         return;
262     }
263 
264     if (!request.getHeaderField("Sec-WebSocket-Version", &value)) {
265         return;
266     }
267 
268     char *end;
269     long version = strtol(value.c_str(), &end, 10);
270 
271     if (end == value.c_str() || *end != '\0' || version < 13) {
272         return;
273     }
274 
275     if (!request.getHeaderField("Sec-WebSocket-Key", &value)) {
276         return;
277     }
278 
279     *httpResultCode = 101;
280 
281     (*responseHeaders)["Connection"] = "Upgrade";
282     (*responseHeaders)["Upgrade"] = "websocket";
283 
284     std::string tmp = value;
285     tmp += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
286 
287     CC_SHA1_CTX ctx;
288     int res = CC_SHA1_Init(&ctx);
289     CHECK_EQ(res, 1);
290 
291     res = CC_SHA1_Update(
292                 &ctx, tmp.c_str(), static_cast<CC_LONG>(tmp.size()));
293 
294     CHECK_EQ(res, 1);
295 
296     unsigned char digest[20];  // 160 bit
297     res = CC_SHA1_Final(digest, &ctx);
298     CHECK_EQ(res, 1);
299 
300     std::string acceptKey;
301     encodeBase64(digest, sizeof(digest), &acceptKey);
302 
303     (*responseHeaders)["Sec-WebSocket-Accept"] = acceptKey;
304 
305     clientSocket->setWebSocketHandler(handler);
306 }
307 
handleStaticFileRequest(const StaticFileInfo & info,const HTTPRequest & request,int32_t * httpResultCode,std::unordered_map<std::string,std::string> * responseHeaders,std::string * body)308 void HTTPServer::handleStaticFileRequest(
309         const StaticFileInfo &info,
310         const HTTPRequest &request,
311         int32_t *httpResultCode,
312         std::unordered_map<std::string, std::string> *responseHeaders,
313         std::string *body) {
314     (void)request;
315 
316     if (std::holds_alternative<std::string>(info.mPathOrContent)) {
317         const auto &path = std::get<std::string>(info.mPathOrContent);
318 
319         std::unique_ptr<FILE, std::function<int(FILE *)>> file(
320                 fopen(path.c_str(), "r"),
321                 fclose);
322 
323         if (!file) {
324             *httpResultCode = 404;
325             return;
326         }
327 
328         fseek(file.get(), 0, SEEK_END);
329         long fileSize = ftell(file.get());
330         fseek(file.get(), 0, SEEK_SET);
331 
332         (*responseHeaders)["Content-Length"] = std::to_string(fileSize);
333 
334         if (info.mMimeType) {
335             (*responseHeaders)["Content-Type"] = *info.mMimeType;
336         } else {
337             (*responseHeaders)["Content-Type"] = GuessMimeType(path);
338         }
339 
340         while (!feof(file.get())) {
341             char buffer[1024];
342             auto n = fread(buffer, 1, sizeof(buffer), file.get());
343 
344             body->append(buffer, n);
345         }
346     } else {
347         CHECK(std::holds_alternative<std::vector<uint8_t>>(
348                     info.mPathOrContent));
349 
350         const auto &content =
351             std::get<std::vector<uint8_t>>(info.mPathOrContent);
352 
353         body->append(content.begin(), content.end());
354 
355         (*responseHeaders)["Content-Length"] = std::to_string(content.size());
356     }
357 
358     *httpResultCode = 200;
359 }
360 
361 // static
GuessMimeType(const std::string & path)362 std::string HTTPServer::GuessMimeType(const std::string &path) {
363     auto dotPos = path.rfind('.');
364     if (dotPos != std::string::npos) {
365         auto extension = std::string(path, dotPos + 1);
366 
367         static std::unordered_map<std::string, std::string>
368             sMimeTypeByExtension {
369 
370             { "html", "text/html" },
371             { "htm", "text/html" },
372             { "css", "text/css" },
373             { "js", "text/javascript" },
374         };
375 
376         auto it = sMimeTypeByExtension.find(extension);
377         if (it != sMimeTypeByExtension.end()) {
378             return it->second;
379         }
380     }
381 
382     return "application/octet-stream";
383 }
384 
certificate_pem_path() const385 std::optional<std::string> HTTPServer::certificate_pem_path() const {
386     return mSocketTLS->certificate_pem_path();
387 }
388 
private_key_pem_path() const389 std::optional<std::string> HTTPServer::private_key_pem_path() const {
390     return mSocketTLS->private_key_pem_path();
391 }
392