Line data Source code
1 : #include "fd_http_resolver.h"
2 : #include "fd_ssresolve.h"
3 :
4 : #include "../../../util/log/fd_log.h"
5 : #include "../../../util/fd_util.h"
6 :
7 : #include <poll.h>
8 : #include <errno.h>
9 : #include <unistd.h>
10 : #include <sys/socket.h>
11 : #include <netinet/in.h>
12 : #include <netinet/tcp.h>
13 :
14 : #if FD_HAS_OPENSSL
15 : #include <openssl/ssl.h>
16 : #include "../../../waltz/openssl/fd_openssl_tile.h"
17 : #endif
18 :
19 0 : #define PEER_STATE_UNRESOLVED (0)
20 0 : #define PEER_STATE_REFRESHING (1)
21 0 : #define PEER_STATE_VALID (2)
22 0 : #define PEER_STATE_INVALID (3)
23 :
24 0 : #define PEER_DEADLINE_NANOS_VALID (5L*1000L*1000L*1000L) /* 5 seconds */
25 0 : #define PEER_DEADLINE_NANOS_RESOLVE (2L*1000L*1000L*1000L) /* 2 seconds */
26 0 : #define PEER_DEADLINE_NANOS_INVALID (5L*1000L*1000L*1000L) /* 5 seconds */
27 :
28 : /* FIXME: The fds/fds_len/idx logic is fragile, replace with something
29 : that duplicates less state / etc. */
30 :
31 : struct fd_ssresolve_peer {
32 : fd_sspeer_key_t key;
33 : fd_ip4_port_t addr;
34 : int is_https;
35 : ulong full_slot;
36 : ulong incr_slot;
37 : uchar full_hash[ FD_HASH_FOOTPRINT ];
38 : uchar incr_hash[ FD_HASH_FOOTPRINT ];
39 :
40 : fd_ssresolve_t * full_ssresolve;
41 : fd_ssresolve_t * inc_ssresolve;
42 :
43 : struct {
44 : ulong next;
45 : } pool;
46 :
47 : struct {
48 : ulong next;
49 : ulong prev;
50 : } deadline;
51 :
52 : struct {
53 : ulong idx;
54 : } fd;
55 :
56 : int state;
57 : long deadline_nanos;
58 : };
59 : typedef struct fd_ssresolve_peer fd_ssresolve_peer_t;
60 :
61 : #define POOL_NAME peer_pool
62 0 : #define POOL_T fd_ssresolve_peer_t
63 : #define POOL_IDX_T ulong
64 0 : #define POOL_NEXT pool.next
65 : #include "../../../util/tmpl/fd_pool.c"
66 :
67 : #define DLIST_NAME deadline_list
68 : #define DLIST_ELE_T fd_ssresolve_peer_t
69 0 : #define DLIST_PREV deadline.prev
70 0 : #define DLIST_NEXT deadline.next
71 : #include "../../../util/tmpl/fd_dlist.c"
72 :
73 : static inline void
74 0 : clear_peer_snapshot_data( fd_ssresolve_peer_t * peer ) {
75 0 : peer->full_slot = FD_SSPEER_SLOT_UNKNOWN;
76 0 : peer->incr_slot = FD_SSPEER_SLOT_UNKNOWN;
77 0 : fd_memset( peer->full_hash, 0, FD_HASH_FOOTPRINT );
78 0 : fd_memset( peer->incr_hash, 0, FD_HASH_FOOTPRINT );
79 0 : }
80 :
81 : struct fd_http_resolver_private {
82 : fd_ssresolve_peer_t * pool;
83 : deadline_list_t * unresolved;
84 : deadline_list_t * resolving;
85 : deadline_list_t * valid;
86 : deadline_list_t * invalid;
87 :
88 : ulong fds_len;
89 : struct pollfd * fds;
90 : ulong * fds_idx;
91 :
92 : int incremental_snapshot_fetch;
93 :
94 : void * cb_arg;
95 : fd_http_resolver_on_resolve_fn_t on_resolve_cb;
96 :
97 : #if FD_HAS_OPENSSL
98 : SSL_CTX * ssl_ctx;
99 : #endif
100 :
101 : ulong magic; /* ==FD_HTTP_RESOLVER_MAGIC */
102 : };
103 :
104 : FD_FN_CONST ulong
105 0 : fd_http_resolver_align( void ) {
106 0 : return fd_ulong_max( alignof(fd_http_resolver_t), fd_ulong_max( peer_pool_align(), fd_ulong_max( deadline_list_align(), fd_ulong_max( alignof(struct pollfd), alignof(ulong) ) ) ) );
107 0 : }
108 :
109 : FD_FN_CONST ulong
110 0 : fd_http_resolver_footprint( ulong peers_cnt ) {
111 0 : ulong l;
112 0 : l = FD_LAYOUT_INIT;
113 0 : l = FD_LAYOUT_APPEND( l, alignof(fd_http_resolver_t), sizeof(fd_http_resolver_t) );
114 0 : l = FD_LAYOUT_APPEND( l, peer_pool_align(), peer_pool_footprint( peers_cnt ) );
115 0 : l = FD_LAYOUT_APPEND( l, deadline_list_align(), deadline_list_footprint() );
116 0 : l = FD_LAYOUT_APPEND( l, deadline_list_align(), deadline_list_footprint() );
117 0 : l = FD_LAYOUT_APPEND( l, deadline_list_align(), deadline_list_footprint() );
118 0 : l = FD_LAYOUT_APPEND( l, deadline_list_align(), deadline_list_footprint() );
119 0 : l = FD_LAYOUT_APPEND( l, alignof(struct pollfd), 2UL*peers_cnt*sizeof(struct pollfd) );
120 0 : l = FD_LAYOUT_APPEND( l, alignof(ulong), 2UL*peers_cnt*sizeof(ulong) );
121 :
122 0 : for( ulong i=0UL; i<peers_cnt*2UL; i++ ) {
123 0 : l = FD_LAYOUT_APPEND( l, fd_ssresolve_align(), fd_ssresolve_footprint() );
124 0 : }
125 0 : return FD_LAYOUT_FINI( l, fd_http_resolver_align() );
126 0 : }
127 :
128 : void *
129 : fd_http_resolver_new( void * shmem,
130 : ulong peers_cnt,
131 : int incremental_snapshot_fetch,
132 : fd_http_resolver_on_resolve_fn_t on_resolve_cb,
133 0 : void * cb_arg ) {
134 0 : if( FD_UNLIKELY( !shmem ) ) {
135 0 : FD_LOG_WARNING(( "NULL shmem" ));
136 0 : return NULL;
137 0 : }
138 :
139 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_http_resolver_align() ) ) ) {
140 0 : FD_LOG_WARNING(( "unaligned shmem" ));
141 0 : return NULL;
142 0 : }
143 :
144 0 : if( FD_UNLIKELY( peers_cnt<1UL ) ) {
145 0 : FD_LOG_WARNING(( "max_peers must be at least 1" ));
146 0 : return NULL;
147 0 : }
148 :
149 0 : FD_SCRATCH_ALLOC_INIT( l, shmem );
150 0 : fd_http_resolver_t * resolver = FD_SCRATCH_ALLOC_APPEND( l, fd_http_resolver_align(), sizeof(fd_http_resolver_t) );
151 0 : void * _pool = FD_SCRATCH_ALLOC_APPEND( l, peer_pool_align(), peer_pool_footprint( peers_cnt ) );
152 0 : void * _unresolved = FD_SCRATCH_ALLOC_APPEND( l, deadline_list_align(), deadline_list_footprint() );
153 0 : void * _resolving = FD_SCRATCH_ALLOC_APPEND( l, deadline_list_align(), deadline_list_footprint() );
154 0 : void * _invalid = FD_SCRATCH_ALLOC_APPEND( l, deadline_list_align(), deadline_list_footprint() );
155 0 : void * _valid = FD_SCRATCH_ALLOC_APPEND( l, deadline_list_align(), deadline_list_footprint() );
156 0 : struct pollfd * fds = FD_SCRATCH_ALLOC_APPEND( l, alignof(struct pollfd), 2UL*peers_cnt*sizeof(struct pollfd) );
157 0 : ulong * fds_idx = FD_SCRATCH_ALLOC_APPEND( l, alignof(ulong), 2UL*peers_cnt*sizeof(ulong) );
158 :
159 0 : resolver->pool = peer_pool_join( peer_pool_new( _pool, peers_cnt ) );
160 0 : resolver->unresolved = deadline_list_join( deadline_list_new( _unresolved ) );
161 0 : resolver->resolving = deadline_list_join( deadline_list_new( _resolving ) );
162 0 : resolver->invalid = deadline_list_join( deadline_list_new( _invalid ) );
163 0 : resolver->valid = deadline_list_join( deadline_list_new( _valid ) );
164 :
165 0 : resolver->fds_len = 0UL;
166 0 : resolver->fds = fds;
167 0 : resolver->fds_idx = fds_idx;
168 :
169 0 : for( ulong i=0UL; i<peer_pool_max( resolver->pool ); i++ ) {
170 0 : void * _full_ssresolve = FD_SCRATCH_ALLOC_APPEND( l, fd_ssresolve_align(), fd_ssresolve_footprint() );
171 0 : void * _inc_ssresolve = FD_SCRATCH_ALLOC_APPEND( l, fd_ssresolve_align(), fd_ssresolve_footprint() );
172 0 : resolver->pool[ i ].full_ssresolve = fd_ssresolve_join( fd_ssresolve_new( _full_ssresolve ) );
173 0 : resolver->pool[ i ].inc_ssresolve = fd_ssresolve_join( fd_ssresolve_new( _inc_ssresolve ) );
174 0 : }
175 :
176 0 : resolver->incremental_snapshot_fetch = incremental_snapshot_fetch;
177 0 : resolver->cb_arg = cb_arg;
178 0 : resolver->on_resolve_cb = on_resolve_cb;
179 :
180 0 : #if FD_HAS_OPENSSL
181 0 : SSL_CTX * ssl_ctx = SSL_CTX_new( TLS_client_method() );
182 0 : if( FD_UNLIKELY( !ssl_ctx ) ) {
183 0 : FD_LOG_ERR(( "SSL_CTX_new failed" ));
184 0 : }
185 :
186 0 : if( FD_UNLIKELY( !SSL_CTX_set_min_proto_version( ssl_ctx, TLS1_3_VERSION ) ) ) {
187 0 : FD_LOG_ERR(( "SSL_CTX_set_min_proto_version(ssl_ctx,TLS1_3_VERSION) failed" ));
188 0 : }
189 :
190 : /* transfering ownership of ssl_ctx by assignment */
191 0 : resolver->ssl_ctx = ssl_ctx;
192 :
193 0 : fd_ossl_load_certs( resolver->ssl_ctx );
194 0 : #endif
195 :
196 0 : FD_COMPILER_MFENCE();
197 0 : FD_VOLATILE( resolver->magic ) = FD_HTTP_RESOLVER_MAGIC;
198 0 : FD_COMPILER_MFENCE();
199 :
200 0 : return (void *)resolver;
201 0 : }
202 :
203 : fd_http_resolver_t *
204 0 : fd_http_resolver_join( void * shresolver ) {
205 0 : if( FD_UNLIKELY( !shresolver ) ) {
206 0 : FD_LOG_WARNING(( "NULL shresolver" ));
207 0 : return NULL;
208 0 : }
209 :
210 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shresolver, fd_http_resolver_align() ) ) ) {
211 0 : FD_LOG_WARNING(( "misaligned shresolver" ));
212 0 : return NULL;
213 0 : }
214 :
215 0 : fd_http_resolver_t * resolver = (fd_http_resolver_t *)shresolver;
216 :
217 0 : if( FD_UNLIKELY( resolver->magic!=FD_HTTP_RESOLVER_MAGIC ) ) {
218 0 : FD_LOG_WARNING(( "bad magic" ));
219 0 : return NULL;
220 0 : }
221 :
222 0 : return resolver;
223 0 : }
224 :
225 : int
226 : fd_http_resolver_add( fd_http_resolver_t * resolver,
227 : fd_ip4_port_t addr,
228 : char const * hostname,
229 : int is_https,
230 0 : fd_sspeer_selector_t * selector ) {
231 0 : if( !peer_pool_free( resolver->pool ) ) {
232 0 : FD_LOG_WARNING(( "peer pool exhausted" ));
233 0 : return -1;
234 0 : }
235 0 : fd_ssresolve_peer_t * peer = peer_pool_ele_acquire( resolver->pool );
236 0 : memset( &peer->key.url, 0, sizeof(peer->key.url) );
237 0 : if( FD_LIKELY( hostname ) ) {
238 0 : strncpy( peer->key.url.hostname, hostname, sizeof(peer->key.url.hostname) - 1UL );
239 0 : peer->key.url.hostname[ sizeof(peer->key.url.hostname) - 1UL ] = '\0';
240 0 : } else {
241 0 : peer->key.url.hostname[ 0 ] = '\0';
242 0 : }
243 0 : peer->key.url.resolved_addr = addr;
244 0 : peer->key.is_url = 1;
245 0 : peer->state = PEER_STATE_UNRESOLVED;
246 0 : peer->addr = addr;
247 0 : peer->is_https = is_https;
248 0 : peer->fd.idx = ULONG_MAX;
249 0 : peer->full_slot = FD_SSPEER_SLOT_UNKNOWN;
250 0 : peer->incr_slot = FD_SSPEER_SLOT_UNKNOWN;
251 0 : fd_memset( peer->full_hash, 0, FD_HASH_FOOTPRINT );
252 0 : fd_memset( peer->incr_hash, 0, FD_HASH_FOOTPRINT );
253 :
254 : /* The peer needs to be added to the selector, in order to guarantee
255 : that any subsequent update on the selector is able to find it.
256 : At this time, latency, full/incr slot, as well as full/incr hash
257 : are unknown. */
258 0 : ulong score = fd_sspeer_selector_add( selector, &peer->key, addr, FD_SSPEER_LATENCY_UNKNOWN,
259 0 : FD_SSPEER_SLOT_UNKNOWN, FD_SSPEER_SLOT_UNKNOWN, NULL, NULL );
260 0 : if( FD_UNLIKELY( score==FD_SSPEER_SCORE_INVALID ) ) {
261 : /* If unable to add, then release the element back to the pool. */
262 0 : FD_LOG_WARNING(( "failed to add peer to selector (hostname \"%s\" addr=" FD_IP4_ADDR_FMT ":%hu score=%lu)",
263 0 : peer->key.url.hostname[ 0 ] ? peer->key.url.hostname : "(none)",
264 0 : FD_IP4_ADDR_FMT_ARGS( peer->addr.addr ), fd_ushort_bswap( peer->addr.port ), score ));
265 0 : peer_pool_ele_release( resolver->pool, peer );
266 0 : return -1;
267 0 : }
268 : /* Add to the unresolved list. */
269 0 : deadline_list_ele_push_tail( resolver->unresolved, peer, resolver->pool );
270 0 : return 0;
271 0 : }
272 :
273 : static int
274 : create_socket( fd_http_resolver_t * resolver,
275 0 : fd_ssresolve_peer_t * peer ) {
276 0 : int sockfd = socket( AF_INET, SOCK_STREAM|SOCK_NONBLOCK, 0 );
277 0 : if( FD_UNLIKELY( -1==sockfd ) ) FD_LOG_ERR(( "socket failed (%i-%s)", errno, strerror( errno ) ));
278 :
279 0 : int optval = 1;
280 0 : if( FD_UNLIKELY( -1==setsockopt( sockfd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(int) ) ) ) {
281 0 : FD_LOG_ERR(( "setsockopt() failed (%d-%s)", errno, fd_io_strerror( errno ) ));
282 0 : }
283 :
284 0 : struct sockaddr_in addr = {
285 0 : .sin_family = AF_INET,
286 0 : .sin_port = peer->addr.port,
287 0 : .sin_addr = { .s_addr = peer->addr.addr }
288 0 : };
289 :
290 0 : if( FD_UNLIKELY( -1==connect( sockfd, fd_type_pun( &addr ), sizeof(addr) ) && errno!=EINPROGRESS ) ) {
291 0 : if( FD_UNLIKELY( -1==close( sockfd ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
292 0 : return -1;
293 0 : }
294 :
295 0 : resolver->fds[ resolver->fds_len ] = (struct pollfd){
296 0 : .fd = sockfd,
297 0 : .events = POLLIN|POLLOUT,
298 0 : .revents = 0
299 0 : };
300 :
301 0 : return 0;
302 0 : }
303 :
304 : static int
305 : peer_connect( fd_http_resolver_t * resolver,
306 0 : fd_ssresolve_peer_t * peer ) {
307 0 : int err;
308 0 : err = create_socket( resolver, peer ); /* full */
309 0 : if( FD_UNLIKELY( err ) ) return err;
310 0 : resolver->fds_idx[ resolver->fds_len ] = peer_pool_idx( resolver->pool, peer );
311 0 : peer->fd.idx = resolver->fds_len;
312 0 : resolver->fds_len++;
313 :
314 0 : if( FD_UNLIKELY( peer->is_https ) ) {
315 0 : #if FD_HAS_OPENSSL
316 0 : fd_ssresolve_init_https( peer->full_ssresolve, peer->addr, resolver->fds[ peer->fd.idx ].fd, 1, peer->key.url.hostname, resolver->ssl_ctx );
317 : #else
318 : FD_LOG_ERR(( "peer %s requires https but firedancer is built without openssl support. Please remove this peer from your validator config.", peer->key.url.hostname ));
319 : #endif
320 0 : } else {
321 0 : fd_ssresolve_init( peer->full_ssresolve, peer->addr, resolver->fds[ peer->fd.idx ].fd, 1, peer->key.url.hostname );
322 0 : }
323 :
324 0 : if( FD_LIKELY( resolver->incremental_snapshot_fetch ) ) {
325 0 : err = create_socket( resolver, peer ); /* incremental */
326 0 : if( FD_UNLIKELY( err ) ) return err;
327 0 : resolver->fds_idx[ resolver->fds_len ] = peer_pool_idx( resolver->pool, peer );
328 0 : resolver->fds_len++;
329 0 : if( FD_UNLIKELY( peer->is_https ) ) {
330 0 : #if FD_HAS_OPENSSL
331 0 : fd_ssresolve_init_https( peer->inc_ssresolve, peer->addr, resolver->fds[ peer->fd.idx+1UL ].fd, 0, peer->key.url.hostname, resolver->ssl_ctx );
332 : #else
333 : FD_LOG_ERR(( "peer requires https but firedancer is built without openssl support" ));
334 : #endif
335 0 : } else {
336 0 : fd_ssresolve_init( peer->inc_ssresolve, peer->addr, resolver->fds[ peer->fd.idx+1UL ].fd, 0, peer->key.url.hostname );
337 0 : }
338 0 : } else {
339 0 : resolver->fds[ resolver->fds_len ] = (struct pollfd) {
340 0 : .fd = -1,
341 0 : .events = 0,
342 0 : .revents = 0
343 0 : };
344 0 : resolver->fds_idx[ resolver->fds_len ] = ULONG_MAX;
345 0 : resolver->fds_len++;
346 0 : }
347 :
348 0 : return 0;
349 0 : }
350 :
351 : static inline void
352 : remove_peer( fd_http_resolver_t * resolver,
353 0 : ulong idx ) {
354 0 : FD_TEST( idx<resolver->fds_len );
355 :
356 0 : fd_ssresolve_peer_t * cur_peer = peer_pool_ele( resolver->pool, resolver->fds_idx[ idx ] );
357 0 : fd_ssresolve_cancel( cur_peer->full_ssresolve );
358 0 : fd_ssresolve_cancel( cur_peer->inc_ssresolve );
359 :
360 0 : if( FD_UNLIKELY( resolver->fds_len==2UL ) ) {
361 0 : resolver->fds_len = 0UL;
362 0 : return;
363 0 : }
364 :
365 0 : resolver->fds[ idx ] = resolver->fds[ resolver->fds_len-2UL ];
366 0 : resolver->fds_idx[ idx ] = resolver->fds_idx[ resolver->fds_len-2UL ];
367 :
368 0 : resolver->fds[ idx+1UL ] = resolver->fds[ resolver->fds_len-1UL ];
369 0 : resolver->fds_idx[ idx+1UL ] = resolver->fds_idx[ resolver->fds_len-1UL ];
370 :
371 0 : fd_ssresolve_peer_t * peer = peer_pool_ele( resolver->pool, resolver->fds_idx[ idx ] );
372 0 : peer->fd.idx = idx;
373 :
374 0 : resolver->fds_len -= 2UL;
375 0 : }
376 :
377 : static inline void
378 : unresolve_peer( fd_http_resolver_t * resolver,
379 : fd_ssresolve_peer_t * peer,
380 0 : long now ) {
381 0 : FD_TEST( peer->state==PEER_STATE_UNRESOLVED || peer->state==PEER_STATE_REFRESHING );
382 0 : remove_peer( resolver, peer->fd.idx );
383 0 : deadline_list_ele_remove( resolver->resolving, peer, resolver->pool );
384 0 : peer->state = PEER_STATE_INVALID;
385 0 : peer->deadline_nanos = now + PEER_DEADLINE_NANOS_INVALID;
386 0 : deadline_list_ele_push_tail( resolver->invalid, peer, resolver->pool );
387 0 : }
388 :
389 : static inline int
390 : poll_resolve( fd_http_resolver_t * resolver,
391 : struct pollfd * pfd,
392 : fd_ssresolve_peer_t * peer,
393 : fd_ssresolve_t * ssresolve,
394 : ulong idx,
395 0 : long now ) {
396 0 : FD_TEST( !fd_ssresolve_is_done( ssresolve ) );
397 0 : if( FD_LIKELY( pfd->revents & POLLOUT ) ) {
398 0 : int res = fd_ssresolve_advance_poll_out( ssresolve );
399 :
400 0 : if( FD_UNLIKELY( res==FD_SSRESOLVE_ADVANCE_ERROR ) ) {
401 0 : unresolve_peer( resolver, peer_pool_ele( resolver->pool, resolver->fds_idx[ idx ] ), now );
402 0 : return -1;
403 0 : }
404 0 : }
405 :
406 0 : if( FD_LIKELY( pfd->revents & POLLIN ) ) {
407 0 : fd_ssresolve_result_t resolve_result;
408 0 : int res = fd_ssresolve_advance_poll_in( ssresolve, &resolve_result );
409 :
410 0 : if( FD_UNLIKELY( res==FD_SSRESOLVE_ADVANCE_ERROR ) ) {
411 0 : unresolve_peer( resolver, peer_pool_ele( resolver->pool, resolver->fds_idx[ idx ] ), now );
412 0 : return -1;
413 0 : } else if( FD_UNLIKELY( res==FD_SSRESOLVE_ADVANCE_AGAIN ) ) {
414 0 : return -1;
415 0 : } else if( FD_LIKELY( res==FD_SSRESOLVE_ADVANCE_RESULT ) ) {
416 0 : FD_TEST( peer->deadline_nanos>now );
417 :
418 0 : if( resolve_result.base_slot==ULONG_MAX ) {
419 0 : peer->full_slot = resolve_result.slot;
420 0 : fd_memcpy( peer->full_hash, resolve_result.hash, FD_HASH_FOOTPRINT );
421 0 : } else {
422 0 : peer->full_slot = resolve_result.base_slot;
423 0 : peer->incr_slot = resolve_result.slot;
424 0 : fd_memcpy( peer->incr_hash, resolve_result.hash, FD_HASH_FOOTPRINT );
425 0 : }
426 0 : }
427 0 : }
428 :
429 0 : return 0;
430 0 : }
431 :
432 : static inline void
433 : poll_advance( fd_http_resolver_t * resolver,
434 0 : long now ) {
435 0 : if( FD_LIKELY( !resolver->fds_len ) ) return;
436 :
437 0 : int nfds = fd_syscall_poll( resolver->fds, (uint)resolver->fds_len, 0 );
438 0 : if( FD_LIKELY( !nfds ) ) return;
439 0 : else if( FD_UNLIKELY( -1==nfds && errno==EINTR ) ) return;
440 0 : else if( FD_UNLIKELY( -1==nfds ) ) FD_LOG_ERR(( "poll failed (%i-%s)", errno, strerror( errno ) ));
441 :
442 0 : for( ulong i=0UL; i<resolver->fds_len; i++) {
443 :
444 0 : struct pollfd * pfd = &resolver->fds[ i ];
445 0 : if( FD_UNLIKELY( pfd->fd==-1 ) ) continue;
446 :
447 0 : fd_ssresolve_peer_t * peer = peer_pool_ele( resolver->pool, resolver->fds_idx[ i ] );
448 0 : int full = i&1UL ? 0 : 1; /* even indices are full, odd indices are incremental */
449 0 : fd_ssresolve_t * ssresolve = full ? peer->full_ssresolve : peer->inc_ssresolve;
450 :
451 : /* Process pending I/O before checking for errors. POLLIN can
452 : coexist with POLLHUP when the server sends a response and then
453 : closes the connection (common for HTTP HEAD redirects). */
454 0 : if( FD_LIKELY( !fd_ssresolve_is_done( ssresolve ) ) ) {
455 0 : int res = poll_resolve( resolver, pfd, peer, ssresolve, i, now );
456 0 : if( FD_UNLIKELY( res ) ) continue;
457 0 : }
458 :
459 : /* Only react to POLLERR/POLLHUP if the ssresolve hasn't completed
460 : yet. After a redirect is parsed the server often closes the
461 : connection, which is harmless. */
462 0 : if( FD_UNLIKELY( (pfd->revents & (POLLERR|POLLHUP)) && !fd_ssresolve_is_done( ssresolve ) ) ) {
463 0 : unresolve_peer( resolver, peer_pool_ele( resolver->pool, resolver->fds_idx[ i ] ), now );
464 0 : continue;
465 0 : }
466 :
467 : /* Once both the full and incremental snapshots are resolved, we can
468 : mark the peer valid and remove the peer from the list of peers to
469 : ping. */
470 0 : if( FD_LIKELY( fd_ssresolve_is_done( peer->full_ssresolve ) &&
471 0 : (!resolver->incremental_snapshot_fetch || fd_ssresolve_is_done( peer->inc_ssresolve ) ) ) ) {
472 0 : peer->state = PEER_STATE_VALID;
473 0 : peer->deadline_nanos = now + PEER_DEADLINE_NANOS_VALID;
474 :
475 0 : deadline_list_ele_remove( resolver->resolving, peer, resolver->pool );
476 0 : deadline_list_ele_push_tail( resolver->valid, peer, resolver->pool );
477 0 : remove_peer( resolver, peer->fd.idx );
478 :
479 0 : resolver->on_resolve_cb( resolver->cb_arg, &peer->key, peer->full_slot, peer->incr_slot, peer->full_hash,
480 0 : peer->incr_slot!=FD_SSPEER_SLOT_UNKNOWN ? peer->incr_hash : NULL );
481 0 : }
482 0 : }
483 0 : }
484 :
485 : void
486 : fd_http_resolver_advance( fd_http_resolver_t * resolver,
487 : long now,
488 0 : fd_sspeer_selector_t * selector ) {
489 0 : while( !deadline_list_is_empty( resolver->unresolved, resolver->pool ) ) {
490 0 : fd_ssresolve_peer_t * peer = deadline_list_ele_pop_head( resolver->unresolved, resolver->pool );
491 :
492 0 : FD_LOG_INFO(( "resolving " FD_IP4_ADDR_FMT ":%hu", FD_IP4_ADDR_FMT_ARGS( peer->addr.addr ), fd_ushort_bswap( peer->addr.port ) ));
493 : /* Clear stale snapshot data so the new resolve cycle starts clean.
494 : Without this, a previously-valid peer could carry stale
495 : incr_slot/incr_hash through the invalid->unresolved cycle. */
496 0 : clear_peer_snapshot_data( peer );
497 : /* Re-add the peer to the selector with unknown data. The peer may
498 : have been removed from the selector during a previous timeout or
499 : failed re-resolve. The add call may fail if the selector is
500 : full, which is fine — the peer will still attempt to resolve and
501 : the next cycle will try again. */
502 0 : fd_sspeer_selector_add( selector, &peer->key, peer->addr, FD_SSPEER_LATENCY_UNKNOWN,
503 0 : FD_SSPEER_SLOT_UNKNOWN, FD_SSPEER_SLOT_UNKNOWN, NULL, NULL );
504 0 : int result = peer_connect( resolver, peer );
505 0 : if( FD_UNLIKELY( -1==result ) ) {
506 0 : peer->state = PEER_STATE_INVALID;
507 0 : peer->deadline_nanos = now + PEER_DEADLINE_NANOS_INVALID;
508 0 : deadline_list_ele_push_tail( resolver->invalid, peer, resolver->pool );
509 0 : } else {
510 0 : peer->state = PEER_STATE_REFRESHING;
511 0 : peer->deadline_nanos = now + PEER_DEADLINE_NANOS_RESOLVE;
512 0 : deadline_list_ele_push_tail( resolver->resolving, peer, resolver->pool );
513 0 : }
514 0 : }
515 :
516 0 : while( !deadline_list_is_empty( resolver->resolving, resolver->pool ) ) {
517 0 : fd_ssresolve_peer_t * peer = deadline_list_ele_peek_head( resolver->resolving, resolver->pool );
518 0 : if( FD_LIKELY( peer->deadline_nanos>now ) ) break;
519 :
520 0 : deadline_list_ele_pop_head( resolver->resolving, resolver->pool );
521 0 : peer->state = PEER_STATE_INVALID;
522 0 : peer->deadline_nanos = now + PEER_DEADLINE_NANOS_INVALID;
523 0 : deadline_list_ele_push_tail( resolver->invalid, peer, resolver->pool );
524 0 : remove_peer( resolver, peer->fd.idx );
525 :
526 0 : fd_sspeer_selector_remove( selector, &peer->key );
527 0 : }
528 :
529 0 : while( !deadline_list_is_empty( resolver->invalid, resolver->pool ) ) {
530 0 : fd_ssresolve_peer_t * peer = deadline_list_ele_peek_head( resolver->invalid, resolver->pool );
531 0 : if( FD_LIKELY( peer->deadline_nanos>now ) ) break;
532 :
533 0 : deadline_list_ele_pop_head( resolver->invalid, resolver->pool );
534 :
535 0 : peer->state = PEER_STATE_UNRESOLVED;
536 0 : peer->deadline_nanos = 0L;
537 0 : deadline_list_ele_push_tail( resolver->unresolved, peer, resolver->pool );
538 0 : }
539 :
540 0 : while( !deadline_list_is_empty( resolver->valid, resolver->pool ) ) {
541 0 : fd_ssresolve_peer_t * peer = deadline_list_ele_peek_head( resolver->valid, resolver->pool );
542 0 : if( FD_LIKELY( peer->deadline_nanos>now ) ) break;
543 :
544 0 : deadline_list_ele_pop_head( resolver->valid, resolver->pool );
545 :
546 : /* Clear stale snapshot data before re-resolving so the peer
547 : does not carry data from the previous resolve cycle. */
548 0 : clear_peer_snapshot_data( peer );
549 0 : int result = peer_connect( resolver, peer );
550 0 : if( FD_UNLIKELY( -1==result ) ) {
551 0 : peer->state = PEER_STATE_INVALID;
552 0 : peer->deadline_nanos = now + PEER_DEADLINE_NANOS_INVALID;
553 0 : deadline_list_ele_push_tail( resolver->invalid, peer, resolver->pool );
554 0 : fd_sspeer_selector_remove( selector, &peer->key );
555 0 : } else {
556 0 : peer->state = PEER_STATE_REFRESHING;
557 0 : peer->deadline_nanos = now + PEER_DEADLINE_NANOS_RESOLVE;
558 0 : deadline_list_ele_push_tail( resolver->resolving, peer, resolver->pool );
559 0 : }
560 0 : }
561 :
562 0 : poll_advance( resolver, now );
563 0 : }
|