Fix
[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 > 0)
254 {
255 struct timeval waittime;
256
257 waittime.tv_sec= server->root->snd_timeout / 1000000;
258 waittime.tv_usec= server->root->snd_timeout % 1000000;
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 > 0)
269 {
270 struct timeval waittime;
271
272 waittime.tv_sec= server->root->rcv_timeout / 1000000;
273 waittime.tv_usec= server->root->rcv_timeout % 1000000;
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 assert(error == 0);
289
290 // This is not considered a fatal error
291 if (error == -1)
292 {
293 #if 0
294 perror("setsockopt(SO_NOSIGPIPE)");
295 #endif
296 }
297 }
298 #endif
299
300 if (server->root->flags.no_block)
301 {
302 struct linger linger;
303
304 linger.l_onoff= 1;
305 linger.l_linger= 0; /* By default on close() just drop the socket */
306 int error= setsockopt(server->fd, SOL_SOCKET, SO_LINGER,
307 &linger, (socklen_t)sizeof(struct linger));
308 (void)(error);
309 assert(error == 0);
310 }
311
312 if (server->root->flags.tcp_nodelay)
313 {
314 int flag= 1;
315
316 int error= setsockopt(server->fd, IPPROTO_TCP, TCP_NODELAY,
317 &flag, (socklen_t)sizeof(int));
318 (void)(error);
319 assert(error == 0);
320 }
321
322 if (server->root->flags.tcp_keepalive)
323 {
324 int flag= 1;
325
326 int error= setsockopt(server->fd, SOL_SOCKET, SO_KEEPALIVE,
327 &flag, (socklen_t)sizeof(int));
328 (void)(error);
329 assert(error == 0);
330 }
331
332 #ifdef TCP_KEEPIDLE
333 if (server->root->tcp_keepidle > 0)
334 {
335 int error= setsockopt(server->fd, IPPROTO_TCP, TCP_KEEPIDLE,
336 &server->root->tcp_keepidle, (socklen_t)sizeof(int));
337 (void)(error);
338 assert(error == 0);
339 }
340 #endif
341
342 if (server->root->send_size > 0)
343 {
344 int error= setsockopt(server->fd, SOL_SOCKET, SO_SNDBUF,
345 &server->root->send_size, (socklen_t)sizeof(int));
346 (void)(error);
347 assert(error == 0);
348 }
349
350 if (server->root->recv_size > 0)
351 {
352 int error= setsockopt(server->fd, SOL_SOCKET, SO_RCVBUF,
353 &server->root->recv_size, (socklen_t)sizeof(int));
354 (void)(error);
355 assert(error == 0);
356 }
357
358
359 /* libmemcached will always use nonblocking IO to avoid write deadlocks */
360 set_socket_nonblocking(server);
361 }
362
363 static memcached_return_t unix_socket_connect(org::libmemcached::Instance* server)
364 {
365 #ifndef WIN32
366 WATCHPOINT_ASSERT(server->fd == INVALID_SOCKET);
367
368 if ((server->fd= socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
369 {
370 memcached_set_errno(*server, errno, NULL);
371 return MEMCACHED_CONNECTION_FAILURE;
372 }
373
374 struct sockaddr_un servAddr;
375
376 memset(&servAddr, 0, sizeof (struct sockaddr_un));
377 servAddr.sun_family= AF_UNIX;
378 strncpy(servAddr.sun_path, server->hostname, sizeof(servAddr.sun_path)); /* Copy filename */
379
380 do {
381 if (connect(server->fd, (struct sockaddr *)&servAddr, sizeof(servAddr)) < 0)
382 {
383 switch (errno)
384 {
385 case EINPROGRESS:
386 case EALREADY:
387 case EINTR:
388 continue;
389
390 case EISCONN: /* We were spinning waiting on connect */
391 {
392 assert(0); // Programmer error
393 break;
394 }
395
396 default:
397 WATCHPOINT_ERRNO(errno);
398 memcached_set_errno(*server, errno, MEMCACHED_AT);
399 return MEMCACHED_CONNECTION_FAILURE;
400 }
401 }
402 } while (0);
403 server->state= MEMCACHED_SERVER_STATE_CONNECTED;
404
405 WATCHPOINT_ASSERT(server->fd != INVALID_SOCKET);
406
407 return MEMCACHED_SUCCESS;
408 #else
409 (void)server;
410 return MEMCACHED_NOT_SUPPORTED;
411 #endif
412 }
413
414 static memcached_return_t network_connect(org::libmemcached::Instance* server)
415 {
416 bool timeout_error_occured= false;
417
418 WATCHPOINT_ASSERT(server->fd == INVALID_SOCKET);
419 WATCHPOINT_ASSERT(server->cursor_active_ == 0);
420
421 /*
422 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.
423 */
424 if (server->address_info == NULL or server->address_info_next == NULL)
425 {
426 WATCHPOINT_ASSERT(server->state == MEMCACHED_SERVER_STATE_NEW);
427 server->address_info_next= NULL;
428 memcached_return_t rc;
429 uint32_t counter= 5;
430 while (--counter)
431 {
432 if ((rc= set_hostinfo(server)) != MEMCACHED_TIMEOUT)
433 {
434 break;
435 }
436
437 #ifndef WIN32
438 struct timespec dream, rem;
439
440 dream.tv_nsec= 1000;
441 dream.tv_sec= 0;
442
443 nanosleep(&dream, &rem);
444 #endif
445 }
446
447 if (memcached_failed(rc))
448 {
449 return rc;
450 }
451 }
452
453 if (server->address_info_next == NULL)
454 {
455 server->address_info_next= server->address_info;
456 server->state= MEMCACHED_SERVER_STATE_ADDRINFO;
457 }
458
459 /* Create the socket */
460 while (server->address_info_next and server->fd == INVALID_SOCKET)
461 {
462 /* Memcache server does not support IPV6 in udp mode, so skip if not ipv4 */
463 if (memcached_is_udp(server->root) and server->address_info_next->ai_family != AF_INET)
464 {
465 server->address_info_next= server->address_info_next->ai_next;
466 continue;
467 }
468
469 int type= server->address_info_next->ai_socktype;
470 if (HAVE_SOCK_CLOEXEC)
471 {
472 type|= SOCK_CLOEXEC;
473 }
474
475 if ((server->fd= socket(server->address_info_next->ai_family,
476 type,
477 server->address_info_next->ai_protocol)) < 0)
478 {
479 return memcached_set_errno(*server, get_socket_errno(), NULL);
480 }
481
482 if (HAVE_SOCK_CLOEXEC == 0)
483 {
484 #ifdef FD_CLOEXEC
485 int rval;
486 do
487 {
488 rval= fcntl (server->fd, F_SETFD, FD_CLOEXEC);
489 } while (rval == -1 && (errno == EINTR or errno == EAGAIN));
490 #endif
491 }
492
493 set_socket_options(server);
494
495 /* connect to server */
496 if ((connect(server->fd, server->address_info_next->ai_addr, server->address_info_next->ai_addrlen) != SOCKET_ERROR))
497 {
498 server->state= MEMCACHED_SERVER_STATE_CONNECTED;
499 return MEMCACHED_SUCCESS;
500 }
501
502 /* An error occurred */
503 switch (get_socket_errno())
504 {
505 case ETIMEDOUT:
506 timeout_error_occured= true;
507 break;
508
509 case EAGAIN:
510 #if EWOULDBLOCK != EAGAIN
511 case EWOULDBLOCK:
512 #endif
513 case EINPROGRESS: // nonblocking mode - first return
514 case EALREADY: // nonblocking mode - subsequent returns
515 {
516 server->state= MEMCACHED_SERVER_STATE_IN_PROGRESS;
517 memcached_return_t rc= connect_poll(server);
518
519 if (memcached_success(rc))
520 {
521 server->state= MEMCACHED_SERVER_STATE_CONNECTED;
522 return MEMCACHED_SUCCESS;
523 }
524
525 // A timeout here is treated as an error, we will not retry
526 if (rc == MEMCACHED_TIMEOUT)
527 {
528 timeout_error_occured= true;
529 }
530 }
531 break;
532
533 case EISCONN: // we are connected :-)
534 WATCHPOINT_ASSERT(0); // This is a programmer's error
535 break;
536
537 case EINTR: // Special case, we retry ai_addr
538 WATCHPOINT_ASSERT(server->fd != INVALID_SOCKET);
539 (void)closesocket(server->fd);
540 server->fd= INVALID_SOCKET;
541 continue;
542
543 default:
544 break;
545 }
546
547 WATCHPOINT_ASSERT(server->fd != INVALID_SOCKET);
548 (void)closesocket(server->fd);
549 server->fd= INVALID_SOCKET;
550 server->address_info_next= server->address_info_next->ai_next;
551 }
552
553 WATCHPOINT_ASSERT(server->fd == INVALID_SOCKET);
554
555 if (timeout_error_occured)
556 {
557 if (server->fd != INVALID_SOCKET)
558 {
559 (void)closesocket(server->fd);
560 server->fd= INVALID_SOCKET;
561 }
562 }
563
564 WATCHPOINT_STRING("Never got a good file descriptor");
565
566 if (memcached_has_current_error(*server))
567 {
568 return memcached_instance_error_return(server);
569 }
570
571 if (timeout_error_occured and server->state < MEMCACHED_SERVER_STATE_IN_PROGRESS)
572 {
573 return memcached_set_error(*server, MEMCACHED_TIMEOUT, MEMCACHED_AT);
574 }
575
576 return memcached_set_error(*server, MEMCACHED_CONNECTION_FAILURE, MEMCACHED_AT); /* The last error should be from connect() */
577 }
578
579
580 /*
581 backoff_handling()
582
583 Based on time/failure count fail the connect without trying. This prevents waiting in a state where
584 we get caught spending cycles just waiting.
585 */
586 static memcached_return_t backoff_handling(org::libmemcached::Instance* server, bool& in_timeout)
587 {
588 struct timeval curr_time;
589 bool _gettime_success= (gettimeofday(&curr_time, NULL) == 0);
590
591 /*
592 If we hit server_failure_limit then something is completely wrong about the server.
593
594 1) If autoeject is enabled we do that.
595 2) If not? We go into timeout again, there is much else to do :(
596 */
597 if (server->server_failure_counter >= server->root->server_failure_limit)
598 {
599 /*
600 We just auto_eject if we hit this point
601 */
602 if (_is_auto_eject_host(server->root))
603 {
604 set_last_disconnected_host(server);
605
606 // Retry dead servers if requested
607 if (_gettime_success and server->root->dead_timeout > 0)
608 {
609 server->next_retry= curr_time.tv_sec +server->root->dead_timeout;
610
611 // We only retry dead servers once before assuming failure again
612 server->server_failure_counter= server->root->server_failure_limit -1;
613 }
614
615 memcached_return_t rc;
616 if (memcached_failed(rc= run_distribution((memcached_st *)server->root)))
617 {
618 return memcached_set_error(*server, rc, MEMCACHED_AT, memcached_literal_param("Backoff handling failed during run_distribution"));
619 }
620
621 return memcached_set_error(*server, MEMCACHED_SERVER_MARKED_DEAD, MEMCACHED_AT);
622 }
623
624 server->state= MEMCACHED_SERVER_STATE_IN_TIMEOUT;
625
626 // Sanity check/setting
627 if (server->next_retry == 0)
628 {
629 server->next_retry= 1;
630 }
631 }
632
633 if (server->state == MEMCACHED_SERVER_STATE_IN_TIMEOUT)
634 {
635 /*
636 If next_retry is less then our current time, then we reset and try everything again.
637 */
638 if (_gettime_success and server->next_retry < curr_time.tv_sec)
639 {
640 server->state= MEMCACHED_SERVER_STATE_NEW;
641 }
642 else
643 {
644 return memcached_set_error(*server, MEMCACHED_SERVER_TEMPORARILY_DISABLED, MEMCACHED_AT);
645 }
646
647 in_timeout= true;
648 }
649
650 return MEMCACHED_SUCCESS;
651 }
652
653 static memcached_return_t _memcached_connect(org::libmemcached::Instance* server, const bool set_last_disconnected)
654 {
655 assert(server);
656 if (server->fd != INVALID_SOCKET)
657 {
658 return MEMCACHED_SUCCESS;
659 }
660
661 LIBMEMCACHED_MEMCACHED_CONNECT_START();
662
663 bool in_timeout= false;
664 memcached_return_t rc;
665 if (memcached_failed(rc= backoff_handling(server, in_timeout)))
666 {
667 set_last_disconnected_host(server);
668 return rc;
669 }
670
671 if (LIBMEMCACHED_WITH_SASL_SUPPORT and server->root->sasl.callbacks and memcached_is_udp(server->root))
672 {
673 return memcached_set_error(*server, MEMCACHED_INVALID_HOST_PROTOCOL, MEMCACHED_AT, memcached_literal_param("SASL is not supported for UDP connections"));
674 }
675
676 if (server->hostname[0] == '/')
677 {
678 server->type= MEMCACHED_CONNECTION_UNIX_SOCKET;
679 }
680
681 /* We need to clean up the multi startup piece */
682 switch (server->type)
683 {
684 case MEMCACHED_CONNECTION_UDP:
685 case MEMCACHED_CONNECTION_TCP:
686 rc= network_connect(server);
687
688 if (LIBMEMCACHED_WITH_SASL_SUPPORT)
689 {
690 if (server->fd != INVALID_SOCKET and server->root->sasl.callbacks)
691 {
692 rc= memcached_sasl_authenticate_connection(server);
693 if (memcached_failed(rc) and server->fd != INVALID_SOCKET)
694 {
695 WATCHPOINT_ASSERT(server->fd != INVALID_SOCKET);
696 (void)closesocket(server->fd);
697 server->fd= INVALID_SOCKET;
698 }
699 }
700 }
701 break;
702
703 case MEMCACHED_CONNECTION_UNIX_SOCKET:
704 rc= unix_socket_connect(server);
705 break;
706 }
707
708 if (memcached_success(rc))
709 {
710 server->mark_server_as_clean();
711 memcached_version_instance(server);
712 return rc;
713 }
714 else if (set_last_disconnected)
715 {
716 set_last_disconnected_host(server);
717 if (memcached_has_current_error(*server))
718 {
719 memcached_mark_server_for_timeout(server);
720 assert(memcached_failed(memcached_instance_error_return(server)));
721 }
722 else
723 {
724 memcached_set_error(*server, rc, MEMCACHED_AT);
725 memcached_mark_server_for_timeout(server);
726 }
727
728 LIBMEMCACHED_MEMCACHED_CONNECT_END();
729
730 if (in_timeout)
731 {
732 char buffer[1024];
733 int snprintf_length= snprintf(buffer, sizeof(buffer), "%s:%d", server->hostname, int(server->port()));
734 return memcached_set_error(*server, MEMCACHED_SERVER_TEMPORARILY_DISABLED, MEMCACHED_AT, buffer, snprintf_length);
735 }
736 }
737
738 return rc;
739 }
740
741 memcached_return_t memcached_connect_try(org::libmemcached::Instance* server)
742 {
743 if (server and server->root and server->root->state.is_parsing)
744 {
745 return MEMCACHED_SUCCESS;
746 }
747
748 return _memcached_connect(server, false);
749 }
750
751 memcached_return_t memcached_connect(org::libmemcached::Instance* server)
752 {
753 return _memcached_connect(server, true);
754 }