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