10000 add file_server and file_download · flames85/cpp-netlib-demo@1dd1cb3 · GitHub
[go: up one dir, main page]

Skip to content

Commit 1dd1cb3

Browse files
author
shaoqi
committed
add file_server and file_download
1 parent 9422cb3 commit 1dd1cb3

File tree

7 files changed

+355
-10
lines changed

7 files changed

+355
-10
lines changed

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,11 @@ sudo cp /home/shaoqi/source/cpp-netlib-0.12.0-final/deps/asio/asio/include/asio
5959

6060
官方网页上的例子是旧版的!(可是为何都0.12版了,还在用老版的根本编译不过的example?)
6161

62-
#### 4. 根据官方例子写的更详细的例子
62+
#### 4. 根据官方例子写的更详细的例子, 都是http协议相关
6363

6464
+ http_client
6565
+ http_server
66+
+ file_server
67+
+ file_download
68+
6669

file_download/CMakeLists.txt

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
cmake_minimum_required(VERSION 3.7)
2+
project(file_download)
3+
set(CMAKE_CXX_STANDARD 11)
4+
5+
########################################
6+
if($ENV{CLION_IDE})
7+
message(STATUS "Using clion ide")
8+
SET(CPP_NETLIB_PATH "/opt/cpp-netlib")
9+
endif()
10+
########################################
11+
12+
###### cpp-netlib
13+
if(DEFINED CPP_NETLIB_PATH)
14+
SET(CPP_NETLIB_INCLUDE_DIR ${CPP_NETLIB_PATH}/include)
15+
SET(CPP_NETLIB_LIBRARY_DIR ${CPP_NETLIB_PATH}/lib)
16+
17+
INCLUDE_DIRECTORIES(${CPP_NETLIB_INCLUDE_DIR})
18+
# INCLUDE_DIRECTORIES(${CPP_NETLIB_INCLUDE_DIR}/boost)
19+
20+
LINK_DIRECTORIES(${CPP_NETLIB_LIBRARY_DIR})
21+
SET(CPP_NETLIB_LIBRARIES
22+
libcppnetlib-client-connections.a
23+
libcppnetlib-uri.a
24+
)
25+
26+
message(STATUS "CPP_NETLIB_INCLUDE_DIR: ${CPP_NETLIB_INCLUDE_DIR}")
27+
message(STATUS "CPP_NETLIB_LIBRARY_DIR: ${CPP_NETLIB_LIBRARY_DIR}")
28+
message(STATUS "CPP_NETLIB_LIBRARIES: ${CPP_NETLIB_LIBRARIES}")
29+
else()
30+
message(SEND_ERROR "Please set CPP_NETLIB_PATH")
31+
endif()
32+
33+
##### openssl
34+
find_package(OpenSSL)
35+
36+
set(SOURCE_FILES simple_wget.cpp)
37+
38+
add_executable(${PROJECT_NAME} ${SOURCE_FILES})
39+
40+
target_link_libraries(${PROJECT_NAME}
41+
${CPP_NETLIB_LIBRARIES}
42+
${OPENSSL_LIBRARIES}
43+
pthread
44+
)

file_download/simple_wget.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#include <boost/network/protocol/http/client.hpp>
2+
#include <boost/network/uri.hpp>
3+
#include <string>
4+
#include <fstream>
5+
#include <iostream>
6+
7+
namespace http = boost::network::http;
8+
namespace uri = boost::network::uri;
9+
10+
namespace {
11+
std::string get_filename(const uri::uri &url) {
12+
std::string path = uri::path(url);
13+
std::size_t index = path.find_last_of('/');
14+
std::string filename = path.substr(index + 1);
15+
return filename.empty() ? "index.html" : filename;
16+
}
17+
} // namespace
18+
19+
int main()
20+
{
21+
http::client client;
22+
try
23+
{
24+
http::client::request request("http://127.0.0.1:8000/file_server");
25+
http::client::response response = client.get(request);
26+
27+
// 从url中获取文件名
28+
std::string filename = get_filename(request.uri());
29+
std::cout << "Saving to: " << filename << std::endl;
30+
// 从get的body中写入文件
31+
std::ofstream ofs(filename.c_str());
32+
ofs << static_cast<std::string>(body(response));
33+
}
34+
catch (std::exception &e) {
35+
std::cerr << e.what() << std::endl;
36+
return 1;
37+
}
38+
39+
return 0;
40+
}

