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