如何创建一个升压SSL的iostream? [英] How to create a boost ssl iostream?

查看:232
本文介绍了如何创建一个升压SSL的iostream?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我添加HTTPS支持,code。使用升压TCP :: iostream的,做的输入和输出(作为HTTP服务器)。

我发现的例子(和有一个工作玩具HTTPS服务器)做使用boost :: ASIO ::读/升压:: ASIO :: SSL写入输入/输出,但没有使用的iostream和<&LT ; >>运营商。如何打开一个SSL ::流到iostream?

工作code:

 的#include<升压/ asio.hpp>
#包括LT&;升压/ ASIO / ssl.hpp>
#包括LT&;升压/ foreach.hpp>
#包括LT&;&iostream的GT;
#包括LT&;&sstream GT;
#包括LT&;串GT;使用命名空间std;
使用名字空间boost;
使用boost ::支持ASIO ::知识产权:: TCP;TYPEDEF提高:: ASIO :: SSL ::流<提高:: ASIO ::知识产权:: TCP ::插座> ssl_stream;字符串HTT preply(INT n状态,常量字符串和放大器; strMsg)
{
    串strStatus;
    如果(n状态== 200)strStatus =OK;
    否则,如果(n状态== 400)strStatus =错误的请求;
    否则,如果(n状态== 404)strStatus =未找到;
    否则,如果(n状态== 500)strStatus =内部服务器错误;
    ostringstream S;
    小号所述&;&下; HTTP / 1.1&所述;&下; n状态<< << strStatus<< \\ r \\ n
      << 连接:关闭\\ r \\ n
      << 内容长度:<< strMsg.size()&所述;&下; \\ r \\ n
      << 内容类型:应用程序/ JSON \\ r \\ n
      << 日期:星期六,2009年09 7 12时04分08秒GMT \\ r \\ n
      << 服务器:JSON-RPC / 1.0 \\ r \\ n
      << \\ r \\ n
      << strMsg;
    返回s.str();
}诠释的main()
{
    //绑定到127.0.0.1回环所以插座只能在本地访问
    提高:: ASIO :: io_service对象io_service对象;
    TCP ::端点endpoint(提高:: ASIO ::知识产权:: address_v4 ::环回(),1111);
    TCP ::承兑人(io_service对象,终点);    提高:: ASIO :: SSL ::语境上下文(io_service对象,提高:: ASIO :: SSL ::背景:: sslv23);
    context.set_options(
        提高:: ASIO :: SSL ::环境:: default_workarounds
        |提高:: ASIO :: SSL ::背景:: no_sslv2);
    context.use_certificate_chain_file(server.cert);
    context.use_private_key_file(server.pem,提高:: ASIO :: SSL ::背景:: PEM);    对于(;;)
    {
        //接受连接
        ssl_stream流(io_service对象,背景);
        TCP ::端点peer_endpoint;
        acceptor.accept(stream.lowest_layer(),peer_endpoint);
        提高::系统::错误_ code EC;
        stream.handshake(提高:: ASIO :: SSL :: stream_base ::服务器,EC);        如果(!EC){
            提高:: ASIO ::写(数据流,提高:: ASIO ::缓​​冲区(HTT preply(200,Okely-Dokely \\ n)));
            //我真想写的:
            // iostream_object<< HTT preply(200,Okely-Dokely \\ n)<<的std ::冲洗;
        }
    }
}

这似乎是SSL :: stream_service将是答案,但是这是一个死胡同。

使用boost ::输入输出流(如接受的答案建议)是正确的做法;这里的工作code我已经结束了:

 的#include<升压/ asio.hpp>
