使用C++ REST SDK实现静态网站服务器的示例

目的

通过实现简易的静态网站服务器,来了解HTTP服务器的基本内容,以及C++ REST SDK如何使用。

静态网站

静态网站是指内容全部是静态的,不需要动态生成,当客户端请求指定资源时,将资源回复给客户端即可,不需要有进一步的交互;服务器上只是寄存了一些HTML、CSS、JavaScript以及资源文件。

对于静态网站,我们的HTTP服务器只需要实现GET功能,根据客户端请求的资源URI返回给客户端即可,HTTP 方法:GET 对比 POST

实现思路

  1. 使用http_listener实现对本地端口的http请求监听;
  2. 接收到请求后根据请求的相对URI获取所请求的文件
  3. 打开文件并发送内容及内容类型

MiniHTTPServer

http_listener需要指定监听的地址,当收到资源请求时,需要确定从哪个路径取文件,并提供启动和关闭接口:

#include <cpprest\http_listener.h>
#include <cpprest\filestream.h>
 
class MiniHTTPServer
{
public:
    explicit MiniHTTPServer(utility::string_t strUrl);//根据地址创建http_listener
    ~MiniHTTPServer(); //关闭服务器
    //设定文档根目录
    void setDocumentRoot(const utility::string_t& strWWW) { m_strWWW = strWWW;};
 
public:
    pplx::task<void> start();  //启动服务器
    pplx::task<void> stop(); //关闭服务器
    //处理错误
    static void handle_error(pplx::task<void>& t);
private:
    utility::string_t m_strWWW;//根路径
    std::unique_ptr<web::http::experimental::listener::http_listener> m_listener;
};

构造服务器:

MiniHTTPServer::MiniHTTPServer(utility::string_t strUrl)
    :m_strWWW(U(".")),m_listener(new http_listener(strUrl))
{
}

启动/停止服务器:

pplx::task<void> MiniHTTPServer::start()
{
    return m_listener->open().then([](auto t){ handle_error(t); });
}
 
pplx::task<void> MiniHTTPServer::stop()
{
    return m_listener->close().then([](auto t){ handle_error(t); });
}

使用方法如下:

#include "MiniHTTPServer.h"
 
 //使能宽字符输出
#include <io.h>
#include <fcntl.h>
int main(int argc, char** argv)
{
    _setmode(_fileno(stdout),_O_WTEXT);
 
    if (argc != 2)
    {
        std::wcout <<L"Usage:MiniHTTPServer.exe port\n";
        return -1;
    }
 
    //合成服务器地址
    utility::string_t strAddr = U("http://localhost:");
    strAddr.append(utility::conversions::to_string_t(argv[1]));
   
    //构造服务器并启用
    MiniHTTPServer oServer(strAddr);
    oServer.start().wait();
 
    std::wcout <<L"监听来自("<<strAddr<<L")的请求"<<std::endl;
 
    std::wcout << L"按回车关闭服务器.";
    std::string strVal;
    std::getline(std::cin,strVal);
 
    //关闭服务器
    oServer.stop().wait();
 
    return 0;
}

注册HTTP的GET请求处理句柄

//处理HTTP的GET请求并回复数据
void handle_get(web::http::http_request request);

//注册
MiniHTTPServer::MiniHTTPServer(utility::string_t strUrl)
    :m_strWWW(U(".")),m_listener(new http_listener(strUrl))
{
    //处理GET请求
    m_listener->support(methods::GET,[this](auto request){
        handle_get(request);
    });
}

调试用:输出请求的http_headers

现在启动服务器就可以监听客户端的GET请求了,目前可以输出请求的http_header来查看客户端请求信息:

utility::string_t MiniHTTPServer::dump_http_header(web::http::http_headers oHeader)
{
    stringstream_t ss;
    ss<<U("->http_header\n");
    for (auto const& oVal:oHeader)
    {
        ss<<oVal.first<<U(":")<<oVal.second<<U("\n");
    }
    ss<<U("<-http_header\n");
    return ss.str();
}

使用方法:

void MiniHTTPServer::handle_get(web::http::http_request request)
{
    auto path = request.relative_uri().path();
    ucout<<"HTTP GET:"<<path<<std::endl;
    ucout<<dump_http_header(request.headers());
    ......
}

根据请求URI得到资源类型

根据后缀得到资源类型:

utility::string_t MiniHTTPServer::resource_type(const utility::string_t& strSuffix)
{
    std::map<utility::string_t,utility::string_t> oVals;
    oVals[U(".html")] = U("text/html");
    oVals[U(".js")] = U("application/javascript");
    oVals[U(".css")] = U("text/css");
    oVals[U(".png")] = U("application/octet-stream");
    oVals[U(".jpg")] = U("application/octet-stream");
 
    auto pIt = oVals.find(strSuffix);
    if (pIt != oVals.end())
        return pIt->second;
    return U("application/octet-stream");
}

根据URI路径得到资源文件路径和资源类型:

std::pair<utility::string_t, utility::string_t> MiniHTTPServer::resource(const utility::string_t& strPath)
{
    //如果是ROOT,寻找index
    auto strVal = (strPath == U("/"))? U("/index.html"):strPath;
    fs::path oPath(m_strWWW);
    auto oVal = fs::absolute(oPath/strVal);
    return std::make_pair(utility::string_t(oVal),resource_type(oVal.extension()));
}

添加到GET请求处理中:

auto path = request.relative_uri().path();
ucout<<"HTTP GET:"<<path<<std::endl;
ucout<<dump_http_header(request.headers());
 
auto oVals = resource(path);
ucout<<U("映射到本地的资源为")<<oVals.first<<std::endl;
 
if (!fs::exists(oVals.first))
{
    request.reply(status_codes::NotFound,U("Path not found")).then([](auto t){ handle_error(t); });
    return;
}

向客户端发送资源文件

使用提供的异步输入流和请求回复接口向客户端发送资源文件:

//打开异步输入流
concurrency::streams::fstream::open_istream(oVals.first,std::ios::in).then([=](concurrency::streams::istream is){
    //发送异步输入流
    request.reply(status_codes::OK,is,oVals.second).then([](auto t) { handle_error(t); });
}).then([=](auto& t){
    try
    {
        t.get();
    }
    catch (...)
    {
        //打开文件失败,返回错误信息
        request.reply(status_codes::InternalError).then([](auto t) { handle_error(t); });
    }
});

复制一些静态网站内容并运行

运行效果:

服务器输出信息
浏览器访问页面

总结

以上实现了一个非常简单的静态网站服务器,只有非常基本的功能,但是为懂得HTTP库使用的人揭开了HTTP服务器的面纱,得以一窥HTTP服务器的背后。

后续将继续了解C++ REST SDK提供的功能,为构造云端连接应用做好准备。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 136,790评论 19 139
  • http协议有http0.9,http1.0,http1.1和http2三个版本,但是现在浏览器使用的是htt...
    一现_阅读 2,011评论 0 3
  • API定义规范 本规范设计基于如下使用场景: 请求频率不是非常高:如果产品的使用周期内请求频率非常高,建议使用双通...
    有涯逐无涯阅读 2,970评论 0 6
  • 一、概念(载录于:http://www.cnblogs.com/EricaMIN1987_IT/p/3837436...
    yuantao123434阅读 8,767评论 6 152
  • 一眨眼17年已经过去四分之一,剩下的时间也不太多,我总算是下定决心开始学习了。由于我天生比较愚笨,相对于那些拥有过...
    夏野阅读 636评论 0 0

友情链接更多精彩内容