Annotation of libwww/Library/src/HTTCP.c, revision 2.118
2.31 frystyk 1: /* HTTCP.c
2.83 frystyk 2: ** TCP SPECIFIC CODE
2.31 frystyk 3: **
2.42 frystyk 4: ** (c) COPYRIGHT MIT 1995.
2.31 frystyk 5: ** Please first read the full copyright statement in the file COPYRIGH.
2.118 ! kahan 6: ** @(#) $Id: HTTCP.c,v 2.117 2000/06/21 08:33:45 kahan Exp $
1.1 timbl 7: **
8: ** This code is in common between client and server sides.
9: **
10: ** 16 Jan 92 TBL Fix strtol() undefined on CMU Mach.
11: ** 25 Jun 92 JFG Added DECNET option through TCP socket emulation.
2.7 duns 12: ** 13 Sep 93 MD Added correct return of vmserrorno for HTInetStatus.
13: ** Added decoding of vms error message for MULTINET.
2.13 frystyk 14: ** 31 May 94 HF Added cache on host id's; now use inet_ntoa() to
15: ** HTInetString and some other fixes. Added HTDoConnect
16: ** and HTDoAccept
1.1 timbl 17: */
18:
2.36 frystyk 19: /* Library include files */
2.108 frystyk 20: #include "wwwsys.h"
2.83 frystyk 21: #include "WWWUtil.h"
22: #include "WWWCore.h"
2.58 frystyk 23: #include "HTReqMan.h"
2.54 frystyk 24: #include "HTNetMan.h"
2.36 frystyk 25: #include "HTTCP.h" /* Implemented here */
2.29 frystyk 26:
2.95 frystyk 27: #include "HTHstMan.h"
28:
2.36 frystyk 29: /* VMS stuff */
30: #ifdef VMS
31: #ifndef MULTINET
32: #define FD_SETSIZE 32
33: #else /* Multinet */
34: #define FD_SETSIZE 256
35: #endif /* Multinet */
36: #endif /* VMS */
37:
2.13 frystyk 38: /* Macros and other defines */
2.113 frystyk 39: /* x ms penalty on a multi-homed host if IP-address is unreachable */
40: #define TCP_DELAY 30000
2.24 frystyk 41:
2.113 frystyk 42: /* x ms penalty on a multi-homed host if IP-address is down for unknown reason */
43: #define TCP_PENALTY 60000
2.24 frystyk 44:
2.116 kahan 45: /* empirical study in socket call error codes
46: yovavm@contact.com : added handling for WSAEINVAL error code (Windows)
47: "When calling connect() in the second time, after the first call to
48: connect() returned WSAEWOULDBLOCK, an error of WSAEINVAL is returned.
49: It happens often on WinNT & Win95, and rarely on Win2K & Win98, where in
50: most cases the second call to connect() returns WSAEISCON (10056).
51: jose@w3.org : didn't add that test for Unix, as the connect() doc (Linux
52: and Solaris) says it's not needed.
2.93 eric 53: */
54: #ifdef _WINSOCKAPI_ /* windows */
55: #define NETCALL_ERROR(ret) (ret == SOCKET_ERROR)
56: #define NETCALL_DEADSOCKET(err) (err == WSAEBADF)
57: #define NETCALL_WOULDBLOCK(err) (err == WSAEWOULDBLOCK)
2.116 kahan 58: #define NETCALL_INVAL(err) (err == WSAEINVAL)
2.93 eric 59: #else /* _WINSOCKAPI_ unix */
60: #define NETCALL_ERROR(ret) (ret < 0)
61: #define NETCALL_DEADSOCKET(err) (err == EBADF)
62: #if defined(EAGAIN) && defined(EALREADY)
63: #define NETCALL_WOULDBLOCK(err) (err == EINPROGRESS || \
64: err == EALREADY || \
65: err == EAGAIN)
66: #else /* (EAGAIN && EALREADY) */
67: #ifdef EALREADY
68: #define NETCALL_WOULDBLOCK(err) (err == EINPROGRESS || err == EALREADY)
69: #else /* EALREADY */
70: #ifdef EAGAIN
71: #define NETCALL_WOULDBLOCK(err) (err == EINPROGRESS || err == EAGAIN)
72: #else /* EAGAIN */
73: #define NETCALL_WOULDBLOCK(err) (err == EINPROGRESS)
74: #endif /* !EAGAIN */
75: #endif /* !EALREADY */
76: #endif /* !(EAGAIN && EALREADY) */
77: #endif /* !_WINSOCKAPI_ done */
2.112 frystyk 78:
79: #if defined(__svr4__) || defined (_WINSOCKAPI_)
80: #define HT_HOSTUNREACHABLE(e) ((e)==ECONNREFUSED || (e)==ETIMEDOUT || \
81: (e)==ENETUNREACH || (e)==EHOSTUNREACH || \
82: (e)==EHOSTDOWN)
83: #else
84: #define HT_HOSTUNREACHABLE(e) ((e)==ECONNREFUSED || (e)==ETIMEDOUT || \
85: (e)==ENETUNREACH || (e)==EHOSTUNREACH || \
86: (e)==EHOSTDOWN || (e)==EINVAL)
87: #endif
88:
2.13 frystyk 89: /* ------------------------------------------------------------------------- */
2.19 frystyk 90: /* CONNECTION ESTABLISHMENT MANAGEMENT */
91: /* ------------------------------------------------------------------------- */
2.13 frystyk 92:
2.95 frystyk 93: /* _makeSocket - create a socket, if !preemptive, set FIONBIO
2.115 frystyk 94: ** returns sockfd or INVSOC if error
95: */
96: PRIVATE int _makeSocket (HTHost * host, HTRequest * request, int preemptive)
2.93 eric 97: {
2.95 frystyk 98: int status = 1;
2.115 frystyk 99: SOCKET sockfd = INVSOC;
2.93 eric 100: #ifdef DECNET
2.95 frystyk 101: if ((sockfd=socket(AF_DECnet, SOCK_STREAM, 0))==INVSOC)
2.93 eric 102: #else
2.95 frystyk 103: if ((sockfd=socket(AF_INET, SOCK_STREAM,IPPROTO_TCP))==INVSOC)
2.93 eric 104: #endif
2.112 frystyk 105: {
106: HTRequest_addSystemError(request, ERR_FATAL, socerrno, NO, "socket");
2.115 frystyk 107: return INVSOC;
2.112 frystyk 108: }
2.114 frystyk 109: HTTRACE(PROT_TRACE, "Socket...... Created %d\n" _ sockfd);
2.93 eric 110:
2.95 frystyk 111: /* Increase the number of sockets by one */
112: HTNet_increaseSocket();
2.97 frystyk 113:
114: /*
115: ** If we have compiled without Nagle's algorithm then try and turn
116: ** it off now
117: */
2.105 frystyk 118: #if defined(HT_NO_NAGLE) && defined(HAVE_SETSOCKOPT) && defined(TCP_NODELAY)
2.97 frystyk 119: {
120: int disable = 1;
2.101 frystyk 121: status = setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY,
122: (char *) &disable, sizeof(int));
2.97 frystyk 123: if (status == -1) {
2.114 frystyk 124: HTTRACE(PROT_TRACE, "Socket...... Could not disable Nagle's algorithm - error %d\n" _
2.97 frystyk 125: sockfd);
126: } else {
2.114 frystyk 127: HTTRACE(PROT_TRACE, "Socket...... Turned off Nagle's algorithm\n");
2.97 frystyk 128: }
129: }
130: #endif
2.93 eric 131:
2.95 frystyk 132: /* If non-blocking protocol then change socket status
133: ** I use fcntl() so that I can ask the status before I set it.
134: ** See W. Richard Stevens (Advan. Prog. in UNIX environment, p.364)
135: ** Be CAREFULL with the old `O_NDELAY' - it will not work as read()
136: ** returns 0 when blocking and NOT -1. FNDELAY is ONLY for BSD and
137: ** does NOT work on SVR4 systems. O_NONBLOCK is POSIX.
138: */
139: if (!preemptive) {
2.93 eric 140: #ifdef _WINSOCKAPI_
2.95 frystyk 141: { /* begin windows scope */
142: long levents = FD_READ | FD_WRITE | FD_ACCEPT |
143: FD_CONNECT | FD_CLOSE ;
144: int rv = 0 ;
145: u_long one = 1;
146:
147: status = ioctlsocket(sockfd, FIONBIO, &one) ==
148: SOCKET_ERROR ? -1 : 0;
149: } /* end scope */
2.93 eric 150: #else /* _WINSOCKAPI_ */
151: #if defined(VMS)
2.95 frystyk 152: {
153: int enable = 1;
154: status = IOCTL(sockfd, FIONBIO, &enable);
155: }
2.93 eric 156: #else /* VMS */
2.95 frystyk 157: if((status = fcntl(sockfd, F_GETFL, 0)) != -1) {
2.93 eric 158: #ifdef O_NONBLOCK
2.95 frystyk 159: status |= O_NONBLOCK; /* POSIX */
2.93 eric 160: #else /* O_NONBLOCK */
161: #ifdef F_NDELAY
162: status |= F_NDELAY; /* BSD */
163: #endif /* F_NDELAY */
164: #endif /* !O_NONBLOCK */
2.95 frystyk 165: status = fcntl(sockfd, F_SETFL, status);
166: }
2.93 eric 167: #endif /* !VMS */
168: #endif /* !_WINSOCKAPI_ */
2.114 frystyk 169: HTTRACE(PROT_TRACE, "Socket...... %slocking socket\n" _ status == -1 ? "B" : "Non-b");
2.95 frystyk 170: } else
2.114 frystyk 171: HTTRACE(PROT_TRACE, "Socket...... Blocking socket\n");
2.95 frystyk 172:
2.115 frystyk 173: return sockfd;
174: }
2.93 eric 175:
2.115 frystyk 176: /*
177: ** Associate the channel with the host and create an input and and output stream
178: ** for this host/channel
179: */
180: PRIVATE BOOL createChannelAndTransportStreams (HTHost * host, SOCKET sockfd, HTTransport * trans)
181: {
182: if (host && sockfd!=INVSOC && trans) {
183: HTHost_setChannel(host, HTChannel_new(sockfd, NULL, YES));
184: HTHost_getInput(host, trans, NULL, 0);
185: HTHost_getOutput(host, trans, NULL, 0);
186: return YES;
187: }
188: return NO;
2.93 eric 189: }
190:
2.13 frystyk 191: /* HTDoConnect()
192: **
193: ** Note: Any port indication in URL, e.g., as `host:port' overwrites
2.54 frystyk 194: ** the default port value.
2.13 frystyk 195: **
2.40 frystyk 196: ** returns HT_ERROR Error has occured or interrupted
197: ** HT_OK if connected
198: ** HT_WOULD_BLOCK if operation would have blocked
2.13 frystyk 199: */
2.115 frystyk 200: PUBLIC int HTDoConnect (HTNet * net)
2.13 frystyk 201: {
2.115 frystyk 202: HTHost * host = HTNet_host(net);
2.88 frystyk 203: HTRequest * request = HTNet_request(net);
2.115 frystyk 204: char * hostname = HTHost_name(host);
2.95 frystyk 205: int preemptive = net->preemptive;
206: int status = HT_OK;
2.13 frystyk 207:
2.55 frystyk 208: /* Jump into the state machine */
209: while (1) {
2.115 frystyk 210: switch (host->tcpstate) {
2.55 frystyk 211: case TCP_BEGIN:
2.91 frystyk 212: {
213: /*
214: ** Add the net object to the host object found above. If the
215: ** host is idle then we can start the request right away,
216: ** otherwise we must wait until it is free.
217: */
2.115 frystyk 218: if ((status = HTHost_addNet(host, net)) == HT_PENDING)
2.114 frystyk 219: HTTRACE(PROT_TRACE, "HTDoConnect. Pending...\n");
2.91 frystyk 220:
221: /*
2.95 frystyk 222: ** If we are pending then return here, otherwise go to next state
2.91 frystyk 223: ** which is setting up a channel
224: */
2.115 frystyk 225: host->tcpstate = TCP_CHANNEL;
226: HTTRACE(PROT_TRACE, "HTHost %p going to state TCP_CHANNEL.\n" _ host);
2.91 frystyk 227: if (status == HT_PENDING) return HT_PENDING;
228: }
229: break;
2.83 frystyk 230:
2.91 frystyk 231: case TCP_CHANNEL:
2.83 frystyk 232: /*
2.91 frystyk 233: ** The next state depends on whether we have a connection
234: ** or not - if so then we can jump directly to connect() to
235: ** test it - otherwise we must around DNS to get the name
2.93 eric 236: ** Resolved
2.83 frystyk 237: */
2.115 frystyk 238: if (HTHost_channel(host) == NULL) {
239: host->tcpstate = TCP_DNS;
240: HTTRACE(PROT_TRACE, "HTHost %p going to state TCP_DNS.\n" _ host);
2.95 frystyk 241: } else {
242:
243: /*
244: ** There is now one more using the channel
245: */
2.115 frystyk 246: HTChannel_upSemaphore(host->channel);
2.95 frystyk 247:
248: /*
249: ** We are now all set and can jump to connected mode
250: */
2.115 frystyk 251: host->tcpstate = TCP_CONNECTED;
252: HTTRACE(PROT_TRACE, "HTHost %p going to state TCP_CONNECTED.\n" _ host);
2.95 frystyk 253: }
2.115 frystyk 254: hostname = HTHost_name(host);
2.55 frystyk 255: break;
256:
2.91 frystyk 257: case TCP_DNS:
2.115 frystyk 258: if ((status = HTParseInet(host, hostname, request)) < 0) {
2.114 frystyk 259: HTTRACE(PROT_TRACE, "HTDoConnect. Can't locate `%s\'\n" _ hostname);
2.91 frystyk 260: HTRequest_addError(request, ERR_FATAL, NO,
261: HTERR_NO_REMOTE_HOST,
262: (void *) hostname, strlen(hostname),
263: "HTDoConnect");
2.115 frystyk 264: host->tcpstate = TCP_DNS_ERROR;
265: HTTRACE(PROT_TRACE, "HTHost %p going to state TCP_ERROR.\n" _ host);
2.27 frystyk 266: break;
2.55 frystyk 267: }
2.115 frystyk 268: if (!HTHost_retry(host) && status > 1) /* If multiple homes */
269: HTHost_setRetry(host, status);
270: host->tcpstate = TCP_NEED_SOCKET;
271: HTTRACE(PROT_TRACE, "HTHost %p going to state TCP_NEED_SOCKET.\n" _ host);
2.55 frystyk 272: break;
273:
2.115 frystyk 274: case TCP_NEED_SOCKET:
275: {
276: SOCKET sockfd;
277:
278: /* Create a new socket */
279: if ((sockfd = _makeSocket(host, request, preemptive)) == INVSOC) {
280: host->tcpstate = TCP_ERROR;
281: break;
282: }
283:
284: /* Create channnel and streams */
285: createChannelAndTransportStreams (host, sockfd, net->transport);
2.95 frystyk 286:
2.27 frystyk 287: /* If multi-homed host then start timer on connection */
2.115 frystyk 288: if (HTHost_retry(host)) host->connecttime = HTGetTimeInMillis();
2.65 frystyk 289:
2.115 frystyk 290: /* Progress notification */
2.65 frystyk 291: {
292: HTAlertCallback *cbf = HTAlert_find(HT_PROG_CONNECT);
2.95 frystyk 293: if (cbf) (*cbf)(request, HT_PROG_CONNECT, HT_MSG_NULL,
2.91 frystyk 294: NULL, hostname, NULL);
2.65 frystyk 295: }
2.115 frystyk 296: host->tcpstate = TCP_NEED_CONNECT;
297: HTTRACE(PROT_TRACE, "HTHost %p going to state TCP_NEED_CONNECT.\n" _ host);
2.55 frystyk 298: break;
2.115 frystyk 299: }
300:
301: case TCP_NEED_CONNECT:
2.118 ! kahan 302: #ifdef _WINSOCKAPI_
! 303: HTHost_register(host, net, HTEvent_CONNECT);
! 304: #endif /* _WINSOCKAPI_ */
2.115 frystyk 305: status = connect(HTChannel_socket(host->channel), (struct sockaddr *) &host->sock_addr,
306: sizeof(host->sock_addr));
2.55 frystyk 307: /*
308: * According to the Sun man page for connect:
309: * EINPROGRESS The socket is non-blocking and the con-
310: * nection cannot be completed immediately.
311: * It is possible to select(2) for comple-
312: * tion by selecting the socket for writ-
313: * ing.
314: * According to the Motorola SVR4 man page for connect:
315: * EAGAIN The socket is non-blocking and the con-
316: * nection cannot be completed immediately.
317: * It is possible to select for completion
318: * by selecting the socket for writing.
319: * However, this is only possible if the
320: * socket STREAMS module is the topmost
321: * module on the protocol stack with a
322: * write service procedure. This will be
323: * the normal case.
324: */
2.93 eric 325: if (NETCALL_ERROR(status))
2.55 frystyk 326: {
2.93 eric 327: if (NETCALL_WOULDBLOCK(socerrno))
2.55 frystyk 328: {
2.114 frystyk 329: HTTRACE(PROT_TRACE, "HTDoConnect. WOULD BLOCK `%s'\n" _ hostname);
2.118 ! kahan 330: #ifndef _WINSOCKAPI_
! 331:
2.115 frystyk 332: HTHost_register(host, net, HTEvent_CONNECT);
2.118 ! kahan 333: #endif /* _WINSOCKAPI_ */
2.55 frystyk 334: return HT_WOULD_BLOCK;
335: }
2.116 kahan 336: #ifdef _WINSOCKAPI_
337: /*
338: * yovavm@contact.com
339: *
340: * According to Microsoft docs, the error code WSAEALREADY is
341: * described as:
342: * "A nonblocking connect call is in progress on the specified
343: * socket. Note In order to preserve backward compatibility,
344: * this error is reported as WSAEINVAL to Windows Sockets 1.1
345: * applications that link to either Winsock.dll or
346: * Wsock32.dll."
347: */
348: if (NETCALL_INVAL(socerrno)) {
349: host->tcpstate = TCP_CONNECTED;
2.117 kahan 350: HTTRACE(PROT_TRACE, "Connection to HTHost %p is already in progress.\n" _ host);
2.116 kahan 351: break;
352: }
353: #endif /* _WINSOCKAPI_ */
354:
2.55 frystyk 355: if (socerrno == EISCONN) {
2.115 frystyk 356: host->tcpstate = TCP_CONNECTED;
357: HTTRACE(PROT_TRACE, "HTHost %p going to state TCP_CONNECTED.\n" _ host);
2.55 frystyk 358: break;
359: }
2.93 eric 360: if (NETCALL_DEADSOCKET(socerrno)) /* We lost the socket */
2.58 frystyk 361: {
2.115 frystyk 362: host->tcpstate = TCP_NEED_SOCKET;
363: HTTRACE(PROT_TRACE, "HTHost %p going to state TCP_NEED_SOCKET.\n" _ host);
2.55 frystyk 364: break;
2.27 frystyk 365: }
2.115 frystyk 366: if (HTHost_retry(host)) {
367: host->connecttime = HTGetTimeInMillis() - host->connecttime;
2.54 frystyk 368: /* Added EINVAL `invalid argument' as this is what I
369: get back from a non-blocking connect where I should
370: get `connection refused' on BSD. SVR4 gives SIG_PIPE */
2.112 frystyk 371: if (HT_HOSTUNREACHABLE(socerrno))
2.115 frystyk 372: host->connecttime += TCP_DELAY;
2.54 frystyk 373: else
2.115 frystyk 374: host->connecttime += TCP_PENALTY;
375: HTDNS_updateWeigths(host->dns, HTHost_home(host), host->connecttime);
2.55 frystyk 376: }
2.115 frystyk 377: host->tcpstate = TCP_ERROR;
378: HTTRACE(PROT_TRACE, "HTHost %p going to state TCP_ERROR.\n" _ host);
2.95 frystyk 379: } else {
2.115 frystyk 380: host->tcpstate = TCP_CONNECTED;
381: HTTRACE(PROT_TRACE, "HTHost %p going to state TCP_CONNECTED.\n" _ host);
2.95 frystyk 382: }
2.55 frystyk 383: break;
384:
385: case TCP_CONNECTED:
2.115 frystyk 386: HTHost_unregister(host, net, HTEvent_CONNECT);
387: if (HTHost_retry(host)) {
388: host->connecttime = HTGetTimeInMillis() - host->connecttime;
389: HTDNS_updateWeigths(host->dns, HTHost_home(host), host->connecttime);
390: }
391: HTHost_setRetry(host, 0);
392: host->tcpstate = TCP_IN_USE;
393: HTTRACE(PROT_TRACE, "HTHost %p connected.\n" _ host);
2.55 frystyk 394: return HT_OK;
395: break;
2.100 eric 396:
2.115 frystyk 397: /* Once a host is connected, subsequent connections are immediately OK */
2.100 eric 398: case TCP_IN_USE:
2.115 frystyk 399: if ((status = HTHost_addNet(host, net)) == HT_PENDING) {
2.114 frystyk 400: HTTRACE(PROT_TRACE, "HTDoConnect. Pending...\n");
2.100 eric 401: return HT_PENDING;
402: }
403:
2.115 frystyk 404: HTChannel_upSemaphore(host->channel);
2.100 eric 405: return HT_OK;
2.109 frystyk 406:
407: case TCP_DNS_ERROR:
2.115 frystyk 408: HTHost_setRetry(host, 0);
409: host->tcpstate = TCP_BEGIN;
410: HTTRACE(PROT_TRACE, "HTHost %p going to state TCP_BEGIN.\n" _ host);
2.112 frystyk 411: return HT_NO_HOST;
2.109 frystyk 412: break;
2.55 frystyk 413:
2.56 frystyk 414: case TCP_NEED_BIND:
415: case TCP_NEED_LISTEN:
2.55 frystyk 416: case TCP_ERROR:
2.115 frystyk 417: if (HTChannel_socket(host->channel) != INVSOC) {
418: NETCLOSE(HTChannel_socket(host->channel));
419: if (HTHost_isPersistent(host)) { /* Inherited socket */
420: HTHost_setPersistent(host, NO, HT_TP_SINGLE);
421: host->tcpstate = TCP_NEED_SOCKET;
422: HTTRACE(PROT_TRACE, "HTHost %p going to state TCP_NEED_SOCKET.\n" _ host);
2.55 frystyk 423: break;
424: }
425: }
426:
427: /* Do we have more homes to try? */
2.115 frystyk 428: HTHost_decreaseRetry(host);
429: if (HT_HOSTUNREACHABLE(socerrno) && HTHost_retry(host) > 0) {
2.112 frystyk 430: HTRequest_addSystemError(request, ERR_NON_FATAL, socerrno, NO,"connect");
2.115 frystyk 431: host->tcpstate = TCP_DNS;
432: HTTRACE(PROT_TRACE, "HTHost %p going to state TCP_DNS.\n" _ host);
2.24 frystyk 433: break;
434: }
2.112 frystyk 435: HTRequest_addSystemError(request, ERR_FATAL, socerrno, NO, "connect");
2.91 frystyk 436: HTDNS_delete(hostname);
2.115 frystyk 437: HTHost_setRetry(host, 0);
438: host->tcpstate = TCP_BEGIN;
439: HTTRACE(PROT_TRACE, "HTHost %p going to state TCP_BEGIN.\n" _ host);
2.55 frystyk 440: return HT_ERROR;
441: break;
2.24 frystyk 442: }
2.55 frystyk 443: }
2.13 frystyk 444: }
445:
2.56 frystyk 446: /* HTDoAccept()
447: ** ------------
448: ** This function makes a non-blocking accept which will turn up as ready
449: ** read in the select.
450: ** Returns
451: ** HT_ERROR Error has occured or interrupted
452: ** HT_OK if connected
453: ** HT_WOULD_BLOCK if operation would have blocked
2.13 frystyk 454: */
2.115 frystyk 455: PUBLIC int HTDoAccept (HTNet * listening, HTNet * accepting)
2.13 frystyk 456: {
2.115 frystyk 457: HTHost * host = HTNet_host(listening);
458: HTRequest * request = HTNet_request(accepting);
459: int size = sizeof(host->sock_addr);
2.13 frystyk 460: int status;
2.115 frystyk 461: if (!request || HTNet_socket(listening)==INVSOC) {
2.114 frystyk 462: HTTRACE(PROT_TRACE, "HTDoAccept.. Invalid socket\n");
2.56 frystyk 463: return HT_ERROR;
2.13 frystyk 464: }
2.65 frystyk 465:
2.115 frystyk 466: status = accept(HTNet_socket(listening), (struct sockaddr *) &host->sock_addr, &size);
2.93 eric 467: if (NETCALL_ERROR(status))
2.23 duns 468: {
2.93 eric 469: if (NETCALL_WOULDBLOCK(socerrno))
2.56 frystyk 470: {
2.115 frystyk 471: HTTRACE(PROT_TRACE, "HTDoAccept.. WOULD BLOCK %d\n" _ HTNet_socket(listening));
472: HTHost_register(host, listening, HTEvent_ACCEPT);
2.56 frystyk 473: return HT_WOULD_BLOCK;
474: }
2.65 frystyk 475: HTRequest_addSystemError(request, ERR_WARN, socerrno, YES, "accept");
2.114 frystyk 476: HTTRACE(PROT_TRACE, "HTDoAccept.. Accept failed\n");
2.56 frystyk 477: return HT_ERROR;
2.23 duns 478: }
2.71 frystyk 479:
2.114 frystyk 480: HTTRACE(PROT_TRACE, "Accepted.... socket %d\n" _ status);
2.88 frystyk 481:
2.115 frystyk 482: /* Remember the new socket we got and close the old one */
483: NETCLOSE(HTNet_socket(accepting));
484: HTNet_setSocket(accepting, status);
2.83 frystyk 485:
2.56 frystyk 486: return HT_OK;
487: }
488:
489:
490: /* HTDoListen
491: ** ----------
2.115 frystyk 492: ** Listens on the specified port.
2.56 frystyk 493: ** returns HT_ERROR Error has occured or interrupted
494: ** HT_OK if connected
495: */
2.115 frystyk 496: PUBLIC int HTDoListen (HTNet * listening, HTNet * accepting, int backlog)
2.56 frystyk 497: {
2.115 frystyk 498: HTHost * host = HTNet_host(listening);
499: HTRequest * request = HTNet_request(listening);
500: int preemptive = listening->preemptive;
2.111 frystyk 501: int status = HT_OK;
2.115 frystyk 502: char * hostname = HTHost_name(host);
2.56 frystyk 503:
504: /* Jump into the state machine */
505: while (1) {
2.115 frystyk 506: switch (host->tcpstate) {
2.111 frystyk 507: case TCP_BEGIN:
508: {
509: /*
510: ** Add the net object to the host object found above. If the
511: ** host is idle then we can start the request right away,
512: ** otherwise we must wait until it is free.
513: */
2.115 frystyk 514: if ((status = HTHost_addNet(host, accepting)) == HT_PENDING)
2.114 frystyk 515: HTTRACE(PROT_TRACE, "HTDoListen.. Pending...\n");
2.111 frystyk 516:
517: /*
518: ** If we are pending then return here, otherwise go to next state
519: ** which is setting up a channel
520: */
2.115 frystyk 521: host->tcpstate = TCP_CHANNEL;
522: HTTRACE(PROT_TRACE, "HTHost %p going to state TCP_CHANNEL.\n" _ host);
2.111 frystyk 523: if (status == HT_PENDING) return HT_PENDING;
524: }
525: break;
526:
527: case TCP_CHANNEL:
528: /*
529: ** The next state depends on whether we have a connection or not.
530: */
2.115 frystyk 531: if (HTHost_channel(host) == NULL) {
532: host->tcpstate = TCP_NEED_SOCKET;
533: HTTRACE(PROT_TRACE, "HTHost %p going to state TCP_SOCKET.\n" _ host);
2.111 frystyk 534: } else {
535:
536: /*
537: ** There is now one more using the channel
538: */
2.115 frystyk 539: HTChannel_upSemaphore(host->channel);
2.111 frystyk 540:
541: /*
542: ** We are now all set and can jump to connected mode
543: */
2.115 frystyk 544: host->tcpstate = TCP_CONNECTED;
545: HTTRACE(PROT_TRACE, "HTHost %p going to state TCP_CONNECTED.\n" _ host);
2.56 frystyk 546: }
2.115 frystyk 547: hostname = HTHost_name(host);
2.56 frystyk 548: break;
549:
2.111 frystyk 550: case TCP_NEED_SOCKET:
2.115 frystyk 551: {
552: SOCKET sockfd;
553:
554: /* Create a new socket */
555: if ((sockfd = _makeSocket(host, request, preemptive)) == INVSOC) {
556: host->tcpstate = TCP_ERROR;
2.111 frystyk 557: break;
2.112 frystyk 558: }
2.111 frystyk 559:
2.115 frystyk 560: /* Create channnel and streams */
561: createChannelAndTransportStreams (host, sockfd, accepting->transport);
562:
563: /* Progress nofitication */
2.111 frystyk 564: {
565: HTAlertCallback *cbf = HTAlert_find(HT_PROG_ACCEPT);
566: if (cbf) (*cbf)(request, HT_PROG_ACCEPT, HT_MSG_NULL,
567: NULL, hostname, NULL);
568: }
2.115 frystyk 569: host->tcpstate = TCP_NEED_BIND;
570: HTTRACE(PROT_TRACE, "HTHost %p going to state TCP_NEED_BIND\n" _ host);
2.56 frystyk 571: break;
2.115 frystyk 572: }
2.56 frystyk 573:
2.111 frystyk 574: case TCP_NEED_BIND:
2.115 frystyk 575: HTTRACE(PROT_TRACE, "Socket...... Binding socket %d\n" _ HTNet_socket(listening));
576: status = bind(HTNet_socket(listening), (struct sockaddr *) &host->sock_addr,
577: sizeof(host->sock_addr));
2.111 frystyk 578: if (NETCALL_ERROR(status)) {
2.114 frystyk 579: HTTRACE(PROT_TRACE, "Socket...... Bind failed %d\n" _ socerrno);
2.115 frystyk 580: host->tcpstate = TCP_ERROR;
2.111 frystyk 581: } else {
2.115 frystyk 582: HTTRACE(PROT_TRACE, "Socket...... Starting listening on socket %d\n" _ HTNet_socket(listening));
583: host->tcpstate = TCP_NEED_LISTEN;
2.111 frystyk 584: }
2.56 frystyk 585: break;
586:
2.111 frystyk 587: case TCP_NEED_LISTEN:
2.115 frystyk 588: status = listen(HTNet_socket(listening), backlog);
2.93 eric 589: if (NETCALL_ERROR(status))
2.115 frystyk 590: host->tcpstate = TCP_ERROR;
2.56 frystyk 591: else
2.115 frystyk 592: host->tcpstate = TCP_CONNECTED;
2.56 frystyk 593: break;
594:
2.111 frystyk 595: case TCP_CONNECTED:
2.115 frystyk 596: HTHost_unregister(host, listening, HTEvent_ACCEPT);
597: HTHost_setRetry(host, 0);
598: host->tcpstate = TCP_IN_USE;
599: HTTRACE(PROT_TRACE, "HTHost %p listening.\n" _ host);
2.56 frystyk 600: return HT_OK;
601: break;
2.13 frystyk 602:
2.111 frystyk 603: /* once a host is connected, subsequent connections are immediately OK */
604: case TCP_IN_USE:
2.115 frystyk 605: if ((status = HTHost_addNet(host, accepting)) == HT_PENDING) {
2.114 frystyk 606: HTTRACE(PROT_TRACE, "HTDoListen.. Pending...\n");
2.111 frystyk 607: return HT_PENDING;
608: }
609:
2.115 frystyk 610: HTChannel_upSemaphore(host->channel);
2.111 frystyk 611: return HT_OK;
612:
613: case TCP_NEED_CONNECT:
614: case TCP_DNS:
615: case TCP_DNS_ERROR:
616: case TCP_ERROR:
2.115 frystyk 617: if (HTChannel_socket(host->channel) != INVSOC) {
618: NETCLOSE(HTChannel_socket(host->channel));
619: if (HTHost_isPersistent(host)) { /* Inherited socket */
620: HTHost_setPersistent(host, NO, HT_TP_SINGLE);
621: host->tcpstate = TCP_NEED_SOCKET;
622: HTTRACE(PROT_TRACE, "HTHost %p going to state TCP_NEED_SOCKET.\n" _ host);
2.111 frystyk 623: break;
624: }
625: }
626:
627: HTRequest_addSystemError(request, ERR_FATAL, socerrno, NO, "accept");
2.115 frystyk 628: HTHost_setRetry(host, 0);
629: host->tcpstate = TCP_BEGIN;
630: HTTRACE(PROT_TRACE, "HTHost %p going to state TCP_BEGIN.\n" _ host);
2.56 frystyk 631: return HT_ERROR;
632: break;
633: }
2.38 frystyk 634: }
1.1 timbl 635: }
636:
2.83 frystyk 637: /* HTDoClose
638: ** ---------
639: ** Closes a file descriptor whatever means are available on the current
640: ** platform. If we have unix file descriptors then use this otherwise use
641: ** the ANSI C file descriptors
642: **
643: ** returns HT_ERROR Error has occured or interrupted
644: ** HT_OK if connected
645: ** HT_WOULD_BLOCK if operation would have blocked
646: */
2.87 frystyk 647: PUBLIC int HTDoClose (HTNet * net)
2.83 frystyk 648: {
649: int status = -1;
2.95 frystyk 650: if (net && HTNet_socket(net) != INVSOC) {
2.114 frystyk 651: HTTRACE(PROT_TRACE, "HTDoClose... Close %d\n" _ HTNet_socket(net));
2.95 frystyk 652: status = NETCLOSE(HTNet_socket(net));
653: /* HTEvent_unregister(HTNet_socket(net), (SockOps) FD_ALL); */
2.91 frystyk 654: HTNet_decreaseSocket();
2.95 frystyk 655: HTNet_setSocket(net, INVSOC);
2.91 frystyk 656:
657: /*
658: ** As we have a socket available we check for whether
659: ** we can start any pending requests. We do this by asking for
660: ** pending Host objects. If none then use the current object
661: */
662: HTHost_launchPending(net->host);
663:
664: } else
2.114 frystyk 665: HTTRACE(PROT_TRACE, "HTDoClose... No pending requests\n");
2.83 frystyk 666: return status < 0 ? HT_ERROR : HT_OK;
667: }
2.91 frystyk 668:
2.83 frystyk 669:
Webmaster