#包括LT&;升压/ ASIO / ssl.hpp>
#包括LT&;提升/输入输出流/ concepts.hpp>
#包括LT&;升压/输入输出流/ stream.hpp>
#包括LT&;&sstream GT;
#包括LT&;串GT;
#包括LT&;&iostream的GT;使用空间boost :: ASIO;SSL的typedef ::流< IP :: TCP ::插座> ssl_stream;
//
// iostream的设备,它讲SSL,但也可以说非SSL
//
类ssl_iostream_device:公众的boost :: iostreams的::设备<提高::输入输出流::双向> {
上市:
    ssl_iostream_device(ssl_stream&安培; _stream,布尔_use_ssl):流(_stream)
    {
        USE_SSL = _use_ssl;
        need_handshake = _use_ssl;
    }    无效握手(SSL :: stream_base :: handshake_type角色)
    {
        如果回报(need_handshake!);
        need_handshake = FALSE;
        stream.handshake(作用);
    }
    的std :: streamsize可读取(字符* S,性病:: streamsize可N)
    {
        握手(SSL :: stream_base ::服务器); // HTTPS服务器首先阅读
        如果(USE_SSL)返回stream.read_some(提高:: ASIO ::缓​​存(S,N));
        。返回stream.next_layer()read_some(提高:: ASIO ::缓​​存(S,N));
    }
    的std :: streamsize可写(为const char * S,性病:: streamsize可N)
    {
        握手(SSL :: stream_base ::客户端); // HTTPS客户写第一
        如果(USE_SSL)返回的boost :: ASIO ::写(数据流,提高:: ASIO ::缓​​存(S,N));
        返回的boost :: ASIO ::写(stream.next_layer(),提振:: ASIO ::缓​​存(S,N));
    }私人的:
    布尔need_handshake;
    布尔USE_SSL;
    ssl_stream&安培;流;
};标准::字符串HTT preply(INT n状态,常量标准::字符串&安培; strMsg)
{
    标准::字符串strStatus;
    如果(n状态== 200)strStatus =OK;
    否则,如果(n状态== 400)strStatus =错误的请求;
    否则,如果(n状态== 404)strStatus =未找到;
    否则,如果(n状态== 500)strStatus =内部服务器错误;
    的std :: ostringstream秒;
    小号所述&;&下; HTTP / 1.1&所述;&下; n状态<< << strStatus<< \\ r \\ n
      << 连接:关闭\\ r \\ n
      << 内容长度:<< strMsg.size()&所述;&下; \\ r \\ n
      << 内容类型:应用程序/ JSON \\ r \\ n
      << 日期:星期六,2009年09 7 12时04分08秒GMT \\ r \\ n
      << 服务器:JSON-RPC / 1.0 \\ r \\ n
      << \\ r \\ n
      << strMsg;
    返回s.str();
}
无效handle_request(的std :: iostream的&安培; S)
{
    小号所述&;&下; HTT preply(200,Okely-Dokely \\ n)<<的std ::冲洗;
}INT主(INT ARGC,CHAR *的argv [])
{
    布尔USE_SSL =(的argc&下; = 1);    //绑定到127.0.0.1回环所以插座只能在本地访问
    io_service对象io_service对象;
    IP :: TCP ::端点端点(IP :: address_v4 ::环回(),1111);
    IP :: TCP ::承兑人(io_service对象,终点);    SSL ::语境上下文(io_service对象,SSL ::环境:: sslv23);
    context.set_options(
        SSL ::环境:: default_workarounds
        | SSL ::环境:: no_sslv2);
    context.use_certificate_chain_file(server.cert);
    context.use_private_key_file(server.pem,SSL ::环境:: PEM);    对于(;;)
    {
        IP :: TCP ::端点peer_endpoint;
        ssl_stream _ssl_stream(io_service对象,背景);
        ssl_iostream_device D(_ssl_stream,USE_SSL);
        提高::输入输出流::流< ssl_iostream_device> ssl_iostream(四);        //接受连接
        acceptor.accept(_ssl_stream.lowest_layer(),peer_endpoint);
        性病::字符串的方法;
        标准::字符串路径;
        ssl_iostream>>方法>>路径;        handle_request(ssl_iostream);
    }
}


解决方案

@Guy 的建议(使用提高:: ASIO ::流缓冲)应该工作,它可能是最容易实现的。该方法的主要缺点是,你写的是iostream一切都将缓存在内存中,直到最后,当调用的boost :: ASIO ::写()将在一次转储缓冲器的全部内容到SSL流。 (我要指出,这种缓冲其实是可以在很多情况下可取的,在你的情况下,它可能没有什么区别,因为在所有你说这是一个低容量的应用程序)。

如果这仅仅是一个一次性我可能会使用@盖伊的方式实现它。

话虽这么说 - 有一些你可能宁愿有一个解决方案,允许您使用的iostream调用直接写入到你的 ssl_stream 很好的理由。如果发现这种情况,那么你就需要建立一个扩展自己的包装类的std ::流缓冲,覆盖溢() 同步()(或者其他人根据您的需要)。

