h2load: Reconnect server on connection: close
This commit is contained in:
parent
4e2ff875dc
commit
252df2d22c
110
src/h2load.cc
110
src/h2load.cc
|
@ -108,6 +108,8 @@ void writecb(struct ev_loop *loop, ev_io *w, int revents) {
|
||||||
auto rv = client->do_write();
|
auto rv = client->do_write();
|
||||||
if (rv == Client::ERR_CONNECT_FAIL) {
|
if (rv == Client::ERR_CONNECT_FAIL) {
|
||||||
client->disconnect();
|
client->disconnect();
|
||||||
|
// Try next address
|
||||||
|
client->current_addr = nullptr;
|
||||||
rv = client->connect();
|
rv = client->connect();
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
client->fail();
|
client->fail();
|
||||||
|
@ -220,9 +222,10 @@ void client_request_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
Client::Client(Worker *worker, size_t req_todo)
|
Client::Client(Worker *worker, size_t req_todo)
|
||||||
: worker(worker), ssl(nullptr), next_addr(config.addrs), reqidx(0),
|
: worker(worker), ssl(nullptr), next_addr(config.addrs),
|
||||||
state(CLIENT_IDLE), first_byte_received(false), req_todo(req_todo),
|
current_addr(nullptr), reqidx(0), state(CLIENT_IDLE),
|
||||||
req_started(0), req_done(0), fd(-1) {
|
first_byte_received(false), req_todo(req_todo), req_started(0),
|
||||||
|
req_done(0), fd(-1), new_connection_requested(false) {
|
||||||
ev_io_init(&wev, writecb, 0, EV_WRITE);
|
ev_io_init(&wev, writecb, 0, EV_WRITE);
|
||||||
ev_io_init(&rev, readcb, 0, EV_READ);
|
ev_io_init(&rev, readcb, 0, EV_READ);
|
||||||
|
|
||||||
|
@ -246,48 +249,67 @@ Client::~Client() { disconnect(); }
|
||||||
int Client::do_read() { return readfn(*this); }
|
int Client::do_read() { return readfn(*this); }
|
||||||
int Client::do_write() { return writefn(*this); }
|
int Client::do_write() { return writefn(*this); }
|
||||||
|
|
||||||
|
int Client::make_socket(addrinfo *addr) {
|
||||||
|
fd = util::create_nonblock_socket(addr->ai_family);
|
||||||
|
if (fd == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (config.scheme == "https") {
|
||||||
|
ssl = SSL_new(worker->ssl_ctx);
|
||||||
|
|
||||||
|
auto config = worker->config;
|
||||||
|
|
||||||
|
if (!util::numeric_host(config->host.c_str())) {
|
||||||
|
SSL_set_tlsext_host_name(ssl, config->host.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
SSL_set_fd(ssl, fd);
|
||||||
|
SSL_set_connect_state(ssl);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto rv = ::connect(fd, addr->ai_addr, addr->ai_addrlen);
|
||||||
|
if (rv != 0 && errno != EINPROGRESS) {
|
||||||
|
if (ssl) {
|
||||||
|
SSL_free(ssl);
|
||||||
|
ssl = nullptr;
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
fd = -1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int Client::connect() {
|
int Client::connect() {
|
||||||
|
int rv;
|
||||||
|
|
||||||
record_start_time(&worker->stats);
|
record_start_time(&worker->stats);
|
||||||
|
|
||||||
if (worker->config->conn_inactivity_timeout > 0) {
|
if (worker->config->conn_inactivity_timeout > 0) {
|
||||||
ev_timer_again(worker->loop, &conn_inactivity_watcher);
|
ev_timer_again(worker->loop, &conn_inactivity_watcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (next_addr) {
|
if (current_addr) {
|
||||||
auto addr = next_addr;
|
rv = make_socket(current_addr);
|
||||||
next_addr = next_addr->ai_next;
|
if (rv == -1) {
|
||||||
fd = util::create_nonblock_socket(addr->ai_family);
|
return -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
addrinfo *addr;
|
||||||
|
while (next_addr) {
|
||||||
|
addr = next_addr;
|
||||||
|
next_addr = next_addr->ai_next;
|
||||||
|
rv = make_socket(addr);
|
||||||
|
if (rv == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
continue;
|
return -1;
|
||||||
}
|
|
||||||
if (config.scheme == "https") {
|
|
||||||
ssl = SSL_new(worker->ssl_ctx);
|
|
||||||
|
|
||||||
auto config = worker->config;
|
|
||||||
|
|
||||||
if (!util::numeric_host(config->host.c_str())) {
|
|
||||||
SSL_set_tlsext_host_name(ssl, config->host.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
SSL_set_fd(ssl, fd);
|
|
||||||
SSL_set_connect_state(ssl);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto rv = ::connect(fd, addr->ai_addr, addr->ai_addrlen);
|
current_addr = addr;
|
||||||
if (rv != 0 && errno != EINPROGRESS) {
|
|
||||||
if (ssl) {
|
|
||||||
SSL_free(ssl);
|
|
||||||
ssl = nullptr;
|
|
||||||
}
|
|
||||||
close(fd);
|
|
||||||
fd = -1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fd == -1) {
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
writefn = &Client::connected;
|
writefn = &Client::connected;
|
||||||
|
@ -313,9 +335,19 @@ void Client::restart_timeout() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::fail() {
|
void Client::fail() {
|
||||||
process_abandoned_streams();
|
|
||||||
|
|
||||||
disconnect();
|
disconnect();
|
||||||
|
|
||||||
|
if (new_connection_requested) {
|
||||||
|
new_connection_requested = false;
|
||||||
|
|
||||||
|
// Keep using current address
|
||||||
|
if (connect() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::cerr << "client could not connect to host" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
process_abandoned_streams();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::disconnect() {
|
void Client::disconnect() {
|
||||||
|
@ -505,7 +537,7 @@ void Client::on_status_code(int32_t stream_id, uint16_t status) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::on_stream_close(int32_t stream_id, bool success,
|
void Client::on_stream_close(int32_t stream_id, bool success,
|
||||||
RequestStat *req_stat) {
|
RequestStat *req_stat, bool final) {
|
||||||
req_stat->stream_close_time = std::chrono::steady_clock::now();
|
req_stat->stream_close_time = std::chrono::steady_clock::now();
|
||||||
if (success) {
|
if (success) {
|
||||||
req_stat->completed = true;
|
req_stat->completed = true;
|
||||||
|
@ -525,7 +557,7 @@ void Client::on_stream_close(int32_t stream_id, bool success,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!config.timing_script) {
|
if (!config.timing_script && !final) {
|
||||||
if (req_started < req_todo) {
|
if (req_started < req_todo) {
|
||||||
submit_request();
|
submit_request();
|
||||||
return;
|
return;
|
||||||
|
|
12
src/h2load.h
12
src/h2load.h
|
@ -225,6 +225,11 @@ struct Client {
|
||||||
SSL *ssl;
|
SSL *ssl;
|
||||||
ev_timer request_timeout_watcher;
|
ev_timer request_timeout_watcher;
|
||||||
addrinfo *next_addr;
|
addrinfo *next_addr;
|
||||||
|
// Address for the current address. When try_new_connection() is
|
||||||
|
// used and current_addr is not nullptr, it is used instead of
|
||||||
|
// trying next address though next_addr. To try new address, set
|
||||||
|
// nullptr to current_addr before calling connect().
|
||||||
|
addrinfo *current_addr;
|
||||||
size_t reqidx;
|
size_t reqidx;
|
||||||
ClientState state;
|
ClientState state;
|
||||||
bool first_byte_received;
|
bool first_byte_received;
|
||||||
|
@ -239,11 +244,13 @@ struct Client {
|
||||||
ev_timer conn_active_watcher;
|
ev_timer conn_active_watcher;
|
||||||
ev_timer conn_inactivity_watcher;
|
ev_timer conn_inactivity_watcher;
|
||||||
std::string selected_proto;
|
std::string selected_proto;
|
||||||
|
bool new_connection_requested;
|
||||||
|
|
||||||
enum { ERR_CONNECT_FAIL = -100 };
|
enum { ERR_CONNECT_FAIL = -100 };
|
||||||
|
|
||||||
Client(Worker *worker, size_t req_todo);
|
Client(Worker *worker, size_t req_todo);
|
||||||
~Client();
|
~Client();
|
||||||
|
int make_socket(addrinfo *addr);
|
||||||
int connect();
|
int connect();
|
||||||
void disconnect();
|
void disconnect();
|
||||||
void fail();
|
void fail();
|
||||||
|
@ -256,6 +263,8 @@ struct Client {
|
||||||
void report_tls_info();
|
void report_tls_info();
|
||||||
void report_app_info();
|
void report_app_info();
|
||||||
void terminate_session();
|
void terminate_session();
|
||||||
|
// Asks client to create new connection, instead of just fail.
|
||||||
|
void try_new_connection();
|
||||||
|
|
||||||
int do_read();
|
int do_read();
|
||||||
int do_write();
|
int do_write();
|
||||||
|
@ -277,7 +286,8 @@ struct Client {
|
||||||
void on_header(int32_t stream_id, const uint8_t *name, size_t namelen,
|
void on_header(int32_t stream_id, const uint8_t *name, size_t namelen,
|
||||||
const uint8_t *value, size_t valuelen);
|
const uint8_t *value, size_t valuelen);
|
||||||
void on_status_code(int32_t stream_id, uint16_t status);
|
void on_status_code(int32_t stream_id, uint16_t status);
|
||||||
void on_stream_close(int32_t stream_id, bool success, RequestStat *req_stat);
|
void on_stream_close(int32_t stream_id, bool success, RequestStat *req_stat,
|
||||||
|
bool final = false);
|
||||||
|
|
||||||
void record_request_time(RequestStat *req_stat);
|
void record_request_time(RequestStat *req_stat);
|
||||||
void record_start_time(Stats *stat);
|
void record_start_time(Stats *stat);
|
||||||
|
|
|
@ -71,11 +71,24 @@ int htp_msg_completecb(http_parser *htp) {
|
||||||
auto session = static_cast<Http1Session *>(htp->data);
|
auto session = static_cast<Http1Session *>(htp->data);
|
||||||
auto client = session->get_client();
|
auto client = session->get_client();
|
||||||
|
|
||||||
|
auto final = http_should_keep_alive(htp) == 0;
|
||||||
client->on_stream_close(session->stream_resp_counter_, true,
|
client->on_stream_close(session->stream_resp_counter_, true,
|
||||||
session->req_stats_[session->stream_resp_counter_]);
|
session->req_stats_[session->stream_resp_counter_],
|
||||||
|
final);
|
||||||
|
|
||||||
session->stream_resp_counter_ += 2;
|
session->stream_resp_counter_ += 2;
|
||||||
|
|
||||||
|
if (final) {
|
||||||
|
http_parser_pause(htp, 1);
|
||||||
|
// Connection is going down. If we have still request to do,
|
||||||
|
// create new connection and keep on doing the job.
|
||||||
|
if (client->req_started < client->req_todo) {
|
||||||
|
client->try_new_connection();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -157,6 +170,11 @@ int Http1Session::on_read(const uint8_t *data, size_t len) {
|
||||||
|
|
||||||
auto htperr = HTTP_PARSER_ERRNO(&htp_);
|
auto htperr = HTTP_PARSER_ERRNO(&htp_);
|
||||||
|
|
||||||
|
if (htperr == HPE_PAUSED) {
|
||||||
|
// pause is done only when connection: close is requested
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (htperr != HPE_OK) {
|
if (htperr != HPE_OK) {
|
||||||
std::cerr << "[ERROR] HTTP parse error: "
|
std::cerr << "[ERROR] HTTP parse error: "
|
||||||
<< "(" << http_errno_name(htperr) << ") "
|
<< "(" << http_errno_name(htperr) << ") "
|
||||||
|
|
Loading…
Reference in New Issue