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

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

作者: 长不胖的Garfield | 来源:发表于2016-12-28 17:49 被阅读0次

目的

通过实现简易的静态网站服务器,来了解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提供的功能,为构造云端连接应用做好准备。

相关文章

网友评论

      本文标题:使用C++ REST SDK实现静态网站服务器的示例

      本文链接:https://www.haomeiwen.com/subject/ikgyvttx.html