Skip to content

Commit 7457262

Browse files
authored
Merge pull request #154 from trilogy-libraries/add-connection-check2
Add connection check to Trilogy
2 parents bb368bd + bc403f0 commit 7457262

6 files changed

Lines changed: 133 additions & 0 deletions

File tree

contrib/ruby/ext/trilogy-ruby/cext.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1014,6 +1014,17 @@ static VALUE rb_trilogy_closed(VALUE self)
10141014
}
10151015
}
10161016

1017+
static VALUE rb_trilogy_check(VALUE self)
1018+
{
1019+
struct trilogy_ctx *ctx = get_open_ctx(self);
1020+
1021+
int rc = trilogy_sock_check(ctx->conn.socket);
1022+
if (rc != TRILOGY_OK && rc != TRILOGY_AGAIN) {
1023+
handle_trilogy_error(ctx, rc, "trilogy_sock_check");
1024+
}
1025+
return Qtrue;
1026+
}
1027+
10171028
static VALUE rb_trilogy_discard(VALUE self)
10181029
{
10191030
struct trilogy_ctx *ctx = get_ctx(self);
@@ -1105,6 +1116,7 @@ RUBY_FUNC_EXPORTED void Init_cext()
11051116
rb_define_method(Trilogy, "escape", rb_trilogy_escape, 1);
11061117
rb_define_method(Trilogy, "close", rb_trilogy_close, 0);
11071118
rb_define_method(Trilogy, "closed?", rb_trilogy_closed, 0);
1119+
rb_define_method(Trilogy, "check", rb_trilogy_check, 0);
11081120
rb_define_method(Trilogy, "discard!", rb_trilogy_discard, 0);
11091121
rb_define_method(Trilogy, "last_insert_id", rb_trilogy_last_insert_id, 0);
11101122
rb_define_method(Trilogy, "affected_rows", rb_trilogy_affected_rows, 0);

contrib/ruby/test/client_test.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,20 @@ def test_trilogy_closed?
479479
ensure_closed client
480480
end
481481

482+
def test_trilogy_check
483+
client = new_tcp_client
484+
485+
assert_equal true, client.check
486+
487+
client.close
488+
489+
assert_raises Trilogy::ConnectionClosed do
490+
client.check
491+
end
492+
ensure
493+
ensure_closed client
494+
end
495+
482496
def test_read_timeout
483497
client = new_tcp_client(read_timeout: 0.1)
484498

inc/trilogy/socket.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,4 +111,20 @@ trilogy_sock_t *trilogy_sock_new(const trilogy_sockopt_t *opts);
111111
int trilogy_sock_resolve(trilogy_sock_t *raw);
112112
int trilogy_sock_upgrade_ssl(trilogy_sock_t *raw);
113113

114+
/* trilogy_sock_check - Verify if the socket is still alive and not disconnected.
115+
*
116+
* This check is very cheap to do and reduces the number of errors when for
117+
* example the server has restarted since the connection was opened. In connection
118+
* pooling implementations, this check can be done before the connection is
119+
* returned.
120+
*
121+
* raw - A connected trilogy_sock_t pointer. Using a disconnected trilogy_sock_t is undefined.
122+
*
123+
* Return values:
124+
* TRILOGY_OK - The connection is alive on the client side and can be.
125+
* TRILOGY_CLOSED_CONNECTION - The connection is closed.
126+
* TRILOGY_SYSERR - A system error occurred, check errno.
127+
*/
128+
int trilogy_sock_check(trilogy_sock_t *raw);
129+
114130
#endif

src/socket.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -724,3 +724,25 @@ int trilogy_sock_upgrade_ssl(trilogy_sock_t *_sock)
724724
sock->ssl = NULL;
725725
return TRILOGY_OPENSSL_ERR;
726726
}
727+
728+
int trilogy_sock_check(trilogy_sock_t *_sock)
729+
{
730+
struct trilogy_sock *sock = (struct trilogy_sock *)_sock;
731+
char buf[1];
732+
while (1) {
733+
ssize_t data_read = recv(sock->fd, buf, 1, MSG_PEEK);
734+
if (data_read > 0) {
735+
return TRILOGY_OK;
736+
}
737+
if (data_read == 0) {
738+
return TRILOGY_CLOSED_CONNECTION;
739+
}
740+
if (errno == EINTR) {
741+
continue;
742+
}
743+
if (errno == EAGAIN || errno == EWOULDBLOCK) {
744+
return TRILOGY_OK;
745+
}
746+
return TRILOGY_SYSERR;
747+
}
748+
}

test/runner.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const trilogy_sockopt_t *get_connopt(void) { return &connopt; }
2020
SUITE(packet_parser_test) \
2121
SUITE(charset_test) \
2222
SUITE(blocking_test) \
23+
SUITE(socket_test) \
2324
SUITE(parse_handshake_test) \
2425
SUITE(parse_ok_packet_test) \
2526
SUITE(parse_eof_packet_test) \

test/socket_test.c

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#include <errno.h>
2+
#include <stdio.h>
3+
#include <stdlib.h>
4+
#include <string.h>
5+
6+
#include "test.h"
7+
8+
#include "trilogy/client.h"
9+
#include "trilogy/error.h"
10+
11+
#define do_connect(CONN) \
12+
do { \
13+
int err = trilogy_init(CONN); \
14+
ASSERT_OK(err); \
15+
err = trilogy_connect(CONN, get_connopt()); \
16+
ASSERT_OK(err); \
17+
} while (0)
18+
19+
TEST test_check_connected()
20+
{
21+
trilogy_conn_t conn;
22+
23+
do_connect(&conn);
24+
25+
int err = trilogy_sock_check(conn.socket);
26+
ASSERT_OK(err);
27+
28+
trilogy_free(&conn);
29+
PASS();
30+
}
31+
32+
33+
TEST test_check_disconnected()
34+
{
35+
trilogy_conn_t conn;
36+
37+
do_connect(&conn);
38+
shutdown(trilogy_sock_fd(conn.socket), SHUT_RD);
39+
40+
int err = trilogy_sock_check(conn.socket);
41+
ASSERT_ERR(TRILOGY_CLOSED_CONNECTION, err);
42+
43+
trilogy_free(&conn);
44+
PASS();
45+
}
46+
47+
TEST test_check_closed()
48+
{
49+
trilogy_conn_t conn;
50+
51+
do_connect(&conn);
52+
close_socket(&conn);
53+
54+
int err = trilogy_sock_check(conn.socket);
55+
ASSERT_ERR(TRILOGY_SYSERR, err);
56+
57+
trilogy_free(&conn);
58+
PASS();
59+
}
60+
61+
int socket_test()
62+
{
63+
RUN_TEST(test_check_connected);
64+
RUN_TEST(test_check_disconnected);
65+
RUN_TEST(test_check_closed);
66+
67+
return 0;
68+
}

0 commit comments

Comments
 (0)