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