幸运的是,<一个href=\"http://www.boost.org/doc/libs/1_44_0/libs/iostreams/doc/index.html\"><$c$c>boost::iostreams提供了一个相对简单的方法来做到这一点,而不直接与性病类不必浪费时间。你只需建立自己的类,它实现相应的<一个href=\"http://www.boost.org/doc/libs/1_44_0/libs/iostreams/doc/concepts/device.html\"><$c$c>Device合同。在这种情况下,这是<一个href=\"http://www.boost.org/doc/libs/1_44_0/libs/iostreams/doc/concepts/sink.html\"><$c$c>Sink,和的boost ::输入输出流水槽:: 类提供了一个方便的方式来获得大部分的方式存在。一旦你有一个封装书面形式向您的基础ssl_stream的过程中一个新的水槽类,你所要做的就是创建一个的boost :: iostream的流:: 是为模板新的设备类型,和您去。

它看起来像下面的(这个例子是从<一个适应href=\"http://www.boost.org/doc/libs/1_44_0/libs/iostreams/doc/tutorial/container_sink.html\">here,又见这个计算器的相关帖子):

  // ---这应该被认为是伪code,
// ---它没有经过测试,而且可能甚至不会编译
// ---#包括LT&;提升/输入输出流/ concepts.hpp&GT;
//其他包括不再赘述...TYPEDEF提高:: ASIO :: SSL ::流&LT;提高:: ASIO ::知识产权:: TCP ::插座&GT; ssl_stream;类ssl_iostream_sink:公共汇{
上市:
    ssl_iostream_sink(ssl_stream * theStream)
    {
        流= theStream;
    }    的std :: streamsize可写(为const char * S,性病:: streamsize可N)
    {
        //写最多n个字符到底层
        //数据沉入缓冲S,返回
        //字符数写        提高:: ASIO ::写(*流,提高:: ASIO ::缓​​存(S,N));
    }
私人的:
    ssl_stream *流;
};

现在,您接受循环可能会改变看起来是这样的:

 为(;;)
{
    //接受连接
    ssl_stream流(io_service对象,背景);
    TCP ::端点peer_endpoint;
    acceptor.accept(stream.lowest_layer(),peer_endpoint);
    提高::系统::错误_ code EC;
    stream.handshake(提高:: ASIO :: SSL :: stream_base ::服务器,EC);
    如果(!EC){        //用保鲜iostream的SSL流
        ssl_iostream_sink my_sink(安培;流);
        提高:: :: iostream的流&LT; ssl_iostream_sink&GT; iostream_object(my_sink);        //现在它的工作原理,你想要的方式...
        iostream_object&LT;&LT; HTT preply(200,Okely-Dokely \\ n)&LT;&LT;的std ::冲洗;
    }
}

这办法挂钩SSL流进了iostream框架。因此,现在你应该可以在上面的例子做任何事情 iostream_object ,你通常会与任何其他的std :: ostream的(如标准输出)。而且,你写它的东西,会得到写进幕后ssl_stream。 IOSTREAMS具有内置的缓冲,所以一定程度上缓冲将于内部 - 但这是一件好事 - 这将缓冲,直到它已积累了一些数据合理数量的,那么它会倾倒上的SSL流,回到缓冲。最后的std ::冲洗,的的强制清空缓冲区输出到ssl_stream。

如果您需要在内部缓冲(或其它任何先进的东西)更多的控制,看看在的boost :: iostream的其他很酷的东西可用。具体来说,您可通过浏览<一开始href=\"http://www.boost.org/doc/libs/1_44_0/libs/iostreams/doc/guide/generic_streams.html#stream_buffer\"><$c$c>stream_buffer.

祝你好运!

I'm adding HTTPS support to code that does input and output using boost tcp::iostream (acting as an HTTP server).

I've found examples (and have a working toy HTTPS server) that do SSL input/output using boost::asio::read/boost::asio::write, but none that use iostreams and the << >> operators. How do I turn an ssl::stream into an iostream?

Working code:

#include <boost/asio.hpp> 
#include <boost/asio/ssl.hpp> 
#include <boost/foreach.hpp>
#include <iostream> 
#include <sstream>
#include <string>

using namespace std;
using namespace boost;
using boost::asio::ip::tcp;

typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ssl_stream;

