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