Annotation of libwww/Library/src/HTHost.c, revision 2.12
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.12 ! frystyk 6: ** @(#) $Id: HTHost.c,v 2.11 1996/09/28 00:02:05 kahan 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.12 ! frystyk 38: char * range_units; /* Acceptable range units */
2.8 frystyk 39: HTTransportMode mode; /* Supported mode */
2.1 frystyk 40: HTChannel * channel; /* Persistent channel */
2.8 frystyk 41: HTList * pipeline; /* Pipe line of net objects */
42: HTList * pending; /* List of pending Net objects */
2.1 frystyk 43: time_t expires; /* Persistent channel expires time */
44: };
45:
46: PRIVATE time_t HostTimeout = HOST_TIMEOUT; /* Timeout on host entries */
47: PRIVATE time_t TCPTimeout = TCP_TIMEOUT; /* Timeout on persistent channels */
48:
2.8 frystyk 49: PRIVATE HTList ** HostTable = NULL;
50: PRIVATE HTList * PendHost = NULL; /* List of pending host elements */
2.1 frystyk 51:
52: /* ------------------------------------------------------------------------- */
53:
54: PRIVATE void free_object (HTHost * me)
55: {
56: if (me) {
57: HT_FREE(me->hostname);
58: HT_FREE(me->type);
2.12 ! frystyk 59: HT_FREE(me->server);
! 60: HT_FREE(me->user_agent);
! 61: HT_FREE(me->range_units);
2.3 eric 62: if (me->channel) {
2.5 eric 63: HTChannel_delete(me->channel, HT_OK);
2.3 eric 64: me->channel = NULL;
65: }
2.8 frystyk 66: HTList_delete(me->pipeline);
67: HTList_delete(me->pending);
2.1 frystyk 68: HT_FREE(me);
69: }
70: }
71:
72: PRIVATE BOOL delete_object (HTList * list, HTHost * me)
73: {
2.2 frystyk 74: if (CORE_TRACE) HTTrace("Host info... object %p from list %p\n", me, list);
2.1 frystyk 75: HTList_removeObject(list, (void *) me);
76: free_object(me);
77: return YES;
78: }
79:
80: /*
81: ** Search the host info cache for a host object or create a new one
82: ** and add it. Examples of host names are
83: **
84: ** www.w3.org
85: ** www.foo.com:8000
86: ** 18.52.0.18
87: **
88: ** Returns Host object or NULL if error. You may get back an already
89: ** existing host object - you're not guaranteed a new one each time.
90: */
91: PUBLIC HTHost * HTHost_new (char * host)
92: {
93: HTList * list = NULL; /* Current list in cache */
94: HTHost * pres = NULL;
95: if (!host) {
2.2 frystyk 96: if (CORE_TRACE) HTTrace("Host info... Bad argument\n");
2.1 frystyk 97: return NULL;
98: }
99:
100: /* Find a hash for this host */
101: {
102: int hash = 0;
103: char *ptr;
104: for (ptr=host; *ptr; ptr++)
105: hash = (int) ((hash * 3 + (*(unsigned char *) ptr)) % HASH_SIZE);
106: if (!HostTable) {
107: if ((HostTable = (HTList **) HT_CALLOC(HASH_SIZE,
108: sizeof(HTList *))) == NULL)
109: HT_OUTOFMEM("HTHost_find");
110: }
111: if (!HostTable[hash]) HostTable[hash] = HTList_new();
112: list = HostTable[hash];
113: }
114:
115: /* Search the cache */
116: {
117: HTList * cur = list;
118: while ((pres = (HTHost *) HTList_nextObject(cur))) {
119: if (!strcmp(pres->hostname, host)) {
2.8 frystyk 120: if (HTHost_isIdle(pres) && time(NULL)>pres->ntime+HostTimeout){
2.2 frystyk 121: if (CORE_TRACE)
2.1 frystyk 122: HTTrace("Host info... Collecting host info %p\n",pres);
123: delete_object(list, pres);
124: pres = NULL;
125: }
126: break;
127: }
128: }
129: }
130:
2.8 frystyk 131: /* If not found then create new Host object, else use existing one */
2.1 frystyk 132: if (pres) {
133: if (pres->channel) {
134: if (pres->expires < time(NULL)) { /* Cached channel is cold */
2.2 frystyk 135: if (CORE_TRACE)
2.1 frystyk 136: HTTrace("Host info... Persistent channel %p gotten cold\n",
137: pres->channel);
2.5 eric 138: HTChannel_delete(pres->channel, HT_OK);
2.1 frystyk 139: pres->channel = NULL;
140: } else {
2.2 frystyk 141: if (CORE_TRACE)
2.1 frystyk 142: HTTrace("Host info... REUSING CHANNEL %p\n",pres->channel);
143: }
144: }
145: } else {
146: if ((pres = (HTHost *) HT_CALLOC(1, sizeof(HTHost))) == NULL)
147: HT_OUTOFMEM("HTHost_add");
148: StrAllocCopy(pres->hostname, host);
149: pres->ntime = time(NULL);
2.8 frystyk 150: pres->mode = HT_TP_SINGLE;
2.2 frystyk 151: if (CORE_TRACE)
2.1 frystyk 152: HTTrace("Host info... added `%s\' to list %p\n", host, list);
153: HTList_addObject(list, (void *) pres);
154: }
155: return pres;
2.9 frystyk 156: }
157:
158: /*
159: ** Search the host info cache for a host object. Examples of host names:
160: **
161: ** www.w3.org
162: ** www.foo.com:8000
163: ** 18.52.0.18
164: **
165: ** Returns Host object or NULL if not found.
166: */
167: PUBLIC HTHost * HTHost_find (char * host)
168: {
169: HTList * list = NULL; /* Current list in cache */
170: HTHost * pres = NULL;
171: if (CORE_TRACE)
172: HTTrace("Host info... Looking for `%s\'\n", host ? host : "<null>");
173:
174: /* Find a hash for this host */
175: if (host && HostTable) {
176: int hash = 0;
177: char *ptr;
178: for (ptr=host; *ptr; ptr++)
179: hash = (int) ((hash * 3 + (*(unsigned char *) ptr)) % HASH_SIZE);
180: if (!HostTable[hash]) return NULL;
181: list = HostTable[hash];
182:
183: /* Search the cache */
184: {
185: HTList * cur = list;
186: while ((pres = (HTHost *) HTList_nextObject(cur))) {
187: if (!strcmp(pres->hostname, host)) {
188: if (time(NULL) > pres->ntime + HostTimeout) {
189: if (CORE_TRACE)
190: HTTrace("Host info... Collecting host %p\n", pres);
191: delete_object(list, pres);
192: pres = NULL;
193: } else {
194: if (CORE_TRACE)
195: HTTrace("Host info... Found `%s\'\n", host);
196: }
197: return pres;
198: }
199: }
200: }
201: }
202: return NULL;
2.1 frystyk 203: }
204:
205: /*
2.8 frystyk 206: ** Get and set the hostname of the remote host
207: */
208: PUBLIC char * HTHost_name (HTHost * host)
209: {
210: return host ? host->hostname : NULL;
211: }
212:
213: /*
2.1 frystyk 214: ** Get and set the type class of the remote host
215: */
216: PUBLIC char * HTHost_class (HTHost * host)
217: {
218: return host ? host->type : NULL;
219: }
220:
221: PUBLIC void HTHost_setClass (HTHost * host, char * s_class)
222: {
223: if (host && s_class) StrAllocCopy(host->type, s_class);
224: }
225:
226: /*
227: ** Get and set the version of the remote host
228: */
229: PUBLIC int HTHost_version (HTHost *host)
230: {
231: return host ? host->version : 0;
232: }
233:
234: PUBLIC void HTHost_setVersion (HTHost * host, int version)
235: {
236: if (host) host->version = version;
237: }
238:
239: /*
240: ** Get and set the cache timeout for persistent entries.
241: ** The default value is TCP_TIMEOUT
242: */
243: PUBLIC void HTHost_setPersistTimeout (time_t timeout)
244: {
245: TCPTimeout = timeout;
246: }
247:
248: PUBLIC time_t HTHost_persistTimeout (time_t timeout)
249: {
250: return TCPTimeout;
251: }
252:
253: /* Persistent Connection Expiration
254: ** --------------------------------
255: ** Should normally not be used. If, then use calendar time.
256: */
257: PUBLIC void HTHost_setPersistExpires (HTHost * host, time_t expires)
258: {
259: if (host) host->expires = expires;
260: }
261:
262: PUBLIC time_t HTHost_persistExpires (HTHost * host)
263: {
264: return host ? host->expires : -1;
265: }
266:
267: /*
2.6 frystyk 268: ** Public methods for this host
269: */
270: PUBLIC HTMethod HTHost_publicMethods (HTHost * me)
271: {
272: return me ? me->methods : METHOD_INVALID;
273: }
274:
275: PUBLIC void HTHost_setPublicMethods (HTHost * me, HTMethod methodset)
276: {
277: if (me) me->methods = methodset;
278: }
279:
280: PUBLIC void HTHost_appendPublicMethods (HTHost * me, HTMethod methodset)
281: {
282: if (me) me->methods |= methodset;
283: }
284:
285: /*
286: ** Get and set the server name of the remote host
287: */
288: PUBLIC char * HTHost_server (HTHost * host)
289: {
290: return host ? host->server : NULL;
291: }
292:
293: PUBLIC BOOL HTHost_setServer (HTHost * host, const char * server)
294: {
295: if (host && server) {
296: StrAllocCopy(host->server, server);
297: return YES;
298: }
299: return NO;
300: }
301:
302: /*
303: ** Get and set the userAgent name of the remote host
304: */
305: PUBLIC char * HTHost_userAgent (HTHost * host)
306: {
307: return host ? host->user_agent : NULL;
308: }
309:
310: PUBLIC BOOL HTHost_setUserAgent (HTHost * host, const char * userAgent)
311: {
312: if (host && userAgent) {
313: StrAllocCopy(host->user_agent, userAgent);
314: return YES;
2.12 ! frystyk 315: }
! 316: return NO;
! 317: }
! 318:
! 319: /*
! 320: ** Get and set acceptable range units
! 321: */
! 322: PUBLIC char * HTHost_rangeUnits (HTHost * host)
! 323: {
! 324: return host ? host->range_units : NULL;
! 325: }
! 326:
! 327: PUBLIC BOOL HTHost_setRangeUnits (HTHost * host, const char * units)
! 328: {
! 329: if (host && units) {
! 330: StrAllocCopy(host->range_units, units);
! 331: return YES;
! 332: }
! 333: return NO;
! 334: }
! 335:
! 336: /*
! 337: ** Checks whether a specific range unit is OK. We always say
! 338: ** YES except if we have a specific statement from the server that
! 339: ** it doesn't understand byte ranges - that is - it has sent "none"
! 340: ** in a "Accept-Range" response header
! 341: */
! 342: PUBLIC BOOL HTHost_isRangeUnitAcceptable (HTHost * host, const char * unit)
! 343: {
! 344: if (host && unit) {
! 345: #if 0
! 346: if (host->range_units) {
! 347: char * start = strcasestr(host->range_units, "none");
! 348:
! 349: /*
! 350: ** Check that "none" is infact a token. It could be part of some
! 351: ** other valid string, so we'd better check for it.
! 352: */
! 353: if (start) {
! 354:
! 355:
! 356: }
! 357: return NO;
! 358: }
! 359: #endif
! 360: return strcasecomp(unit, "bytes") ? NO : YES;
2.6 frystyk 361: }
362: return NO;
363: }
364:
2.1 frystyk 365: /* HTHost_catchClose
366: ** -----------------
367: ** This function is registered when the socket is idle so that we get
368: ** a notification if the socket closes at the other end. At this point
369: ** we can't use the request object as it might have been freed a long
370: ** time ago.
371: */
372: PUBLIC int HTHost_catchClose (SOCKET soc, HTRequest * request, SockOps ops)
373: {
2.2 frystyk 374: if (CORE_TRACE)
2.1 frystyk 375: HTTrace("Catch Close. called with socket %d with ops %x\n",
376: soc, (unsigned) ops);
377: if (ops == FD_READ) {
378: HTChannel * ch = HTChannel_find(soc); /* Find associated channel */
2.8 frystyk 379: HTHost * host = HTChannel_host(ch); /* and associated host */
2.1 frystyk 380: if (ch && host) {
2.2 frystyk 381: if (CORE_TRACE) HTTrace("Catch Close. CLOSING socket %d\n", soc);
2.8 frystyk 382: HTHost_clearChannel(host, HT_OK);
2.1 frystyk 383: } else {
2.2 frystyk 384: if (CORE_TRACE) HTTrace("Catch Close. socket %d NOT FOUND!\n",soc);
2.1 frystyk 385: }
386: }
2.4 eric 387: HTEvent_unregister(soc, (SockOps) FD_ALL);
2.1 frystyk 388: return HT_OK;
389: }
390:
391: /*
392: ** As soon as we know that this host accepts persistent connections,
393: ** we associated the channel with the host.
394: ** We don't want more than MaxSockets-2 connections to be persistent in
395: ** order to avoid deadlock.
396: */
2.8 frystyk 397: PUBLIC BOOL HTHost_setChannel (HTHost * host,
398: HTChannel * channel,
399: HTTransportMode mode)
2.1 frystyk 400: {
2.6 frystyk 401: if (!host || !channel) return NO;
2.2 frystyk 402: if (host->channel) {
403: if (CORE_TRACE) HTTrace("Host info... %p already persistent\n", host);
404: return YES;
405: } else {
2.1 frystyk 406: SOCKET sockfd = HTChannel_socket(channel);
2.8 frystyk 407: if (sockfd != INVSOC && HTNet_availablePersistentSockets() > 0) {
2.1 frystyk 408: host->channel = channel;
2.8 frystyk 409: host->mode = mode;
2.1 frystyk 410: host->expires = time(NULL) + TCPTimeout; /* Default timeout */
2.8 frystyk 411: HTChannel_setHost(channel, host);
412: HTNet_increasePersistentSocket();
2.2 frystyk 413: if (CORE_TRACE)
2.1 frystyk 414: HTTrace("Host info... added host %p as persistent\n", host);
415: return YES;
416: } else {
2.2 frystyk 417: if (CORE_TRACE)
418: HTTrace("Host info... no room for persistent socket %d\n",
2.7 frystyk 419: sockfd);
2.1 frystyk 420: }
421: }
422: return NO;
423: }
424:
425: /*
426: ** Find persistent channel associated with this host.
427: */
428: PUBLIC HTChannel * HTHost_channel (HTHost * host)
429: {
430: return host ? host->channel : NULL;
431: }
432:
433: /*
434: ** Clear the persistent entry by deleting the channel object. Note that
435: ** the channel object is only deleted if it's not used anymore.
436: */
2.8 frystyk 437: PUBLIC BOOL HTHost_clearChannel (HTHost * host, int status)
2.1 frystyk 438: {
439: if (host && host->channel) {
2.8 frystyk 440: HTChannel_setHost(host->channel, NULL);
2.10 frystyk 441:
442: /*
443: ** We don't want to recursively delete ourselves so if we are
444: ** called from within the stream pipe then don't delete the channel
445: ** at this point
446: */
2.8 frystyk 447: HTChannel_delete(host->channel, status);
2.1 frystyk 448: host->expires = 0;
449: host->channel = NULL;
2.8 frystyk 450: HTNet_decreasePersistentSocket();
2.2 frystyk 451: if (CORE_TRACE)
452: HTTrace("Host info... removed host %p as persistent\n", host);
2.1 frystyk 453: return YES;
454: }
455: return NO;
456: }
457:
458: /*
459: ** Check whether we have a persistent channel or not
460: */
461: PUBLIC BOOL HTHost_isPersistent (HTHost * host)
462: {
463: return host && host->channel;
2.8 frystyk 464: }
465:
466: /*
467: ** Handle the connection mode. The mode may change mode in the
468: ** middle of a connection.
469: */
470: PUBLIC HTTransportMode HTHost_mode (HTHost * host, BOOL * active)
471: {
472: return host ? host->mode : HT_TP_SINGLE;
473: }
474:
475: /*
476: ** If the new mode is lower than the old mode then adjust the pipeline
477: ** accordingly. That is, if we are going into single mode then move
478: ** all entries in the pipeline and move the rest to the pending
479: ** queue. They will get launched at a later point in time.
480: */
481: PUBLIC BOOL HTHost_setMode (HTHost * host, HTTransportMode mode)
482: {
483: if (host) {
484: /*
485: ** Check the new mode and see if we must adjust the queues.
486: */
487: if (mode == HT_TP_SINGLE && host->mode > mode) {
488: int piped = HTList_count(host->pipeline);
489: if (piped > 0) {
490: int cnt;
491: if (CORE_TRACE)
492: HTTrace("Host info... Moving %d Net objects from pipe line to pending queue\n", piped);
493: if (!host->pending) host->pending = HTList_new();
494: for (cnt=0; cnt<piped; cnt++) {
495: HTNet * net = HTList_removeFirstObject(host->pipeline);
496: HTList_appendObject(host->pending, net);
497: }
498: }
499: }
500: host->mode = mode;
501: return YES;
502: }
503: return NO;
504: }
505:
506: /*
507: ** Check whether a host is idle meaning if it is ready for a new
508: ** request which depends on the mode of the host. If the host is
509: ** idle, i.e. ready for use then return YES else NO. If the host supports
510: ** persistent connections then still only return idle if no requests are
511: ** ongoing.
512: */
513: PUBLIC BOOL HTHost_isIdle (HTHost * host)
514: {
515: return (host && HTList_count(host->pipeline) <= 0);
516: }
517:
518: /*
519: ** Add a net object to the host object. If the host
520: ** is idle then add to active list (pipeline) else add
521: ** it to the pending list
522: ** Return HT_PENDING if we must pend, HT_OK, or HT_ERROR
523: */
524: PUBLIC int HTHost_addNet (HTHost * host, HTNet * net)
525: {
526: if (host && net) {
527: int status = HT_OK;
528:
529: /* Check to see if we can get a socket */
530: if (HTNet_availableSockets() <= 0) {
531: if (!PendHost) PendHost = HTList_new();
532: if (CORE_TRACE)
533: HTTrace("Host info... Add Host %p as pending\n", host);
534: HTList_addObject(PendHost, host);
535: status = HT_PENDING;
536: }
537:
538: /* Add to either active or pending queue */
539: if (HTHost_isIdle(host)) {
540: if (CORE_TRACE) HTTrace("Host info... Add Net %p to pipeline of host %p\n", net, host);
541: if (!host->pipeline) host->pipeline = HTList_new();
542: HTList_addObject(host->pipeline, net);
543:
544: /*
545: ** We have been idle and must hence unregister our catch close
546: ** event handler
547: */
548: if (host->channel) {
549: SOCKET sockfd = HTChannel_socket(host->channel);
550: HTEvent_unregister(sockfd, (SockOps) FD_CLOSE);
551: }
552: } else {
553: if (CORE_TRACE) HTTrace("Host info... Add Net %p as pending\n", net);
554: if (!host->pending) host->pending = HTList_new();
555: HTList_addObject(host->pending, net);
556: status = HT_PENDING;
557: }
558: return status;
559: }
560: return HT_ERROR;
561: }
562:
563: PUBLIC BOOL HTHost_deleteNet (HTHost * host, HTNet * net)
564: {
565: if (host && net) {
566: if (CORE_TRACE)
567: HTTrace("Host info... Remove Net %p from pipe line\n", net);
568: HTList_removeObject(host->pipeline, net);
569: HTList_removeObject(host->pending, net);
570: return YES;
571: }
572: return NO;
573: }
574:
575: /*
576: ** Handle pending host objects.
577: ** There are two ways we can end up with pending reqyests:
578: ** 1) If we are out of sockets then register new host objects as pending.
579: ** 2) If we are pending on a connection then register new net objects as
580: ** pending
581: ** This set of functions handles pending host objects and can start new
582: ** requests as resources get available
583: */
584:
585: /*
586: ** Check this host object for any pending requests and return the next
587: ** registered Net object.
588: */
589: PUBLIC HTNet * HTHost_nextPendingNet (HTHost * host)
590: {
591: HTNet * net = NULL;
592: if (host && host->pending && host->pipeline) {
2.11 kahan 593: /*JK 23/Sep/96 Bug correction. Associated the following lines to the
594: **above if. There was a missing pair of brackets.
595: */
596: if ((net = (HTNet *) HTList_removeFirstObject(host->pending)) != NULL) {
597: if (PROT_TRACE)
598: HTTrace("Host info... Popping %p from pending net queue\n",
599: net);
2.8 frystyk 600: HTList_addObject(host->pipeline, net);
2.11 kahan 601: }
2.8 frystyk 602: }
603: return net;
604: }
605:
606: /*
607: ** Return the current list of pending host obejcts waiting for a socket
608: */
609: PUBLIC HTHost * HTHost_nextPendingHost (void)
610: {
611: HTHost * host = NULL;
612: if (PendHost) {
613: if ((host = (HTHost *) HTList_removeFirstObject(PendHost)) != NULL)
614: if (PROT_TRACE)
615: HTTrace("Host info... Poping %p from pending host queue\n",
616: host);
617: }
618: return host;
619: }
620:
621: /*
622: ** Start the next pending request if any. First we look for pending
623: ** requests for the same host and then we check for any other pending
624: ** hosts
625: */
626: PUBLIC BOOL HTHost_launchPending (HTHost * host)
627: {
628: int available = HTNet_availableSockets();
629:
630: if (!host) {
631: if (PROT_TRACE) HTTrace("Host info... Bad arguments\n");
632: return NO;
633: }
634:
635: /*
636: ** Check if we do have resources available for a new request
637: ** This can either be reusing an existing connection or opening a new one
638: */
639: if (available > 0 || host->mode >= HT_TP_PIPELINE) {
640:
641: /*
642: ** Check the current Host obejct for pending Net objects
643: */
644: if (host) {
645: HTNet * net = HTHost_nextPendingNet(host);
646: if (net) return HTNet_start(net);
647: }
648:
649: /*
650: ** Check for other pending Host objects
651: */
652: {
653: HTHost * pending = HTHost_nextPendingHost();
654: if (pending) {
655: HTNet * net = HTHost_nextPendingNet(pending);
656: if (net) return HTNet_start(net);
657: }
658: }
659:
660: /*
661: ** If nothing pending then register our catch close event handler to
662: ** have something catching the socket if the remote server closes the
663: ** connection, for example due to timeout.
664: */
665: if (PROT_TRACE) HTTrace("Host info... Nothing pending\n");
666: if (host->channel) {
667: SOCKET sockfd = HTChannel_socket(host->channel);
668: HTEvent_register(sockfd, 0, (SockOps) FD_CLOSE,
669: HTHost_catchClose, HT_PRIORITY_MAX);
670: }
671: } else
672: if (PROT_TRACE) HTTrace("Host info... No available sockets\n");
673: return NO;
2.1 frystyk 674: }
2.11 kahan 675:
676:
677:
678:
2.1 frystyk 679:
Webmaster