file_server/CMakeLists.txt

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
cmake_minimum_required(VERSION 3.7)
2+
project(file_server)
3+
set(CMAKE_CXX_STANDARD 11)
4+
5+
########################################
6+
if($ENV{CLION_IDE})
7+
message(STATUS "Using clion ide")
8+
SET(CPP_NETLIB_PATH "/opt/cpp-netlib")
9+
endif()
10+
########################################
11+
12+
###### cpp-netlib
13+
if(DEFINED CPP_NETLIB_PATH)
14+
SET(CPP_NETLIB_INCLUDE_DIR ${CPP_NETLIB_PATH}/include)
15+
SET(CPP_NETLIB_LIBRARY_DIR ${CPP_NETLIB_PATH}/lib)
16+
17+
INCLUDE_DIRECTORIES(${CPP_NETLIB_INCLUDE_DIR})
18+
# INCLUDE_DIRECTORIES(${CPP_NETLIB_INCLUDE_DIR}/boost)
19+
20+
LINK_DIRECTORIES(${CPP_NETLIB_LIBRARY_DIR})
21+
SET(CPP_NETLIB_LIBRARIES
22+
libcppnetlib-server-parsers.a
23+
)
24+
25+
message(STATUS "CPP_NETLIB_INCLUDE_DIR: ${CPP_NETLIB_INCLUDE_DIR}")
26+
message(STATUS "CPP_NETLIB_LIBRARY_DIR: ${CPP_NETLIB_LIBRARY_DIR}")
27+
message(STATUS "CPP_NETLIB_LIBRARIES: ${CPP_NETLIB_LIBRARIES}")
28+
else()
29+
message(SEND_ERROR "Please set CPP_NETLIB_PATH")
30+
endif()
31+
32+
33+
set(SOURCE_FILES fileserver.cpp)
34+
35+
add_executable(${PROJECT_NAME} ${SOURCE_FILES})
36+
37+
target_link_libraries(${PROJECT_NAME}
38+
${CPP_NETLIB_LIBRARIES}
39+
pthread
40+
)

