Annotation of libwww/Library/src/HTHost.c, revision 2.11
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.11 ! kahan 6: ** @(#) $Id: HTHost.c,v 2.10 1996/08/24 18:09:57 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);
2.10 frystyk 391:
392: /*
393: ** We don't want to recursively delete ourselves so if we are
394: ** called from within the stream pipe then don't delete the channel
395: ** at this point
396: */
2.8 frystyk 397: HTChannel_delete(host->channel, status);
2.1 frystyk 398: host->expires = 0;
399: host->channel = NULL;
2.8 frystyk 400: HTNet_decreasePersistentSocket();
2.2 frystyk 401: if (CORE_TRACE)
402: HTTrace("Host info... removed host %p as persistent\n", host);
2.1 frystyk 403: return YES;
404: }
405: return NO;
406: }
407:
408: /*
409: ** Check whether we have a persistent channel or not
410: */
411: PUBLIC BOOL HTHost_isPersistent (HTHost * host)
412: {
413: return host && host->channel;
2.8 frystyk 414: }
415:
416: /*
417: ** Handle the connection mode. The mode may change mode in the
418: ** middle of a connection.
419: */
420: PUBLIC HTTransportMode HTHost_mode (HTHost * host, BOOL * active)
421: {
422: return host ? host->mode : HT_TP_SINGLE;
423: }
424:
425: /*
426: ** If the new mode is lower than the old mode then adjust the pipeline
427: ** accordingly. That is, if we are going into single mode then move
428: ** all entries in the pipeline and move the rest to the pending
429: ** queue. They will get launched at a later point in time.
430: */
431: PUBLIC BOOL HTHost_setMode (HTHost * host, HTTransportMode mode)
432: {
433: if (host) {
434: /*
435: ** Check the new mode and see if we must adjust the queues.
436: */
437: if (mode == HT_TP_SINGLE && host->mode > mode) {
438: int piped = HTList_count(host->pipeline);
439: if (piped > 0) {
440: int cnt;
441: if (CORE_TRACE)
442: HTTrace("Host info... Moving %d Net objects from pipe line to pending queue\n", piped);
443: if (!host->pending) host->pending = HTList_new();
444: for (cnt=0; cnt<piped; cnt++) {
445: HTNet * net = HTList_removeFirstObject(host->pipeline);
446: HTList_appendObject(host->pending, net);
447: }
448: }
449: }
450: host->mode = mode;
451: return YES;
452: }
453: return NO;
454: }
455:
456: /*
457: ** Check whether a host is idle meaning if it is ready for a new
458: ** request which depends on the mode of the host. If the host is
459: ** idle, i.e. ready for use then return YES else NO. If the host supports
460: ** persistent connections then still only return idle if no requests are
461: ** ongoing.
462: */
463: PUBLIC BOOL HTHost_isIdle (HTHost * host)
464: {
465: return (host && HTList_count(host->pipeline) <= 0);
466: }
467:
468: /*
469: ** Add a net object to the host object. If the host
470: ** is idle then add to active list (pipeline) else add
471: ** it to the pending list
472: ** Return HT_PENDING if we must pend, HT_OK, or HT_ERROR
473: */
474: PUBLIC int HTHost_addNet (HTHost * host, HTNet * net)
475: {
476: if (host && net) {
477: int status = HT_OK;
478:
479: /* Check to see if we can get a socket */
480: if (HTNet_availableSockets() <= 0) {
481: if (!PendHost) PendHost = HTList_new();
482: if (CORE_TRACE)
483: HTTrace("Host info... Add Host %p as pending\n", host);
484: HTList_addObject(PendHost, host);
485: status = HT_PENDING;
486: }
487:
488: /* Add to either active or pending queue */
489: if (HTHost_isIdle(host)) {
490: if (CORE_TRACE) HTTrace("Host info... Add Net %p to pipeline of host %p\n", net, host);
491: if (!host->pipeline) host->pipeline = HTList_new();
492: HTList_addObject(host->pipeline, net);
493:
494: /*
495: ** We have been idle and must hence unregister our catch close
496: ** event handler
497: */
498: if (host->channel) {
499: SOCKET sockfd = HTChannel_socket(host->channel);
500: HTEvent_unregister(sockfd, (SockOps) FD_CLOSE);
501: }
502: } else {
503: if (CORE_TRACE) HTTrace("Host info... Add Net %p as pending\n", net);
504: if (!host->pending) host->pending = HTList_new();
505: HTList_addObject(host->pending, net);
506: status = HT_PENDING;
507: }
508: return status;
509: }
510: return HT_ERROR;
511: }
512:
513: PUBLIC BOOL HTHost_deleteNet (HTHost * host, HTNet * net)
514: {
515: if (host && net) {
516: if (CORE_TRACE)
517: HTTrace("Host info... Remove Net %p from pipe line\n", net);
518: HTList_removeObject(host->pipeline, net);
519: HTList_removeObject(host->pending, net);
520: return YES;
521: }
522: return NO;
523: }
524:
525: /*
526: ** Handle pending host objects.
527: ** There are two ways we can end up with pending reqyests:
528: ** 1) If we are out of sockets then register new host objects as pending.
529: ** 2) If we are pending on a connection then register new net objects as
530: ** pending
531: ** This set of functions handles pending host objects and can start new
532: ** requests as resources get available
533: */
534:
535: /*
536: ** Check this host object for any pending requests and return the next
537: ** registered Net object.
538: */
539: PUBLIC HTNet * HTHost_nextPendingNet (HTHost * host)
540: {
541: HTNet * net = NULL;
542: if (host && host->pending && host->pipeline) {
2.11 ! kahan 543: /*JK 23/Sep/96 Bug correction. Associated the following lines to the
! 544: **above if. There was a missing pair of brackets.
! 545: */
! 546: if ((net = (HTNet *) HTList_removeFirstObject(host->pending)) != NULL) {
! 547: if (PROT_TRACE)
! 548: HTTrace("Host info... Popping %p from pending net queue\n",
! 549: net);
2.8 frystyk 550: HTList_addObject(host->pipeline, net);
2.11 ! kahan 551: }
2.8 frystyk 552: }
553: return net;
554: }
555:
556: /*
557: ** Return the current list of pending host obejcts waiting for a socket
558: */
559: PUBLIC HTHost * HTHost_nextPendingHost (void)
560: {
561: HTHost * host = NULL;
562: if (PendHost) {
563: if ((host = (HTHost *) HTList_removeFirstObject(PendHost)) != NULL)
564: if (PROT_TRACE)
565: HTTrace("Host info... Poping %p from pending host queue\n",
566: host);
567: }
568: return host;
569: }
570:
571: /*
572: ** Start the next pending request if any. First we look for pending
573: ** requests for the same host and then we check for any other pending
574: ** hosts
575: */
576: PUBLIC BOOL HTHost_launchPending (HTHost * host)
577: {
578: int available = HTNet_availableSockets();
579:
580: if (!host) {
581: if (PROT_TRACE) HTTrace("Host info... Bad arguments\n");
582: return NO;
583: }
584:
585: /*
586: ** Check if we do have resources available for a new request
587: ** This can either be reusing an existing connection or opening a new one
588: */
589: if (available > 0 || host->mode >= HT_TP_PIPELINE) {
590:
591: /*
592: ** Check the current Host obejct for pending Net objects
593: */
594: if (host) {
595: HTNet * net = HTHost_nextPendingNet(host);
596: if (net) return HTNet_start(net);
597: }
598:
599: /*
600: ** Check for other pending Host objects
601: */
602: {
603: HTHost * pending = HTHost_nextPendingHost();
604: if (pending) {
605: HTNet * net = HTHost_nextPendingNet(pending);
606: if (net) return HTNet_start(net);
607: }
608: }
609:
610: /*
611: ** If nothing pending then register our catch close event handler to
612: ** have something catching the socket if the remote server closes the
613: ** connection, for example due to timeout.
614: */
615: if (PROT_TRACE) HTTrace("Host info... Nothing pending\n");
616: if (host->channel) {
617: SOCKET sockfd = HTChannel_socket(host->channel);
618: HTEvent_register(sockfd, 0, (SockOps) FD_CLOSE,
619: HTHost_catchClose, HT_PRIORITY_MAX);
620: }
621: } else
622: if (PROT_TRACE) HTTrace("Host info... No available sockets\n");
623: return NO;
2.1 frystyk 624: }
2.11 ! kahan 625:
! 626:
! 627:
! 628:
2.1 frystyk 629:
Webmaster