Annotation of libwww/Library/src/HTHost.c, revision 2.9
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.9 ! frystyk 6: ** @(#) $Id: HTHost.c,v 2.8 1996/08/19 18:30:35 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;
2.9 ! frystyk 152: }
! 153:
! 154: /*
! 155: ** Search the host info cache for a host object. Examples of host names:
! 156: **
! 157: ** www.w3.org
! 158: ** www.foo.com:8000
! 159: ** 18.52.0.18
! 160: **
! 161: ** Returns Host object or NULL if not found.
! 162: */
! 163: PUBLIC HTHost * HTHost_find (char * host)
! 164: {
! 165: HTList * list = NULL; /* Current list in cache */
! 166: HTHost * pres = NULL;
! 167: if (CORE_TRACE)
! 168: HTTrace("Host info... Looking for `%s\'\n", host ? host : "<null>");
! 169:
! 170: /* Find a hash for this host */
! 171: if (host && HostTable) {
! 172: int hash = 0;
! 173: char *ptr;
! 174: for (ptr=host; *ptr; ptr++)
! 175: hash = (int) ((hash * 3 + (*(unsigned char *) ptr)) % HASH_SIZE);
! 176: if (!HostTable[hash]) return NULL;
! 177: list = HostTable[hash];
! 178:
! 179: /* Search the cache */
! 180: {
! 181: HTList * cur = list;
! 182: while ((pres = (HTHost *) HTList_nextObject(cur))) {
! 183: if (!strcmp(pres->hostname, host)) {
! 184: if (time(NULL) > pres->ntime + HostTimeout) {
! 185: if (CORE_TRACE)
! 186: HTTrace("Host info... Collecting host %p\n", pres);
! 187: delete_object(list, pres);
! 188: pres = NULL;
! 189: } else {
! 190: if (CORE_TRACE)
! 191: HTTrace("Host info... Found `%s\'\n", host);
! 192: }
! 193: return pres;
! 194: }
! 195: }
! 196: }
! 197: }
! 198: return NULL;
2.1 frystyk 199: }
200:
201: /*
2.8 frystyk 202: ** Get and set the hostname of the remote host
203: */
204: PUBLIC char * HTHost_name (HTHost * host)
205: {
206: return host ? host->hostname : NULL;
207: }
208:
209: /*
2.1 frystyk 210: ** Get and set the type class of the remote host
211: */
212: PUBLIC char * HTHost_class (HTHost * host)
213: {
214: return host ? host->type : NULL;
215: }
216:
217: PUBLIC void HTHost_setClass (HTHost * host, char * s_class)
218: {
219: if (host && s_class) StrAllocCopy(host->type, s_class);
220: }
221:
222: /*
223: ** Get and set the version of the remote host
224: */
225: PUBLIC int HTHost_version (HTHost *host)
226: {
227: return host ? host->version : 0;
228: }
229:
230: PUBLIC void HTHost_setVersion (HTHost * host, int version)
231: {
232: if (host) host->version = version;
233: }
234:
235: /*
236: ** Get and set the cache timeout for persistent entries.
237: ** The default value is TCP_TIMEOUT
238: */
239: PUBLIC void HTHost_setPersistTimeout (time_t timeout)
240: {
241: TCPTimeout = timeout;
242: }
243:
244: PUBLIC time_t HTHost_persistTimeout (time_t timeout)
245: {
246: return TCPTimeout;
247: }
248:
249: /* Persistent Connection Expiration
250: ** --------------------------------
251: ** Should normally not be used. If, then use calendar time.
252: */
253: PUBLIC void HTHost_setPersistExpires (HTHost * host, time_t expires)
254: {
255: if (host) host->expires = expires;
256: }
257:
258: PUBLIC time_t HTHost_persistExpires (HTHost * host)
259: {
260: return host ? host->expires : -1;
261: }
262:
263: /*
2.6 frystyk 264: ** Public methods for this host
265: */
266: PUBLIC HTMethod HTHost_publicMethods (HTHost * me)
267: {
268: return me ? me->methods : METHOD_INVALID;
269: }
270:
271: PUBLIC void HTHost_setPublicMethods (HTHost * me, HTMethod methodset)
272: {
273: if (me) me->methods = methodset;
274: }
275:
276: PUBLIC void HTHost_appendPublicMethods (HTHost * me, HTMethod methodset)
277: {
278: if (me) me->methods |= methodset;
279: }
280:
281: /*
282: ** Get and set the server name of the remote host
283: */
284: PUBLIC char * HTHost_server (HTHost * host)
285: {
286: return host ? host->server : NULL;
287: }
288:
289: PUBLIC BOOL HTHost_setServer (HTHost * host, const char * server)
290: {
291: if (host && server) {
292: StrAllocCopy(host->server, server);
293: return YES;
294: }
295: return NO;
296: }
297:
298: /*
299: ** Get and set the userAgent name of the remote host
300: */
301: PUBLIC char * HTHost_userAgent (HTHost * host)
302: {
303: return host ? host->user_agent : NULL;
304: }
305:
306: PUBLIC BOOL HTHost_setUserAgent (HTHost * host, const char * userAgent)
307: {
308: if (host && userAgent) {
309: StrAllocCopy(host->user_agent, userAgent);
310: return YES;
311: }
312: return NO;
313: }
314:
2.1 frystyk 315: /* HTHost_catchClose
316: ** -----------------
317: ** This function is registered when the socket is idle so that we get
318: ** a notification if the socket closes at the other end. At this point
319: ** we can't use the request object as it might have been freed a long
320: ** time ago.
321: */
322: PUBLIC int HTHost_catchClose (SOCKET soc, HTRequest * request, SockOps ops)
323: {
2.2 frystyk 324: if (CORE_TRACE)
2.1 frystyk 325: HTTrace("Catch Close. called with socket %d with ops %x\n",
326: soc, (unsigned) ops);
327: if (ops == FD_READ) {
328: HTChannel * ch = HTChannel_find(soc); /* Find associated channel */
2.8 frystyk 329: HTHost * host = HTChannel_host(ch); /* and associated host */
2.1 frystyk 330: if (ch && host) {
2.2 frystyk 331: if (CORE_TRACE) HTTrace("Catch Close. CLOSING socket %d\n", soc);
2.8 frystyk 332: HTHost_clearChannel(host, HT_OK);
2.1 frystyk 333: } else {
2.2 frystyk 334: if (CORE_TRACE) HTTrace("Catch Close. socket %d NOT FOUND!\n",soc);
2.1 frystyk 335: }
336: }
2.4 eric 337: HTEvent_unregister(soc, (SockOps) FD_ALL);
2.1 frystyk 338: return HT_OK;
339: }
340:
341: /*
342: ** As soon as we know that this host accepts persistent connections,
343: ** we associated the channel with the host.
344: ** We don't want more than MaxSockets-2 connections to be persistent in
345: ** order to avoid deadlock.
346: */
2.8 frystyk 347: PUBLIC BOOL HTHost_setChannel (HTHost * host,
348: HTChannel * channel,
349: HTTransportMode mode)
2.1 frystyk 350: {
2.6 frystyk 351: if (!host || !channel) return NO;
2.2 frystyk 352: if (host->channel) {
353: if (CORE_TRACE) HTTrace("Host info... %p already persistent\n", host);
354: return YES;
355: } else {
2.1 frystyk 356: SOCKET sockfd = HTChannel_socket(channel);
2.8 frystyk 357: if (sockfd != INVSOC && HTNet_availablePersistentSockets() > 0) {
2.1 frystyk 358: host->channel = channel;
2.8 frystyk 359: host->mode = mode;
2.1 frystyk 360: host->expires = time(NULL) + TCPTimeout; /* Default timeout */
2.8 frystyk 361: HTChannel_setHost(channel, host);
362: HTNet_increasePersistentSocket();
2.2 frystyk 363: if (CORE_TRACE)
2.1 frystyk 364: HTTrace("Host info... added host %p as persistent\n", host);
365: return YES;
366: } else {
2.2 frystyk 367: if (CORE_TRACE)
368: HTTrace("Host info... no room for persistent socket %d\n",
2.7 frystyk 369: sockfd);
2.1 frystyk 370: }
371: }
372: return NO;
373: }
374:
375: /*
376: ** Find persistent channel associated with this host.
377: */
378: PUBLIC HTChannel * HTHost_channel (HTHost * host)
379: {
380: return host ? host->channel : NULL;
381: }
382:
383: /*
384: ** Clear the persistent entry by deleting the channel object. Note that
385: ** the channel object is only deleted if it's not used anymore.
386: */
2.8 frystyk 387: PUBLIC BOOL HTHost_clearChannel (HTHost * host, int status)
2.1 frystyk 388: {
389: if (host && host->channel) {
2.8 frystyk 390: HTChannel_setHost(host->channel, NULL);
391: HTChannel_delete(host->channel, status);
2.1 frystyk 392: host->expires = 0;
393: host->channel = NULL;
2.8 frystyk 394: HTNet_decreasePersistentSocket();
2.2 frystyk 395: if (CORE_TRACE)
396: HTTrace("Host info... removed host %p as persistent\n", host);
2.1 frystyk 397: return YES;
398: }
399: return NO;
400: }
401:
402: /*
403: ** Check whether we have a persistent channel or not
404: */
405: PUBLIC BOOL HTHost_isPersistent (HTHost * host)
406: {
407: return host && host->channel;
2.8 frystyk 408: }
409:
410: /*
411: ** Handle the connection mode. The mode may change mode in the
412: ** middle of a connection.
413: */
414: PUBLIC HTTransportMode HTHost_mode (HTHost * host, BOOL * active)
415: {
416: return host ? host->mode : HT_TP_SINGLE;
417: }
418:
419: /*
420: ** If the new mode is lower than the old mode then adjust the pipeline
421: ** accordingly. That is, if we are going into single mode then move
422: ** all entries in the pipeline and move the rest to the pending
423: ** queue. They will get launched at a later point in time.
424: */
425: PUBLIC BOOL HTHost_setMode (HTHost * host, HTTransportMode mode)
426: {
427: if (host) {
428: /*
429: ** Check the new mode and see if we must adjust the queues.
430: */
431: if (mode == HT_TP_SINGLE && host->mode > mode) {
432: int piped = HTList_count(host->pipeline);
433: if (piped > 0) {
434: int cnt;
435: if (CORE_TRACE)
436: HTTrace("Host info... Moving %d Net objects from pipe line to pending queue\n", piped);
437: if (!host->pending) host->pending = HTList_new();
438: for (cnt=0; cnt<piped; cnt++) {
439: HTNet * net = HTList_removeFirstObject(host->pipeline);
440: HTList_appendObject(host->pending, net);
441: }
442: }
443: }
444: host->mode = mode;
445: return YES;
446: }
447: return NO;
448: }
449:
450: /*
451: ** Check whether a host is idle meaning if it is ready for a new
452: ** request which depends on the mode of the host. If the host is
453: ** idle, i.e. ready for use then return YES else NO. If the host supports
454: ** persistent connections then still only return idle if no requests are
455: ** ongoing.
456: */
457: PUBLIC BOOL HTHost_isIdle (HTHost * host)
458: {
459: return (host && HTList_count(host->pipeline) <= 0);
460: }
461:
462: /*
463: ** Add a net object to the host object. If the host
464: ** is idle then add to active list (pipeline) else add
465: ** it to the pending list
466: ** Return HT_PENDING if we must pend, HT_OK, or HT_ERROR
467: */
468: PUBLIC int HTHost_addNet (HTHost * host, HTNet * net)
469: {
470: if (host && net) {
471: int status = HT_OK;
472:
473: /* Check to see if we can get a socket */
474: if (HTNet_availableSockets() <= 0) {
475: if (!PendHost) PendHost = HTList_new();
476: if (CORE_TRACE)
477: HTTrace("Host info... Add Host %p as pending\n", host);
478: HTList_addObject(PendHost, host);
479: status = HT_PENDING;
480: }
481:
482: /* Add to either active or pending queue */
483: if (HTHost_isIdle(host)) {
484: if (CORE_TRACE) HTTrace("Host info... Add Net %p to pipeline of host %p\n", net, host);
485: if (!host->pipeline) host->pipeline = HTList_new();
486: HTList_addObject(host->pipeline, net);
487:
488: /*
489: ** We have been idle and must hence unregister our catch close
490: ** event handler
491: */
492: if (host->channel) {
493: SOCKET sockfd = HTChannel_socket(host->channel);
494: HTEvent_unregister(sockfd, (SockOps) FD_CLOSE);
495: }
496: } else {
497: if (CORE_TRACE) HTTrace("Host info... Add Net %p as pending\n", net);
498: if (!host->pending) host->pending = HTList_new();
499: HTList_addObject(host->pending, net);
500: status = HT_PENDING;
501: }
502: return status;
503: }
504: return HT_ERROR;
505: }
506:
507: PUBLIC BOOL HTHost_deleteNet (HTHost * host, HTNet * net)
508: {
509: if (host && net) {
510: if (CORE_TRACE)
511: HTTrace("Host info... Remove Net %p from pipe line\n", net);
512: HTList_removeObject(host->pipeline, net);
513: HTList_removeObject(host->pending, net);
514: return YES;
515: }
516: return NO;
517: }
518:
519: /*
520: ** Handle pending host objects.
521: ** There are two ways we can end up with pending reqyests:
522: ** 1) If we are out of sockets then register new host objects as pending.
523: ** 2) If we are pending on a connection then register new net objects as
524: ** pending
525: ** This set of functions handles pending host objects and can start new
526: ** requests as resources get available
527: */
528:
529: /*
530: ** Check this host object for any pending requests and return the next
531: ** registered Net object.
532: */
533: PUBLIC HTNet * HTHost_nextPendingNet (HTHost * host)
534: {
535: HTNet * net = NULL;
536: if (host && host->pending && host->pipeline) {
537: if ((net = (HTNet *) HTList_removeFirstObject(host->pending)) != NULL)
538: if (PROT_TRACE)
539: HTTrace("Host info... Popping %p from pending net queue\n",
540: net);
541: HTList_addObject(host->pipeline, net);
542: }
543: return net;
544: }
545:
546: /*
547: ** Return the current list of pending host obejcts waiting for a socket
548: */
549: PUBLIC HTHost * HTHost_nextPendingHost (void)
550: {
551: HTHost * host = NULL;
552: if (PendHost) {
553: if ((host = (HTHost *) HTList_removeFirstObject(PendHost)) != NULL)
554: if (PROT_TRACE)
555: HTTrace("Host info... Poping %p from pending host queue\n",
556: host);
557: }
558: return host;
559: }
560:
561: /*
562: ** Start the next pending request if any. First we look for pending
563: ** requests for the same host and then we check for any other pending
564: ** hosts
565: */
566: PUBLIC BOOL HTHost_launchPending (HTHost * host)
567: {
568: int available = HTNet_availableSockets();
569:
570: if (!host) {
571: if (PROT_TRACE) HTTrace("Host info... Bad arguments\n");
572: return NO;
573: }
574:
575: /*
576: ** Check if we do have resources available for a new request
577: ** This can either be reusing an existing connection or opening a new one
578: */
579: if (available > 0 || host->mode >= HT_TP_PIPELINE) {
580:
581: /*
582: ** Check the current Host obejct for pending Net objects
583: */
584: if (host) {
585: HTNet * net = HTHost_nextPendingNet(host);
586: if (net) return HTNet_start(net);
587: }
588:
589: /*
590: ** Check for other pending Host objects
591: */
592: {
593: HTHost * pending = HTHost_nextPendingHost();
594: if (pending) {
595: HTNet * net = HTHost_nextPendingNet(pending);
596: if (net) return HTNet_start(net);
597: }
598: }
599:
600: /*
601: ** If nothing pending then register our catch close event handler to
602: ** have something catching the socket if the remote server closes the
603: ** connection, for example due to timeout.
604: */
605: if (PROT_TRACE) HTTrace("Host info... Nothing pending\n");
606: if (host->channel) {
607: SOCKET sockfd = HTChannel_socket(host->channel);
608: HTEvent_register(sockfd, 0, (SockOps) FD_CLOSE,
609: HTHost_catchClose, HT_PRIORITY_MAX);
610: }
611: } else
612: if (PROT_TRACE) HTTrace("Host info... No available sockets\n");
613: return NO;
2.1 frystyk 614: }
615:
Webmaster