file_server/fileserver.cpp

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
// Copyright 2010 Dean Michael Berris.
2+
// Distributed under the Boost Software License, Version 1.0.
3+
// (See accompanying file LICENSE_1_0.txt or copy at
4+
// http://www.boost.org/LICENSE_1_0.txt)
5+
6+
#include <memory>
7+
#include <thread>
8+
#include <boost/network/include/http/server.hpp>
9+
#include <sys/mman.h>
10+
#include <sys/types.h>
11+
#include <sys/stat.h>
12+
#include <sys/fcntl.h>
13+
#include <unistd.h>
14+
#include <iostream>
15+
#include <cassert>
16+
17+
namespace http = boost::network::http;
18+
namespace utils = boost::network::utils;
19+
20+
struct file_server;
21+
typedef http::server<file_server> server;
22+
23+
struct file_cache
24+
{
25+
// <real_filename, <region, file-size> >
26+
typedef std::map<std::string, std::pair<void *, std::size_t> > region_map;
27+
// <real_filename, <headers> >
28+
typedef std::map<std::string, std::vector<server::response_header> > meta_map;
29+
30+
std::string doc_root_;
31+
region_map regions_;
32+
meta_map file_headers_;
33+
std::mutex cache_mutex_;
34+
35+
explicit file_cache(std::string doc_root) : doc_root_(std::move(doc_root)) {}
36+
37+
~file_cache() throw()
38+
{
39+
for (auto &region : regions_)
40+
{
41+
munmap(region.second.first, region.second.second);
42+
}
43+
}
44+
45+
bool has(std::string const &path)
46+
{
47+
std::unique_lock<std::mutex> lock(cache_mutex_);
48+
return regions_.find(doc_root_ + path) != regions_.end();
49+
}
50+
51+
bool add(std::string const &path)
52+
{
53+
std::unique_lock<std::mutex> lock(cache_mutex_);
54+
std::string real_filename = doc_root_ + path;
55+
if (regions_.find(real_filename) != regions_.end()) return true;
56+
#ifdef O_NOATIME
57+
int fd = open(real_filename.c_str(), O_RDONLY | O_NOATIME | O_NONBLOCK);
58+
#else
59+
int fd = open(real_filename.c_str(), O_RDONLY | O_NONBLOCK);
60+
#endif
61+
if (fd == -1) return false;
62+
off_t size = lseek(fd, 0, SEEK_END);
63+
if (size == -1) {
64+
close(fd);
65+
return false;
66+
}
67+
void *region = mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0);
68+
if (region == MAP_FAILED)
69+
{
70+
close(fd);
71+
return false;
72+
}
73+
74+
regions_.insert(std::make_pair(real_filename, std::make_pair(region, size)));
75+
static server::response_header common_headers[] = {
76+
{"Connection", "close"},
77+
{"Content-Type", "x-application/octet-stream"},
78+
{"Content-Length", "0"}};
79+
std::vector<server::response_header> headers(common_headers,
80+
common_headers + 3);
81+
headers[2].value = std::to_string(size);
82+
file_headers_.insert(std::make_pair(real_filename, headers));
83+
close(fd);
84+
return true;
85+
}
86+
87+
std::pair<void *, std::size_t> get(std::string const &path)
88+
{
89+
std::unique_lock<std::mutex> lock(cache_mutex_);
90+
region_map::const_iterator region = regions_.find(doc_root_ + path);
91+
if (region != regions_.end())
92+
return region->second;
93+
else
94+
return std::pair<void *, std::size_t>(0, 0);
95+
}
96+
97+
boost::iterator_range<std::vector<server::response_header>::iterator> meta(std::string const &path)
98+
{
99+
std::unique_lock<std::mutex> lock(cache_mutex_);
100+
static std::vector<server::response_header> empty_vector;
101+
auto headers = file_headers_.find(doc_root_ + path);
102+
if (headers != file_headers_.end()) {
103+
// 返回一个headers的范围,信息是缓存了的headers
104+
auto begin = headers->second.begin();
105+
auto end = headers->second.end();
106+
return boost::make_iterator_range(begin, end);
107+
} else {
108+
// 没有命中headers,返回一个空的header
109+
return boost::make_iterator_range(empty_vector);
110+
}
111+
}
112+
};
113+
114+
115+
116+
struct connection_handler : std::enable_shared_from_this<connection_handler>
117+
{
118+
explicit connection_handler(file_cache &cache) : file_cache_(cache) {}
119+
120+
file_cache &file_cache_;
121+
122+
void operator()(std::string const &path,
123+
server::connection_ptr connection,
124+
bool serve_body)
125+
{
126+
bool ok = file_cache_.has(path);
127+
128+
if (!ok)
129+
ok = file_cache_.add(path);
130+
131+
if (ok) {
132+
send_headers(file_cache_.meta(path), connection);
133+
if (serve_body) {
134+
send_file(file_cache_.get(path), 0, connection);
135+
}
136+
} else {
137+
not_found(path, connection);
138+
}
139+
}
140+
141+
void not_found(std::string const &, server::connection_ptr connection)
142+
{
143+
static server::response_header headers[] = {{"Connection", "close"},
144+
{"Content-Type", "text/plain"}};
145+
connection->set_status(server::connection::not_found);
146+
connection->set_headers(boost::make_iterator_range(headers, headers + 2));
147+
connection->write("File Not Found!");
148+
}
149+
150+
template <class Range>
151+
void send_headers(Range const &headers, server::connection_ptr connection) {
152+
connection->set_status(server::connection::ok);
153+
connection->set_headers(headers);
154+
}
155+
156+
void send_file(std::pair<void *, std::size_t> mmaped_region,
157+
off_t offset,
158+
server::connection_ptr connection)
159+
{
160+
// chunk it up page by page
161+
std::size_t adjusted_offset = offset + 4096;
162+
off_t rightmost_bound = std::min(mmaped_region.second, adjusted_offset);
163+
auto self = this->shared_from_this();
164+
connection->write(asio::const_buffers_1(static_cast<char const *>(mmaped_region.first) + offset, rightmost_bound - offset),
165+
[=] (std::error_code const &ec) {
166+
self->handle_chunk(mmaped_region, rightmost_bound, connection, ec);
167+
});
168+
}
169+
170+
void handle_chunk(std::pair<void *, std::size_t> mmaped_region,
171+
off_t offset,
172+
server::connection_ptr connection,
173+
std::error_code const &ec)
174+
{
175+
assert(offset >= 0);
176+
if (!ec && static_cast<std::size_t>(offset) < mmaped_region.second) {
177+
send_file(mmaped_region, offset, connection);
178+
}
179+
}
180+
};
181+
182+
183+
struct file_server
184+
{
185+
explicit file_server(file_cache &cache) : file_cache_(cache) {}
186+
file_cache &file_cache_;
187+
188+
void operator()(server::request const &request,
189+
server::connection_ptr connection)
190+
{
191+
if (request.method == "HEAD")
192+
{
193+
std::shared_ptr<connection_handler> handler(new connection_handler(file_cache_));
194+
(*handler)(request.destination, connection, false);
195+
}
196+
else if (request.method == "GET")
197+
{
198+
std::shared_ptr<connection_handler> handler(new connection_handler(file_cache_));
199+
(*handler)(request.destination, connection, true);
200+
}
201+
else
202+
{
203+
static server::response_header error_headers[] = {{"Connection", "close"}};
204+
connection->set_status(server::connection::not_supported);
205+
connection->set_headers(boost::make_iterator_range(error_headers, error_headers + 1));
206+
connection->write("Method not supported.");
207+
}
208+
}
209+
};
210+
211+
// 可用浏览器测试: http://127.0.0.1:8000/file_server 下载file_server文件
212+
int main(int, char *[]) {
213+
try
214+
{
215+
file_cache cache(".");
216+
file_server handler(cache);
217+
server::options options(handler);
218+
options.reuse_address(true);
219+
server instance(options.thread_pool(std::make_shared<utils::thread_pool>(4)).address("0.0.0.0").port("8000"));
220+
instance.run();
221+
}
222+
catch (std::exception &e) {
223+
std::cerr << e.what() << std::endl;
224+
}
225+
}

http_client/CMakeLists.txt

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,14 @@ if(DEFINED CPP_NETLIB_PATH)
2020
LINK_DIRECTORIES(${CPP_NETLIB_LIBRARY_DIR})
2121
SET(CPP_NETLIB_LIBRARIES
2222
libcppnetlib-client-connections.a
23-
libcppnetlib-server-parsers.a
2423
libcppnetlib-uri.a
25-
libgmock.a
26-
libgmock_main.a
2724
)
2825

2926
message(STATUS "CPP_NETLIB_INCLUDE_DIR: ${CPP_NETLIB_INCLUDE_DIR}")
3027
message(STATUS "CPP_NETLIB_LIBRARY_DIR: ${CPP_NETLIB_LIBRARY_DIR}")
3128
message(STATUS "CPP_NETLIB_LIBRARIES: ${CPP_NETLIB_LIBRARIES}")
3229
else()
33-
message(SEND_ERROR "Please set PROTOBUF_PATH")
30+
message(SEND_ERROR "Please set CPP_NETLIB_PATH")
3431
endif()
3532

3633
##### openssl

0 commit comments

Comments
 (0)
0