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