#include "test.h"
#include "panda/cast.h"
#include "panda/protocol/websocket/ConnectRequest.h"
#include "panda/protocol/websocket/inc.h"
// #include <openssl/dh.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
// #include <openssl/conf.h>
// #include <openssl/engine.h>
// #include <chrono>
// #include <iostream>
// #include <catch2/reporters/catch_reporter_registrars.hpp>
// #include <catch2/reporters/catch_reporter_event_listener.hpp>
using panda::unievent::Timer;
using panda::unievent::TimerSP;
using unievent::Pipe;
using unievent::Tcp;
bool secure = false;
int TServer::dcnt;
// int TClient::dcnt;
// static int64_t _time_mark;
TServerSP make_server (const LoopSP& loop, Server::Config cfg) {
TServerSP server = new TServer(loop);
if (!cfg.locations.size()) {
Location loc;
loc.host = "127.0.0.1";
if (secure) { loc.ssl_ctx = TServer::get_context("ca"); }
cfg.locations.push_back(loc);
} else if (secure) {
cfg.locations.front().ssl_ctx = TServer::get_context("ca");
}
cfg.tcp_nodelay = true;
server->configure(cfg);
server->run();
return server;
}
SslContext TServer::get_context (string cert_name) {
auto ctx = SSL_CTX_new(SSLv23_server_method());
auto r = SslContext::attach(ctx);
string path("tests/cert");
string cert = path + "/" + cert_name + ".pem";
string key = path + "/" + cert_name + ".key";
int err;
err = SSL_CTX_use_certificate_file(ctx, cert.c_str(), SSL_FILETYPE_PEM);
assert(err);
err = SSL_CTX_use_PrivateKey_file(ctx, key.c_str(), SSL_FILETYPE_PEM);
assert(err);
err = SSL_CTX_check_private_key(ctx);
assert(err);
return r;
}
SslContext TClient::get_context(string cert_name, const string& ca_name) {
auto ctx = SSL_CTX_new(SSLv23_client_method());
auto r = SslContext::attach(ctx);
string path("tests/cert");
string ca = path + "/" + ca_name + ".pem";
string cert = path + "/" + cert_name + ".pem";
string key = path + "/" + cert_name + ".key";
int err;
err = SSL_CTX_load_verify_locations(ctx, ca.c_str(), nullptr);
assert(err);
err = SSL_CTX_use_certificate_file(ctx, cert.c_str(), SSL_FILETYPE_PEM);
if (err != 1) printf("SSL CERT ERROR: %s (load cert %s)\n", ERR_error_string(ERR_get_error(), NULL), cert.c_str());
assert(err);
err = SSL_CTX_use_PrivateKey_file(ctx, key.c_str(), SSL_FILETYPE_PEM);
assert(err);
SSL_CTX_check_private_key(ctx);
assert(err);
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, nullptr);
SSL_CTX_set_verify_depth(ctx, 4);
return r;
}
ServerPair::ServerPair (const LoopSP& loop, Server::Config cfg, bool unixsock) {
server = make_server(loop, cfg);
server->connection_event.add([this](auto, auto& cli, auto){
this->sconn = cli;
});
TcpSP tcp;
PipeSP pipe;
if (unixsock) { pipe = new Pipe(loop); conn = pipe; }
else { tcp = new Tcp(loop); conn = tcp; }
if (secure) { conn->use_ssl( TClient::get_context("ca")); }
conn->connect_event.add([this](auto& conn, auto& err, auto){
if (err) {
printf("server pair connect error: %s\n", err.what().c_str());
throw err;
}
ConnectRequestSP req = new protocol::websocket::ConnectRequest();
req->uri = new URI("ws://example.com");
conn->write(parser.connect_request(req));
});
conn->read_event.add([this](auto& self, auto, auto& str, auto& err){
if (parser.established()) return;
if (err) {
printf("server pair client read error: %s\n", err.what().c_str());
throw err;
}
auto res = parser.connect(str);
if (!res) return;
conn->read_event.remove(self);
conn->loop()->stop();
});
if (unixsock) pipe->connect(panda::dyn_cast<Pipe*>(server->listeners()[0].get())->sockname().value());
else tcp->connect(server->sockaddr().value());
loop->run();
}
void ServerPair::enable_echo () {
sconn->message_event.remove_all();
sconn->message_event.add([](auto& sconn, auto& msg){
sconn->message().opcode(msg->opcode()).send(msg->payload.begin(), msg->payload.end());
});
}
void ServerPair::autorespond (const string& str) {
if (!autores) {
autores = true;
sconn->message_event.add([this](auto, auto&){
if (!autoresponse_queue.size()) return;
auto res = autoresponse_queue.front();
autoresponse_queue.pop_front();
sconn->send_text(res);
});
}
autoresponse_queue.push_back(str);
}
void ServerPair::send (const string& str, Opcode opcode) {
auto bin = parser.message().opcode(opcode).send(str);
conn->write(bin);
}
// struct SSLVerifyReseter : Catch::EventListenerBase {
// using EventListenerBase::EventListenerBase; // inherit constructor
// void testCaseStarting( Catch::TestCaseInfo const& ) override {
// default_ssl_verify = false;
// }
// };
// CATCH_REGISTER_LISTENER(SSLVerifyReseter);
// string active_scheme() { return string(secure ? "https" : "http"); }
// int64_t get_time () {
// using namespace std::chrono;
// return duration_cast< milliseconds >(steady_clock::now().time_since_epoch()).count();
// }
// void time_mark () { _time_mark = get_time(); }
// int64_t time_elapsed () { return get_time() - _time_mark; }
// std::vector<ResponseSP> await_responses (const std::vector<RequestSP>& reqs, const LoopSP& loop) {
// std::vector<ResponseSP> r;
// for (auto& req : reqs) {
// req->response_event.add([&](auto, auto& res, auto& err){
// if (err) throw err.what();
// r.emplace_back(res);
// if (r.size() == reqs.size()) loop->stop();
// });
// }
// loop->run();
// return r;
// }
// ResponseSP await_any (const std::vector<RequestSP>& reqs, const LoopSP& loop) {
// ResponseSP r;
// for (auto& req : reqs) {
// req->response_event.add([&](auto, auto& res, auto& err){
// if (err) throw err;
// r = res;
// loop->stop();
// });
// }
// loop->run();
// return r;
// }
// ResponseSP await_response (const RequestSP& req, const LoopSP& loop) { return await_responses({req}, loop)[0]; }
// TServerSP make_ssl_server (const LoopSP& loop) {
// auto server_ctx = TServer::get_context("ca");
// auto err = SSL_CTX_load_verify_locations(server_ctx, "tests/cert/ca.pem", nullptr);
// assert(err);
// SSL_CTX_set_verify(server_ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);
// SSL_CTX_set_verify_depth(server_ctx, 4);
// Server::Location loc;
// loc.host = "127.0.0.1";
// loc.ssl_ctx = server_ctx;
// Server::Config server_cfg;
// server_cfg.locations.push_back(loc);
// server_cfg.tcp_nodelay = true;
// TServerSP server = new TServer(loop);
// server->configure(server_cfg);
// server->run();
// return server;
// }
// string TServer::location () const {
// auto sa = sockaddr().value();
// return sa.ip() + ':' + panda::to_string(sa.port());
// }
// NetLoc TServer::netloc () const {
// return { sockaddr()->ip(), sockaddr()->port(), nullptr, {} };
// }
// string TServer::uri () const {
// string uri = secure ? string("https://") : string("http://");
// uri += sockaddr()->ip();
// uri += ":";
// uri += to_string(sockaddr()->port());
// uri += "/";
// return uri;
// }
// void TClient::request (const RequestSP& req) {
// req->tcp_nodelay = true;
// if (sa) {
// req->uri->host(sa.ip());
// req->uri->port(sa.port());
// }
// if (secure) req->uri->scheme("https");
// Client::request(req);
// }
// ResponseSP TClient::get_response (const RequestSP& req) {
// ResponseSP response;
// req->response_event.add([this, &response](auto, auto& res, auto& err){
// if (err) throw err;
// response = res;
// this->loop()->stop();
// });
// request(req);
// loop()->run();
// return response;
// }
// ResponseSP TClient::get_response (const string& uri, Headers&& headers, Body&& body, bool chunked) {
// auto b = Request::Builder().uri(uri).headers(std::move(headers)).body(std::move(body));
// if (chunked) b.chunked();
// return get_response(b.build());
// }
// ErrorCode TClient::get_error (const RequestSP& req) {
// ErrorCode error;
// req->response_event.add([this, &error](auto, auto, auto& err){
// error = err;
// this->loop()->stop();
// });
// request(req);
// loop()->run();
// return error;
// }
// ErrorCode TClient::get_error (const string& uri, Headers&& headers, Body&& body, bool chunked) {
// auto b = Request::Builder().uri(uri).headers(std::move(headers)).body(std::move(body));
// if (chunked) b.chunked();
// return get_error(b.build());
// }
// TClientSP TPool::request (const RequestSP& req) {
// TClientSP client = dynamic_pointer_cast<TClient>(Pool::request(req));
// return client;
// }
// static TcpSP make_socks_server (const LoopSP& loop, const net::SockAddr& sa) {
// TcpSP server = new Tcp(loop);
// server->bind(sa);
// server->listen(128);
// server->connection_event.add([](auto server, auto stream, auto& err) {
// if (err) throw err;
// std::shared_ptr<int> state = std::make_shared<int>(0);
// TcpSP client = new Tcp(server->loop());
// client->read_event.add([stream](auto, auto& buf, auto& err) {
// if (err) throw err;
// // read from remote server
// stream->write(buf);
// });
// client->eof_event.add([stream](auto) mutable {
// stream->shutdown();
// });
// client->write_event.add([](auto, auto& err, auto) { if (err) throw err; });
// stream->read_event.add([client, state](auto stream, auto& buf, auto&err) {
// if (err) throw err;
// switch (*state) {
// case 0: {
// stream->write("\x05\x00");
// *state = 1;
// break;
// }
// case 1: {
// string request_type = buf.substr(0, 4);
// if (request_type == string("\x05\x01\x00\x03")) {
// int host_length = buf[4];
// string host = buf.substr(5, host_length);
// uint16_t port = ntohs(*(uint16_t*)buf.substr(5 + host_length).data());
// client->connect("127.0.0.1", port);
// client->connect_event.add([](auto, auto& err, auto){ if (err) throw err; });
// } else {
// throw std::runtime_error("bad request");
// }
// stream->write("\x05\x00\x00\x01\xFF\xFF\xFF\xFF\xFF\xFF");
// *state = 2;
// break;
// }
// case 2: {
// // write to remote server
// client->write(buf);
// break;
// }
// }
// });
// });
// return server;
// }
// TProxy new_proxy(const LoopSP& loop, const net::SockAddr& sa) {
// auto server = make_socks_server(loop, sa);
// auto real_sa = server->sockaddr().value();
// URISP url = new URI(string("socks5://") + real_sa.ip() + ":" + to_string(real_sa.port()));
// return TProxy { server, url };
// }
// ClientPair::ClientPair (const LoopSP& loop, bool with_proxy) {
// server = make_server(loop, {});
// client = new TClient(loop);
// client->sa = server->sockaddr().value();
// if (with_proxy) {
// proxy = new_proxy(loop);
// }
// }
// RawResponseSP ServerPair::get_response () {
// if (!response_queue.size()) {
// conn->read_event.remove_all();
// conn->eof_event.remove_all();
// conn->read_event.add([&, this](auto, auto& str, auto& err) {
// if (err) throw err;
// while (str) {
// if (!parser.context_request()) {
// if (source_request) parser.set_context_request(source_request);
// else parser.set_context_request(new RawRequest(Request::Method::Get, new URI("/")));
// }
// auto result = parser.parse_shift(str);
// if (result.error) {
// WARN(result.error);
// throw result.error;
// }
// if (result.state != State::done) return;
// response_queue.push_back(result.response);
// }
// if (response_queue.size()) conn->loop()->stop();
// });
// conn->eof_event.add([&, this](auto){
// eof = get_time();
// auto result = parser.eof();
// if (result.error) throw result.error;
// if (result.response) response_queue.push_back(result.response);
// conn->loop()->stop();
// });
// conn->loop()->run();
// conn->read_event.remove_all();
// conn->eof_event.remove_all();
// }
// if (!response_queue.size()) throw std::runtime_error("no response");
// auto ret = response_queue.front();
// response_queue.pop_front();
// return ret;
// }
// int64_t ServerPair::wait_eof (int tmt) {
// if (eof) return eof;
// TimerSP timer;
// if (tmt) timer = Timer::create(tmt, [this](auto) {
// conn->loop()->stop();
// }, conn->loop());
// conn->eof_event.add([this](auto){
// eof = get_time();
// conn->loop()->stop();
// });
// conn->loop()->run();
// return eof;
// }