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