Annotation of libwww/Library/src/HTHost.c, revision 2.8
2.1 frystyk 1: /* HTHost.c
2: ** REMOTE HOST INFORMATION
3: **
4: ** (c) COPYRIGHT MIT 1995.
5: ** Please first read the full copyright statement in the file COPYRIGH.
2.8 ! frystyk 6: ** @(#) $Id: HTHost.c,v 2.7 1996/08/05 17:22:31 frystyk Exp $
2.1 frystyk 7: **
8: ** This object manages the information that we know about a remote host.
9: ** This can for example be what type of host it is, and what version
10: ** it is using. We also keep track of persistent connections
11: **
12: ** April 96 HFN Written
13: */
14:
15: /* Library include files */
16: #include "sysdep.h"
17: #include "WWWUtil.h"
18: #include "HTParse.h"
19: #include "HTAlert.h"
20: #include "HTError.h"
21: #include "HTNetMan.h"
22: #include "HTTrans.h"
23: #include "HTHost.h" /* Implemented here */
24:
25: #define HOST_TIMEOUT 43200L /* Default host timeout is 12 h */
26: #define TCP_TIMEOUT 3600L /* Default TCP timeout i 1 h */
27: #define HASH_SIZE 67
28:
29: /* Type definitions and global variables etc. local to this module */
30: struct _HTHost {
31: char * hostname; /* name of host + optional port */
32: time_t ntime; /* Creation time */
33: char * type; /* Peer type */
34: int version; /* Peer version */
2.6 frystyk 35: HTMethod methods; /* Public methods (bit-flag) */
36: char * server; /* Server name */
37: char * user_agent; /* User Agent */
2.8 ! frystyk 38: HTTransportMode mode; /* Supported mode */
2.1 frystyk 39: HTChannel * channel; /* Persistent channel */
2.8 ! frystyk 40: HTList * pipeline; /* Pipe line of net objects */
! 41: HTList * pending; /* List of pending Net objects */
2.1 frystyk 42: time_t expires; /* Persistent channel expires time */
43: };
44:
45: PRIVATE time_t HostTimeout = HOST_TIMEOUT; /* Timeout on host entries */
46: PRIVATE time_t TCPTimeout = TCP_TIMEOUT; /* Timeout on persistent channels */
47:
2.8 ! frystyk 48: PRIVATE HTList ** HostTable = NULL;
! 49: PRIVATE HTList * PendHost = NULL; /* List of pending host elements */
2.1 frystyk 50:
51: /* ------------------------------------------------------------------------- */
52:
53: PRIVATE void free_object (HTHost * me)
54: {
55: if (me) {
56: HT_FREE(me->hostname);
57: HT_FREE(me->type);
2.3 eric 58: if (me->channel) {
2.5 eric 59: HTChannel_delete(me->channel, HT_OK);
2.3 eric 60: me->channel = NULL;
61: }
2.8 ! frystyk 62: HTList_delete(me->pipeline);
! 63: HTList_delete(me->pending);
2.1 frystyk 64: HT_FREE(me);
65: }
66: }
67:
68: PRIVATE BOOL delete_object (HTList * list, HTHost * me)
69: {
2.2 frystyk 70: if (CORE_TRACE) HTTrace("Host info... object %p from list %p\n", me, list);
2.1 frystyk 71: HTList_removeObject(list, (void *) me);
72: free_object(me);
73: return YES;
74: }
75:
76: /*
77: ** Search the host info cache for a host object or create a new one
78: ** and add it. Examples of host names are
79: **
80: ** www.w3.org
81: ** www.foo.com:8000
82: ** 18.52.0.18
83: **
84: ** Returns Host object or NULL if error. You may get back an already
85: ** existing host object - you're not guaranteed a new one each time.
86: */
87: PUBLIC HTHost * HTHost_new (char * host)
88: {
89: HTList * list = NULL; /* Current list in cache */
90: HTHost * pres = NULL;
91: if (!host) {
2.2 frystyk 92: if (CORE_TRACE) HTTrace("Host info... Bad argument\n");
2.1 frystyk 93: return NULL;
94: }
95:
96: /* Find a hash for this host */
97: {
98: int hash = 0;
99: char *ptr;
100: for (ptr=host; *ptr; ptr++)
101: hash = (int) ((hash * 3 + (*(unsigned char *) ptr)) % HASH_SIZE);
102: if (!HostTable) {
103: if ((HostTable = (HTList **) HT_CALLOC(HASH_SIZE,
104: sizeof(HTList *))) == NULL)
105: HT_OUTOFMEM("HTHost_find");
106: }
107: if (!HostTable[hash]) HostTable[hash] = HTList_new();
108: list = HostTable[hash];
109: }
110:
111: /* Search the cache */
112: {
113: HTList * cur = list;
114: while ((pres = (HTHost *) HTList_nextObject(cur))) {
115: if (!strcmp(pres->hostname, host)) {
2.8 ! frystyk 116: if (HTHost_isIdle(pres) && time(NULL)>pres->ntime+HostTimeout){
2.2 frystyk 117: if (CORE_TRACE)
2.1 frystyk 118: HTTrace("Host info... Collecting host info %p\n",pres);
119: delete_object(list, pres);
120: pres = NULL;
121: }
122: break;
123: }
124: }
125: }
126:
2.8 ! frystyk 127: /* If not found then create new Host object, else use existing one */
2.1 frystyk 128: if (pres) {
129: if (pres->channel) {
130: if (pres->expires < time(NULL)) { /* Cached channel is cold */
2.2 frystyk 131: if (CORE_TRACE)
2.1 frystyk 132: HTTrace("Host info... Persistent channel %p gotten cold\n",
133: pres->channel);
2.5 eric 134: HTChannel_delete(pres->channel, HT_OK);
2.1 frystyk 135: pres->channel = NULL;
136: } else {
2.2 frystyk 137: if (CORE_TRACE)
2.1 frystyk 138: HTTrace("Host info... REUSING CHANNEL %p\n",pres->channel);
139: }
140: }
141: } else {
142: if ((pres = (HTHost *) HT_CALLOC(1, sizeof(HTHost))) == NULL)
143: HT_OUTOFMEM("HTHost_add");
144: StrAllocCopy(pres->hostname, host);
145: pres->ntime = time(NULL);
2.8 ! frystyk 146: pres->mode = HT_TP_SINGLE;
2.2 frystyk 147: if (CORE_TRACE)
2.1 frystyk 148: HTTrace("Host info... added `%s\' to list %p\n", host, list);
149: HTList_addObject(list, (void *) pres);
150: }
151: return pres;
152: }
153:
154: /*
2.8 ! frystyk 155: ** Get and set the hostname of the remote host
! 156: */
! 157: PUBLIC char * HTHost_name (HTHost * host)
! 158: {
! 159: return host ? host->hostname : NULL;
! 160: }
! 161:
! 162: /*
2.1 frystyk 163: ** Get and set the type class of the remote host
164: */
165: PUBLIC char * HTHost_class (HTHost * host)
166: {
167: return host ? host->type : NULL;
168: }
169:
170: PUBLIC void HTHost_setClass (HTHost * host, char * s_class)
171: {
172: if (host && s_class) StrAllocCopy(host->type, s_class);
173: }
174:
175: /*
176: ** Get and set the version of the remote host
177: */
178: PUBLIC int HTHost_version (HTHost *host)
179: {
180: return host ? host->version : 0;
181: }
182:
183: PUBLIC void HTHost_setVersion (HTHost * host, int version)
184: {
185: if (host) host->version = version;
186: }
187:
188: /*
189: ** Get and set the cache timeout for persistent entries.
190: ** The default value is TCP_TIMEOUT
191: */
192: PUBLIC void HTHost_setPersistTimeout (time_t timeout)
193: {
194: TCPTimeout = timeout;
195: }
196:
197: PUBLIC time_t HTHost_persistTimeout (time_t timeout)
198: {
199: return TCPTimeout;
200: }
201:
202: /* Persistent Connection Expiration
203: ** --------------------------------
204: ** Should normally not be used. If, then use calendar time.
205: */
206: PUBLIC void HTHost_setPersistExpires (HTHost * host, time_t expires)
207: {
208: if (host) host->expires = expires;
209: }
210:
211: PUBLIC time_t HTHost_persistExpires (HTHost * host)
212: {
213: return host ? host->expires : -1;
214: }
215:
216: /*
2.6 frystyk 217: ** Public methods for this host
218: */
219: PUBLIC HTMethod HTHost_publicMethods (HTHost * me)
220: {
221: return me ? me->methods : METHOD_INVALID;
222: }
223:
224: PUBLIC void HTHost_setPublicMethods (HTHost * me, HTMethod methodset)
225: {
226: if (me) me->methods = methodset;
227: }
228:
229: PUBLIC void HTHost_appendPublicMethods (HTHost * me, HTMethod methodset)
230: {
231: if (me) me->methods |= methodset;
232: }
233:
234: /*
235: ** Get and set the server name of the remote host
236: */
237: PUBLIC char * HTHost_server (HTHost * host)
238: {
239: return host ? host->server : NULL;
240: }
241:
242: PUBLIC BOOL HTHost_setServer (HTHost * host, const char * server)
243: {
244: if (host && server) {
245: StrAllocCopy(host->server, server);
246: return YES;
247: }
248: return NO;
249: }
250:
251: /*
252: ** Get and set the userAgent name of the remote host
253: */
254: PUBLIC char * HTHost_userAgent (HTHost * host)
255: {
256: return host ? host->user_agent : NULL;
257: }
258:
259: PUBLIC BOOL HTHost_setUserAgent (HTHost * host, const char * userAgent)
260: {
261: if (host && userAgent) {
262: StrAllocCopy(host->user_agent, userAgent);
263: return YES;
264: }
265: return NO;
266: }
267:
2.1 frystyk 268: /* HTHost_catchClose
269: ** -----------------
270: ** This function is registered when the socket is idle so that we get
271: ** a notification if the socket closes at the other end. At this point
272: ** we can't use the request object as it might have been freed a long
273: ** time ago.
274: */
275: PUBLIC int HTHost_catchClose (SOCKET soc, HTRequest * request, SockOps ops)
276: {
2.2 frystyk 277: if (CORE_TRACE)
2.1 frystyk 278: HTTrace("Catch Close. called with socket %d with ops %x\n",
279: soc, (unsigned) ops);
280: if (ops == FD_READ) {
281: HTChannel * ch = HTChannel_find(soc); /* Find associated channel */
2.8 ! frystyk 282: HTHost * host = HTChannel_host(ch); /* and associated host */
2.1 frystyk 283: if (ch && host) {
2.2 frystyk 284: if (CORE_TRACE) HTTrace("Catch Close. CLOSING socket %d\n", soc);
2.8 ! frystyk 285: HTHost_clearChannel(host, HT_OK);
2.1 frystyk 286: } else {
2.2 frystyk 287: if (CORE_TRACE) HTTrace("Catch Close. socket %d NOT FOUND!\n",soc);
2.1 frystyk 288: }
289: }
2.4 eric 290: HTEvent_unregister(soc, (SockOps) FD_ALL);
2.1 frystyk 291: return HT_OK;
292: }
293:
294: /*
295: ** As soon as we know that this host accepts persistent connections,
296: ** we associated the channel with the host.
297: ** We don't want more than MaxSockets-2 connections to be persistent in
298: ** order to avoid deadlock.
299: */
2.8 ! frystyk 300: PUBLIC BOOL HTHost_setChannel (HTHost * host,
! 301: HTChannel * channel,
! 302: HTTransportMode mode)
2.1 frystyk 303: {
2.6 frystyk 304: if (!host || !channel) return NO;
2.2 frystyk 305: if (host->channel) {
306: if (CORE_TRACE) HTTrace("Host info... %p already persistent\n", host);
307: return YES;
308: } else {
2.1 frystyk 309: SOCKET sockfd = HTChannel_socket(channel);
2.8 ! frystyk 310: if (sockfd != INVSOC && HTNet_availablePersistentSockets() > 0) {
2.1 frystyk 311: host->channel = channel;
2.8 ! frystyk 312: host->mode = mode;
2.1 frystyk 313: host->expires = time(NULL) + TCPTimeout; /* Default timeout */
2.8 ! frystyk 314: HTChannel_setHost(channel, host);
! 315: HTNet_increasePersistentSocket();
2.2 frystyk 316: if (CORE_TRACE)
2.1 frystyk 317: HTTrace("Host info... added host %p as persistent\n", host);
318: return YES;
319: } else {
2.2 frystyk 320: if (CORE_TRACE)
321: HTTrace("Host info... no room for persistent socket %d\n",
2.7 frystyk 322: sockfd);
2.1 frystyk 323: }
324: }
325: return NO;
326: }
327:
328: /*
329: ** Find persistent channel associated with this host.
330: */
331: PUBLIC HTChannel * HTHost_channel (HTHost * host)
332: {
333: return host ? host->channel : NULL;
334: }
335:
336: /*
337: ** Clear the persistent entry by deleting the channel object. Note that
338: ** the channel object is only deleted if it's not used anymore.
339: */
2.8 ! frystyk 340: PUBLIC BOOL HTHost_clearChannel (HTHost * host, int status)
2.1 frystyk 341: {
342: if (host && host->channel) {
2.8 ! frystyk 343: HTChannel_setHost(host->channel, NULL);
! 344: HTChannel_delete(host->channel, status);
2.1 frystyk 345: host->expires = 0;
346: host->channel = NULL;
2.8 ! frystyk 347: HTNet_decreasePersistentSocket();
2.2 frystyk 348: if (CORE_TRACE)
349: HTTrace("Host info... removed host %p as persistent\n", host);
2.1 frystyk 350: return YES;
351: }
352: return NO;
353: }
354:
355: /*
356: ** Check whether we have a persistent channel or not
357: */
358: PUBLIC BOOL HTHost_isPersistent (HTHost * host)
359: {
360: return host && host->channel;
2.8 ! frystyk 361: }
! 362:
! 363: /*
! 364: ** Handle the connection mode. The mode may change mode in the
! 365: ** middle of a connection.
! 366: */
! 367: PUBLIC HTTransportMode HTHost_mode (HTHost * host, BOOL * active)
! 368: {
! 369: return host ? host->mode : HT_TP_SINGLE;
! 370: }
! 371:
! 372: /*
! 373: ** If the new mode is lower than the old mode then adjust the pipeline
! 374: ** accordingly. That is, if we are going into single mode then move
! 375: ** all entries in the pipeline and move the rest to the pending
! 376: ** queue. They will get launched at a later point in time.
! 377: */
! 378: PUBLIC BOOL HTHost_setMode (HTHost * host, HTTransportMode mode)
! 379: {
! 380: if (host) {
! 381: /*
! 382: ** Check the new mode and see if we must adjust the queues.
! 383: */
! 384: if (mode == HT_TP_SINGLE && host->mode > mode) {
! 385: int piped = HTList_count(host->pipeline);
! 386: if (piped > 0) {
! 387: int cnt;
! 388: if (CORE_TRACE)
! 389: HTTrace("Host info... Moving %d Net objects from pipe line to pending queue\n", piped);
! 390: if (!host->pending) host->pending = HTList_new();
! 391: for (cnt=0; cnt<piped; cnt++) {
! 392: HTNet * net = HTList_removeFirstObject(host->pipeline);
! 393: HTList_appendObject(host->pending, net);
! 394: }
! 395: }
! 396: }
! 397: host->mode = mode;
! 398: return YES;
! 399: }
! 400: return NO;
! 401: }
! 402:
! 403: /*
! 404: ** Check whether a host is idle meaning if it is ready for a new
! 405: ** request which depends on the mode of the host. If the host is
! 406: ** idle, i.e. ready for use then return YES else NO. If the host supports
! 407: ** persistent connections then still only return idle if no requests are
! 408: ** ongoing.
! 409: */
! 410: PUBLIC BOOL HTHost_isIdle (HTHost * host)
! 411: {
! 412: return (host && HTList_count(host->pipeline) <= 0);
! 413: }
! 414:
! 415: /*
! 416: ** Add a net object to the host object. If the host
! 417: ** is idle then add to active list (pipeline) else add
! 418: ** it to the pending list
! 419: ** Return HT_PENDING if we must pend, HT_OK, or HT_ERROR
! 420: */
! 421: PUBLIC int HTHost_addNet (HTHost * host, HTNet * net)
! 422: {
! 423: if (host && net) {
! 424: int status = HT_OK;
! 425:
! 426: /* Check to see if we can get a socket */
! 427: if (HTNet_availableSockets() <= 0) {
! 428: if (!PendHost) PendHost = HTList_new();
! 429: if (CORE_TRACE)
! 430: HTTrace("Host info... Add Host %p as pending\n", host);
! 431: HTList_addObject(PendHost, host);
! 432: status = HT_PENDING;
! 433: }
! 434:
! 435: /* Add to either active or pending queue */
! 436: if (HTHost_isIdle(host)) {
! 437: if (CORE_TRACE) HTTrace("Host info... Add Net %p to pipeline of host %p\n", net, host);
! 438: if (!host->pipeline) host->pipeline = HTList_new();
! 439: HTList_addObject(host->pipeline, net);
! 440:
! 441: /*
! 442: ** We have been idle and must hence unregister our catch close
! 443: ** event handler
! 444: */
! 445: if (host->channel) {
! 446: SOCKET sockfd = HTChannel_socket(host->channel);
! 447: HTEvent_unregister(sockfd, (SockOps) FD_CLOSE);
! 448: }
! 449: } else {
! 450: if (CORE_TRACE) HTTrace("Host info... Add Net %p as pending\n", net);
! 451: if (!host->pending) host->pending = HTList_new();
! 452: HTList_addObject(host->pending, net);
! 453: status = HT_PENDING;
! 454: }
! 455: return status;
! 456: }
! 457: return HT_ERROR;
! 458: }
! 459:
! 460: PUBLIC BOOL HTHost_deleteNet (HTHost * host, HTNet * net)
! 461: {
! 462: if (host && net) {
! 463: if (CORE_TRACE)
! 464: HTTrace("Host info... Remove Net %p from pipe line\n", net);
! 465: HTList_removeObject(host->pipeline, net);
! 466: HTList_removeObject(host->pending, net);
! 467: return YES;
! 468: }
! 469: return NO;
! 470: }
! 471:
! 472: /*
! 473: ** Handle pending host objects.
! 474: ** There are two ways we can end up with pending reqyests:
! 475: ** 1) If we are out of sockets then register new host objects as pending.
! 476: ** 2) If we are pending on a connection then register new net objects as
! 477: ** pending
! 478: ** This set of functions handles pending host objects and can start new
! 479: ** requests as resources get available
! 480: */
! 481:
! 482: /*
! 483: ** Check this host object for any pending requests and return the next
! 484: ** registered Net object.
! 485: */
! 486: PUBLIC HTNet * HTHost_nextPendingNet (HTHost * host)
! 487: {
! 488: HTNet * net = NULL;
! 489: if (host && host->pending && host->pipeline) {
! 490: if ((net = (HTNet *) HTList_removeFirstObject(host->pending)) != NULL)
! 491: if (PROT_TRACE)
! 492: HTTrace("Host info... Popping %p from pending net queue\n",
! 493: net);
! 494: HTList_addObject(host->pipeline, net);
! 495: }
! 496: return net;
! 497: }
! 498:
! 499: /*
! 500: ** Return the current list of pending host obejcts waiting for a socket
! 501: */
! 502: PUBLIC HTHost * HTHost_nextPendingHost (void)
! 503: {
! 504: HTHost * host = NULL;
! 505: if (PendHost) {
! 506: if ((host = (HTHost *) HTList_removeFirstObject(PendHost)) != NULL)
! 507: if (PROT_TRACE)
! 508: HTTrace("Host info... Poping %p from pending host queue\n",
! 509: host);
! 510: }
! 511: return host;
! 512: }
! 513:
! 514: /*
! 515: ** Start the next pending request if any. First we look for pending
! 516: ** requests for the same host and then we check for any other pending
! 517: ** hosts
! 518: */
! 519: PUBLIC BOOL HTHost_launchPending (HTHost * host)
! 520: {
! 521: int available = HTNet_availableSockets();
! 522:
! 523: if (!host) {
! 524: if (PROT_TRACE) HTTrace("Host info... Bad arguments\n");
! 525: return NO;
! 526: }
! 527:
! 528: /*
! 529: ** Check if we do have resources available for a new request
! 530: ** This can either be reusing an existing connection or opening a new one
! 531: */
! 532: if (available > 0 || host->mode >= HT_TP_PIPELINE) {
! 533:
! 534: /*
! 535: ** Check the current Host obejct for pending Net objects
! 536: */
! 537: if (host) {
! 538: HTNet * net = HTHost_nextPendingNet(host);
! 539: if (net) return HTNet_start(net);
! 540: }
! 541:
! 542: /*
! 543: ** Check for other pending Host objects
! 544: */
! 545: {
! 546: HTHost * pending = HTHost_nextPendingHost();
! 547: if (pending) {
! 548: HTNet * net = HTHost_nextPendingNet(pending);
! 549: if (net) return HTNet_start(net);
! 550: }
! 551: }
! 552:
! 553: /*
! 554: ** If nothing pending then register our catch close event handler to
! 555: ** have something catching the socket if the remote server closes the
! 556: ** connection, for example due to timeout.
! 557: */
! 558: if (PROT_TRACE) HTTrace("Host info... Nothing pending\n");
! 559: if (host->channel) {
! 560: SOCKET sockfd = HTChannel_socket(host->channel);
! 561: HTEvent_register(sockfd, 0, (SockOps) FD_CLOSE,
! 562: HTHost_catchClose, HT_PRIORITY_MAX);
! 563: }
! 564: } else
! 565: if (PROT_TRACE) HTTrace("Host info... No available sockets\n");
! 566: return NO;
2.1 frystyk 567: }
568:
Webmaster