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