-
Notifications
You must be signed in to change notification settings - Fork 137
Add additional async getaddrinfo step to outgoing tcp connections #301
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,14 +25,16 @@ | |
/* Hold state for a single connection request. */ | ||
struct uvTcpConnect | ||
{ | ||
struct UvTcp *t; /* Transport implementation */ | ||
struct raft_uv_connect *req; /* User request */ | ||
uv_buf_t handshake; /* Handshake data */ | ||
struct uv_tcp_s *tcp; /* TCP connection socket handle */ | ||
struct uv_connect_s connect; /* TCP connection request */ | ||
struct uv_write_s write; /* TCP handshake request */ | ||
int status; /* Returned to the request callback */ | ||
queue queue; /* Pending connect queue */ | ||
struct UvTcp *t; /* Transport implementation */ | ||
struct raft_uv_connect *req; /* User request */ | ||
uv_buf_t handshake; /* Handshake data */ | ||
struct uv_tcp_s *tcp; /* TCP connection socket handle */ | ||
struct uv_getaddrinfo_s getaddrinfo; /* DNS resolve request */ | ||
struct uv_connect_s connect; /* TCP connection request */ | ||
struct uv_write_s write; /* TCP handshake request */ | ||
int status; /* Returned to the request callback */ | ||
bool resolving; /* Indicate name resolving in progress */ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Aligning with the comments above would be a bit more aesthetically pleasing. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As the project has a .clang-format file in it's root folder I ran a clang-format on all files I touched. My expectation was all format will be fine after that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Interesting: Using the clang-format option AlignTrailingComments: true seems not to work on comment blocks, but just on single line comments // |
||
queue queue; /* Pending connect queue */ | ||
}; | ||
|
||
/* Encode an handshake message into the given buffer. */ | ||
|
@@ -65,6 +67,7 @@ static void uvTcpConnectFinish(struct uvTcpConnect *connect) | |
int status = connect->status; | ||
QUEUE_REMOVE(&connect->queue); | ||
RaftHeapFree(connect->handshake.base); | ||
uv_freeaddrinfo(connect->getaddrinfo.addrinfo); | ||
raft_free(connect); | ||
req->cb(req, stream, status); | ||
} | ||
|
@@ -88,7 +91,13 @@ static void uvTcpConnectAbort(struct uvTcpConnect *connect) | |
{ | ||
QUEUE_REMOVE(&connect->queue); | ||
QUEUE_PUSH(&connect->t->aborting, &connect->queue); | ||
uv_close((struct uv_handle_s *)connect->tcp, uvTcpConnectUvCloseCb); | ||
uv_cancel((struct uv_req_s *)&connect->getaddrinfo); | ||
/* Call uv_close on the tcp handle, if there is no getaddrinfo request | ||
in flight. Data structures may only be freed after the uvGetAddrInfoCb was | ||
triggered. Tcp handle will be closed in the uvGetAddrInfoCb in this case. */ | ||
if (!connect->resolving) { | ||
uv_close((struct uv_handle_s *)connect->tcp, uvTcpConnectUvCloseCb); | ||
} | ||
} | ||
|
||
/* The handshake TCP write completes. Fire the connect callback. */ | ||
|
@@ -146,55 +155,111 @@ static void uvTcpConnectUvConnectCb(struct uv_connect_s *req, int status) | |
uvTcpConnectAbort(connect); | ||
} | ||
|
||
/* Create a new TCP handle and submit a connection request to the event loop. */ | ||
static int uvTcpConnectStart(struct uvTcpConnect *r, const char *address) | ||
/* The hostname resolve is finished */ | ||
static void uvGetAddrInfoCb(uv_getaddrinfo_t *req, | ||
int status, | ||
struct addrinfo *res) | ||
{ | ||
struct UvTcp *t = r->t; | ||
struct sockaddr_in addr; | ||
struct uvTcpConnect *connect = req->data; | ||
struct UvTcp *t = connect->t; | ||
int rv; | ||
|
||
rv = uvIpParse(address, &addr); | ||
connect->resolving = | ||
false; /* Indicate we are in the name resolving phase */ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably better to write "Indicate that we are done with the resolving phase" |
||
|
||
if (t->closing) { | ||
connect->status = RAFT_CANCELED; | ||
|
||
/* We need to close the tcp handle to to connection attempt */ | ||
uv_close((struct uv_handle_s *)connect->tcp, uvTcpConnectUvCloseCb); | ||
return; | ||
} | ||
|
||
if (status < 0) { | ||
ErrMsgPrintf(t->transport->errmsg, "uv_getaddrinfo(): %s", | ||
uv_err_name(status)); | ||
connect->status = RAFT_NOCONNECTION; | ||
goto err; | ||
} | ||
rv = uv_tcp_connect(&connect->connect, connect->tcp, | ||
(const struct sockaddr *)res->ai_addr, | ||
uvTcpConnectUvConnectCb); | ||
if (rv != 0) { | ||
/* UNTESTED: since parsing succeed, this should fail only because of | ||
* lack of system resources */ | ||
ErrMsgPrintf(t->transport->errmsg, "uv_tcp_connect(): %s", | ||
uv_strerror(rv)); | ||
connect->status = RAFT_NOCONNECTION; | ||
goto err; | ||
} | ||
|
||
return; | ||
|
||
err: | ||
uvTcpConnectAbort(connect); | ||
} | ||
/* Create a new TCP handle and submit a connection request to the event loop. */ | ||
static int uvTcpConnectStart(struct uvTcpConnect *r, const char *address) | ||
{ | ||
static struct addrinfo hints = { | ||
.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG | AI_NUMERICHOST, | ||
.ai_family = AF_INET, | ||
.ai_socktype = SOCK_STREAM, | ||
.ai_protocol = 0}; | ||
struct UvTcp *t = r->t; | ||
char hostname[NI_MAXHOST]; | ||
char service[NI_MAXSERV]; | ||
int rv; | ||
|
||
r->handshake.base = NULL; | ||
|
||
/* Initialize the handshake buffer. */ | ||
rv = uvTcpEncodeHandshake(t->id, t->address, &r->handshake); | ||
if (rv != 0) { | ||
assert(rv == RAFT_NOMEM); | ||
ErrMsgOom(r->t->transport->errmsg); | ||
ErrMsgOom(t->transport->errmsg); | ||
goto err; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I initialise the pointer to NULL in the beginning of the function. As the raft_free (like the posix free is safe to be invoked on a NULL pointer) it safe from my perspective. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's just the default port if none is specified. We should indeed document it, or perhaps return an error instead. |
||
} | ||
|
||
r->tcp = RaftHeapMalloc(sizeof *r->tcp); | ||
if (r->tcp == NULL) { | ||
ErrMsgOom(t->transport->errmsg); | ||
rv = RAFT_NOMEM; | ||
goto err_after_encode_handshake; | ||
goto err; | ||
} | ||
|
||
rv = uv_tcp_init(r->t->loop, r->tcp); | ||
assert(rv == 0); | ||
r->tcp->data = r; | ||
|
||
rv = uv_tcp_connect(&r->connect, r->tcp, (struct sockaddr *)&addr, | ||
uvTcpConnectUvConnectCb); | ||
if (rv != 0) { | ||
/* UNTESTED: since parsing succeed, this should fail only because of | ||
* lack of system resources */ | ||
ErrMsgPrintf(t->transport->errmsg, "uv_tcp_connect(): %s", | ||
rv = uvIpAddrSplit(address, hostname, sizeof(hostname), service, | ||
sizeof(service)); | ||
if (rv) { | ||
ErrMsgPrintf(t->transport->errmsg, | ||
"uv_tcp_connect(): Cannot split %s into host and service", | ||
address); | ||
rv = RAFT_NOCONNECTION; | ||
goto err_after_tcp_init; | ||
} | ||
rv = uv_getaddrinfo(r->t->loop, &r->getaddrinfo, &uvGetAddrInfoCb, hostname, | ||
service, &hints); | ||
if (rv) { | ||
ErrMsgPrintf(t->transport->errmsg, | ||
"uv_tcp_connect(): Cannot initiate getaddrinfo %s", | ||
uv_strerror(rv)); | ||
rv = RAFT_NOCONNECTION; | ||
goto err_after_tcp_init; | ||
} | ||
r->resolving = true; /* Indicate we are in the name resolving phase */ | ||
|
||
return 0; | ||
|
||
err_after_tcp_init: | ||
uv_close((uv_handle_t *)r->tcp, (uv_close_cb)RaftHeapFree); | ||
err_after_encode_handshake: | ||
RaftHeapFree(r->handshake.base); | ||
|
||
err: | ||
RaftHeapFree(r->handshake.base); | ||
|
||
return rv; | ||
} | ||
|
||
|
@@ -221,8 +286,9 @@ int UvTcpConnect(struct raft_uv_transport *transport, | |
r->req = req; | ||
r->status = 0; | ||
r->write.data = r; | ||
r->getaddrinfo.data = r; | ||
r->resolving = false; | ||
r->connect.data = r; | ||
|
||
req->cb = cb; | ||
|
||
/* Keep track of the pending request */ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's special about
8080
? Maybe document this somewhere?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a question for @freeekanayaka as I kept it fully compatible with the existing uvIpParse (which was used before). And he introduced this default with the first version of the file visible to me in the repo.