読者です 読者をやめる 読者になる 読者になる

堕(惰)プログラマ開発記録

タイトル変えようかなとも思ってるけれど,思い浮かばない

Boost.AsioでSSL非同期通信

C++ Boost

ちょこちょこ、boost::asio::streambufで例外が投げられるけど、
一応まともに動くコードになりました。
io_service::workって作ってないとスレッドが落ちちゃうのね…

template<boost::asio::ssl::context_base::method SSL_TYPE = boost::asio::ssl::context::sslv23>
class async_https{
public:
  async_https(boost::asio::io_service& io_service,string_type* response)
    : io_service_(io_service),protocol_("https"),work_(std::auto_ptr<boost::asio::io_service::work>(new boost::asio::io_service::work(io_service))), ctx_(io_service,SSL_TYPE), socket_(io_service,ctx_), resolver_(io_service)
  {
    response_ = response;
    thread = boost::thread(boost::bind(&boost::asio::io_service::run,&io_service_));
    disconnect = false;
  }
  string_type protocol() const  {return protocol_;}

  std::auto_ptr<boost::asio::io_service::work> work_;
  boost::asio::io_service& io_service_;
  const string_type protocol_;

  ~async_https(){
    thread.detach();
    thread.join();
    io_service_.reset();
  }
        
  void start(const std::string& host,boost::asio::streambuf *request){
    std::ostream os(&request_);
    os << request;
    boost::asio::ip::tcp::resolver::query query(host, "https");
    resolver_.async_resolve(
      query,
      boost::bind(&async_https::handle_resolve, this, boost::asio::placeholders::error, boost::asio::placeholders::iterator)
      );
  }
            
  void stop(){
    disconnect=true;
  }
        
private:
  //ヘッダー処理関数
  void header_process(std::string *data)
  {
    //レスポンスチェック
    std::istringstream response_stream(*data);
    std::string http_version;
    response_stream >> http_version;
    unsigned int status_code;
    response_stream >> status_code;
    std::string status_message;
    response_stream >> status_message;

    //ヘッダー切り捨て
    data->erase(0,data->find("\r\n\r\n")+4);
        
    if (!response_stream || http_version.substr(0, 5) != "HTTP/")
    {
      throw std::invalid_argument("Invalid response");
    }
  }

  //async_resolve後ハンドル
  void handle_resolve(const boost::system::error_code& err,boost::asio::ip::tcp::resolver::iterator endpoint_iterator)
  {
    if(!err)
    {
      //正常
      boost::asio::ip::tcp::endpoint endpoint= *endpoint_iterator;
      socket_.lowest_layer().async_connect(
        endpoint,
        boost::bind(&async_https::handle_connect, this, boost::asio::placeholders::error, ++endpoint_iterator)
        );

    }
    else throw std::invalid_argument("Error: "+err.message());
  }

  //async_connect後ハンドル
  void handle_connect(const boost::system::error_code& err,boost::asio::ip::tcp::resolver::iterator endpoint_iterator)
  {
    if(!err)
    {
      //正常接続
      socket_.async_handshake(
        boost::asio::ssl::stream<boost::asio::ip::tcp::socket>::client,
        boost::bind(&async_https::handle_handshake, this, boost::asio::placeholders::error)
        );    
    }
    else if(endpoint_iterator != boost::asio::ip::tcp::resolver::iterator())
    {
      //接続ミス
      socket_.lowest_layer().close();
      boost::asio::ip::tcp::endpoint endpoint= *endpoint_iterator;
      socket_.lowest_layer().async_connect(
        endpoint,
        boost::bind(&async_https::handle_connect, this, boost::asio::placeholders::error, ++endpoint_iterator)
        );

    }
    else throw std::invalid_argument("Error: "+err.message());
  }

  //async_handshake後ハンドル
  void handle_handshake(const boost::system::error_code& err)
  {
    if(!err)
    {
      //正常ハンドシェイク
      boost::asio::async_write(
      socket_,
      request_,
      boost::bind(&async_https::handle_write_request, this, boost::asio::placeholders::error)
      );
    }
    else throw std::invalid_argument("Error: "+err.message());
  }

  //async_write後ハンドル
  void handle_write_request(const boost::system::error_code& err)
  {
    if(!err)
    {
      //正常送信
      read();
    }
    else throw std::invalid_argument("Error: "+err.message());
  }

  void read()
  {
    boost::asio::async_read(
        socket_,
        response_stream_,
        boost::asio::transfer_at_least(1),
        boost::bind(&async_https::handle_read,this,boost::asio::placeholders::error)
        );
  }
  
  //readハンドル
  void handle_read(const boost::system::error_code& err)
  {
    if(!err)
    {
      buffer = static_cast<std::string>(boost::asio::buffer_cast<const char*>(response_stream_.data()));
          
      header_process(&buffer);
          
      *response_ = buffer;

      if(disconnect)
        return;

      read();
    }
    else if (err == boost::asio::error::eof || err == boost::asio::error::shut_down )
    {
      std::cout << "Error: "+err.message();
    }
  }

  boost::thread thread;
  bool disconnect;
  boost::asio::ssl::context ctx_;
  boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket_;
  boost::asio::ip::tcp::resolver resolver_;
  boost::asio::streambuf request_;
  boost::asio::streambuf response_stream_;
  std::string *response_;
  std::string buffer;
};

あとはチャンクの処理を組み込ませたいんだよな・・・。
非同期だと末尾に変なゴミがついて、うまく+=で追加できないんだよ。


「ここはこうした方が良いよ」って事があったら、コメントお願いします。
私自身もBoost.Asioについて詳しくわかっていないので。