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