blob: d1d506157bda5c8a74173b0af3078f69ab3fe991 [file] [log] [blame]
Zhenkai Zhua9a7e1d2013-02-25 18:29:07 -08001//
2// request_handler.cpp
3// ~~~~~~~~~~~~~~~~~~~
4//
5// Copyright (c) 2003-2012 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 "request_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"
Zhenkai Zhud9429222013-02-25 22:34:09 -080019#include <QIODevice>
20#include <QFile>
21#include <QDataStream>
22#include <QString>
23#include "logging.h"
24
25INIT_LOGGER("HttpServer")
Zhenkai Zhua9a7e1d2013-02-25 18:29:07 -080026
27namespace http {
28namespace server {
29
30request_handler::request_handler(const std::string& doc_root)
Zhenkai Zhud9429222013-02-25 22:34:09 -080031 : doc_root_(doc_root.c_str())
Zhenkai Zhua9a7e1d2013-02-25 18:29:07 -080032{
33}
34
35void request_handler::handle_request(const request& req, reply& rep)
36{
37 // Decode url to path.
38 std::string request_path;
39 if (!url_decode(req.uri, request_path))
40 {
41 rep = reply::stock_reply(reply::bad_request);
42 return;
43 }
44
45 // Request path must be absolute and not contain "..".
46 if (request_path.empty() || request_path[0] != '/'
47 || request_path.find("..") != std::string::npos)
48 {
49 rep = reply::stock_reply(reply::bad_request);
50 return;
51 }
52
53 // If path ends in slash (i.e. is a directory) then add "index.html".
54 if (request_path[request_path.size() - 1] == '/')
55 {
56 request_path += "index.html";
57 }
58
59 // Determine the file extension.
60 std::size_t last_slash_pos = request_path.find_last_of("/");
61 std::size_t last_dot_pos = request_path.find_last_of(".");
62 std::string extension;
63 if (last_dot_pos != std::string::npos && last_dot_pos > last_slash_pos)
64 {
65 extension = request_path.substr(last_dot_pos + 1);
66 }
67
68 // Open the file to send back.
Zhenkai Zhud9429222013-02-25 22:34:09 -080069 // The following is a hack to make the server understands Qt's
70 // resource system, so that the html resources can be managed using Qt's
71 // resource system (e.g. no need to worry about the location of html)
72 // in Mac OS, it will be inside the bundle, in Linux, perhaps somewhere
73 // in /usr/local/share
74 QString full_path = doc_root_.absolutePath() + QString(request_path.c_str());
75 QFile file(full_path);
76 if (!file.exists() || !file.open(QIODevice::ReadOnly))
Zhenkai Zhua9a7e1d2013-02-25 18:29:07 -080077 {
78 rep = reply::stock_reply(reply::not_found);
79 return;
80 }
81
Zhenkai Zhud9429222013-02-25 22:34:09 -080082 _LOG_DEBUG("Serving file: " << request_path);
Zhenkai Zhua9a7e1d2013-02-25 18:29:07 -080083 // Fill out the reply to be sent to the client.
84 rep.status = reply::ok;
85 char buf[512];
Zhenkai Zhud9429222013-02-25 22:34:09 -080086 QDataStream in(&file);
87 while (true)
88 {
89 int bytes = in.readRawData(buf, sizeof(buf));
90 if (bytes > 0)
91 {
92 rep.content.append(buf, bytes);
93 }
94 else
95 {
96 break;
97 }
98 }
Zhenkai Zhua9a7e1d2013-02-25 18:29:07 -080099 rep.headers.resize(2);
100 rep.headers[0].name = "Content-Length";
101 rep.headers[0].value = boost::lexical_cast<std::string>(rep.content.size());
102 rep.headers[1].name = "Content-Type";
103 rep.headers[1].value = mime_types::extension_to_type(extension);
104}
105
106bool request_handler::url_decode(const std::string& in, std::string& out)
107{
108 out.clear();
109 out.reserve(in.size());
110 for (std::size_t i = 0; i < in.size(); ++i)
111 {
112 if (in[i] == '%')
113 {
114 if (i + 3 <= in.size())
115 {
116 int value = 0;
117 std::istringstream is(in.substr(i + 1, 2));
118 if (is >> std::hex >> value)
119 {
120 out += static_cast<char>(value);
121 i += 2;
122 }
123 else
124 {
125 return false;
126 }
127 }
128 else
129 {
130 return false;
131 }
132 }
133 else if (in[i] == '+')
134 {
135 out += ' ';
136 }
137 else
138 {
139 out += in[i];
140 }
141 }
142 return true;
143}
144
145} // namespace server
146} // namespace http