Annotation of libwww/Library/src/HTTCP.c, revision 2.115.2.1
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.115.2.1! kahan 6: ** @(#) $Id: HTTCP.c,v 2.116 2000/06/07 10:14:27 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.115.2.1! 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.115.2.1! 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:
302: status = connect(HTChannel_socket(host->channel), (struct sockaddr *) &host->sock_addr,
303: sizeof(host->sock_addr));
2.55 frystyk 304: /*
305: * According to the Sun man page for connect:
306: * EINPROGRESS The socket is non-blocking and the con-
307: * nection cannot be completed immediately.
308: * It is possible to select(2) for comple-
309: * tion by selecting the socket for writ-
310: * ing.
311: * According to the Motorola SVR4 man page for connect:
312: * EAGAIN The socket is non-blocking and the con-
313: * nection cannot be completed immediately.
314: * It is possible to select for completion
315: * by selecting the socket for writing.
316: * However, this is only possible if the
317: * socket STREAMS module is the topmost
318: * module on the protocol stack with a
319: * write service procedure. This will be
320: * the normal case.
321: */
2.93 eric 322: if (NETCALL_ERROR(status))
2.55 frystyk 323: {
2.93 eric 324: if (NETCALL_WOULDBLOCK(socerrno))
2.55 frystyk 325: {
2.114 frystyk 326: HTTRACE(PROT_TRACE, "HTDoConnect. WOULD BLOCK `%s'\n" _ hostname);
2.115 frystyk 327: HTHost_register(host, net, HTEvent_CONNECT);
2.55 frystyk 328: return HT_WOULD_BLOCK;
329: }
2.115.2.1! kahan 330: #ifdef _WINSOCKAPI_
! 331: /*
! 332: * yovavm@contact.com
! 333: *
! 334: * According to Microsoft docs, the error code WSAEALREADY is
! 335: * described as:
! 336: * "A nonblocking connect call is in progress on the specified
! 337: * socket. Note In order to preserve backward compatibility,
! 338: * this error is reported as WSAEINVAL to Windows Sockets 1.1
! 339: * applications that link to either Winsock.dll or
! 340: * Wsock32.dll."
! 341: */
! 342: if (NETCALL_INVAL(socerrno)) {
! 343: host->tcpstate = TCP_CONNECTED;
! 344: HTTRACE(PROT_TRACE, "Connection to HTHost %p is already in
! 345: progress.\n" _ host);
! 346: break;
! 347: }
! 348: #endif /* _WINSOCKAPI_ */
! 349:
2.55 frystyk 350: if (socerrno == EISCONN) {
2.115 frystyk 351: host->tcpstate = TCP_CONNECTED;
352: HTTRACE(PROT_TRACE, "HTHost %p going to state TCP_CONNECTED.\n" _ host);
2.55 frystyk 353: break;
354: }
2.93 eric 355: if (NETCALL_DEADSOCKET(socerrno)) /* We lost the socket */
2.58 frystyk 356: {
2.115 frystyk 357: host->tcpstate = TCP_NEED_SOCKET;
358: HTTRACE(PROT_TRACE, "HTHost %p going to state TCP_NEED_SOCKET.\n" _ host);
2.55 frystyk 359: break;
2.27 frystyk 360: }
2.115 frystyk 361: if (HTHost_retry(host)) {
362: host->connecttime = HTGetTimeInMillis() - host->connecttime;
2.54 frystyk 363: /* Added EINVAL `invalid argument' as this is what I
364: get back from a non-blocking connect where I should
365: get `connection refused' on BSD. SVR4 gives SIG_PIPE */
2.112 frystyk 366: if (HT_HOSTUNREACHABLE(socerrno))
2.115 frystyk 367: host->connecttime += TCP_DELAY;
2.54 frystyk 368: else
2.115 frystyk 369: host->connecttime += TCP_PENALTY;
370: HTDNS_updateWeigths(host->dns, HTHost_home(host), host->connecttime);
2.55 frystyk 371: }
2.115 frystyk 372: host->tcpstate = TCP_ERROR;
373: HTTRACE(PROT_TRACE, "HTHost %p going to state TCP_ERROR.\n" _ host);
2.95 frystyk 374: } else {
2.115 frystyk 375: host->tcpstate = TCP_CONNECTED;
376: HTTRACE(PROT_TRACE, "HTHost %p going to state TCP_CONNECTED.\n" _ host);
2.95 frystyk 377: }
2.55 frystyk 378: break;
379:
380: case TCP_CONNECTED:
2.115 frystyk 381: HTHost_unregister(host, net, HTEvent_CONNECT);
382: if (HTHost_retry(host)) {
383: host->connecttime = HTGetTimeInMillis() - host->connecttime;
384: HTDNS_updateWeigths(host->dns, HTHost_home(host), host->connecttime);
385: }
386: HTHost_setRetry(host, 0);
387: host->tcpstate = TCP_IN_USE;
388: HTTRACE(PROT_TRACE, "HTHost %p connected.\n" _ host);
2.55 frystyk 389: return HT_OK;
390: break;
2.100 eric 391:
2.115 frystyk 392: /* Once a host is connected, subsequent connections are immediately OK */
2.100 eric 393: case TCP_IN_USE:
2.115 frystyk 394: if ((status = HTHost_addNet(host, net)) == HT_PENDING) {
2.114 frystyk 395: HTTRACE(PROT_TRACE, "HTDoConnect. Pending...\n");
2.100 eric 396: return HT_PENDING;
397: }
398:
2.115 frystyk 399: HTChannel_upSemaphore(host->channel);
2.100 eric 400: return HT_OK;
2.109 frystyk 401:
402: case TCP_DNS_ERROR:
2.115 frystyk 403: HTHost_setRetry(host, 0);
404: host->tcpstate = TCP_BEGIN;
405: HTTRACE(PROT_TRACE, "HTHost %p going to state TCP_BEGIN.\n" _ host);
2.112 frystyk 406: return HT_NO_HOST;
2.109 frystyk 407: break;
2.55 frystyk 408:
2.56 frystyk 409: case TCP_NEED_BIND:
410: case TCP_NEED_LISTEN:
2.55 frystyk 411: case TCP_ERROR:
2.115 frystyk 412: if (HTChannel_socket(host->channel) != INVSOC) {
413: NETCLOSE(HTChannel_socket(host->channel));
414: if (HTHost_isPersistent(host)) { /* Inherited socket */
415: HTHost_setPersistent(host, NO, HT_TP_SINGLE);
416: host->tcpstate = TCP_NEED_SOCKET;
417: HTTRACE(PROT_TRACE, "HTHost %p going to state TCP_NEED_SOCKET.\n" _ host);
2.55 frystyk 418: break;
419: }
420: }
421:
422: /* Do we have more homes to try? */
2.115 frystyk 423: HTHost_decreaseRetry(host);
424: if (HT_HOSTUNREACHABLE(socerrno) && HTHost_retry(host) > 0) {
2.112 frystyk 425: HTRequest_addSystemError(request, ERR_NON_FATAL, socerrno, NO,"connect");
2.115 frystyk 426: host->tcpstate = TCP_DNS;
427: HTTRACE(PROT_TRACE, "HTHost %p going to state TCP_DNS.\n" _ host);
2.24 frystyk 428: break;
429: }
2.112 frystyk 430: HTRequest_addSystemError(request, ERR_FATAL, socerrno, NO, "connect");
2.91 frystyk 431: HTDNS_delete(hostname);
2.115 frystyk 432: HTHost_setRetry(host, 0);
433: host->tcpstate = TCP_BEGIN;
434: HTTRACE(PROT_TRACE, "HTHost %p going to state TCP_BEGIN.\n" _ host);
2.55 frystyk 435: return HT_ERROR;
436: break;
2.24 frystyk 437: }
2.55 frystyk 438: }
2.13 frystyk 439: }
440:
2.56 frystyk 441: /* HTDoAccept()
442: ** ------------
443: ** This function makes a non-blocking accept which will turn up as ready
444: ** read in the select.
445: ** Returns
446: ** HT_ERROR Error has occured or interrupted
447: ** HT_OK if connected
448: ** HT_WOULD_BLOCK if operation would have blocked
2.13 frystyk 449: */
2.115 frystyk 450: PUBLIC int HTDoAccept (HTNet * listening, HTNet * accepting)
2.13 frystyk 451: {
2.115 frystyk 452: HTHost * host = HTNet_host(listening);
453: HTRequest * request = HTNet_request(accepting);
454: int size = sizeof(host->sock_addr);
2.13 frystyk 455: int status;
2.115 frystyk 456: if (!request || HTNet_socket(listening)==INVSOC) {
2.114 frystyk 457: HTTRACE(PROT_TRACE, "HTDoAccept.. Invalid socket\n");
2.56 frystyk 458: return HT_ERROR;
2.13 frystyk 459: }
2.65 frystyk 460:
2.115 frystyk 461: status = accept(HTNet_socket(listening), (struct sockaddr *) &host->sock_addr, &size);
2.93 eric 462: if (NETCALL_ERROR(status))
2.23 duns 463: {
2.93 eric 464: if (NETCALL_WOULDBLOCK(socerrno))
2.56 frystyk 465: {
2.115 frystyk 466: HTTRACE(PROT_TRACE, "HTDoAccept.. WOULD BLOCK %d\n" _ HTNet_socket(listening));
467: HTHost_register(host, listening, HTEvent_ACCEPT);
2.56 frystyk 468: return HT_WOULD_BLOCK;
469: }
2.65 frystyk 470: HTRequest_addSystemError(request, ERR_WARN, socerrno, YES, "accept");
2.114 frystyk 471: HTTRACE(PROT_TRACE, "HTDoAccept.. Accept failed\n");
2.56 frystyk 472: return HT_ERROR;
2.23 duns 473: }
2.71 frystyk 474:
2.114 frystyk 475: HTTRACE(PROT_TRACE, "Accepted.... socket %d\n" _ status);
2.88 frystyk 476:
2.115 frystyk 477: /* Remember the new socket we got and close the old one */
478: NETCLOSE(HTNet_socket(accepting));
479: HTNet_setSocket(accepting, status);
2.83 frystyk 480:
2.56 frystyk 481: return HT_OK;
482: }
483:
484:
485: /* HTDoListen
486: ** ----------
2.115 frystyk 487: ** Listens on the specified port.
2.56 frystyk 488: ** returns HT_ERROR Error has occured or interrupted
489: ** HT_OK if connected
490: */
2.115 frystyk 491: PUBLIC int HTDoListen (HTNet * listening, HTNet * accepting, int backlog)
2.56 frystyk 492: {
2.115 frystyk 493: HTHost * host = HTNet_host(listening);
494: HTRequest * request = HTNet_request(listening);
495: int preemptive = listening->preemptive;
2.111 frystyk 496: int status = HT_OK;
2.115 frystyk 497: char * hostname = HTHost_name(host);
2.56 frystyk 498:
499: /* Jump into the state machine */
500: while (1) {
2.115 frystyk 501: switch (host->tcpstate) {
2.111 frystyk 502: case TCP_BEGIN:
503: {
504: /*
505: ** Add the net object to the host object found above. If the
506: ** host is idle then we can start the request right away,
507: ** otherwise we must wait until it is free.
508: */
2.115 frystyk 509: if ((status = HTHost_addNet(host, accepting)) == HT_PENDING)
2.114 frystyk 510: HTTRACE(PROT_TRACE, "HTDoListen.. Pending...\n");
2.111 frystyk 511:
512: /*
513: ** If we are pending then return here, otherwise go to next state
514: ** which is setting up a channel
515: */
2.115 frystyk 516: host->tcpstate = TCP_CHANNEL;
517: HTTRACE(PROT_TRACE, "HTHost %p going to state TCP_CHANNEL.\n" _ host);
2.111 frystyk 518: if (status == HT_PENDING) return HT_PENDING;
519: }
520: break;
521:
522: case TCP_CHANNEL:
523: /*
524: ** The next state depends on whether we have a connection or not.
525: */
2.115 frystyk 526: if (HTHost_channel(host) == NULL) {
527: host->tcpstate = TCP_NEED_SOCKET;
528: HTTRACE(PROT_TRACE, "HTHost %p going to state TCP_SOCKET.\n" _ host);
2.111 frystyk 529: } else {
530:
531: /*
532: ** There is now one more using the channel
533: */
2.115 frystyk 534: HTChannel_upSemaphore(host->channel);
2.111 frystyk 535:
536: /*
537: ** We are now all set and can jump to connected mode
538: */
2.115 frystyk 539: host->tcpstate = TCP_CONNECTED;
540: HTTRACE(PROT_TRACE, "HTHost %p going to state TCP_CONNECTED.\n" _ host);
2.56 frystyk 541: }
2.115 frystyk 542: hostname = HTHost_name(host);
2.56 frystyk 543: break;
544:
2.111 frystyk 545: case TCP_NEED_SOCKET:
2.115 frystyk 546: {
547: SOCKET sockfd;
548:
549: /* Create a new socket */
550: if ((sockfd = _makeSocket(host, request, preemptive)) == INVSOC) {
551: host->tcpstate = TCP_ERROR;
2.111 frystyk 552: break;
2.112 frystyk 553: }
2.111 frystyk 554:
2.115 frystyk 555: /* Create channnel and streams */
556: createChannelAndTransportStreams (host, sockfd, accepting->transport);
557:
558: /* Progress nofitication */
2.111 frystyk 559: {
560: HTAlertCallback *cbf = HTAlert_find(HT_PROG_ACCEPT);
561: if (cbf) (*cbf)(request, HT_PROG_ACCEPT, HT_MSG_NULL,
562: NULL, hostname, NULL);
563: }
2.115 frystyk 564: host->tcpstate = TCP_NEED_BIND;
565: HTTRACE(PROT_TRACE, "HTHost %p going to state TCP_NEED_BIND\n" _ host);
2.56 frystyk 566: break;
2.115 frystyk 567: }
2.56 frystyk 568:
2.111 frystyk 569: case TCP_NEED_BIND:
2.115 frystyk 570: HTTRACE(PROT_TRACE, "Socket...... Binding socket %d\n" _ HTNet_socket(listening));
571: status = bind(HTNet_socket(listening), (struct sockaddr *) &host->sock_addr,
572: sizeof(host->sock_addr));
2.111 frystyk 573: if (NETCALL_ERROR(status)) {
2.114 frystyk 574: HTTRACE(PROT_TRACE, "Socket...... Bind failed %d\n" _ socerrno);
2.115 frystyk 575: host->tcpstate = TCP_ERROR;
2.111 frystyk 576: } else {
2.115 frystyk 577: HTTRACE(PROT_TRACE, "Socket...... Starting listening on socket %d\n" _ HTNet_socket(listening));
578: host->tcpstate = TCP_NEED_LISTEN;
2.111 frystyk 579: }
2.56 frystyk 580: break;
581:
2.111 frystyk 582: case TCP_NEED_LISTEN:
2.115 frystyk 583: status = listen(HTNet_socket(listening), backlog);
2.93 eric 584: if (NETCALL_ERROR(status))
2.115 frystyk 585: host->tcpstate = TCP_ERROR;
2.56 frystyk 586: else
2.115 frystyk 587: host->tcpstate = TCP_CONNECTED;
2.56 frystyk 588: break;
589:
2.111 frystyk 590: case TCP_CONNECTED:
2.115 frystyk 591: HTHost_unregister(host, listening, HTEvent_ACCEPT);
592: HTHost_setRetry(host, 0);
593: host->tcpstate = TCP_IN_USE;
594: HTTRACE(PROT_TRACE, "HTHost %p listening.\n" _ host);
2.56 frystyk 595: return HT_OK;
596: break;
2.13 frystyk 597:
2.111 frystyk 598: /* once a host is connected, subsequent connections are immediately OK */
599: case TCP_IN_USE:
2.115 frystyk 600: if ((status = HTHost_addNet(host, accepting)) == HT_PENDING) {
2.114 frystyk 601: HTTRACE(PROT_TRACE, "HTDoListen.. Pending...\n");
2.111 frystyk 602: return HT_PENDING;
603: }
604:
2.115 frystyk 605: HTChannel_upSemaphore(host->channel);
2.111 frystyk 606: return HT_OK;
607:
608: case TCP_NEED_CONNECT:
609: case TCP_DNS:
610: case TCP_DNS_ERROR:
611: case TCP_ERROR:
2.115 frystyk 612: if (HTChannel_socket(host->channel) != INVSOC) {
613: NETCLOSE(HTChannel_socket(host->channel));
614: if (HTHost_isPersistent(host)) { /* Inherited socket */
615: HTHost_setPersistent(host, NO, HT_TP_SINGLE);
616: host->tcpstate = TCP_NEED_SOCKET;
617: HTTRACE(PROT_TRACE, "HTHost %p going to state TCP_NEED_SOCKET.\n" _ host);
2.111 frystyk 618: break;
619: }
620: }
621:
622: HTRequest_addSystemError(request, ERR_FATAL, socerrno, NO, "accept");
2.115 frystyk 623: HTHost_setRetry(host, 0);
624: host->tcpstate = TCP_BEGIN;
625: HTTRACE(PROT_TRACE, "HTHost %p going to state TCP_BEGIN.\n" _ host);
2.56 frystyk 626: return HT_ERROR;
627: break;
628: }
2.38 frystyk 629: }
1.1 timbl 630: }
631:
2.83 frystyk 632: /* HTDoClose
633: ** ---------
634: ** Closes a file descriptor whatever means are available on the current
635: ** platform. If we have unix file descriptors then use this otherwise use
636: ** the ANSI C file descriptors
637: **
638: ** returns HT_ERROR Error has occured or interrupted
639: ** HT_OK if connected
640: ** HT_WOULD_BLOCK if operation would have blocked
641: */
2.87 frystyk 642: PUBLIC int HTDoClose (HTNet * net)
2.83 frystyk 643: {
644: int status = -1;
2.95 frystyk 645: if (net && HTNet_socket(net) != INVSOC) {
2.114 frystyk 646: HTTRACE(PROT_TRACE, "HTDoClose... Close %d\n" _ HTNet_socket(net));
2.95 frystyk 647: status = NETCLOSE(HTNet_socket(net));
648: /* HTEvent_unregister(HTNet_socket(net), (SockOps) FD_ALL); */
2.91 frystyk 649: HTNet_decreaseSocket();
2.95 frystyk 650: HTNet_setSocket(net, INVSOC);
2.91 frystyk 651:
652: /*
653: ** As we have a socket available we check for whether
654: ** we can start any pending requests. We do this by asking for
655: ** pending Host objects. If none then use the current object
656: */
657: HTHost_launchPending(net->host);
658:
659: } else
2.114 frystyk 660: HTTRACE(PROT_TRACE, "HTDoClose... No pending requests\n");
2.83 frystyk 661: return status < 0 ? HT_ERROR : HT_OK;
662: }
2.91 frystyk 663:
2.83 frystyk 664:
Webmaster