Title:在cpp-netlib基础上实现http文件服务功能
Author:Kagula
Date:2017-06-01
Environment:
[1]Boost 1.64
本机安装目录 D:\SDK\boost_1_64_0
[2]Windows10、VS2017 Update2
[3]OpenCL 1.0.2k
本机安装目录 D:\SDK\OpenSSL_1_0_2k_vc2007
[4]CMake 3.8.1
[5]cpp-netlib-0.12.0-final
本机安装目录 D:\SDK\cpp-netlib-0.12.0-final
[6]Python 3.6.1
[7]ActivePerl 5.16
Introduction:
在hello world server例子上增加了文件服务功能,直接贴代码。
测试
Author:Kagula
Date:2017-06-01
Environment:
[1]Boost 1.64
本机安装目录 D:\SDK\boost_1_64_0
[2]Windows10、VS2017 Update2
[3]OpenCL 1.0.2k
本机安装目录 D:\SDK\OpenSSL_1_0_2k_vc2007
[4]CMake 3.8.1
[5]cpp-netlib-0.12.0-final
本机安装目录 D:\SDK\cpp-netlib-0.12.0-final
[6]Python 3.6.1
[7]ActivePerl 5.16
Introduction:
在hello world server例子上增加了文件服务功能,直接贴代码。
测试
测试效果图
a.html
<h1>测试测试<h1>
<img src="/temp/22.jpg" alt=""/>
主程序的源代码
测试项目的Debug模式头文件搜索路径
D:\SDK\cpp-netlib-0.12.0-final\deps\asio\asio\include;D:\SDK\cpp-netlib-0.12.0-final;D:\SDK\boost_1_64_0;
测试项目的Debug模式库文件搜索路径
D:\SDK\cpp-netlib-0.12.0-final\vs2017\libs\network\src\Debug;D:\SDK\boost_1_64_0\vc2017\lib;
main.cpp
#include <boost/network/protocol/http/server.hpp>
#include <iostream>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <vector>
#include "HttpProcess\fileHandler.hpp"
namespace http = boost::network::http;
using namespace std;
struct hello_world;
typedef http::server<hello_world> server;
#ifdef _WIN32
#pragma comment(lib, "cppnetlib-server-parsers.lib")
#endif
struct hello_world {
void operator()(server::request const &request, server::connection_ptr connection) {
server::string_type ip = source(request);
unsigned int port = request.source_port;
string url = request.destination;//什么都不跟为"/",否则"/mypath".
string method = request.method;//可能的值"GET"
string content_type, content_length;
for (const auto& header : request.headers) {
if (header.name == "Content-Type") content_type = header.value;
if (header.name == "Content-Length") content_length = header.value;
if (!content_type.empty() && !content_length.empty()) break;
}
server::response_header headers[] = { { "Connection", "close" },
{ "Content-Type", " image/jpeg" },
{ "Content-Length", "0" } };
if (content_type.empty() &&
content_length.empty())// && url.find_last_of('.') != std::string::npos
{
std::string contentType;
std::string content;
if (GetFile(url,contentType,content)==0)
{
//成功返回文件
headers[1].value = contentType;
headers[2].value = std::to_string(content.size());
connection->set_status(server::connection::ok);
connection->set_headers(boost::make_iterator_range(headers, headers + 2));
connection->write(content);
return;
}
}
//提示文件没有找到
std::string strError = "404 File Not Found!";
headers[1].value = "text/plain";
headers[2].value = std::to_string(strError.size());
connection->set_status(server::connection::not_found);
connection->set_headers(boost::make_iterator_range(headers, headers + 2));
connection->write(strError);
}
};
int main(int argc, char** argv)
{
cout << "测试cpp-netlib文件服务实现!" << endl;
if (argc != 3) {
std::cerr << "Usage: " << argv[0] << " address port" << std::endl;
return 1;
}
std::cout << "IP:" << argv[1] << ",Port:" << argv[2] << std::endl;
try {
/*<< Creates the request handler. >>*/
hello_world handler;
/*<< Creates the server. >>*/
server::options options(handler);
server server_(options.address(argv[1]).port(argv[2]));
/*<< Runs the server. >>*/
server_.run();
}
catch (std::exception &e) {
std::cerr << e.what() << std::endl;
return 1;
}
return 0;
}
依赖的源文件
共有六个源文件具体如下:
fileHander.hpp
#ifndef _FILEHANDLER_H_
#define _FILEHANDLER_H_
#include <string>
int GetFile(const std::string &path, std::string &contentType,std::string &content);
#endif // _FILEHANDLER_H_
fileHander.cpp
#include "fileHandler.hpp"
#include "mime_types.hpp"
#include "LocalFileReadManager.h"
#include <boost/filesystem.hpp>
const char g_documentRoot[] = { "d:" };//因为是做测试,先随便填一个路径。
kagula::LocalFileReadManager g_localFileReadManager;
int GetFile(const std::string &path, std::string &contentType, std::string &content)
{
std::string fullPath(g_documentRoot);
fullPath.append(path);
//是不是支持的Minetype?
boost::filesystem::path dir(fullPath);
std::string extension = dir.extension().string();
if (extension.size() > 0)
extension = extension.substr(1,extension.size()-1);//去掉“.”符号。
contentType = extension_to_type(extension);
if (contentType.length()<1)
{
return -1;
}
//读文件
if (g_localFileReadManager.getFileContent(fullPath,content) == false)
{
return -3;
}
return 0;
}
mime_types.hpp
//
// mime_types.hpp
// ~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef HTTP_SERVER4_MIME_TYPES_HPP
#define HTTP_SERVER4_MIME_TYPES_HPP
#include <string>
/// Convert a file extension into a MIME type.
const char* extension_to_type(const std::string& extension);
#endif // HTTP_SERVER4_MIME_TYPES_HPP
mime_types.cpp
//
// mime_types.cpp
// ~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "mime_types.hpp"
struct mapping
{
const char* extension;
const char* mime_type;
} mappings[] =
{
{ "gif", "image/gif" },
{ "htm", "text/html" },
{ "html","text/html" },
{ "jpg", "image/jpeg"},
{ "png", "image/png" },
{ "css", "text/css" },
{ "json","text/json" },
{ "txt", "text/plain" },
{ 0, 0 } // Marks end of list.
};
const char* extension_to_type(const std::string& extension)
{
for (mapping* m = mappings; m->extension; ++m)
{
if (m->extension == extension)
{
return m->mime_type;
}
}
return "";
}
LocalFileReadManager.h
#ifndef _LocalFileRWManager_H_
#define _LocalFileRWManager_H_
#include <boost/thread/mutex.hpp>
#include <string>
#include <map>
/*
Title: 文件内容读取管理
Purpose: 把常用的文件缓存在内存里,
免得下次又要重新读一遍,主要用在编写http服务端程序中。
Author: kagula
Date: 2016-12-28
Environment:
[1]VS2013 Update5
Note:
为了提高读文件速度,以后考虑不检查文件的最后修改时间。
*/
namespace kagula
{
struct LocalFileInfo
{
std::string content;
unsigned long long timeStamp;
};
class LocalFileReadManager
{
public:
bool getFileContent(const std::string &fullPath, std::string &content);
protected:
unsigned long long _sizeBuffer = 0;
const unsigned long long _sizeMAXBuffer = 64 * 1024 * 1024;//64MB
std::map<std::string, LocalFileInfo> _mapFile;
bool _getFileContent(const std::string &full_path, std::string &content);
void addFile2Buffer(const std::string &full_path, const std::string &content, const long long timeStamp);
boost::mutex _mutexRW;
};
}
#endif
LocalFileReadManager.cpp
#include "LocalFileReadManager.h"
#include <boost/thread/locks.hpp>
#include <istream>
#include <fstream>
#include <sys/types.h>
#include <sys/stat.h>
namespace kagula
{
bool GetFileInfo(const std::string &full_path, unsigned long long &fileSize, unsigned long long &timeStamp)
{
struct stat buf;
int result;
result = stat(full_path.c_str(), &buf);
if (result != 0)
return false;
fileSize = buf.st_size;
timeStamp = buf.st_mtime;
return true;
}//function
bool LocalFileReadManager::getFileContent(const std::string &fullPath, std::string &content)
{
unsigned long long fileSize;
unsigned long long timeStamp;
if (!GetFileInfo(fullPath, fileSize, timeStamp))
return false;
{
boost::lock_guard<boost::mutex> l(_mutexRW);
if (_mapFile.find(fullPath) != _mapFile.end())
{
if (timeStamp == _mapFile[fullPath].timeStamp)
{
content = _mapFile[fullPath].content;
return true;
}//if
}//if
}
content.clear();
if (!_getFileContent(fullPath, content))
return false;
//Not cache big file.
if (fileSize > _sizeMAXBuffer / 10)
return true;
addFile2Buffer(fullPath, content, timeStamp);
return true;
}//function
bool LocalFileReadManager::_getFileContent(const std::string &full_path, std::string &content)
{
std::ifstream is(full_path.c_str(), std::ios::in | std::ios::binary);
if (!is)
{
return false;
}//if
char buf[4 * 1024];
while (is.read(buf, sizeof(buf)).gcount() > 0)
content.append(buf, is.gcount());
return true;
}//function
void LocalFileReadManager::addFile2Buffer(const std::string &full_path, const std::string &content, const long long timeStamp)
{
boost::lock_guard<boost::mutex> l(_mutexRW);
//
while (_sizeBuffer + content.size() > _sizeMAXBuffer)
{
std::map<std::string, LocalFileInfo>::iterator iter = _mapFile.begin();
_sizeBuffer -= iter->second.content.size();
_mapFile.erase(iter);
}//while
//
_mapFile[full_path].content = content;
_mapFile[full_path].timeStamp = timeStamp;
_sizeBuffer += content.size();
}//function
}//namespace