string HTTPReply(int nStatus, const string& strMsg)
{
    string strStatus;
    if (nStatus == 200) strStatus = "OK";
    else if (nStatus == 400) strStatus = "Bad Request";
    else if (nStatus == 404) strStatus = "Not Found";
    else if (nStatus == 500) strStatus = "Internal Server Error";
    ostringstream s;
    s << "HTTP/1.1 " << nStatus << " " << strStatus << "\r\n"
      << "Connection: close\r\n"
      << "Content-Length: " << strMsg.size() << "\r\n"
      << "Content-Type: application/json\r\n"
      << "Date: Sat, 09 Jul 2009 12:04:08 GMT\r\n"
      << "Server: json-rpc/1.0\r\n"
      << "\r\n"
      << strMsg;
    return s.str();
}

int main() 
{ 
    // Bind to loopback 127.0.0.1 so the socket can only be accessed locally                                            
    boost::asio::io_service io_service;
    tcp::endpoint endpoint(boost::asio::ip::address_v4::loopback(), 1111);
    tcp::acceptor acceptor(io_service, endpoint);

    boost::asio::ssl::context context(io_service, boost::asio::ssl::context::sslv23);
    context.set_options(
        boost::asio::ssl::context::default_workarounds
        | boost::asio::ssl::context::no_sslv2);
    context.use_certificate_chain_file("server.cert");
    context.use_private_key_file("server.pem", boost::asio::ssl::context::pem);

    for(;;)
    {
        // Accept connection                                                                                            
        ssl_stream stream(io_service, context);
        tcp::endpoint peer_endpoint;
        acceptor.accept(stream.lowest_layer(), peer_endpoint);
        boost::system::error_code ec;
        stream.handshake(boost::asio::ssl::stream_base::server, ec);

        if (!ec) {
            boost::asio::write(stream, boost::asio::buffer(HTTPReply(200, "Okely-Dokely\n")));
            // I really want to write:
            // iostream_object << HTTPReply(200, "Okely-Dokely\n") << std::flush;
        }
    }
}

It seems like the ssl::stream_service would be the answer, but that is a dead end.

Using boost::iostreams (as suggested by accepted answer) is the right approach; here's the working code I've ended up with:

#include <boost/asio.hpp> 
#include <boost/asio/ssl.hpp> 
#include <boost/iostreams/concepts.hpp>
#include <boost/iostreams/stream.hpp>
#include <sstream>
#include <string>
#include <iostream>

using namespace boost::asio;

typedef ssl::stream<ip::tcp::socket> ssl_stream;


//
// IOStream device that speaks SSL but can also speak non-SSL
//
class ssl_iostream_device : public boost::iostreams::device<boost::iostreams::bidirectional> {
public:
    ssl_iostream_device(ssl_stream &_stream, bool _use_ssl ) : stream(_stream)
    {
        use_ssl = _use_ssl;
        need_handshake = _use_ssl;
    }

    void handshake(ssl::stream_base::handshake_type role)
    {
        if (!need_handshake) return;
        need_handshake = false;
        stream.handshake(role);
    }
    std::streamsize read(char* s, std::streamsize n)
    {
        handshake(ssl::stream_base::server); // HTTPS servers read first
        if (use_ssl) return stream.read_some(boost::asio::buffer(s, n));
        return stream.next_layer().read_some(boost::asio::buffer(s, n));
    }
    std::streamsize write(const char* s, std::streamsize n)
    {
        handshake(ssl::stream_base::client); // HTTPS clients write first
        if (use_ssl) return boost::asio::write(stream, boost::asio::buffer(s, n));
        return boost::asio::write(stream.next_layer(), boost::asio::buffer(s, n));
    }

private:
    bool need_handshake;
    bool use_ssl;
    ssl_stream& stream;
};

std::string HTTPReply(int nStatus, const std::string& strMsg)
{
    std::string strStatus;
    if (nStatus == 200) strStatus = "OK";
    else if (nStatus == 400) strStatus = "Bad Request";
    else if (nStatus == 404) strStatus = "Not Found";
    else if (nStatus == 500) strStatus = "Internal Server Error";
    std::ostringstream s;
    s << "HTTP/1.1 " << nStatus << " " << strStatus << "\r\n"
      << "Connection: close\r\n"
      << "Content-Length: " << strMsg.size() << "\r\n"
      << "Content-Type: application/json\r\n"
      << "Date: Sat, 09 Jul 2009 12:04:08 GMT\r\n"
      << "Server: json-rpc/1.0\r\n"
      << "\r\n"
      << strMsg;
    return s.str();
}


void handle_request(std::iostream& s)
{
    s << HTTPReply(200, "Okely-Dokely\n") << std::flush;
}

