Update, add class keyword to solve build issue for Bison 2.7 on OSX.
[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(_WIN32)
361 #else
362 #if defined(SO_NOSIGPIPE)
363 if (SO_NOSIGPIPE)
364 {
365 int set= 1;
366 int error= setsockopt(server->fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int));
367
368 assert(error == 0);
369
370 // This is not considered a fatal error
371 if (error == -1)
372 {
373 #if 0
374 perror("setsockopt(SO_NOSIGPIPE)");
375 #endif
376 }
377 }
378 #endif // SO_NOSIGPIPE
379 #endif // _WIN32
380
381 if (server->root->flags.no_block)
382 {
383 struct linger linger;
384
385 linger.l_onoff= 1;
386 linger.l_linger= 0; /* By default on close() just drop the socket */
387 int error= setsockopt(server->fd, SOL_SOCKET, SO_LINGER,
388 (char*)&linger, (socklen_t)sizeof(struct linger));
389 (void)(error);
390 assert(error == 0);
391 }
392
393 if (TCP_NODELAY)
394 {
395 if (server->root->flags.tcp_nodelay)
396 {
397 int flag= 1;
398
399 int error= setsockopt(server->fd, IPPROTO_TCP, TCP_NODELAY,
400 (char*)&flag, (socklen_t)sizeof(int));
401 (void)(error);
402 assert(error == 0);
403 }
404 }
405
406 if (server->root->flags.tcp_keepalive)
407 {
408 int flag= 1;
409
410 int error= setsockopt(server->fd, SOL_SOCKET, SO_KEEPALIVE,
411 (char*)&flag, (socklen_t)sizeof(int));
412 (void)(error);
413 assert(error == 0);
414 }
415
416 if (TCP_KEEPIDLE)
417 {
418 if (server->root->tcp_keepidle > 0)
419 {
420 int error= setsockopt(server->fd, IPPROTO_TCP, TCP_KEEPIDLE,
421 (char*)&server->root->tcp_keepidle, (socklen_t)sizeof(int));
422 (void)(error);
423 assert(error == 0);
424 }
425 }
426
427 if (server->root->send_size > 0)
428 {
429 int error= setsockopt(server->fd, SOL_SOCKET, SO_SNDBUF,
430 (char*)&server->root->send_size, (socklen_t)sizeof(int));
431 (void)(error);
432 assert(error == 0);
433 }
434
435 if (server->root->recv_size > 0)
436 {
437 int error= setsockopt(server->fd, SOL_SOCKET, SO_RCVBUF,
438 (char*)&server->root->recv_size, (socklen_t)sizeof(int));
439 (void)(error);
440 assert(error == 0);
441 }
442
443 /* libmemcached will always use nonblocking IO to avoid write deadlocks */
444 set_socket_nonblocking(server);
445
446 return true;
447 }
448
449 static memcached_return_t unix_socket_connect(org::libmemcached::Instance* server)
450 {
451 #ifndef WIN32
452 WATCHPOINT_ASSERT(server->fd == INVALID_SOCKET);
453
454 do {
455 int type= SOCK_STREAM;
456 if (SOCK_CLOEXEC)
457 {
458 type|= SOCK_CLOEXEC;
459 }
460
461 if (SOCK_NONBLOCK)
462 {
463 type|= SOCK_NONBLOCK;
464 }
465
466 if ((server->fd= socket(AF_UNIX, type, 0)) < 0)
467 {
468 return memcached_set_errno(*server, errno, NULL);
469 }
470
471 struct sockaddr_un servAddr;
472
473 memset(&servAddr, 0, sizeof (struct sockaddr_un));
474 servAddr.sun_family= AF_UNIX;
475 strncpy(servAddr.sun_path, server->hostname, sizeof(servAddr.sun_path)); /* Copy filename */
476
477 if (connect(server->fd, (struct sockaddr *)&servAddr, sizeof(servAddr)) < 0)
478 {
479 switch (errno)
480 {
481 case EINPROGRESS:
482 case EALREADY:
483 server->events(POLLOUT);
484 break;
485
486 case EINTR:
487 (void)closesocket(server->fd);
488 server->fd= INVALID_SOCKET;
489 continue;
490
491 case EISCONN: /* We were spinning waiting on connect */
492 {
493 assert(0); // Programmer error
494 (void)closesocket(server->fd);
495 server->fd= INVALID_SOCKET;
496 continue;
497 }
498
499 default:
500 WATCHPOINT_ERRNO(errno);
501 (void)closesocket(server->fd);
502 server->fd= INVALID_SOCKET;
503 return memcached_set_errno(*server, errno, MEMCACHED_AT);
504 }
505 }
506 } while (0);
507 server->state= MEMCACHED_SERVER_STATE_CONNECTED;
508
509 WATCHPOINT_ASSERT(server->fd != INVALID_SOCKET);
510
511 return MEMCACHED_SUCCESS;
512 #else
513 (void)server;
514 return MEMCACHED_NOT_SUPPORTED;
515 #endif
516 }
517
518 static memcached_return_t network_connect(org::libmemcached::Instance* server)
519 {
520 bool timeout_error_occured= false;
521
522 WATCHPOINT_ASSERT(server->fd == INVALID_SOCKET);
523 WATCHPOINT_ASSERT(server->cursor_active_ == 0);
524
525 /*
526 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.
527 */
528 if (server->address_info == NULL or server->address_info_next == NULL)
529 {
530 WATCHPOINT_ASSERT(server->state == MEMCACHED_SERVER_STATE_NEW);
531 server->address_info_next= NULL;
532 memcached_return_t rc= set_hostinfo(server);
533
534 if (memcached_failed(rc))
535 {
536 return rc;
537 }
538 }
539
540 if (server->address_info_next == NULL)
541 {
542 server->address_info_next= server->address_info;
543 server->state= MEMCACHED_SERVER_STATE_ADDRINFO;
544 }
545
546 /* Create the socket */
547 while (server->address_info_next and server->fd == INVALID_SOCKET)
548 {
549 /* Memcache server does not support IPV6 in udp mode, so skip if not ipv4 */
550 if (memcached_is_udp(server->root) and server->address_info_next->ai_family != AF_INET)
551 {
552 server->address_info_next= server->address_info_next->ai_next;
553 continue;
554 }
555
556 int type= server->address_info_next->ai_socktype;
557 if (SOCK_CLOEXEC)
558 {
559 type|= SOCK_CLOEXEC;
560 }
561
562 if (SOCK_NONBLOCK)
563 {
564 type|= SOCK_NONBLOCK;
565 }
566
567 server->fd= socket(server->address_info_next->ai_family,
568 type,
569 server->address_info_next->ai_protocol);
570
571 if (int(server->fd) == SOCKET_ERROR)
572 {
573 return memcached_set_errno(*server, get_socket_errno(), NULL);
574 }
575
576 if (set_socket_options(server) == false)
577 {
578 (void)closesocket(server->fd);
579 return MEMCACHED_CONNECTION_FAILURE;
580 }
581
582 /* connect to server */
583 if ((connect(server->fd, server->address_info_next->ai_addr, server->address_info_next->ai_addrlen) != SOCKET_ERROR))
584 {
585 server->state= MEMCACHED_SERVER_STATE_CONNECTED;
586 return MEMCACHED_SUCCESS;
587 }
588
589 /* An error occurred */
590 switch (get_socket_errno())
591 {
592 case ETIMEDOUT:
593 timeout_error_occured= true;
594 break;
595
596 case EAGAIN:
597 #if EWOULDBLOCK != EAGAIN
598 case EWOULDBLOCK:
599 #endif
600 case EINPROGRESS: // nonblocking mode - first return
601 case EALREADY: // nonblocking mode - subsequent returns
602 {
603 server->events(POLLOUT);
604 server->state= MEMCACHED_SERVER_STATE_IN_PROGRESS;
605 memcached_return_t rc= connect_poll(server);
606
607 if (memcached_success(rc))
608 {
609 server->state= MEMCACHED_SERVER_STATE_CONNECTED;
610 return MEMCACHED_SUCCESS;
611 }
612
613 // A timeout here is treated as an error, we will not retry
614 if (rc == MEMCACHED_TIMEOUT)
615 {
616 timeout_error_occured= true;
617 }
618 }
619 break;
620
621 case EISCONN: // we are connected :-)
622 WATCHPOINT_ASSERT(0); // This is a programmer's error
623 break;
624
625 case EINTR: // Special case, we retry ai_addr
626 WATCHPOINT_ASSERT(server->fd != INVALID_SOCKET);
627 (void)closesocket(server->fd);
628 server->fd= INVALID_SOCKET;
629 continue;
630
631 case ECONNREFUSED:
632 // Probably not running service
633
634 default:
635 break;
636 }
637
638 WATCHPOINT_ASSERT(server->fd != INVALID_SOCKET);
639 (void)closesocket(server->fd);
640 server->fd= INVALID_SOCKET;
641 server->address_info_next= server->address_info_next->ai_next;
642 }
643
644 WATCHPOINT_ASSERT(server->fd == INVALID_SOCKET);
645
646 if (timeout_error_occured)
647 {
648 if (server->fd != INVALID_SOCKET)
649 {
650 (void)closesocket(server->fd);
651 server->fd= INVALID_SOCKET;
652 }
653 }
654
655 WATCHPOINT_STRING("Never got a good file descriptor");
656
657 if (memcached_has_current_error(*server))
658 {
659 return memcached_instance_error_return(server);
660 }
661
662 if (timeout_error_occured and server->state < MEMCACHED_SERVER_STATE_IN_PROGRESS)
663 {
664 return memcached_set_error(*server, MEMCACHED_TIMEOUT, MEMCACHED_AT);
665 }
666
667 return memcached_set_error(*server, MEMCACHED_CONNECTION_FAILURE, MEMCACHED_AT); /* The last error should be from connect() */
668 }
669
670
671 /*
672 backoff_handling()
673
674 Based on time/failure count fail the connect without trying. This prevents waiting in a state where
675 we get caught spending cycles just waiting.
676 */
677 static memcached_return_t backoff_handling(org::libmemcached::Instance* server, bool& in_timeout)
678 {
679 struct timeval curr_time;
680 bool _gettime_success= (gettimeofday(&curr_time, NULL) == 0);
681
682 /*
683 If we hit server_failure_limit then something is completely wrong about the server.
684
685 1) If autoeject is enabled we do that.
686 2) If not? We go into timeout again, there is much else to do :(
687 */
688 if (server->server_failure_counter >= server->root->server_failure_limit)
689 {
690 /*
691 We just auto_eject if we hit this point
692 */
693 if (_is_auto_eject_host(server->root))
694 {
695 set_last_disconnected_host(server);
696
697 // Retry dead servers if requested
698 if (_gettime_success and server->root->dead_timeout > 0)
699 {
700 server->next_retry= curr_time.tv_sec +server->root->dead_timeout;
701
702 // We only retry dead servers once before assuming failure again
703 server->server_failure_counter= server->root->server_failure_limit -1;
704 }
705
706 memcached_return_t rc;
707 if (memcached_failed(rc= run_distribution((memcached_st *)server->root)))
708 {
709 return memcached_set_error(*server, rc, MEMCACHED_AT, memcached_literal_param("Backoff handling failed during run_distribution"));
710 }
711
712 return memcached_set_error(*server, MEMCACHED_SERVER_MARKED_DEAD, MEMCACHED_AT);
713 }
714
715 server->state= MEMCACHED_SERVER_STATE_IN_TIMEOUT;
716
717 // Sanity check/setting
718 if (server->next_retry == 0)
719 {
720 server->next_retry= 1;
721 }
722 }
723
724 if (server->state == MEMCACHED_SERVER_STATE_IN_TIMEOUT)
725 {
726 /*
727 If next_retry is less then our current time, then we reset and try everything again.
728 */
729 if (_gettime_success and server->next_retry < curr_time.tv_sec)
730 {
731 server->state= MEMCACHED_SERVER_STATE_NEW;
732 }
733 else
734 {
735 return memcached_set_error(*server, MEMCACHED_SERVER_TEMPORARILY_DISABLED, MEMCACHED_AT);
736 }
737
738 in_timeout= true;
739 }
740
741 return MEMCACHED_SUCCESS;
742 }
743
744 static memcached_return_t _memcached_connect(org::libmemcached::Instance* server, const bool set_last_disconnected)
745 {
746 assert(server);
747 if (server->fd != INVALID_SOCKET)
748 {
749 return MEMCACHED_SUCCESS;
750 }
751
752 LIBMEMCACHED_MEMCACHED_CONNECT_START();
753
754 bool in_timeout= false;
755 memcached_return_t rc;
756 if (memcached_failed(rc= backoff_handling(server, in_timeout)))
757 {
758 set_last_disconnected_host(server);
759 return rc;
760 }
761
762 if (LIBMEMCACHED_WITH_SASL_SUPPORT and server->root->sasl.callbacks and memcached_is_udp(server->root))
763 {
764 return memcached_set_error(*server, MEMCACHED_INVALID_HOST_PROTOCOL, MEMCACHED_AT, memcached_literal_param("SASL is not supported for UDP connections"));
765 }
766
767 if (server->hostname[0] == '/')
768 {
769 server->type= MEMCACHED_CONNECTION_UNIX_SOCKET;
770 }
771
772 /* We need to clean up the multi startup piece */
773 switch (server->type)
774 {
775 case MEMCACHED_CONNECTION_UDP:
776 case MEMCACHED_CONNECTION_TCP:
777 rc= network_connect(server);
778
779 if (LIBMEMCACHED_WITH_SASL_SUPPORT)
780 {
781 if (server->fd != INVALID_SOCKET and server->root->sasl.callbacks)
782 {
783 rc= memcached_sasl_authenticate_connection(server);
784 fprintf(stderr, "%s:%d %s\n", __FILE__, __LINE__, memcached_strerror(NULL, rc));
785 if (memcached_failed(rc) and server->fd != INVALID_SOCKET)
786 {
787 WATCHPOINT_ASSERT(server->fd != INVALID_SOCKET);
788 (void)closesocket(server->fd);
789 server->fd= INVALID_SOCKET;
790 }
791 }
792 }
793 break;
794
795 case MEMCACHED_CONNECTION_UNIX_SOCKET:
796 rc= unix_socket_connect(server);
797 break;
798 }
799
800 if (memcached_success(rc))
801 {
802 server->mark_server_as_clean();
803 memcached_version_instance(server);
804 return rc;
805 }
806 else if (set_last_disconnected)
807 {
808 set_last_disconnected_host(server);
809 if (memcached_has_current_error(*server))
810 {
811 memcached_mark_server_for_timeout(server);
812 assert(memcached_failed(memcached_instance_error_return(server)));
813 }
814 else
815 {
816 memcached_set_error(*server, rc, MEMCACHED_AT);
817 memcached_mark_server_for_timeout(server);
818 }
819
820 LIBMEMCACHED_MEMCACHED_CONNECT_END();
821
822 if (in_timeout)
823 {
824 char buffer[1024];
825 int snprintf_length= snprintf(buffer, sizeof(buffer), "%s:%d", server->hostname, int(server->port()));
826 return memcached_set_error(*server, MEMCACHED_SERVER_TEMPORARILY_DISABLED, MEMCACHED_AT, buffer, snprintf_length);
827 }
828 }
829
830 return rc;
831 }
832
833 memcached_return_t memcached_connect(org::libmemcached::Instance* server)
834 {
835 return _memcached_connect(server, true);
836 }