]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // |
2 | // file_handler.cpp | |
3 | // ~~~~~~~~~~~~~~~~ | |
4 | // | |
5 | // Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) | |
6 | // | |
7 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | |
8 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
9 | // | |
10 | ||
11 | #include "file_handler.hpp" | |
12 | #include <fstream> | |
13 | #include <sstream> | |
14 | #include <string> | |
15 | #include <boost/lexical_cast.hpp> | |
16 | #include "mime_types.hpp" | |
17 | #include "reply.hpp" | |
18 | #include "request.hpp" | |
19 | ||
20 | namespace http { | |
21 | namespace server4 { | |
22 | ||
23 | file_handler::file_handler(const std::string& doc_root) | |
24 | : doc_root_(doc_root) | |
25 | { | |
26 | } | |
27 | ||
28 | void file_handler::operator()(const request& req, reply& rep) | |
29 | { | |
30 | // Decode url to path. | |
31 | std::string request_path; | |
32 | if (!url_decode(req.uri, request_path)) | |
33 | { | |
34 | rep = reply::stock_reply(reply::bad_request); | |
35 | return; | |
36 | } | |
37 | ||
38 | // Request path must be absolute and not contain "..". | |
39 | if (request_path.empty() || request_path[0] != '/' | |
40 | || request_path.find("..") != std::string::npos) | |
41 | { | |
42 | rep = reply::stock_reply(reply::bad_request); | |
43 | return; | |
44 | } | |
45 | ||
46 | // If path ends in slash (i.e. is a directory) then add "index.html". | |
47 | if (request_path[request_path.size() - 1] == '/') | |
48 | { | |
49 | request_path += "index.html"; | |
50 | } | |
51 | ||
52 | // Determine the file extension. | |
53 | std::size_t last_slash_pos = request_path.find_last_of("/"); | |
54 | std::size_t last_dot_pos = request_path.find_last_of("."); | |
55 | std::string extension; | |
56 | if (last_dot_pos != std::string::npos && last_dot_pos > last_slash_pos) | |
57 | { | |
58 | extension = request_path.substr(last_dot_pos + 1); | |
59 | } | |
60 | ||
61 | // Open the file to send back. | |
62 | std::string full_path = doc_root_ + request_path; | |
63 | std::ifstream is(full_path.c_str(), std::ios::in | std::ios::binary); | |
64 | if (!is) | |
65 | { | |
66 | rep = reply::stock_reply(reply::not_found); | |
67 | return; | |
68 | } | |
69 | ||
70 | // Fill out the reply to be sent to the client. | |
71 | rep.status = reply::ok; | |
72 | char buf[512]; | |
73 | while (is.read(buf, sizeof(buf)).gcount() > 0) | |
74 | rep.content.append(buf, is.gcount()); | |
75 | rep.headers.resize(2); | |
76 | rep.headers[0].name = "Content-Length"; | |
77 | rep.headers[0].value = boost::lexical_cast<std::string>(rep.content.size()); | |
78 | rep.headers[1].name = "Content-Type"; | |
79 | rep.headers[1].value = mime_types::extension_to_type(extension); | |
80 | } | |
81 | ||
82 | bool file_handler::url_decode(const std::string& in, std::string& out) | |
83 | { | |
84 | out.clear(); | |
85 | out.reserve(in.size()); | |
86 | for (std::size_t i = 0; i < in.size(); ++i) | |
87 | { | |
88 | if (in[i] == '%') | |
89 | { | |
90 | if (i + 3 <= in.size()) | |
91 | { | |
92 | int value = 0; | |
93 | std::istringstream is(in.substr(i + 1, 2)); | |
94 | if (is >> std::hex >> value) | |
95 | { | |
96 | out += static_cast<char>(value); | |
97 | i += 2; | |
98 | } | |
99 | else | |
100 | { | |
101 | return false; | |
102 | } | |
103 | } | |
104 | else | |
105 | { | |
106 | return false; | |
107 | } | |
108 | } | |
109 | else if (in[i] == '+') | |
110 | { | |
111 | out += ' '; | |
112 | } | |
113 | else | |
114 | { | |
115 | out += in[i]; | |
116 | } | |
117 | } | |
118 | return true; | |
119 | } | |
120 | ||
121 | } // namespace server4 | |
122 | } // namespace http |