int main(int argc, char* argv[])
{ 
    bool use_ssl = (argc <= 1);

    // Bind to loopback 127.0.0.1 so the socket can only be accessed locally                                            
    io_service io_service;
    ip::tcp::endpoint endpoint(ip::address_v4::loopback(), 1111);
    ip::tcp::acceptor acceptor(io_service, endpoint);

    ssl::context context(io_service, ssl::context::sslv23);
    context.set_options(
        ssl::context::default_workarounds
        | ssl::context::no_sslv2);
    context.use_certificate_chain_file("server.cert");
    context.use_private_key_file("server.pem", ssl::context::pem);

    for(;;)
    {
        ip::tcp::endpoint peer_endpoint;
        ssl_stream _ssl_stream(io_service, context);
        ssl_iostream_device d(_ssl_stream, use_ssl);
        boost::iostreams::stream<ssl_iostream_device> ssl_iostream(d);

        // Accept connection                                                                                            
        acceptor.accept(_ssl_stream.lowest_layer(), peer_endpoint);
        std::string method;
        std::string path;
        ssl_iostream >> method >> path;

        handle_request(ssl_iostream);
    }
}

解决方案

@Guy's suggestion (using boost::asio::streambuf) should work, and it's probably the easiest to implement. The main drawback to that approach is that everything you write to the iostream will be buffered in memory until the end, when the call to boost::asio::write() will dump the entire contents of the buffer onto the ssl stream at once. (I should note that this kind of buffering can actually be desirable in many cases, and in your case it probably makes no difference at all since you've said it's a low-volume application).

If this is just a "one-off" I would probably implement it using @Guy's approach.

That being said -- there are a number of good reasons that you might rather have a solution that allows you to use iostream calls to write directly into your ssl_stream. If you find that this is the case, then you'll need to build your own wrapper class that extends std::streambuf, overriding overflow(), and sync() (and maybe others depending on your needs).

Fortunately, boost::iostreams provides a relatively easy way to do this without having to mess around with the std classes directly. You just build your own class that implements the appropriate Device contract. In this case that's Sink, and the boost::iostreams::sink class is provided as a convenient way to get most of the way there. Once you have a new Sink class that encapsulates the process of writing to your underlying ssl_stream, all you have to do is create a boost::iostreams::stream that is templated to your new device type, and off you go.

It will look something like the following (this example is adapted from here, see also this related stackoverflow post):

//---this should be considered to be "pseudo-code", 
//---it has not been tested, and probably won't even compile
//---

#include <boost/iostreams/concepts.hpp>
// other includes omitted for brevity ...

typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ssl_stream;

class ssl_iostream_sink : public sink {
public:
    ssl_iostream_sink( ssl_stream *theStream )
    {
        stream = theStream;
    }

    std::streamsize write(const char* s, std::streamsize n)
    {
        // Write up to n characters to the underlying 
        // data sink into the buffer s, returning the 
        // number of characters written

        boost::asio::write(*stream, boost::asio::buffer(s, n));
    }
private:
    ssl_stream *stream;
};

Now, your accept loop might change to look something like this:

for(;;)
{
    // Accept connection                                                                                            
    ssl_stream stream(io_service, context);
    tcp::endpoint peer_endpoint;
    acceptor.accept(stream.lowest_layer(), peer_endpoint);
    boost::system::error_code ec;
    stream.handshake(boost::asio::ssl::stream_base::server, ec);


    if (!ec) {

        // wrap the ssl stream with iostream
        ssl_iostream_sink my_sink(&stream);
        boost::iostream::stream<ssl_iostream_sink> iostream_object(my_sink);

        // Now it works the way you want...
        iostream_object << HTTPReply(200, "Okely-Dokely\n") << std::flush;
    }
}

That approach hooks the ssl stream into the iostream framework. So now you should be able to do anything to iostream_object in the above example, that you would normally do with any other std::ostream (like stdout). And the stuff that you write to it will get written into the ssl_stream behind the scenes. Iostreams has built-in buffering, so some degree of buffering will take place internally -- but this is a good thing -- it will buffer until it has accumulated some reasonable amount of data, then it will dump it on the ssl stream, and go back to buffering. The final std::flush, should force it to empty the buffer out to the ssl_stream.

If you need more control over internal buffering (or any other advanced stuff), have a look at the other cool stuff available in boost::iostreams. Specifically, you might start by looking at stream_buffer.

Good luck!

这篇关于如何创建一个升压SSL的iostream?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