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