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