Added connection opening timeout handling
[wmaker-crm.git] / WINGs / connection.c
blobd6670cc6fb71e4c3291634d37e1e443a77209d36
1 /*
2 * WINGs WMConnection function library
3 *
4 * Copyright (c) 1999-2000 Dan Pascu
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 * TODO:
24 * - decide if we want to support connections with external sockets, else
25 * clean up the structure of the unneeded members.
30 #include "../src/config.h"
32 #include <unistd.h>
33 #include <fcntl.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <stdarg.h>
38 #include <errno.h>
39 #include <sys/socket.h>
40 #include <netinet/in.h>
41 #include <arpa/inet.h>
42 #include <netdb.h>
43 #include <signal.h>
44 #ifdef __FreeBSD__
45 #include <sys/signal.h>
46 #endif
48 #include "WINGs.h"
51 /* Some older systems does not define this (linux libc5, maybe others too) */
52 #ifndef SHUT_RDWR
53 # define SHUT_RDWR 2
54 #endif
56 /* For SunOS */
57 #ifndef SA_RESTART
58 # define SA_RESTART 0
59 #endif
61 /* For Solaris */
62 #ifndef INADDR_NONE
63 # define INADDR_NONE -1
64 #endif
66 /* Stuff for setting the sockets into non-blocking mode. */
67 /*#ifdef __POSIX_SOURCE
68 # define NONBLOCK_OPT O_NONBLOCK
69 #else
70 # define NONBLOCK_OPT FNDELAY
71 #endif*/
73 #define NONBLOCK_OPT O_NONBLOCK
75 #define NETBUF_SIZE 4096
77 #define DEF_TIMEOUT 600 /* 600 seconds == 10 minutes */
81 int WCErrorCode = 0;
83 static Bool SigInitialized = False;
85 static unsigned int DefaultTimeout = DEF_TIMEOUT;
86 static unsigned int OpenTimeout = DEF_TIMEOUT;
90 typedef struct TimeoutData {
91 unsigned timeout;
92 WMHandlerID *handler;
93 } TimeoutData;
96 typedef struct W_Connection {
97 int sock; /* the socket we speak through */
99 struct {
100 WMHandlerID *read; /* the input read handler */
101 WMHandlerID *write; /* the input write handler */
102 WMHandlerID *exception; /* the input exception handler */
103 } handler;
105 ConnectionDelegate *delegate; /* client delegates */
106 void *clientData; /* client data */
107 unsigned int uflags; /* flags for the client */
109 WMBag *outputQueue;
110 unsigned bufPos;
112 TimeoutData sendTimeout;
113 TimeoutData openTimeout;
115 WMConnectionState state;
116 WMConnectionTimeoutState timeoutState;
118 char *address;
119 char *service;
120 char *protocol;
122 Bool closeOnRelease;
123 Bool wasNonBlocking;
124 Bool isNonBlocking;
126 } W_Connection;
130 static void
131 clearOutputQueue(WMConnection *cPtr) /*FOLD00*/
133 int i;
135 cPtr->bufPos = 0;
137 for (i=0; i<WMGetBagItemCount(cPtr->outputQueue); i++)
138 WMReleaseData(WMGetFromBag(cPtr->outputQueue, i));
140 WMEmptyBag(cPtr->outputQueue);
144 static void
145 openTimeout(void *cdata) /*FOLD00*/
147 WMConnection *cPtr = (WMConnection*) cdata;
149 cPtr->openTimeout.handler = NULL;
150 if (cPtr->handler.write) {
151 WMDeleteInputHandler(cPtr->handler.write);
152 cPtr->handler.write = NULL;
154 if (cPtr->state != WCConnected) {
155 cPtr->state = WCTimedOut;
156 cPtr->timeoutState = WCTWhileOpening;
157 if (cPtr->delegate && cPtr->delegate->didTimeout) {
158 (*cPtr->delegate->didTimeout)(cPtr->delegate, cPtr);
159 } else {
160 WMCloseConnection(cPtr);
161 cPtr->state = WCTimedOut; /* the above set state to WCClosed */
167 static void
168 sendTimeout(void *cdata) /*FOLD00*/
170 WMConnection *cPtr = (WMConnection*) cdata;
172 cPtr->sendTimeout.handler = NULL;
173 if (cPtr->handler.write) {
174 WMDeleteInputHandler(cPtr->handler.write);
175 cPtr->handler.write = NULL;
177 if (WMGetBagItemCount(cPtr->outputQueue)>0) {
178 clearOutputQueue(cPtr);
179 cPtr->state = WCTimedOut;
180 cPtr->timeoutState = WCTWhileSending;
181 if (cPtr->delegate && cPtr->delegate->didTimeout) {
182 (*cPtr->delegate->didTimeout)(cPtr->delegate, cPtr);
183 } else {
184 WMCloseConnection(cPtr);
185 cPtr->state = WCTimedOut; /* the above set state to WCClosed */
191 static void
192 inputHandler(int fd, int mask, void *clientData) /*FOLD00*/
194 WMConnection *cPtr = (WMConnection*)clientData;
196 if (cPtr->state==WCClosed || cPtr->state==WCDied)
197 return;
199 if ((mask & WIWriteMask)) {
200 if (cPtr->state == WCInProgress) {
201 Bool failed;
202 int result;
203 int len = sizeof(result);
205 if (getsockopt(cPtr->sock, SOL_SOCKET, SO_ERROR,
206 (void*)&result, &len) == 0 && result != 0) {
207 cPtr->state = WCFailed;
208 WCErrorCode = result;
209 failed = True;
210 /* should call wsyserrorwithcode(result, ...) here? */
211 } else {
212 cPtr->state = WCConnected;
213 failed = False;
216 if (cPtr->handler.write) {
217 WMDeleteInputHandler(cPtr->handler.write);
218 cPtr->handler.write = NULL;
221 if (cPtr->openTimeout.handler) {
222 WMDeleteTimerHandler(cPtr->openTimeout.handler);
223 cPtr->openTimeout.handler = NULL;
226 if (cPtr->delegate && cPtr->delegate->didInitialize)
227 (*cPtr->delegate->didInitialize)(cPtr->delegate, cPtr);
229 /* we use failed and not cPtr->state here, because cPtr may be
230 * destroyed by the delegate called above if the connection failed
232 if (failed)
233 return;
234 } else if (cPtr->state == WCConnected) {
235 WMFlushConnection(cPtr);
239 if (!cPtr->delegate)
240 return;
242 /* if the connection died, may get destroyed in the delegate, so retain */
243 wretain(cPtr);
245 if ((mask & WIReadMask) && cPtr->delegate->didReceiveInput)
246 (*cPtr->delegate->didReceiveInput)(cPtr->delegate, cPtr);
248 if ((mask & WIExceptMask) && cPtr->delegate->didCatchException)
249 (*cPtr->delegate->didCatchException)(cPtr->delegate, cPtr);
251 wrelease(cPtr);
255 static Bool
256 setSocketNonBlocking(int sock, Bool flag) /*FOLD00*/
258 int state;
259 Bool isNonBlock;
261 state = fcntl(sock, F_GETFL, 0);
263 if (state < 0) {
264 wsyserror("Failed to get socket flags with fcntl.");
265 return False;
268 isNonBlock = (state & NONBLOCK_OPT) != 0;
270 if (flag) {
271 if (isNonBlock)
272 return True;
273 state |= NONBLOCK_OPT;
274 } else {
275 if (!isNonBlock)
276 return True;
277 state &= ~NONBLOCK_OPT;
280 if (fcntl(sock, F_SETFL, state) < 0) {
281 wsyserror("Failed to set socket flags with fcntl.");
282 return False;
285 return True;
289 static void
290 setConnectionAddress(WMConnection *cPtr, struct sockaddr_in *socketaddr) /*FOLD00*/
292 wassertr(cPtr->address==NULL);
294 cPtr->address = wstrdup(inet_ntoa(socketaddr->sin_addr));
295 cPtr->service = wmalloc(16);
296 sprintf(cPtr->service, "%hu", ntohs(socketaddr->sin_port));
297 cPtr->protocol = wstrdup("tcp");
301 static struct sockaddr_in*
302 getSocketAddress(char* name, char* service, char* protocol) /*FOLD00*/
304 static struct sockaddr_in socketaddr;
305 struct servent *sp;
307 if (!protocol || protocol[0]=='\0')
308 protocol = "tcp";
310 memset(&socketaddr, 0, sizeof(struct sockaddr_in));
311 socketaddr.sin_family = AF_INET;
314 * If we were given a hostname, we use any address for that host.
315 * Otherwise we expect the given name to be an address unless it is
316 * NULL (any address).
318 if (name && name[0]!='\0') {
319 WMHost *host = WMGetHostWithName(name);
321 if (!host)
322 return NULL; /* name is not a hostname nor a number and dot adr */
324 name = WMGetHostAddress(host);
325 #ifndef HAVE_INET_ATON
326 if ((socketaddr.sin_addr.s_addr = inet_addr(name)) == INADDR_NONE) {
327 #else
328 if (inet_aton(name, &socketaddr.sin_addr) == 0) {
329 #endif
330 WMReleaseHost(host);
331 return NULL;
333 WMReleaseHost(host);
334 } else {
335 socketaddr.sin_addr.s_addr = htonl(INADDR_ANY);
338 if (!service || service[0]=='\0') {
339 socketaddr.sin_port = 0;
340 } else if ((sp = getservbyname(service, protocol))==0) {
341 char *endptr;
342 unsigned portNumber;
344 portNumber = strtoul(service, &endptr, 10);
346 if (service[0]!='\0' && *endptr=='\0' && portNumber<65536) {
347 socketaddr.sin_port = htons(portNumber);
348 } else {
349 return NULL;
351 } else {
352 socketaddr.sin_port = sp->s_port;
355 return &socketaddr;
359 static WMConnection*
360 createConnectionWithSocket(int sock, Bool closeOnRelease) /*FOLD00*/
362 WMConnection *cPtr;
363 struct sigaction sig_action;
365 cPtr = wmalloc(sizeof(WMConnection));
366 wretain(cPtr);
367 memset(cPtr, 0, sizeof(WMConnection));
369 cPtr->sock = sock;
370 cPtr->openTimeout.timeout = OpenTimeout;
371 cPtr->openTimeout.handler = NULL;
372 cPtr->sendTimeout.timeout = DefaultTimeout;
373 cPtr->sendTimeout.handler = NULL;
374 cPtr->closeOnRelease = closeOnRelease;
375 cPtr->outputQueue = WMCreateBag(16);
376 cPtr->state = WCNotConnected;
377 cPtr->timeoutState = WCTNone;
379 /* ignore dead pipe */
380 if (!SigInitialized) {
381 sig_action.sa_handler = SIG_IGN;
382 sig_action.sa_flags = SA_RESTART;
383 sigaction(SIGPIPE, &sig_action, NULL);
384 SigInitialized = True;
387 return cPtr;
391 #if 0
392 WMConnection*
393 WMCreateConnectionWithSocket(int sock, Bool closeOnRelease) /*FOLD00*/
395 WMConnection *cPtr;
396 struct sockaddr_in clientname;
397 int size, n;
399 cPtr = createConnectionWithSocket(sock, closeOnRelease);
400 cPtr->wasNonBlocking = WMIsConnectionNonBlocking(cPtr);
401 cPtr->isNonBlocking = cPtr->wasNonBlocking;
403 /* some way to find out if it is connected, and binded. can't find
404 if it listens though!!!
407 size = sizeof(clientname);
408 n = getpeername(sock, (struct sockaddr*) &clientname, &size);
409 if (n==0) {
410 /* Since we have a peer, it means we are connected */
411 cPtr->state = WCConnected;
412 } else {
413 size = sizeof(clientname);
414 n = getsockname(sock, (struct sockaddr*) &clientname, &size);
415 if (n==0) {
416 /* We don't have a peer, but we are binded to an address.
417 * Assume we are listening on it (we don't know that for sure!)
419 cPtr->state = WCListening;
420 } else {
421 cPtr->state = WCNotConnected;
425 return cPtr;
427 #endif
431 * host is the name on which we want to listen for incoming connections,
432 * and it must be a name of this host, or NULL if we want to listen
433 * on any incoming address.
434 * service is either a service name as present in /etc/services, or the port
435 * number we want to listen on. If NULL, a random port between
436 * 1024 and 65535 will be assigned to us.
437 * protocol is one of "tcp" or "udp". If NULL, "tcp" will be used by default.
438 * currently only "tcp" is supported.
440 WMConnection*
441 WMCreateConnectionAsServerAtAddress(char *host, char *service, char *protocol) /*FOLD00*/
443 WMConnection *cPtr;
444 struct sockaddr_in *socketaddr;
445 int sock, size, on;
447 if ((socketaddr = getSocketAddress(host, service, protocol)) == NULL) {
448 WCErrorCode = 0;
449 wwarning("Bad address-service-protocol combination");
450 return NULL;
453 /* Create the actual socket */
454 sock = socket(PF_INET, SOCK_STREAM, 0);
455 if (sock<0) {
456 WCErrorCode = errno;
457 wsyserror("Unable to create socket");
458 return NULL;
462 * Set socket options. We try to make the port reusable and have it
463 * close as fast as possible without waiting in unnecessary wait states
464 * on close.
466 on = 1;
467 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on));
469 if (bind(sock, (struct sockaddr *)socketaddr, sizeof(*socketaddr)) < 0) {
470 WCErrorCode = errno;
471 wsyserror("Unable to bind to address '%s:%hu'",
472 inet_ntoa(socketaddr->sin_addr),
473 ntohs(socketaddr->sin_port));
474 close(sock);
475 return NULL;
478 if (listen(sock, 10) < 0) {
479 WCErrorCode = errno;
480 wsyserror("Unable to listen on port '%hu'",
481 ntohs(socketaddr->sin_port));
482 close(sock);
483 return NULL;
486 /* Find out what is the address/service/protocol we get */
487 /* In case some of address/service/protocol were NULL */
488 size = sizeof(*socketaddr);
489 if (getsockname(sock, (struct sockaddr*)socketaddr, &size) < 0) {
490 WCErrorCode = errno;
491 wsyserror("Unable to get socket address");
492 close(sock);
493 return NULL;
496 cPtr = createConnectionWithSocket(sock, True);
497 cPtr->state = WCListening;
498 WMSetConnectionNonBlocking(cPtr, True);
500 setConnectionAddress(cPtr, socketaddr);
502 return cPtr;
506 WMConnection*
507 WMCreateConnectionToAddress(char *host, char *service, char *protocol) /*FOLD00*/
509 WMConnection *cPtr;
510 struct sockaddr_in *socketaddr;
511 int sock;
513 if (service==NULL || service[0]=='\0') {
514 WCErrorCode = 0;
515 wwarning("Bad argument - service is not specified");
516 return NULL;
519 if (host==NULL || host[0]=='\0')
520 host = "localhost";
522 if ((socketaddr = getSocketAddress(host, service, protocol)) == NULL) {
523 WCErrorCode = 0;
524 wwarning("Bad address-service-protocol combination");
525 return NULL;
528 /* Create the actual socket */
529 sock = socket(PF_INET, SOCK_STREAM, 0);
530 if (sock<0) {
531 WCErrorCode = errno;
532 wsyserror("Unable to create socket");
533 return NULL;
535 /* make socket blocking while we connect. */
536 setSocketNonBlocking(sock, False);
537 if (connect(sock, (struct sockaddr*)socketaddr, sizeof(*socketaddr)) < 0) {
538 WCErrorCode = errno;
539 wsyserror("Unable to make connection to address '%s:%hu'",
540 inet_ntoa(socketaddr->sin_addr),
541 ntohs(socketaddr->sin_port));
542 close(sock);
543 return NULL;
546 cPtr = createConnectionWithSocket(sock, True);
547 cPtr->state = WCConnected;
548 WMSetConnectionNonBlocking(cPtr, True);
549 setConnectionAddress(cPtr, socketaddr);
551 return cPtr;
555 WMConnection*
556 WMCreateConnectionToAddressAndNotify(char *host, char *service, char *protocol) /*FOLD00*/
558 WMConnection *cPtr;
559 struct sockaddr_in *socketaddr;
560 int sock;
561 Bool isNonBlocking;
563 if (service==NULL || service[0]=='\0') {
564 WCErrorCode = 0;
565 wwarning("Bad argument - service is not specified");
566 return NULL;
569 if (host==NULL || host[0]=='\0')
570 host = "localhost";
572 if ((socketaddr = getSocketAddress(host, service, protocol)) == NULL) {
573 WCErrorCode = 0;
574 wwarning("Bad address-service-protocol combination");
575 return NULL;
578 /* Create the actual socket */
579 sock = socket(PF_INET, SOCK_STREAM, 0);
580 if (sock<0) {
581 WCErrorCode = errno;
582 wsyserror("Unable to create socket");
583 return NULL;
585 isNonBlocking = setSocketNonBlocking(sock, True);
586 if (connect(sock, (struct sockaddr*)socketaddr, sizeof(*socketaddr)) < 0) {
587 if (errno!=EINPROGRESS) {
588 WCErrorCode = errno;
589 wsyserror("Unable to make connection to address '%s:%hu'",
590 inet_ntoa(socketaddr->sin_addr),
591 ntohs(socketaddr->sin_port));
592 close(sock);
593 return NULL;
597 cPtr = createConnectionWithSocket(sock, True);
598 cPtr->state = WCInProgress;
599 cPtr->isNonBlocking = isNonBlocking;
601 cPtr->handler.write = WMAddInputHandler(cPtr->sock, WIWriteMask,
602 inputHandler, cPtr);
604 cPtr->openTimeout.handler =
605 WMAddTimerHandler(cPtr->openTimeout.timeout*1000, openTimeout, cPtr);
607 setConnectionAddress(cPtr, socketaddr);
609 return cPtr;
613 static void
614 removeAllHandlers(WMConnection *cPtr) /*FOLD00*/
616 if (cPtr->handler.read)
617 WMDeleteInputHandler(cPtr->handler.read);
618 if (cPtr->handler.write)
619 WMDeleteInputHandler(cPtr->handler.write);
620 if (cPtr->handler.exception)
621 WMDeleteInputHandler(cPtr->handler.exception);
622 if (cPtr->openTimeout.handler)
623 WMDeleteTimerHandler(cPtr->openTimeout.handler);
624 if (cPtr->sendTimeout.handler)
625 WMDeleteTimerHandler(cPtr->sendTimeout.handler);
627 cPtr->handler.read = NULL;
628 cPtr->handler.write = NULL;
629 cPtr->handler.exception = NULL;
630 cPtr->openTimeout.handler = NULL;
631 cPtr->sendTimeout.handler = NULL;
635 void
636 WMDestroyConnection(WMConnection *cPtr) /*FOLD00*/
638 if (cPtr->closeOnRelease && cPtr->sock>=0) {
639 shutdown(cPtr->sock, SHUT_RDWR);
640 close(cPtr->sock);
643 removeAllHandlers(cPtr);
644 clearOutputQueue(cPtr);
645 WMFreeBag(cPtr->outputQueue);
647 if (cPtr->address) {
648 wfree(cPtr->address);
649 wfree(cPtr->service);
650 wfree(cPtr->protocol);
653 wrelease(cPtr);
657 void
658 WMCloseConnection(WMConnection *cPtr) /*FOLD00*/
660 if (cPtr->sock>=0) {
661 shutdown(cPtr->sock, SHUT_RDWR);
662 close(cPtr->sock);
663 cPtr->sock = -1;
666 removeAllHandlers(cPtr);
667 clearOutputQueue(cPtr);
669 cPtr->state = WCClosed;
673 WMConnection*
674 WMAcceptConnection(WMConnection *listener) /*FOLD00*/
676 struct sockaddr_in clientname;
677 int size;
678 int newSock;
679 WMConnection *newConnection;
681 if (listener->state!=WCListening) {
682 wwarning("Called 'WMAcceptConnection()' on a non-listening connection");
683 WCErrorCode = 0;
684 return NULL;
687 size = sizeof(clientname);
688 newSock = accept(listener->sock, (struct sockaddr*) &clientname, &size);
689 if (newSock<0) {
690 if (errno!=EAGAIN && errno!=EWOULDBLOCK) {
691 WCErrorCode = errno;
692 wsyserror("Could not accept connection");
693 } else {
694 WCErrorCode = 0;
696 return NULL;
699 newConnection = createConnectionWithSocket(newSock, True);
700 WMSetConnectionNonBlocking(newConnection, True);
701 newConnection->state = WCConnected;
702 setConnectionAddress(newConnection, &clientname);
704 return newConnection;
708 char*
709 WMGetConnectionAddress(WMConnection *cPtr) /*FOLD00*/
711 return cPtr->address;
715 char*
716 WMGetConnectionService(WMConnection *cPtr) /*FOLD00*/
718 return cPtr->service;
722 char*
723 WMGetConnectionProtocol(WMConnection *cPtr)
725 return cPtr->protocol;
730 WMGetConnectionSocket(WMConnection *cPtr) /*FOLD00*/
732 return cPtr->sock;
736 WMConnectionState
737 WMGetConnectionState(WMConnection *cPtr) /*FOLD00*/
739 return cPtr->state;
743 WMConnectionTimeoutState
744 WMGetConnectionTimeoutState(WMConnection *cPtr)
746 return cPtr->timeoutState;
750 Bool
751 WMEnqueueConnectionData(WMConnection *cPtr, WMData *data) /*FOLD00*/
753 wassertrv(cPtr->state!=WCNotConnected && cPtr->state!=WCListening, False);
754 wassertrv(cPtr->state!=WCInProgress && cPtr->state!=WCFailed, False);
756 if (cPtr->state!=WCConnected)
757 return False;
759 WMPutInBag(cPtr->outputQueue, WMRetainData(data));
760 return True;
765 WMSendConnectionData(WMConnection *cPtr, WMData *data) /*FOLD00*/
767 int bytes, pos, len, totalTransfer;
768 TimeoutData *tPtr = &cPtr->sendTimeout;
769 const unsigned char *dataBytes;
771 wassertrv(cPtr->state!=WCNotConnected && cPtr->state!=WCListening, -1);
772 wassertrv(cPtr->state!=WCInProgress && cPtr->state!=WCFailed, -1);
774 if (cPtr->state!=WCConnected)
775 return -1;
777 /* If we have no data just flush the queue, else try to send data */
778 if (data && WMGetDataLength(data)>0) {
779 WMPutInBag(cPtr->outputQueue, WMRetainData(data));
780 /* If there already was something in queue, and also a write input
781 * handler is established, it means we were unable to send, so
782 * return and let the write handler notify us when we can send.
784 if (WMGetBagItemCount(cPtr->outputQueue)>1 && cPtr->handler.write)
785 return 0;
788 totalTransfer = 0;
790 while (WMGetBagItemCount(cPtr->outputQueue) > 0) {
791 data = WMGetFromBag(cPtr->outputQueue, 0);
792 dataBytes = (const unsigned char *)WMDataBytes(data);
793 len = WMGetDataLength(data);
794 pos = cPtr->bufPos; /* where we're left last time */
795 while(pos < len) {
796 again:
797 bytes = write(cPtr->sock, dataBytes+pos, len - pos);
798 if(bytes<0) {
799 switch (errno) {
800 case EINTR:
801 goto again;
802 case EWOULDBLOCK:
803 /* save the position where we're left and add a timeout */
804 cPtr->bufPos = pos;
805 if (!tPtr->handler) {
806 tPtr->handler = WMAddTimerHandler(tPtr->timeout*1000,
807 sendTimeout, cPtr);
809 if (!cPtr->handler.write) {
810 cPtr->handler.write =
811 WMAddInputHandler(cPtr->sock, WIWriteMask,
812 inputHandler, cPtr);
814 return totalTransfer;
815 default:
816 WCErrorCode = errno;
817 cPtr->state = WCDied;
818 /*clearOutputQueue(cPtr);*/
819 removeAllHandlers(cPtr);
820 if (cPtr->delegate && cPtr->delegate->didDie)
821 (*cPtr->delegate->didDie)(cPtr->delegate, cPtr);
822 return -1;
825 pos += bytes;
826 totalTransfer += bytes;
828 WMReleaseData(data);
829 WMDeleteFromBag(cPtr->outputQueue, 0);
830 cPtr->bufPos = 0;
831 if (tPtr->handler) {
832 WMDeleteTimerHandler(tPtr->handler);
833 tPtr->handler = NULL;
835 if (cPtr->handler.write) {
836 WMDeleteInputHandler(cPtr->handler.write);
837 cPtr->handler.write = NULL;
841 return totalTransfer;
846 * WMGetConnectionAvailableData(connection):
848 * will return a WMData structure containing the available data on the
849 * specified connection. If connection is non-blocking (default) and no data
850 * is available when this function is called, an empty WMData is returned.
852 * If an error occurs while reading or the other side closed connection,
853 * it will return NULL.
854 * Also trying to read from an already died or closed connection is
855 * considered to be an error condition, and will return NULL.
857 WMData*
858 WMGetConnectionAvailableData(WMConnection *cPtr) /*FOLD00*/
860 char buffer[NETBUF_SIZE];
861 int nbytes;
862 WMData *aData;
864 wassertrv(cPtr->state!=WCNotConnected && cPtr->state!=WCListening, NULL);
865 wassertrv(cPtr->state!=WCInProgress && cPtr->state!=WCFailed, NULL);
867 if (cPtr->state!=WCConnected)
868 return NULL;
870 aData = NULL;
872 again:
873 nbytes = read(cPtr->sock, buffer, NETBUF_SIZE);
874 if (nbytes<0) {
875 switch (errno) {
876 case EINTR:
877 goto again;
878 case EWOULDBLOCK:
879 aData = WMCreateDataWithCapacity(0);
880 break;
881 default:
882 WCErrorCode = errno;
883 cPtr->state = WCDied;
884 removeAllHandlers(cPtr);
885 if (cPtr->delegate && cPtr->delegate->didDie)
886 (*cPtr->delegate->didDie)(cPtr->delegate, cPtr);
887 break;
889 } else if (nbytes==0) { /* the other side has closed connection */
890 cPtr->state = WCClosed;
891 removeAllHandlers(cPtr);
892 if (cPtr->delegate && cPtr->delegate->didDie)
893 (*cPtr->delegate->didDie)(cPtr->delegate, cPtr);
894 } else {
895 aData = WMCreateDataWithBytes(buffer, nbytes);
898 return aData;
902 void
903 WMSetConnectionDelegate(WMConnection *cPtr, ConnectionDelegate *delegate) /*FOLD00*/
905 wassertr(cPtr->sock >= 0);
906 /* Don't try to set the delegate multiple times */
907 wassertr(cPtr->delegate == NULL);
909 cPtr->delegate = delegate;
910 if (delegate && delegate->didReceiveInput && !cPtr->handler.read)
911 cPtr->handler.read = WMAddInputHandler(cPtr->sock, WIReadMask,
912 inputHandler, cPtr);
913 if (delegate && delegate->didCatchException && !cPtr->handler.exception)
914 cPtr->handler.exception = WMAddInputHandler(cPtr->sock, WIExceptMask,
915 inputHandler, cPtr);
919 #if 0
920 Bool
921 WMIsConnectionNonBlocking(WMConnection *cPtr) /*FOLD00*/
923 #if 1
924 int state;
926 state = fcntl(cPtr->sock, F_GETFL, 0);
928 if (state < 0) {
929 wsyserror("Failed to get socket flags with fcntl.");
930 /* If we can't use fcntl on socket, this probably also means we could
931 * not use fcntl to set non-blocking mode, and since a socket defaults
932 * to blocking when created, return False as the best assumption */
933 return False;
936 return ((state & NONBLOCK_OPT)!=0);
937 #else
938 return cPtr->isNonBlocking;
939 #endif
941 #endif
944 void
945 WMSetConnectionNonBlocking(WMConnection *cPtr, Bool flag) /*FOLD00*/
947 if (cPtr->sock < 0)
948 return;
950 if (cPtr->isNonBlocking == flag)
951 return;
953 if (setSocketNonBlocking(cPtr->sock, flag)==True)
954 cPtr->isNonBlocking = flag;
958 void*
959 WMGetConnectionClientData(WMConnection *cPtr) /*FOLD00*/
961 return cPtr->clientData;
965 void
966 WMSetConnectionClientData(WMConnection *cPtr, void *data) /*FOLD00*/
968 cPtr->clientData = data;
972 unsigned int
973 WMGetConnectionFlags(WMConnection *cPtr) /*FOLD00*/
975 return cPtr->uflags;
979 void
980 WMSetConnectionFlags(WMConnection *cPtr, unsigned int flags) /*FOLD00*/
982 cPtr->uflags = flags;
986 void
987 WMSetConnectionDefaultTimeout(unsigned int timeout)
989 if (timeout == 0) {
990 DefaultTimeout = DEF_TIMEOUT;
991 } else {
992 DefaultTimeout = timeout;
997 void
998 WMSetConnectionOpenTimeout(unsigned int timeout)
1000 if (timeout == 0) {
1001 OpenTimeout = DefaultTimeout;
1002 } else {
1003 OpenTimeout = timeout;
1008 void
1009 WMSetConnectionSendTimeout(WMConnection *cPtr, unsigned int timeout) /*FOLD00*/
1011 if (timeout == 0) {
1012 cPtr->sendTimeout.timeout = DefaultTimeout;
1013 } else {
1014 cPtr->sendTimeout.timeout = timeout;