Clean up SOCK_NONBLOCK
[awesomized/libmemcached] / libmemcached / connect.cc
1 /* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
2 *
3 * Libmemcached library
4 *
5 * Copyright (C) 2011 Data Differential, http://datadifferential.com/
6 * Copyright (C) 2006-2010 Brian Aker All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
10 * met:
11 *
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
15 * * Redistributions in binary form must reproduce the above
16 * copyright notice, this list of conditions and the following disclaimer
17 * in the documentation and/or other materials provided with the
18 * distribution.
19 *
20 * * The names of its contributors may not be used to endorse or
21 * promote products derived from this software without specific prior
22 * written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 *
36 */
37
38
39 #include <libmemcached/common.h>
40
41 #include <cassert>
42 #include <ctime>
43 #include <sys/time.h>
44
45 #ifndef SOCK_CLOEXEC
46 # define SOCK_CLOEXEC 0
47 #endif
48
49 #ifndef SOCK_NONBLOCK
50 # define SOCK_NONBLOCK 0
51 #endif
52
53 #ifndef FD_CLOEXEC
54 # define FD_CLOEXEC 0
55 #endif
56
57 static memcached_return_t connect_poll(org::libmemcached::Instance* server)
58 {
59 struct pollfd fds[1];
60 fds[0].fd= server->fd;
61 fds[0].events= POLLOUT;
62
63 size_t loop_max= 5;
64
65 if (server->root->poll_timeout == 0)
66 {
67 return memcached_set_error(*server, MEMCACHED_TIMEOUT, MEMCACHED_AT);
68 }
69
70 while (--loop_max) // Should only loop on cases of ERESTART or EINTR
71 {
72 int number_of;
73 if ((number_of= poll(fds, 1, server->root->connect_timeout)) <= 0)
74 {
75 if (number_of == -1)
76 {
77 int local_errno= get_socket_errno(); // We cache in case closesocket() modifies errno
78 switch (local_errno)
79 {
80 #ifdef TARGET_OS_LINUX
81 case ERESTART:
82 #endif
83 case EINTR:
84 continue;
85
86 case EFAULT:
87 case ENOMEM:
88 return memcached_set_error(*server, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT);
89
90 case EINVAL:
91 return memcached_set_error(*server, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT, memcached_literal_param("RLIMIT_NOFILE exceeded, or if OSX the timeout value was invalid"));
92
93 default: // This should not happen
94 if (fds[0].revents & POLLERR)
95 {
96 int err;
97 socklen_t len= sizeof(err);
98 if (getsockopt(server->fd, SOL_SOCKET, SO_ERROR, &err, &len) == 0)
99 {
100 if (err == 0)
101 {
102 // This should never happen, if it does? Punt.
103 continue;
104 }
105 local_errno= err;
106 }
107 }
108
109 assert_msg(server->fd != INVALID_SOCKET, "poll() was passed an invalid file descriptor");
110 (void)closesocket(server->fd);
111 server->fd= INVALID_SOCKET;
112 server->state= MEMCACHED_SERVER_STATE_NEW;
113
114 return memcached_set_errno(*server, local_errno, MEMCACHED_AT);
115 }
116 }
117 assert(number_of == 0);
118
119 server->io_wait_count.timeouts++;
120 return memcached_set_error(*server, MEMCACHED_TIMEOUT, MEMCACHED_AT);
121 }
122
123 if (fds[0].revents & POLLERR or
124 fds[0].revents & POLLHUP or
125 fds[0].revents & POLLNVAL)
126 {
127 int err;
128 socklen_t len= sizeof (err);
129 if (getsockopt(fds[0].fd, SOL_SOCKET, SO_ERROR, &err, &len) == 0)
130 {
131 // We check the value to see what happened wth the socket.
132 if (err == 0)
133 {
134 return MEMCACHED_SUCCESS;
135 }
136 errno= err;
137 }
138
139 return memcached_set_errno(*server, err, MEMCACHED_AT);
140 }
141 assert(fds[0].revents & POLLIN or fds[0].revents & POLLOUT);
142
143 return MEMCACHED_SUCCESS;
144 }
145
146 // This should only be possible from ERESTART or EINTR;
147 return memcached_set_errno(*server, get_socket_errno(), MEMCACHED_AT);
148 }
149
150 static memcached_return_t set_hostinfo(org::libmemcached::Instance* server)
151 {
152 assert(server->type != MEMCACHED_CONNECTION_UNIX_SOCKET);
153 if (server->address_info)
154 {
155 freeaddrinfo(server->address_info);
156 server->address_info= NULL;
157 server->address_info_next= NULL;
158 }
159
160 char str_port[NI_MAXSERV];
161 int length= snprintf(str_port, NI_MAXSERV, "%u", uint32_t(server->port()));
162 if (length >= NI_MAXSERV or length <= 0)
163 {
164 return MEMCACHED_FAILURE;
165 }
166
167 struct addrinfo hints;
168 memset(&hints, 0, sizeof(struct addrinfo));
169
170 #if 0
171 hints.ai_family= AF_INET;
172 #endif
173 if (memcached_is_udp(server->root))
174 {
175 hints.ai_protocol= IPPROTO_UDP;
176 hints.ai_socktype= SOCK_DGRAM;
177 }
178 else
179 {
180 hints.ai_socktype= SOCK_STREAM;
181 hints.ai_protocol= IPPROTO_TCP;
182 }
183
184 assert(server->address_info == NULL);
185 assert(server->address_info_next == NULL);
186 int errcode;
187 switch(errcode= getaddrinfo(server->hostname, str_port, &hints, &server->address_info))
188 {
189 case 0:
190 break;
191
192 case EAI_AGAIN:
193 if (server->address_info)
194 {
195 freeaddrinfo(server->address_info);
196 server->address_info= NULL;
197 server->address_info_next= NULL;
198 }
199 return memcached_set_error(*server, MEMCACHED_TIMEOUT, MEMCACHED_AT, memcached_string_make_from_cstr(gai_strerror(errcode)));
200
201 case EAI_SYSTEM:
202 if (server->address_info)
203 {
204 freeaddrinfo(server->address_info);
205 server->address_info= NULL;
206 server->address_info_next= NULL;
207 }
208 return memcached_set_errno(*server, errno, MEMCACHED_AT, memcached_literal_param("getaddrinfo(EAI_SYSTEM)"));
209
210 case EAI_BADFLAGS:
211 if (server->address_info)
212 {
213 freeaddrinfo(server->address_info);
214 server->address_info= NULL;
215 server->address_info_next= NULL;
216 }
217 return memcached_set_error(*server, MEMCACHED_INVALID_ARGUMENTS, MEMCACHED_AT, memcached_literal_param("getaddrinfo(EAI_BADFLAGS)"));
218
219 case EAI_MEMORY:
220 if (server->address_info)
221 {
222 freeaddrinfo(server->address_info);
223 server->address_info= NULL;
224 server->address_info_next= NULL;
225 }
226 return memcached_set_error(*server, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT, memcached_literal_param("getaddrinfo(EAI_MEMORY)"));
227
228 default:
229 {
230 if (server->address_info)
231 {
232 freeaddrinfo(server->address_info);
233 server->address_info= NULL;
234 server->address_info_next= NULL;
235 }
236 return memcached_set_error(*server, MEMCACHED_HOST_LOOKUP_FAILURE, MEMCACHED_AT, memcached_string_make_from_cstr(gai_strerror(errcode)));
237 }
238 }
239 server->address_info_next= server->address_info;
240 server->state= MEMCACHED_SERVER_STATE_ADDRINFO;
241
242 return MEMCACHED_SUCCESS;
243 }
244
245 static inline void set_socket_nonblocking(org::libmemcached::Instance* server)
246 {
247 #ifdef WIN32
248 u_long arg= 1;
249 if (ioctlsocket(server->fd, FIONBIO, &arg) == SOCKET_ERROR)
250 {
251 memcached_set_errno(*server, get_socket_errno(), NULL);
252 }
253 #else
254 int flags;
255
256 if (SOCK_NONBLOCK == 0)
257 {
258 do
259 {
260 flags= fcntl(server->fd, F_GETFL, 0);
261 } while (flags == -1 && (errno == EINTR || errno == EAGAIN));
262
263 if (flags == -1)
264 {
265 memcached_set_errno(*server, errno, NULL);
266 }
267 else if ((flags & O_NONBLOCK) == 0)
268 {
269 int rval;
270
271 do
272 {
273 rval= fcntl(server->fd, F_SETFL, flags | O_NONBLOCK);
274 } while (rval == -1 && (errno == EINTR or errno == EAGAIN));
275
276 if (rval == -1)
277 {
278 memcached_set_errno(*server, errno, NULL);
279 }
280 }
281 }
282 #endif
283 }
284
285 static void set_socket_options(org::libmemcached::Instance* server)
286 {
287 assert_msg(server->fd != INVALID_SOCKET, "invalid socket was passed to set_socket_options()");
288
289 if (memcached_is_udp(server->root))
290 {
291 return;
292 }
293
294 #ifdef HAVE_SNDTIMEO
295 if (server->root->snd_timeout > 0)
296 {
297 struct timeval waittime;
298
299 waittime.tv_sec= server->root->snd_timeout / 1000000;
300 waittime.tv_usec= server->root->snd_timeout % 1000000;
301
302 int error= setsockopt(server->fd, SOL_SOCKET, SO_SNDTIMEO,
303 &waittime, (socklen_t)sizeof(struct timeval));
304 (void)error;
305 assert(error == 0);
306 }
307 #endif
308
309 #ifdef HAVE_RCVTIMEO
310 if (server->root->rcv_timeout > 0)
311 {
312 struct timeval waittime;
313
314 waittime.tv_sec= server->root->rcv_timeout / 1000000;
315 waittime.tv_usec= server->root->rcv_timeout % 1000000;
316
317 int error= setsockopt(server->fd, SOL_SOCKET, SO_RCVTIMEO,
318 &waittime, (socklen_t)sizeof(struct timeval));
319 (void)(error);
320 assert(error == 0);
321 }
322 #endif
323
324
325 #if defined(__MACH__) && defined(__APPLE__) || defined(__FreeBSD__)
326 {
327 int set= 1;
328 int error= setsockopt(server->fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int));
329
330 assert(error == 0);
331
332 // This is not considered a fatal error
333 if (error == -1)
334 {
335 #if 0
336 perror("setsockopt(SO_NOSIGPIPE)");
337 #endif
338 }
339 }
340 #endif
341
342 if (server->root->flags.no_block)
343 {
344 struct linger linger;
345
346 linger.l_onoff= 1;
347 linger.l_linger= 0; /* By default on close() just drop the socket */
348 int error= setsockopt(server->fd, SOL_SOCKET, SO_LINGER,
349 &linger, (socklen_t)sizeof(struct linger));
350 (void)(error);
351 assert(error == 0);
352 }
353
354 if (server->root->flags.tcp_nodelay)
355 {
356 int flag= 1;
357
358 int error= setsockopt(server->fd, IPPROTO_TCP, TCP_NODELAY,
359 &flag, (socklen_t)sizeof(int));
360 (void)(error);
361 assert(error == 0);
362 }
363
364 if (server->root->flags.tcp_keepalive)
365 {
366 int flag= 1;
367
368 int error= setsockopt(server->fd, SOL_SOCKET, SO_KEEPALIVE,
369 &flag, (socklen_t)sizeof(int));
370 (void)(error);
371 assert(error == 0);
372 }
373
374 #ifdef TCP_KEEPIDLE
375 if (server->root->tcp_keepidle > 0)
376 {
377 int error= setsockopt(server->fd, IPPROTO_TCP, TCP_KEEPIDLE,
378 &server->root->tcp_keepidle, (socklen_t)sizeof(int));
379 (void)(error);
380 assert(error == 0);
381 }
382 #endif
383
384 if (server->root->send_size > 0)
385 {
386 int error= setsockopt(server->fd, SOL_SOCKET, SO_SNDBUF,
387 &server->root->send_size, (socklen_t)sizeof(int));
388 (void)(error);
389 assert(error == 0);
390 }
391
392 if (server->root->recv_size > 0)
393 {
394 int error= setsockopt(server->fd, SOL_SOCKET, SO_RCVBUF,
395 &server->root->recv_size, (socklen_t)sizeof(int));
396 (void)(error);
397 assert(error == 0);
398 }
399
400 /* libmemcached will always use nonblocking IO to avoid write deadlocks */
401 set_socket_nonblocking(server);
402 }
403
404 static memcached_return_t unix_socket_connect(org::libmemcached::Instance* server)
405 {
406 #ifndef WIN32
407 WATCHPOINT_ASSERT(server->fd == INVALID_SOCKET);
408
409 int type= SOCK_STREAM;
410 if (SOCK_CLOEXEC)
411 {
412 type|= SOCK_CLOEXEC;
413 }
414
415 if (SOCK_NONBLOCK)
416 {
417 type|= SOCK_NONBLOCK;
418 }
419
420 if ((server->fd= socket(AF_UNIX, type, 0)) < 0)
421 {
422 memcached_set_errno(*server, errno, NULL);
423 return MEMCACHED_CONNECTION_FAILURE;
424 }
425
426 struct sockaddr_un servAddr;
427
428 memset(&servAddr, 0, sizeof (struct sockaddr_un));
429 servAddr.sun_family= AF_UNIX;
430 strncpy(servAddr.sun_path, server->hostname, sizeof(servAddr.sun_path)); /* Copy filename */
431
432 do {
433 if (connect(server->fd, (struct sockaddr *)&servAddr, sizeof(servAddr)) < 0)
434 {
435 switch (errno)
436 {
437 case EINPROGRESS:
438 case EALREADY:
439 case EINTR:
440 continue;
441
442 case EISCONN: /* We were spinning waiting on connect */
443 {
444 assert(0); // Programmer error
445 break;
446 }
447
448 default:
449 WATCHPOINT_ERRNO(errno);
450 memcached_set_errno(*server, errno, MEMCACHED_AT);
451 return MEMCACHED_CONNECTION_FAILURE;
452 }
453 }
454 } while (0);
455 server->state= MEMCACHED_SERVER_STATE_CONNECTED;
456
457 WATCHPOINT_ASSERT(server->fd != INVALID_SOCKET);
458
459 return MEMCACHED_SUCCESS;
460 #else
461 (void)server;
462 return MEMCACHED_NOT_SUPPORTED;
463 #endif
464 }
465
466 static memcached_return_t network_connect(org::libmemcached::Instance* server)
467 {
468 bool timeout_error_occured= false;
469
470 WATCHPOINT_ASSERT(server->fd == INVALID_SOCKET);
471 WATCHPOINT_ASSERT(server->cursor_active_ == 0);
472
473 /*
474 We want to check both of these because if address_info_next has been fully tried, we want to do a new lookup to make sure we have picked up on any new DNS information.
475 */
476 if (server->address_info == NULL or server->address_info_next == NULL)
477 {
478 WATCHPOINT_ASSERT(server->state == MEMCACHED_SERVER_STATE_NEW);
479 server->address_info_next= NULL;
480 memcached_return_t rc= set_hostinfo(server);
481
482 if (memcached_failed(rc))
483 {
484 return rc;
485 }
486 }
487
488 if (server->address_info_next == NULL)
489 {
490 server->address_info_next= server->address_info;
491 server->state= MEMCACHED_SERVER_STATE_ADDRINFO;
492 }
493
494 /* Create the socket */
495 while (server->address_info_next and server->fd == INVALID_SOCKET)
496 {
497 /* Memcache server does not support IPV6 in udp mode, so skip if not ipv4 */
498 if (memcached_is_udp(server->root) and server->address_info_next->ai_family != AF_INET)
499 {
500 server->address_info_next= server->address_info_next->ai_next;
501 continue;
502 }
503
504 int type= server->address_info_next->ai_socktype;
505 if (SOCK_CLOEXEC)
506 {
507 type|= SOCK_CLOEXEC;
508 }
509
510 if (SOCK_NONBLOCK)
511 {
512 type|= SOCK_NONBLOCK;
513 }
514
515 if ((server->fd= socket(server->address_info_next->ai_family,
516 type,
517 server->address_info_next->ai_protocol)) < 0)
518 {
519 return memcached_set_errno(*server, get_socket_errno(), NULL);
520 }
521
522 // If SOCK_CLOEXEC exists then we don't need to call the following
523 if (SOCK_CLOEXEC == 0)
524 {
525 if (FD_CLOEXEC)
526 {
527 int rval;
528 do
529 {
530 rval= fcntl (server->fd, F_SETFD, FD_CLOEXEC);
531 } while (rval == -1 && (errno == EINTR or errno == EAGAIN));
532 }
533 }
534
535 set_socket_options(server);
536
537 /* connect to server */
538 if ((connect(server->fd, server->address_info_next->ai_addr, server->address_info_next->ai_addrlen) != SOCKET_ERROR))
539 {
540 server->state= MEMCACHED_SERVER_STATE_CONNECTED;
541 return MEMCACHED_SUCCESS;
542 }
543
544 /* An error occurred */
545 switch (get_socket_errno())
546 {
547 case ETIMEDOUT:
548 timeout_error_occured= true;
549 break;
550
551 case EAGAIN:
552 #if EWOULDBLOCK != EAGAIN
553 case EWOULDBLOCK:
554 #endif
555 case EINPROGRESS: // nonblocking mode - first return
556 case EALREADY: // nonblocking mode - subsequent returns
557 {
558 server->state= MEMCACHED_SERVER_STATE_IN_PROGRESS;
559 memcached_return_t rc= connect_poll(server);
560
561 if (memcached_success(rc))
562 {
563 server->state= MEMCACHED_SERVER_STATE_CONNECTED;
564 return MEMCACHED_SUCCESS;
565 }
566
567 // A timeout here is treated as an error, we will not retry
568 if (rc == MEMCACHED_TIMEOUT)
569 {
570 timeout_error_occured= true;
571 }
572 }
573 break;
574
575 case EISCONN: // we are connected :-)
576 WATCHPOINT_ASSERT(0); // This is a programmer's error
577 break;
578
579 case EINTR: // Special case, we retry ai_addr
580 WATCHPOINT_ASSERT(server->fd != INVALID_SOCKET);
581 (void)closesocket(server->fd);
582 server->fd= INVALID_SOCKET;
583 continue;
584
585 default:
586 break;
587 }
588
589 WATCHPOINT_ASSERT(server->fd != INVALID_SOCKET);
590 (void)closesocket(server->fd);
591 server->fd= INVALID_SOCKET;
592 server->address_info_next= server->address_info_next->ai_next;
593 }
594
595 WATCHPOINT_ASSERT(server->fd == INVALID_SOCKET);
596
597 if (timeout_error_occured)
598 {
599 if (server->fd != INVALID_SOCKET)
600 {
601 (void)closesocket(server->fd);
602 server->fd= INVALID_SOCKET;
603 }
604 }
605
606 WATCHPOINT_STRING("Never got a good file descriptor");
607
608 if (memcached_has_current_error(*server))
609 {
610 return memcached_instance_error_return(server);
611 }
612
613 if (timeout_error_occured and server->state < MEMCACHED_SERVER_STATE_IN_PROGRESS)
614 {
615 return memcached_set_error(*server, MEMCACHED_TIMEOUT, MEMCACHED_AT);
616 }
617
618 return memcached_set_error(*server, MEMCACHED_CONNECTION_FAILURE, MEMCACHED_AT); /* The last error should be from connect() */
619 }
620
621
622 /*
623 backoff_handling()
624
625 Based on time/failure count fail the connect without trying. This prevents waiting in a state where
626 we get caught spending cycles just waiting.
627 */
628 static memcached_return_t backoff_handling(org::libmemcached::Instance* server, bool& in_timeout)
629 {
630 struct timeval curr_time;
631 bool _gettime_success= (gettimeofday(&curr_time, NULL) == 0);
632
633 /*
634 If we hit server_failure_limit then something is completely wrong about the server.
635
636 1) If autoeject is enabled we do that.
637 2) If not? We go into timeout again, there is much else to do :(
638 */
639 if (server->server_failure_counter >= server->root->server_failure_limit)
640 {
641 /*
642 We just auto_eject if we hit this point
643 */
644 if (_is_auto_eject_host(server->root))
645 {
646 set_last_disconnected_host(server);
647
648 // Retry dead servers if requested
649 if (_gettime_success and server->root->dead_timeout > 0)
650 {
651 server->next_retry= curr_time.tv_sec +server->root->dead_timeout;
652
653 // We only retry dead servers once before assuming failure again
654 server->server_failure_counter= server->root->server_failure_limit -1;
655 }
656
657 memcached_return_t rc;
658 if (memcached_failed(rc= run_distribution((memcached_st *)server->root)))
659 {
660 return memcached_set_error(*server, rc, MEMCACHED_AT, memcached_literal_param("Backoff handling failed during run_distribution"));
661 }
662
663 return memcached_set_error(*server, MEMCACHED_SERVER_MARKED_DEAD, MEMCACHED_AT);
664 }
665
666 server->state= MEMCACHED_SERVER_STATE_IN_TIMEOUT;
667
668 // Sanity check/setting
669 if (server->next_retry == 0)
670 {
671 server->next_retry= 1;
672 }
673 }
674
675 if (server->state == MEMCACHED_SERVER_STATE_IN_TIMEOUT)
676 {
677 /*
678 If next_retry is less then our current time, then we reset and try everything again.
679 */
680 if (_gettime_success and server->next_retry < curr_time.tv_sec)
681 {
682 server->state= MEMCACHED_SERVER_STATE_NEW;
683 }
684 else
685 {
686 return memcached_set_error(*server, MEMCACHED_SERVER_TEMPORARILY_DISABLED, MEMCACHED_AT);
687 }
688
689 in_timeout= true;
690 }
691
692 return MEMCACHED_SUCCESS;
693 }
694
695 static memcached_return_t _memcached_connect(org::libmemcached::Instance* server, const bool set_last_disconnected)
696 {
697 assert(server);
698 if (server->fd != INVALID_SOCKET)
699 {
700 return MEMCACHED_SUCCESS;
701 }
702
703 LIBMEMCACHED_MEMCACHED_CONNECT_START();
704
705 bool in_timeout= false;
706 memcached_return_t rc;
707 if (memcached_failed(rc= backoff_handling(server, in_timeout)))
708 {
709 set_last_disconnected_host(server);
710 return rc;
711 }
712
713 if (LIBMEMCACHED_WITH_SASL_SUPPORT and server->root->sasl.callbacks and memcached_is_udp(server->root))
714 {
715 return memcached_set_error(*server, MEMCACHED_INVALID_HOST_PROTOCOL, MEMCACHED_AT, memcached_literal_param("SASL is not supported for UDP connections"));
716 }
717
718 if (server->hostname[0] == '/')
719 {
720 server->type= MEMCACHED_CONNECTION_UNIX_SOCKET;
721 }
722
723 /* We need to clean up the multi startup piece */
724 switch (server->type)
725 {
726 case MEMCACHED_CONNECTION_UDP:
727 case MEMCACHED_CONNECTION_TCP:
728 rc= network_connect(server);
729
730 if (LIBMEMCACHED_WITH_SASL_SUPPORT)
731 {
732 if (server->fd != INVALID_SOCKET and server->root->sasl.callbacks)
733 {
734 rc= memcached_sasl_authenticate_connection(server);
735 if (memcached_failed(rc) and server->fd != INVALID_SOCKET)
736 {
737 WATCHPOINT_ASSERT(server->fd != INVALID_SOCKET);
738 (void)closesocket(server->fd);
739 server->fd= INVALID_SOCKET;
740 }
741 }
742 }
743 break;
744
745 case MEMCACHED_CONNECTION_UNIX_SOCKET:
746 rc= unix_socket_connect(server);
747 break;
748 }
749
750 if (memcached_success(rc))
751 {
752 server->mark_server_as_clean();
753 memcached_version_instance(server);
754 return rc;
755 }
756 else if (set_last_disconnected)
757 {
758 set_last_disconnected_host(server);
759 if (memcached_has_current_error(*server))
760 {
761 memcached_mark_server_for_timeout(server);
762 assert(memcached_failed(memcached_instance_error_return(server)));
763 }
764 else
765 {
766 memcached_set_error(*server, rc, MEMCACHED_AT);
767 memcached_mark_server_for_timeout(server);
768 }
769
770 LIBMEMCACHED_MEMCACHED_CONNECT_END();
771
772 if (in_timeout)
773 {
774 char buffer[1024];
775 int snprintf_length= snprintf(buffer, sizeof(buffer), "%s:%d", server->hostname, int(server->port()));
776 return memcached_set_error(*server, MEMCACHED_SERVER_TEMPORARILY_DISABLED, MEMCACHED_AT, buffer, snprintf_length);
777 }
778 }
779
780 return rc;
781 }
782
783 memcached_return_t memcached_connect_try(org::libmemcached::Instance* server)
784 {
785 if (server and server->root and server->root->state.is_parsing)
786 {
787 return MEMCACHED_SUCCESS;
788 }
789
790 return _memcached_connect(server, false);
791 }
792
793 memcached_return_t memcached_connect(org::libmemcached::Instance* server)
794 {
795 return _memcached_connect(server, true);
796 }