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.
6 **	@(#) $Id: libwww-HTHost.c,v 1.1.1.1 2003/07/04 22:30:05 atterer Exp $
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 "wwwsys.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 "HTTPUtil.h"
24 #include "HTTCP.h"
25 #include "HTHost.h"					 /* Implemented here */
26 #include "HTHstMan.h"
27 
28 #define HOST_OBJECT_TTL	    43200L	     /* Default host timeout is 12 h */
29 
30 #define TCP_IDLE_PASSIVE      120L /* Passive TTL in s on an idle connection */
31 #define TCP_IDLE_ACTIVE     60000L /* Active TTL in ms on an idle connection */
32 
33 #define MAX_PIPES		50   /* maximum number of pipelined requests */
34 #define MAX_HOST_RECOVER	0	      /* Max number of auto recovery */
35 #define DEFAULT_DELAY		30	  /* Default write flush delay in ms */
36 
37 struct _HTInputStream {
38     const HTInputStreamClass *	isa;
39 };
40 
41 PRIVATE int HostEvent(SOCKET soc, void * pVoid, HTEventType type);
42 
43 /* Type definitions and global variables etc. local to this module */
44 PRIVATE time_t	HostTimeout = HOST_OBJECT_TTL;	 /* Timeout for host objects */
45 PRIVATE time_t	HTPassiveTimeout = TCP_IDLE_PASSIVE; /* Passive timeout in s */
46 PRIVATE ms_t	HTActiveTimeout = TCP_IDLE_ACTIVE;   /* Active timeout in ms */
47 
48 PRIVATE HTList	** HostTable = NULL;
49 PRIVATE HTList * PendHost = NULL;	    /* List of pending host elements */
50 
51 /* JK: New functions for interruption the automatic pending request
52    activation */
53 PRIVATE HTHost_ActivateRequestCallback * ActivateReqCBF = NULL;
54 PRIVATE int HTHost_ActivateRequest (HTNet *net);
55 PRIVATE BOOL DoPendingReqLaunch = YES; /* controls automatic activation
56                                               of pending requests */
57 
58 PRIVATE int EventTimeout = -1;		        /* Global Host event timeout */
59 
60 PRIVATE ms_t WriteDelay = DEFAULT_DELAY;		      /* Delay in ms */
61 
62 PRIVATE int MaxPipelinedRequests = MAX_PIPES;
63 
64 /* ------------------------------------------------------------------------- */
65 
free_object(HTHost * me)66 PRIVATE void free_object (HTHost * me)
67 {
68     if (me) {
69 	int i;
70 	HT_FREE(me->hostname);
71 	HT_FREE(me->type);
72 	HT_FREE(me->server);
73 	HT_FREE(me->user_agent);
74 	HT_FREE(me->range_units);
75 
76 	/* Delete the channel (if any) */
77 	if (me->channel) {
78 	    HTChannel_delete(me->channel, HT_OK);
79 	    me->channel = NULL;
80 	}
81 
82 	/* Unregister the events */
83 	for (i = 0; i < HTEvent_TYPES; i++)
84 	    HTEvent_delete(me->events[i]);
85 
86 	/* Delete the timer (if any) */
87 	if (me->timer) HTTimer_delete(me->timer);
88 
89 	/* Delete the queues */
90 	HTList_delete(me->pipeline);
91 	HTList_delete(me->pending);
92 	HT_FREE(me);
93     }
94 }
95 
delete_object(HTList * list,HTHost * me)96 PRIVATE BOOL delete_object (HTList * list, HTHost * me)
97 {
98     HTTRACE(CORE_TRACE, "Host info... object %p from list %p\n" _ me _ list);
99     HTList_removeObject(list, (void *) me);
100     free_object(me);
101     return YES;
102 }
103 
isLastInPipe(HTHost * host,HTNet * net)104 PRIVATE BOOL isLastInPipe (HTHost * host, HTNet * net)
105 {
106     return HTList_lastObject(host->pipeline) == net;
107 }
108 
killPipeline(HTHost * host,HTEventType type)109 PRIVATE BOOL killPipeline (HTHost * host, HTEventType type)
110 {
111     if (host) {
112 	int piped = HTList_count(host->pipeline);
113 	int pending = HTList_count(host->pending);
114 	int cnt;
115 
116 	HTTRACE(CORE_TRACE, "Host kill... Pipeline due to %s event\n" _ HTEvent_type2str(type));
117 
118 	/* Terminate all net objects in pending queue */
119 	for (cnt=0; cnt<pending; cnt++) {
120 	    HTNet * net = HTList_removeLastObject(host->pending);
121 	    if (net) {
122 		HTTRACE(CORE_TRACE, "Host kill... Terminating net object %p from pending queue\n" _ net);
123 		net->registeredFor = 0;
124 		(*net->event.cbf)(HTChannel_socket(host->channel), net->event.param, type);
125 		if (host->lock == net) host->lock = NULL;
126 	    }
127 	}
128 
129 	/* Terminate all net objects in pipeline */
130 	if (piped >= 1) {
131 
132 	    /*
133 	    **  Terminte all net objects in the pipeline
134 	    */
135 	    for (cnt=0; cnt<piped; cnt++) {
136 		HTNet * net = HTList_firstObject(host->pipeline);
137 		if (net) {
138 		    HTTRACE(CORE_TRACE, "Host kill... Terminating net object %p from pipe line\n" _ net);
139 		    net->registeredFor = 0;
140 		    (*net->event.cbf)(HTChannel_socket(host->channel), net->event.param, type);
141 		}
142 	    }
143 	    /* Close the connection, to prevent the server from sending us
144 	       the remainder of the data for the currently active request. */
145 	    HTChannel_setSemaphore(host->channel, 0);
146 	    HTHost_clearChannel(host, HT_INTERRUPTED);
147 	}
148 	return YES;
149     }
150     return NO;
151 }
152 
153 /*
154 **  Silently close an idle persistent connection after
155 **  HTActiveTimeout secs
156 */
IdleTimeoutEvent(HTTimer * timer,void * param,HTEventType type)157 PRIVATE int IdleTimeoutEvent (HTTimer * timer, void * param, HTEventType type)
158 {
159     HTHost * host = (HTHost *) param;
160     SOCKET sockfd = HTChannel_socket(host->channel);
161 
162     HTTimer_delete(timer);
163     host->timer = NULL;
164 
165     return HostEvent (sockfd, host, HTEvent_CLOSE);
166 }
167 
168 /*
169 **	HostEvent - host event manager - recieves events from the event
170 **	manager and dispatches them to the client net objects by calling the
171 **	net object's cbf.
172 **
173 */
HostEvent(SOCKET soc,void * pVoid,HTEventType type)174 PRIVATE int HostEvent (SOCKET soc, void * pVoid, HTEventType type)
175 {
176     HTHost * host = (HTHost *)pVoid;
177 
178     if (type == HTEvent_READ || type == HTEvent_CLOSE || type == HTEvent_ACCEPT) {
179 	HTNet * targetNet;
180 
181 	/* call the first net object */
182 	do {
183 	    int ret;
184 
185             /* netscape and apache servers can do a lazy close well after usage
186              * of previous socket has been dispensed by the library,
187              * the section below makes sure the event does not get miss attributed
188              */
189 	    if (HTChannel_socket(host->channel) != soc && type != HTEvent_ACCEPT && !host->listening) {
190 		HTTRACE(CORE_TRACE, "Host Event.. wild socket %d type = %s real socket is %d\n" _ soc _
191 			type == HTEvent_CLOSE ? "Event_Close" : "Event_Read" _
192 			HTChannel_socket(host->channel));
193 		return HT_OK;
194 	    }
195 
196 	    targetNet = (HTNet *)HTList_firstObject(host->pipeline);
197 	    if (targetNet) {
198 		HTTRACE(CORE_TRACE, "Host Event.. READ passed to `%s\'\n" _
199 			    HTAnchor_physical(HTRequest_anchor(HTNet_request(targetNet))));
200 		if ((ret = (*targetNet->event.cbf)(HTChannel_socket(host->channel),
201 						  targetNet->event.param, type)) != HT_OK)
202 		    return ret;
203 	    }
204 	    if (targetNet == NULL && host->remainingRead > 0) {
205 		HTTRACE(CORE_TRACE, "HostEvent... Error: %d bytes left to read and nowhere to put them\n" _
206 			    host->remainingRead);
207 		host->remainingRead = 0;
208 		/*
209 		**	Fall through to close the channel
210 		*/
211 	    }
212 	/* call pipelined net object to eat all the data in the channel */
213 	} while (host->remainingRead > 0);
214 
215 	/* last target net should have set remainingRead to 0 */
216 	if (targetNet)
217 	    return HT_OK;
218 
219 	/* If there was notargetNet, it should be a close */
220 	HTTRACE(CORE_TRACE, "Host Event.. host %p `%s\' closed connection.\n" _
221 		    host _ host->hostname);
222 
223 	/* Is there garbage in the channel? Let's check: */
224 	{
225 	    char buf[256];
226 	    int ret;
227 	    memset(buf, '\0', sizeof(buf));
228 	    if (HTChannel_socket(host->channel) != INVSOC) {
229 	      while ((ret = NETREAD(HTChannel_socket(host->channel), buf, sizeof(buf)-1)) > 0) {
230 		HTTRACE(CORE_TRACE, "Host Event.. Host %p `%s\' had %d extraneous bytes: `%s\'\n" _
231 			host _ host->hostname _ ret _ 0/*buf*/);
232 		memset(buf, '\0', sizeof(buf));
233 	      }
234 	    }
235 	}
236 	HTHost_clearChannel(host, HT_OK);
237 	return HT_OK; 	     /* extra garbage does not constitute an application error */
238 
239     } else if (type == HTEvent_WRITE || type == HTEvent_CONNECT) {
240 	HTNet * targetNet = (HTNet *)HTList_lastObject(host->pipeline);
241 	if (targetNet) {
242 	    HTTRACE(CORE_TRACE, "Host Event.. WRITE passed to `%s\'\n" _
243 			HTAnchor_physical(HTRequest_anchor(HTNet_request(targetNet))));
244 	    return (*targetNet->event.cbf)(HTChannel_socket(host->channel), targetNet->event.param, type);
245 	}
246 	HTTRACE(CORE_TRACE, "Host Event Host %p (`%s\') dispatched with event %s but doesn't have a target - %d requests made, %d requests in pipe, %d pending\n" _
247 		    host _
248 		    host ? host->hostname : "<null>" _
249 		    HTEvent_type2str(type) _
250 		    host ? host->reqsMade : -1 _
251 		    HTList_count(host->pipeline) _
252 		    HTList_count(host->pending));
253 #if 0
254 	HTDEBUGBREAK("Host Event.. Host %p (`%s\') dispatched with event %d\n" _
255 		     host _ host ? host->hostname : "<null>" _ type);
256 	return HT_ERROR;
257 #else
258 	return HT_OK;
259 #endif
260     } else if (type == HTEvent_TIMEOUT) {
261 	killPipeline(host, HTEvent_TIMEOUT);
262     } else {
263 	HTTRACE(CORE_TRACE, "Don't know how to handle OOB data from `%s\'?\n" _
264 		host->hostname);
265     }
266     return HT_OK;
267 }
268 
269 /*
270 **	Search the host info cache for a host object or create a new one
271 **	and add it. Examples of host names are
272 **
273 **		www.w3.org
274 **		www.foo.com:8000
275 **		18.52.0.18
276 **
277 **	Returns Host object or NULL if error. You may get back an already
278 **	existing host object - you're not guaranteed a new one each time.
279 */
HTHost_new(char * host,u_short u_port)280 PUBLIC HTHost * HTHost_new (char * host, u_short u_port)
281 {
282     HTList * list = NULL;			    /* Current list in cache */
283     HTHost * pres = NULL;
284     int hash = 0;
285     if (!host) {
286 	HTTRACE(CORE_TRACE, "Host info... Bad argument\n");
287 	return NULL;
288     }
289 
290     /* Find a hash for this host */
291     {
292 	char *ptr;
293 	for (ptr=host; *ptr; ptr++)
294 	    hash = (int) ((hash * 3 + (*(unsigned char *) ptr)) % HOST_HASH_SIZE);
295 	if (!HostTable) {
296 	    if ((HostTable = (HTList **) HT_CALLOC(HOST_HASH_SIZE,
297 						   sizeof(HTList *))) == NULL)
298 	        HT_OUTOFMEM("HTHost_find");
299 	}
300 	if (!HostTable[hash]) HostTable[hash] = HTList_new();
301 	list = HostTable[hash];
302     }
303 
304     /* Search the cache */
305     {
306 	HTList * cur = list;
307 	while ((pres = (HTHost *) HTList_nextObject(cur))) {
308 	    if (!strcmp(pres->hostname, host) && u_port == pres->u_port) {
309 		if (HTHost_isIdle(pres) && time(NULL)>pres->ntime+HostTimeout){
310 		    HTTRACE(CORE_TRACE, "Host info... Collecting host info %p\n" _ pres);
311 		    delete_object(list, pres);
312 		    pres = NULL;
313 		}
314 		break;
315 	    }
316 	}
317     }
318 
319     /* If not found then create new Host object, else use existing one */
320     if (pres) {
321 	if (pres->channel) {
322 
323             /*
324             **  If we have a TTL for this TCP connection then
325             **  check that we haven't passed it.
326             */
327             if (pres->expires > 0) {
328                 time_t t = time(NULL);
329                 if (HTHost_isIdle(pres) && pres->expires < t) {
330                     HTTRACE(CORE_TRACE, "Host info... Persistent channel %p gotten cold\n" _
331                         pres->channel);
332 		    HTHost_clearChannel(pres, HT_OK);
333                 } else {
334                     pres->expires = t + HTPassiveTimeout;
335                     HTTRACE(CORE_TRACE, "Host info... REUSING CHANNEL %p\n" _ pres->channel);
336                 }
337             }
338 	} else {
339 	    HTTRACE(CORE_TRACE, "Host info... Found Host %p with no active channel\n" _ pres);
340 	}
341     } else {
342 	if ((pres = (HTHost *) HT_CALLOC(1, sizeof(HTHost))) == NULL)
343 	    HT_OUTOFMEM("HTHost_add");
344 	pres->hash = hash;
345 	StrAllocCopy(pres->hostname, host);
346 	pres->u_port = u_port;
347 	pres->ntime = time(NULL);
348 	pres->mode = HT_TP_SINGLE;
349 	pres->delay = WriteDelay;
350 	pres->inFlush = NO;
351 	{
352 	    int i;
353 	    for (i = 0; i < HTEvent_TYPES; i++)
354 		pres->events[i]= HTEvent_new(HostEvent, pres, HT_PRIORITY_MAX, EventTimeout);
355 	}
356 	HTTRACE(CORE_TRACE, "Host info... added `%s\' with host %p to list %p\n" _
357 		    host _ pres _ list);
358 	HTList_addObject(list, (void *) pres);
359     }
360     return pres;
361 }
362 
HTHost_newWParse(HTRequest * request,char * url,u_short u_port)363 PUBLIC HTHost * HTHost_newWParse (HTRequest * request, char * url, u_short u_port)
364 {
365     char * port;
366     char * fullhost = NULL;
367     char * parsedHost = NULL;
368     SockA * sin;
369     HTHost * me;
370     char * proxy = HTRequest_proxy(request);
371 
372     fullhost = HTParse(proxy ? proxy : url, "", PARSE_HOST);
373 
374     /* If there's an @ then use the stuff after it as a hostname */
375     if (fullhost) {
376 	char * at_sign;
377 	if ((at_sign = strchr(fullhost, '@')) != NULL)
378 	    parsedHost = at_sign+1;
379 	else
380 	    parsedHost = fullhost;
381     }
382     if (!parsedHost || !*parsedHost) {
383 	HTRequest_addError(request, ERR_FATAL, NO, HTERR_NO_HOST,
384 			   NULL, 0, "HTHost_newWParse");
385 	HT_FREE(fullhost);
386 	return NULL;
387     }
388 
389     /* See if the default port should be overridden */
390     if ((port = strchr(parsedHost, ':')) != NULL) {
391 	*port++ = '\0';
392 	if (*port && isdigit((int) *port)) u_port = (u_short) atol(port);
393     }
394     HTTRACE(PROT_TRACE, "HTHost parse Looking up `%s\' on port %u\n" _ parsedHost _ u_port);
395 
396     /* Find information about this host */
397     if ((me = HTHost_new(parsedHost, u_port)) == NULL) {
398 	HTTRACE(PROT_TRACE, "HTHost parse Can't get host info\n");
399 	me->tcpstate = TCP_ERROR;
400 	return NULL;
401     }
402     sin = &me->sock_addr;
403     memset((void *) sin, '\0', sizeof(SockA));
404 #ifdef DECNET
405     sin->sdn_family = AF_DECnet;
406     net->sock_addr.sdn_objnum = port ? (unsigned char)(strtol(port, (char **) 0, 10)) : DNP_OBJ;
407 #else  /* Internet */
408     sin->sin_family = AF_INET;
409     sin->sin_port = htons(u_port);
410 #endif
411     HT_FREE(fullhost);	/* parsedHost points into fullhost */
412     return me;
413 }
414 
415 /*
416 **	Search the host info cache for a host object. Examples of host names:
417 **
418 **		www.w3.org
419 **		www.foo.com:8000
420 **		18.52.0.18
421 **
422 **	Returns Host object or NULL if not found.
423 */
HTHost_find(char * host)424 PUBLIC HTHost * HTHost_find (char * host)
425 {
426     HTList * list = NULL;			    /* Current list in cache */
427     HTHost * pres = NULL;
428     HTTRACE(CORE_TRACE, "Host info... Looking for `%s\'\n" _ host ? host : "<null>");
429 
430     /* Find a hash for this host */
431     if (host && HostTable) {
432 	int hash = 0;
433 	char *ptr;
434 	for (ptr=host; *ptr; ptr++)
435 	    hash = (int) ((hash * 3 + (*(unsigned char *) ptr)) % HOST_HASH_SIZE);
436 	if (!HostTable[hash]) return NULL;
437 	list = HostTable[hash];
438 
439 	/* Search the cache */
440 	{
441 	    HTList * cur = list;
442 	    while ((pres = (HTHost *) HTList_nextObject(cur))) {
443 		if (!strcmp(pres->hostname, host)) {
444 		    if (time(NULL) > pres->ntime + HostTimeout) {
445 			HTTRACE(CORE_TRACE, "Host info... Collecting host %p\n" _ pres);
446 			delete_object(list, pres);
447 			pres = NULL;
448   		    } else {
449 			HTTRACE(CORE_TRACE, "Host info... Found `%s\'\n" _ host);
450 		    }
451 		    return pres;
452 		}
453 	    }
454 	}
455     }
456     return NULL;
457 }
458 
459 /*
460 **	Cleanup the host list
461 */
HTHost_deleteAll(void)462 PUBLIC void HTHost_deleteAll (void)
463 {
464     HTList * list;
465     HTHost * host;
466     int i;
467 
468     if (!HostTable)
469 	return;
470 
471     for (i=0; i < HOST_HASH_SIZE; i++) {
472 	list = HostTable[i];
473 	if (!list) continue;
474 
475 	while ((host = (HTHost *) HTList_removeFirstObject(list)) != NULL)
476 	    free_object(host);
477 
478 	HTList_delete(list);
479     }
480 
481     HT_FREE(HostTable);
482     HostTable = NULL;
483 }
484 
485 /*
486 **	Get and set the hostname of the remote host
487 */
HTHost_name(HTHost * host)488 PUBLIC char * HTHost_name (HTHost * host)
489 {
490      return host ? host->hostname : NULL;
491 }
492 
493 /*
494 **	Get and set the type class of the remote host
495 */
HTHost_class(HTHost * host)496 PUBLIC char * HTHost_class (HTHost * host)
497 {
498      return host ? host->type : NULL;
499 }
500 
HTHost_setClass(HTHost * host,char * s_class)501 PUBLIC void HTHost_setClass (HTHost * host, char * s_class)
502 {
503     if (host && s_class) StrAllocCopy(host->type, s_class);
504 }
505 
506 /*
507 **	Get and set the version of the remote host
508 */
HTHost_version(HTHost * host)509 PUBLIC int HTHost_version (HTHost *host)
510 {
511      return host ? host->version : 0;
512 }
513 
HTHost_setVersion(HTHost * host,int version)514 PUBLIC void HTHost_setVersion (HTHost * host, int version)
515 {
516     if (host) host->version = version;
517 }
518 
519 /*
520 **  Get and set the passive timeout for persistent entries.
521 */
HTHost_setPersistTimeout(time_t timeout)522 PUBLIC BOOL HTHost_setPersistTimeout (time_t timeout)
523 {
524     if (timeout > 0) {
525 	HTPassiveTimeout = timeout;
526 	return YES;
527     }
528     return NO;
529 }
530 
HTHost_persistTimeout(void)531 PUBLIC time_t HTHost_persistTimeout (void)
532 {
533     return HTPassiveTimeout;
534 }
535 
536 /*
537 **  Get and set the active timeout for persistent entries.
538 */
HTHost_setActiveTimeout(ms_t timeout)539 PUBLIC BOOL HTHost_setActiveTimeout (ms_t timeout)
540 {
541     if (timeout > 1000) {
542 	HTActiveTimeout = timeout;
543 	return YES;
544     }
545     return NO;
546 }
547 
HTHost_activeTimeout(void)548 PUBLIC ms_t HTHost_activeTimeout (void)
549 {
550     return HTActiveTimeout;
551 }
552 
553 /*	Persistent Connection Expiration
554 **	--------------------------------
555 **	Should normally not be used. If, then use calendar time.
556 */
HTHost_setPersistExpires(HTHost * host,time_t expires)557 PUBLIC void HTHost_setPersistExpires (HTHost * host, time_t expires)
558 {
559     if (host) host->expires = expires;
560 }
561 
HTHost_persistExpires(HTHost * host)562 PUBLIC time_t HTHost_persistExpires (HTHost * host)
563 {
564     return host ? host->expires : -1;
565 }
566 
HTHost_setReqsPerConnection(HTHost * host,int reqs)567 PUBLIC void HTHost_setReqsPerConnection (HTHost * host, int reqs)
568 {
569     if (host) host->reqsPerConnection = reqs;
570 }
571 
HTHost_reqsPerConnection(HTHost * host)572 PUBLIC int HTHost_reqsPerConnection (HTHost * host)
573 {
574     return host ? host->reqsPerConnection : -1;
575 }
576 
HTHost_setReqsMade(HTHost * host,int reqs)577 PUBLIC void HTHost_setReqsMade (HTHost * host, int reqs)
578 {
579     if (host) host->reqsMade = reqs;
580 }
581 
HTHost_reqsMade(HTHost * host)582 PUBLIC int HTHost_reqsMade (HTHost * host)
583 {
584     return host ? host->reqsMade : -1;
585 }
586 
587 /*
588 **	Public methods for this host
589 */
HTHost_publicMethods(HTHost * me)590 PUBLIC HTMethod HTHost_publicMethods (HTHost * me)
591 {
592     return me ? me->methods : METHOD_INVALID;
593 }
594 
HTHost_setPublicMethods(HTHost * me,HTMethod methodset)595 PUBLIC void HTHost_setPublicMethods (HTHost * me, HTMethod methodset)
596 {
597     if (me) me->methods = methodset;
598 }
599 
HTHost_appendPublicMethods(HTHost * me,HTMethod methodset)600 PUBLIC void HTHost_appendPublicMethods (HTHost * me, HTMethod methodset)
601 {
602     if (me) me->methods |= methodset;
603 }
604 
605 /*
606 **	Get and set the server name of the remote host
607 */
HTHost_server(HTHost * host)608 PUBLIC char * HTHost_server (HTHost * host)
609 {
610      return host ? host->server : NULL;
611 }
612 
HTHost_setServer(HTHost * host,const char * server)613 PUBLIC BOOL HTHost_setServer (HTHost * host, const char * server)
614 {
615     if (host && server) {
616 	StrAllocCopy(host->server, server);
617 	return YES;
618     }
619     return NO;
620 }
621 
622 /*
623 **	Get and set the userAgent name of the remote host
624 */
HTHost_userAgent(HTHost * host)625 PUBLIC char * HTHost_userAgent (HTHost * host)
626 {
627      return host ? host->user_agent : NULL;
628 }
629 
HTHost_setUserAgent(HTHost * host,const char * userAgent)630 PUBLIC BOOL HTHost_setUserAgent (HTHost * host, const char * userAgent)
631 {
632     if (host && userAgent) {
633 	StrAllocCopy(host->user_agent, userAgent);
634 	return YES;
635     }
636     return NO;
637 }
638 
639 /*
640 **	Get and set acceptable range units
641 */
HTHost_rangeUnits(HTHost * host)642 PUBLIC char * HTHost_rangeUnits (HTHost * host)
643 {
644      return host ? host->range_units : NULL;
645 }
646 
HTHost_setRangeUnits(HTHost * host,const char * units)647 PUBLIC BOOL HTHost_setRangeUnits (HTHost * host, const char * units)
648 {
649     if (host && units) {
650 	StrAllocCopy(host->range_units, units);
651 	return YES;
652     }
653     return NO;
654 }
655 
656 /*
657 **	Checks whether a specific range unit is OK. We always say
658 **	YES except if we have a specific statement from the server that
659 **	it doesn't understand byte ranges - that is - it has sent "none"
660 **	in a "Accept-Range" response header
661 */
HTHost_isRangeUnitAcceptable(HTHost * host,const char * unit)662 PUBLIC BOOL HTHost_isRangeUnitAcceptable (HTHost * host, const char * unit)
663 {
664     if (host && unit) {
665 #if 0
666 	if (host->range_units) {
667 	    char * start = HTStrCaseStr(host->range_units, "none");
668 
669 	    /*
670 	    **  Check that "none" is infact a token. It could be part of some
671 	    **  other valid string, so we'd better check for it.
672 	    */
673 	    if (start) {
674 
675 
676 	    }
677 	    return NO;
678 	}
679 #endif
680 	return strcasecomp(unit, "bytes") ? NO : YES;
681     }
682     return NO;
683 }
684 
685 /*
686 **	As soon as we know that this host accepts persistent connections,
687 **	we associated the channel with the host.
688 **	We don't want more than MaxSockets-2 connections to be persistent in
689 **	order to avoid deadlock.
690 */
HTHost_setPersistent(HTHost * host,BOOL persistent,HTTransportMode mode)691 PUBLIC BOOL HTHost_setPersistent (HTHost *		host,
692 				  BOOL			persistent,
693 				  HTTransportMode	mode)
694 {
695     if (!host) return NO;
696 
697     if (!persistent) {
698 	/*
699 	**  We use the HT_IGNORE status code as we don't want to free
700 	**  the stream at this point in time. The situation we want to
701 	**  avoid is that we free the channel from within the stream pipe.
702 	**  This will lead to an infinite look having the stream freing
703 	**  itself.
704 	*/
705 	host->persistent = NO;
706 	return HTHost_clearChannel(host, HT_IGNORE);
707     }
708 
709     /*
710     ** Set the host persistent if not already. Also update the mode to
711     ** the new one - it may have changed
712     */
713     HTHost_setMode(host, mode);
714     if (!host->persistent) {
715 	SOCKET sockfd = HTChannel_socket(host->channel);
716 	if (sockfd != INVSOC && HTNet_availablePersistentSockets() > 0) {
717 	    host->persistent = YES;
718 	    host->expires = time(NULL) + HTPassiveTimeout;     /* Default timeout */
719 	    HTChannel_setHost(host->channel, host);
720 	    HTNet_increasePersistentSocket();
721 	    HTTRACE(CORE_TRACE, "Host info... added host %p as persistent\n" _ host);
722 	    return YES;
723 	} else {
724 	    HTTRACE(CORE_TRACE, "Host info... no room for persistent socket %d\n" _
725 			sockfd);
726 	    return NO;
727 	}
728     } else {
729 	HTTRACE(CORE_TRACE, "Host info... %p already persistent\n" _ host);
730 	return YES;
731     }
732     return NO;
733 }
734 
735 /*
736 **	Check whether we have a persistent channel or not
737 */
HTHost_isPersistent(HTHost * host)738 PUBLIC BOOL HTHost_isPersistent (HTHost * host)
739 {
740     return host && host->persistent;
741 }
742 
743 /*
744 **	Find persistent channel associated with this host.
745 */
HTHost_channel(HTHost * host)746 PUBLIC HTChannel * HTHost_channel (HTHost * host)
747 {
748     return host ? host->channel : NULL;
749 }
750 
751 
752 /*
753 **  Check whether we have got a "close" notification, for example in the
754 **  connection header
755 */
HTHost_setCloseNotification(HTHost * host,BOOL mode)756 PUBLIC BOOL HTHost_setCloseNotification (HTHost * host, BOOL mode)
757 {
758     if (host) {
759 	host->close_notification = mode;
760 	return YES;
761     }
762     return NO;
763 }
764 
HTHost_closeNotification(HTHost * host)765 PUBLIC BOOL HTHost_closeNotification (HTHost * host)
766 {
767     return host && host->close_notification;
768 }
769 
770 /*
771 **	Clear the persistent entry by deleting the channel object. Note that
772 **	the channel object is only deleted if it's not used anymore.
773 */
HTHost_clearChannel(HTHost * host,int status)774 PUBLIC BOOL HTHost_clearChannel (HTHost * host, int status)
775 {
776     if (host && host->channel) {
777 	HTChannel_setHost(host->channel, NULL);
778 
779 	HTEvent_unregister(HTChannel_socket(host->channel), HTEvent_READ);
780 	HTEvent_unregister(HTChannel_socket(host->channel), HTEvent_WRITE);
781 	host->registeredFor = 0;
782 
783 	/*
784 	**  We don't want to recursively delete ourselves so if we are
785 	**  called from within the stream pipe then don't delete the channel
786 	**  at this point
787 	*/
788 	HTChannel_delete(host->channel, status);
789 	host->expires = 0;
790 	host->channel = NULL;
791 	host->tcpstate = TCP_BEGIN;
792 	host->reqsMade = 0;
793 	if (HTHost_isPersistent(host)) {
794 	    HTNet_decreasePersistentSocket();
795 	    host->persistent = NO;
796 	}
797 	host->close_notification = NO;
798 	host->broken_pipe = NO;
799        	host->mode = HT_TP_SINGLE;
800 
801 	host->recovered = 0;
802 
803 	HTTRACE(CORE_TRACE, "Host info... removed host %p as persistent\n" _ host);
804 
805 	if (!HTList_isEmpty(host->pending)) {
806 	    HTTRACE(CORE_TRACE, "Host has %d object(s) pending - attempting launch\n" _ HTList_count(host->pending));
807 	    HTHost_launchPending(host);
808 	}
809 	return YES;
810     }
811     return NO;
812 }
813 
HTHost_doRecover(HTHost * host)814 PUBLIC BOOL HTHost_doRecover (HTHost * host)
815 {
816     return host ? host->do_recover : NO;
817 }
818 
819 /*
820 **	Move all entries in the pipeline and move the rest to the pending
821 **	queue. They will get launched at a later point in time.
822 */
HTHost_recoverPipe(HTHost * host)823 PUBLIC BOOL HTHost_recoverPipe (HTHost * host)
824 {
825     if (host) {
826 	int piped = HTList_count(host->pipeline);
827 
828 	/*
829 	**  First check that we haven't already recovered more than we want
830 	*/
831 	if (host->recovered >= MAX_HOST_RECOVER) {
832 	    HTTRACE(CORE_TRACE, "Host recover %p already %d times - not doing it anymore\n" _ host _ host->recovered);
833 	    HTChannel_setSemaphore(host->channel, 0);
834 	    HTHost_clearChannel(host, HT_INTERRUPTED);
835 	    return NO;
836 	}
837 
838 	/*
839 	**  If we decided to recover and actually have something in the pipe
840 	**  then go ahead.
841 	*/
842 	if (piped > 0) {
843 	    int cnt;
844 	    host->recovered++;
845 	    HTTRACE(CORE_TRACE, "Host recover %p recovered %d times. Moving %d Net objects from pipe line to pending queue\n" _ host _ host->recovered _ piped);
846 
847 	    /*
848 	    **  Unregister this host for all events
849 	    */
850 	    HTEvent_unregister(HTChannel_socket(host->channel), HTEvent_READ);
851 	    HTEvent_unregister(HTChannel_socket(host->channel), HTEvent_WRITE);
852 	    host->registeredFor = 0;
853 
854 	    /*
855 	    **  Set new mode to single until we know what is going on
856 	    */
857 	    host->mode = HT_TP_SINGLE;
858 
859 	    /*
860 	    **  Move all net objects from the net object to the pending queue.
861 	    */
862 	    if (!host->pending) host->pending = HTList_new();
863 	    for (cnt=0; cnt<piped; cnt++) {
864 		HTNet * net = HTList_removeLastObject(host->pipeline);
865 		HTTRACE(CORE_TRACE, "Host recover Resetting net object %p\n" _ net);
866 		net->registeredFor = 0;
867 		(*net->event.cbf)(HTChannel_socket(host->channel), net->event.param, HTEvent_RESET);
868 		HTList_appendObject(host->pending, net);
869 		host->lock = net;
870 	    }
871 
872 	    HTChannel_setSemaphore(host->channel, 0);
873 	    HTHost_clearChannel(host, HT_INTERRUPTED);
874 	    host->do_recover = NO;
875 	}
876 	return YES;
877     }
878     return NO;
879 }
880 
881 /*
882 **	Terminate a pipeline prematurely, for example because of timeout,
883 **	interruption, etc.
884 */
HTHost_killPipe(HTHost * host)885 PUBLIC BOOL HTHost_killPipe (HTHost * host)
886 {
887     return killPipeline(host, HTEvent_CLOSE);
888 }
889 
890 /*
891 **	Handle the connection mode. The mode may change mode in the
892 **	middle of a connection.
893 */
HTHost_mode(HTHost * host,BOOL * active)894 PUBLIC HTTransportMode HTHost_mode (HTHost * host, BOOL * active)
895 {
896     return host ? host->mode : HT_TP_SINGLE;
897 }
898 
899 /*
900 **	If the new mode is lower than the old mode then adjust the pipeline
901 **	accordingly. That is, if we are going into single mode then move
902 **	all entries in the pipeline and move the rest to the pending
903 **	queue. They will get launched at a later point in time.
904 */
HTHost_setMode(HTHost * host,HTTransportMode mode)905 PUBLIC BOOL HTHost_setMode (HTHost * host, HTTransportMode mode)
906 {
907     if (host) {
908 	/*
909 	**  Check the new mode and see if we must adjust the queues.
910 	*/
911 	if (mode == HT_TP_SINGLE && host->mode > mode) {
912 	    int piped = HTList_count(host->pipeline);
913 	    if (piped > 0) {
914 		int cnt;
915 		HTTRACE(CORE_TRACE, "Host info... Moving %d Net objects from pipe line to pending queue\n" _ piped);
916 		if (!host->pending) host->pending = HTList_new();
917 		for (cnt=0; cnt<piped; cnt++) {
918 		    HTNet * net = HTList_removeLastObject(host->pipeline);
919 		    HTTRACE(CORE_TRACE, "Host info... Resetting net object %p\n" _ net);
920 		    (*net->event.cbf)(HTChannel_socket(host->channel), net->event.param, HTEvent_RESET);
921 		    HTList_appendObject(host->pending, net);
922 		}
923 		HTChannel_setSemaphore(host->channel, 0);
924 		HTHost_clearChannel(host, HT_INTERRUPTED);
925 	    }
926 	}
927 
928 	/*
929 	**  If we know that this host is bad then we don't allow anything than
930 	**  single mode. We can't recover connections for the rest of our life
931 	*/
932 	if (mode == HT_TP_PIPELINE && host->recovered > MAX_HOST_RECOVER) {
933 	    HTTRACE(PROT_TRACE, "Host info... %p is bad for pipelining so we won't do it!!!\n" _
934 			host);
935 	} else {
936 	    host->mode = mode;
937 	    HTTRACE(PROT_TRACE, "Host info... New mode is %d for host %p\n" _ host->mode _ host);
938 	}
939     }
940     return NO;
941 }
942 
943 /*
944 **	Check whether a host is idle meaning if it is ready for a new
945 **	request which depends on the mode of the host. If the host is
946 **	idle, i.e. ready for use then return YES else NO. If the host supports
947 **	persistent connections then still only return idle if no requests are
948 **	ongoing.
949 */
HTHost_isIdle(HTHost * host)950 PUBLIC BOOL HTHost_isIdle (HTHost * host)
951 {
952     return (host && HTList_isEmpty(host->pipeline));
953 }
954 
_roomInPipe(HTHost * host)955 PRIVATE BOOL _roomInPipe (HTHost * host)
956 {
957     int count;
958     if (!host ||
959 	(host->reqsPerConnection && host->reqsMade >= host->reqsPerConnection) ||
960 	HTHost_closeNotification(host) || host->broken_pipe)
961 	return NO;
962     count = HTList_count(host->pipeline);
963     switch (host->mode) {
964     case HT_TP_SINGLE:
965 	return count <= 0;
966     case HT_TP_PIPELINE:
967 	return (host->recovered < MAX_HOST_RECOVER || MAX_HOST_RECOVER==0) ?
968 	    (count < MaxPipelinedRequests) : (count <= 0);
969     case HT_TP_INTERLEAVE:
970 	return YES;
971     }
972     return NO;
973 }
974 
975 /*
976 **	Add a net object to the host object. If the host
977 **	is idle then add to active list (pipeline) else add
978 **	it to the pending list
979 **	Return HT_PENDING if we must pend, HT_OK, or HT_ERROR
980 */
HTHost_addNet(HTHost * host,HTNet * net)981 PUBLIC int HTHost_addNet (HTHost * host, HTNet * net)
982 {
983     if (host && net) {
984 	int status = HT_OK;
985 	BOOL doit = (host->doit==net);
986 
987 	/*
988 	**  If we don't have a socket already then check to see if we can get
989 	**  one. Otherwise we put the host object into our pending queue.
990 	*/
991 	if (!host->channel && HTNet_availableSockets() <= 0) {
992 
993 	    /* Create list for pending Host objects */
994 	    if (!PendHost) PendHost = HTList_new();
995 
996 	    /* Add the host object ad pending if not already */
997 	    if (HTList_indexOf(PendHost, host) < 0) HTList_addObject(PendHost, host);
998 
999 	    /*
1000 	    ** Add the Net object to the Host object. If it is the current Net
1001 	    ** obejct holding the lock then add it to the beginning of the list.
1002 	    ** Otherwise add it to the end
1003 	    */
1004 	    if (!host->pending) host->pending = HTList_new();
1005 	    if (host->lock == net)
1006 		HTList_appendObject(host->pending, net);
1007 	    else
1008 		HTList_addObject(host->pending, net);
1009 
1010  	    HTTRACE(CORE_TRACE, "Host info... Added Net %p (request %p) as pending on pending Host %p, %d requests made, %d requests in pipe, %d pending\n" _
1011 			net _ net->request _ host _ host->reqsMade _
1012 			HTList_count(host->pipeline) _ HTList_count(host->pending));
1013 	    return HT_PENDING;
1014 	}
1015 
1016 #if 0
1017 	/*
1018 	** First check whether the net object is already on either queue.
1019 	** Do NOT add extra copies of the HTNet object to
1020 	** the pipeline or pending list (if it's already on the list).
1021 	*/
1022 	if (HTList_indexOf(host->pipeline, net) >= 0) {
1023 	    HTTRACE(CORE_TRACE, "Host info... The Net %p (request %p) is already in pipe,"
1024 			" %d requests made, %d requests in pipe, %d pending\n" _
1025 			net _ net->request _ host->reqsMade _
1026 			HTList_count(host->pipeline) _
1027 			HTList_count(host->pending));
1028 	    HTDEBUGBREAK("Net object %p registered multiple times in pipeline\n" _
1029 			 net);
1030 	    return HT_OK;
1031 	}
1032 
1033 	if (HTList_indexOf(host->pending,  net) >= 0) {
1034 	    HTTRACE(CORE_TRACE, "Host info... The Net %p (request %p) already pending,"
1035 			" %d requests made, %d requests in pipe, %d pending\n" _
1036 			net _ net->request _ host->reqsMade _
1037 			HTList_count(host->pipeline) _
1038 			HTList_count(host->pending));
1039 	    HTDEBUGBREAK("Net object %p registered multiple times in pending queue\n" _
1040 			 net);
1041 
1042 	    return HT_PENDING;
1043 	}
1044 #endif
1045 
1046 	/*
1047 	**  Add net object to either active or pending queue.
1048 	*/
1049 	if (_roomInPipe(host) && (HTList_isEmpty(host->pending) || doit)) {
1050 	    if (doit) host->doit = NULL;
1051 	    if (!host->pipeline) host->pipeline = HTList_new();
1052 	    HTList_addObject(host->pipeline, net);
1053 	    host->reqsMade++;
1054             HTTRACE(CORE_TRACE, "Host info... Added Net %p (request %p) to pipe on Host %p, %d requests made, %d requests in pipe, %d pending\n" _
1055 			net _ net->request _ host _ host->reqsMade _
1056 			HTList_count(host->pipeline) _ HTList_count(host->pending));
1057 
1058 	    /*
1059 	    **  If we have been idle then make sure we delete the timer
1060 	    */
1061 	    if (host->timer) {
1062 		HTTimer_delete(host->timer);
1063 		host->timer = NULL;
1064 	    }
1065 
1066             /*JK: New CBF function
1067 	    ** Call any user-defined callback to say the request will
1068             ** be processed.
1069             */
1070             HTHost_ActivateRequest (net);
1071 
1072 	} else {
1073 	    if (!host->pending) host->pending = HTList_new();
1074 	    HTList_addObject(host->pending, net);
1075 	    HTTRACE(CORE_TRACE, "Host info... Added Net %p (request %p) as pending on Host %p, %d requests made, %d requests in pipe, %d pending\n" _
1076 			net _ net->request _
1077 			host _ host->reqsMade _
1078 			HTList_count(host->pipeline) _ HTList_count(host->pending));
1079 	    status = HT_PENDING;
1080 	}
1081 	return status;
1082     }
1083     return HT_ERROR;
1084 }
1085 
HTHost_free(HTHost * host,int status)1086 PRIVATE BOOL HTHost_free (HTHost * host, int status)
1087 {
1088     if (host->channel) {
1089 
1090 	/* Check if we should keep the socket open */
1091         if (HTHost_isPersistent(host)) {
1092 	    int piped = HTList_count(host->pipeline);
1093             if (HTHost_closeNotification(host)) {
1094 		HTTRACE(CORE_TRACE, "Host Object. got close notifiation on socket %d\n" _
1095 			    HTChannel_socket(host->channel));
1096 
1097 		/*
1098 		**  If more than a single element (this one) in the pipe
1099 		**  then we have to recover gracefully
1100 		*/
1101 		if (piped > 1) {
1102 		    host->reqsPerConnection = host->reqsMade - piped;
1103 		    HTTRACE(CORE_TRACE, "%d requests made, %d in pipe, max %d requests pr connection\n" _
1104 				host->reqsMade _ piped _ host->reqsPerConnection);
1105 		    host->do_recover = YES;
1106 		    /* @@ JK: not clear yet if I need or not to set the channel to NULL. I think not */
1107 		    /* HTChannel_delete(host->channel, status);  */
1108 		    if (HTChannel_delete(host->channel, status)) {
1109 			HTTRACE(CORE_TRACE, "Host Event.. clearing channel on host %p (%s)\n" _ host _ host->hostname);
1110 			host->channel = NULL;
1111 		    }
1112 		} else {
1113 		    HTChannel_setSemaphore(host->channel, 0);
1114 		    HTHost_clearChannel(host, status);
1115 		}
1116 	    } else if (piped<=1 && host->reqsMade==host->reqsPerConnection) {
1117                 HTTRACE(CORE_TRACE, "Host Object. closing persistent socket %d\n" _
1118 			HTChannel_socket(host->channel));
1119 
1120                 /*
1121                 **  By lowering the semaphore we make sure that the channel
1122                 **  is gonna be deleted
1123                 */
1124                 HTChannel_setSemaphore(host->channel, 0);
1125                 HTHost_clearChannel(host, status);
1126 
1127             } else {
1128                 HTTRACE(CORE_TRACE, "Host Object. keeping persistent socket %d\n" _
1129 			HTChannel_socket(host->channel));
1130                 if (HTChannel_delete(host->channel, status)) {
1131 		    HTDEBUGBREAK("Host Event.. Channel unexpected deleted from host %p (%s)\n" _ host _ host->hostname);
1132 		    host->channel = NULL;
1133                 }
1134                 /*
1135                 **  If connection is idle then set a timer so that we close the
1136                 **  connection if idle too long
1137                 */
1138                 if (piped<=1 && HTList_isEmpty(host->pending) && !host->timer) {
1139                     host->timer = HTTimer_new(NULL, IdleTimeoutEvent,
1140 					      host, HTActiveTimeout, YES, NO);
1141                     HTTRACE(PROT_TRACE, "Host........ Object %p going idle...\n" _ host);
1142                 }
1143             }
1144             return YES;
1145         } else {
1146             HTTRACE(CORE_TRACE, "Host Object. closing socket %d\n" _ HTChannel_socket(host->channel));
1147 	    HTChannel_setSemaphore(host->channel, 0);
1148 	    HTHost_clearChannel(host, status);
1149         }
1150     }
1151     return NO;
1152 }
1153 
HTHost_deleteNet(HTHost * host,HTNet * net,int status)1154 PUBLIC BOOL HTHost_deleteNet (HTHost * host, HTNet * net, int status)
1155 {
1156     if (host && net) {
1157         HTTRACE(CORE_TRACE, "Host info... Remove %p from pipe\n" _ net);
1158 
1159 	/* If the Net object is in the pipeline then also update the channel */
1160 	if (host->pipeline && HTList_indexOf(host->pipeline, net) >= 0) {
1161 	    HTHost_free(host, status);
1162 	    HTList_removeObjectAll(host->pipeline, net);
1163 	}
1164 
1165 	HTList_removeObjectAll(host->pending, net); /* just to make sure */
1166 	host->lock = HTList_firstObject(host->pending);
1167 	return YES;
1168     }
1169     return NO;
1170 }
1171 
1172 /*
1173 **	Handle pending host objects.
1174 **	There are two ways we can end up with pending reqyests:
1175 **	 1) If we are out of sockets then register new host objects as pending.
1176 **	 2) If we are pending on a connection then register new net objects as
1177 **	    pending
1178 **	This set of functions handles pending host objects and can start new
1179 **	requests as resources get available
1180 */
1181 
1182 /*
1183 **	Check this host object for any pending requests and return the next
1184 **	registered Net object.
1185 */
HTHost_nextPendingNet(HTHost * host)1186 PUBLIC HTNet * HTHost_nextPendingNet (HTHost * host)
1187 {
1188     HTNet * net = NULL;
1189     if (host && host->pending) {
1190 	/*JK 23/Sep/96 Bug correction. Associated the following lines to the
1191 	**above if. There was a missing pair of brackets.
1192 	*/
1193 	if ((net = (HTNet *) HTList_removeFirstObject(host->pending)) != NULL) {
1194 	    HTTRACE(CORE_TRACE, "Host info... Popping %p from pending net queue on host %p\n" _
1195 			net _ host);
1196 #if 0
1197 	    {
1198 		HTRequest * request = HTNet_request(net);
1199 		char * uri = HTAnchor_address((HTAnchor *) HTRequest_anchor(request));
1200 		fprintf(stderr, "Popping '%s'\n", uri);
1201 	    }
1202 #endif
1203 	    host->doit = net;
1204 	}
1205     }
1206     return net;
1207 }
1208 
1209 /*
1210 **	Return the current list of pending host objects waiting for a socket
1211 */
HTHost_nextPendingHost(void)1212 PUBLIC HTHost * HTHost_nextPendingHost (void)
1213 {
1214     HTHost * host = NULL;
1215     if (PendHost) {
1216 	if ((host = (HTHost *) HTList_removeFirstObject(PendHost)) != NULL)
1217 	    HTTRACE(PROT_TRACE, "Host info... Popping %p from pending host queue\n" _
1218 			host);
1219     }
1220     return host;
1221 }
1222 
1223 /*
1224 **	Start the next pending request if any. First we look for pending
1225 **	requests for the same host and then we check for any other pending
1226 **	hosts
1227 */
HTHost_launchPending(HTHost * host)1228 PUBLIC BOOL HTHost_launchPending (HTHost * host)
1229 {
1230     HTNet * net = NULL;
1231     if (!host) {
1232 	HTTRACE(PROT_TRACE, "Host info... Bad arguments\n");
1233 	return NO;
1234     }
1235 
1236     /*
1237     **  In pipeline we can only have one doing writing at a time.
1238     **  We therefore check that there are no other Net object
1239     **  registered for write
1240     */
1241     if (host->mode == HT_TP_PIPELINE) {
1242 	net = (HTNet *) HTList_lastObject(host->pipeline);
1243 	if (net && net->registeredFor == HTEvent_WRITE)
1244 	    return NO;
1245     }
1246 
1247     /*
1248     **  Check the current Host object for pending Net objects
1249     */
1250     if (_roomInPipe(host) && DoPendingReqLaunch &&
1251 	   (net = HTHost_nextPendingNet(host))) {
1252 	HTHost_ActivateRequest(net);
1253 	HTTRACE(CORE_TRACE, "Launch pending net object %p with %d reqs in pipe (%d reqs made)\n" _
1254 		    net _ HTList_count(host->pipeline) _ host->reqsMade);
1255 	return HTNet_execute(net, HTEvent_WRITE);
1256     }
1257 
1258     /*
1259     **  Check for other pending Host objects
1260     */
1261     if (DoPendingReqLaunch && HTNet_availableSockets() > 0) {
1262 	HTHost * pending = HTHost_nextPendingHost();
1263 	if (pending && (net = HTHost_nextPendingNet(pending))) {
1264 	    if (!pending->pipeline) pending->pipeline = HTList_new();
1265 	    HTList_addObject(pending->pipeline, net);
1266 	    host->reqsMade++;
1267 	    HTTRACE(CORE_TRACE, "Launch pending host object %p, net %p with %d reqs in pipe (%d reqs made)\n" _
1268 			pending _ net _ HTList_count(pending->pipeline) _ pending->reqsMade);
1269 	    HTHost_ActivateRequest(net);
1270 	    return HTNet_execute(net, HTEvent_WRITE);
1271 	}
1272     }
1273     return YES;
1274 }
1275 
HTHost_firstNet(HTHost * host)1276 PUBLIC HTNet * HTHost_firstNet (HTHost * host)
1277 {
1278     return (HTNet *) HTList_firstObject(host->pipeline);
1279 }
1280 
HTHost_numberOfOutstandingNetObjects(HTHost * host)1281 PUBLIC int HTHost_numberOfOutstandingNetObjects (HTHost * host)
1282 {
1283     return host ? HTList_count(host->pipeline) : -1;
1284 }
1285 
HTHost_numberOfPendingNetObjects(HTHost * host)1286 PUBLIC int HTHost_numberOfPendingNetObjects (HTHost * host)
1287 {
1288     return host ? HTList_count(host->pending) : -1;
1289 }
1290 
1291 /*
1292 **	The host event manager keeps track of the state of it's client engines
1293 **	(typically HTTPEvent), accepting multiple blocks on read or write from
1294 **	multiple pipelined engines. It then registers its own engine
1295 **	(HostEvent) with the event manager.
1296 */
HTHost_connect(HTHost * host,HTNet * net,char * url)1297 PUBLIC int HTHost_connect (HTHost * host, HTNet * net, char * url)
1298 {
1299     HTRequest * request = HTNet_request(net);
1300     int status = HT_OK;
1301     if (!host) {
1302 	HTProtocol * protocol = HTNet_protocol(net);
1303 	if ((host = HTHost_newWParse(request, url, HTProtocol_id(protocol))) == NULL)
1304 	    return HT_ERROR;
1305 
1306 	/*
1307 	** If not already locked and without a channel
1308 	** then lock the darn thing with the first Net object
1309 	** pending.
1310 	*/
1311 	if (!host->lock && !host->channel) {
1312 	    HTNet * next_pending = NULL;
1313 	    host->forceWriteFlush = YES;
1314 	    host->lock = (next_pending = HTList_firstObject(host->pending)) ?
1315 		next_pending : net;
1316 	    HTTRACE(CORE_TRACE, "Host connect Grabbing lock on Host %p with %p\n" _ host _ host->lock);
1317 	}
1318 	HTNet_setHost(net, host);
1319     }
1320 
1321     if (!host->lock || (host->lock && host->lock == net)) {
1322 	status = HTDoConnect(net);
1323 	if (status == HT_PENDING)
1324 	    return HT_WOULD_BLOCK;
1325 	else if (status == HT_WOULD_BLOCK) {
1326 	    host->lock = net;
1327 	    return status;
1328 	} else {
1329 
1330 	    /*
1331 	    **  See if there is already a new pending request that should
1332 	    **  take over the current lock
1333 	    */
1334 	    HTNet * next_pending = NULL;
1335 	    if ((next_pending = HTList_firstObject(host->pending))) {
1336 		HTTRACE(CORE_TRACE, "Host connect Changing lock on Host %p to %p\n" _
1337 			host _ next_pending);
1338 		host->lock = next_pending;
1339 	    } else {
1340 		HTTRACE(CORE_TRACE, "Host connect Unlocking Host %p\n" _ host);
1341 		host->lock = NULL;
1342 	    }
1343 	    return status;
1344 	}
1345     } else {
1346 	HTTRACE(CORE_TRACE, "Host connect Host %p already locked with %p\n" _ host _ host->lock);
1347 	if ((status = HTHost_addNet(host, net)) == HT_PENDING) {
1348 	    return HT_PENDING;
1349 	}
1350     }
1351     return HT_ERROR; /* @@@ - some more deletion and stuff here? */
1352 }
1353 
HTHost_listen(HTHost * host,HTNet * net,char * url)1354 PUBLIC int HTHost_listen (HTHost * host, HTNet * net, char * url)
1355 {
1356     HTRequest * request = HTNet_request(net);
1357     int status = HT_OK;
1358     if (!host) {
1359 	HTProtocol * protocol = HTNet_protocol(net);
1360 	if ((host = HTHost_newWParse(request, url, HTProtocol_id(protocol))) == NULL)
1361 	    return HT_ERROR;
1362 
1363 	/*
1364 	** If not already locked and without a channel
1365 	** then lock the darn thing with the first Net object
1366 	** pending.
1367 	*/
1368 	if (!host->lock && !host->channel) {
1369 	    host->forceWriteFlush = YES;
1370 	    host->lock = net;
1371 	}
1372 	HTNet_setHost(net, host);
1373     }
1374 
1375     /*
1376     ** See if we already have a dedicated listen Net object. If not
1377     ** then create one.
1378     */
1379     if (!host->listening) host->listening = HTNet_new(host);
1380 
1381     /*
1382     ** Start listening on the Net object
1383     */
1384     status = HTDoListen(host->listening, net, HT_BACKLOG);
1385     if (status != HT_OK) {
1386 	HTTRACE(CORE_TRACE, "Host listen. On Host %p resulted in %d\n" _ host _ status);
1387 	return status;
1388     }
1389     return HT_OK;
1390 }
1391 
HTHost_accept(HTHost * host,HTNet * net,char * url)1392 PUBLIC int HTHost_accept (HTHost * host, HTNet * net, char * url)
1393 {
1394     int status = HT_OK;
1395     if (!host || !host->listening) {
1396 	HTTRACE(CORE_TRACE, "Host accept. No host object or not listening on anything\n");
1397 	return HT_ERROR;
1398     }
1399 
1400     if (!host->lock || (host->lock && host->lock == net)) {
1401 	status = HTDoAccept(host->listening, net);
1402 	if (status == HT_PENDING)
1403 	    return HT_WOULD_BLOCK;
1404 	else if (status == HT_WOULD_BLOCK) {
1405 	    host->lock = net;
1406 	    return status;
1407 	} else {
1408 
1409 	    /*
1410 	    **  See if there is already a new pending request that should
1411 	    **  take over the current lock
1412 	    */
1413 	    HTNet * next_pending = NULL;
1414 	    if ((next_pending = HTList_firstObject(host->pending))) {
1415 		HTTRACE(CORE_TRACE, "Host connect Changing lock on Host %p to %p\n" _
1416 			host _ next_pending);
1417 		host->lock = next_pending;
1418 	    } else {
1419 		HTTRACE(CORE_TRACE, "Host connect Unlocking Host %p\n" _ host);
1420 		host->lock = NULL;
1421 	    }
1422 	    return status;
1423 	}
1424     } else {
1425 	HTTRACE(CORE_TRACE, "Host connect Host %p already locked with %p\n" _ host _ host->lock);
1426 	if ((status = HTHost_addNet(host, net)) == HT_PENDING) {
1427 	    return HT_PENDING;
1428 	}
1429     }
1430     return HT_ERROR;
1431 }
1432 
1433 /*
1434 **	Rules: SINGLE: one element in pipe, either reading or writing
1435 **		 PIPE: n element in pipe, n-1 reading, 1 writing
1436 */
HTHost_register(HTHost * host,HTNet * net,HTEventType type)1437 PUBLIC int HTHost_register (HTHost * host, HTNet * net, HTEventType type)
1438 {
1439   HTEvent *event;
1440 
1441     if (host && net) {
1442 
1443 	if (type == HTEvent_CLOSE) {
1444 
1445 	    /*
1446 	    **  Unregister this host for all events
1447 	    */
1448 	    HTEvent_unregister(HTChannel_socket(host->channel), HTEvent_READ);
1449 	    HTEvent_unregister(HTChannel_socket(host->channel), HTEvent_WRITE);
1450 	    host->registeredFor = 0;
1451 	    return YES;
1452 
1453 	} else {
1454 
1455 	    /* net object may already be registered */
1456 	    if (HTEvent_BITS(type) & net->registeredFor)
1457 		return NO;
1458 	    net->registeredFor ^= HTEvent_BITS(type);
1459 
1460 	    /* host object may already be registered */
1461 	    if (host->registeredFor & HTEvent_BITS(type))
1462 		return YES;
1463 	    host->registeredFor ^= HTEvent_BITS(type);
1464 
1465 #ifdef WWW_WIN_ASYNC
1466             /* Make sure we are registered for CLOSE on windows */
1467 	    event =  *(host->events+HTEvent_INDEX(HTEvent_CLOSE));
1468 	    HTEvent_register(HTChannel_socket(host->channel), HTEvent_CLOSE, event);
1469 #endif /* WWW_WIN_ASYNC */
1470 
1471             /* JK:  register a request in the event structure */
1472 	    event =  *(host->events+HTEvent_INDEX(type));
1473 	    event->request = HTNet_request (net);
1474 	    return HTEvent_register(HTChannel_socket(host->channel),
1475 				    type, event);
1476 	}
1477 
1478 	return YES;
1479     }
1480     HTTRACE(CORE_TRACE, "HTHost...... Don't register event with bad arguments\n");
1481     return NO;
1482 }
1483 
HTHost_unregister(HTHost * host,HTNet * net,HTEventType type)1484 PUBLIC int HTHost_unregister (HTHost * host, HTNet * net, HTEventType type)
1485 {
1486     if (host && net) {
1487 
1488 	/* net object may not be registered */
1489 	if (!(HTEvent_BITS(type) & net->registeredFor))
1490 	    return NO;
1491 	net->registeredFor ^= HTEvent_BITS(type);
1492 
1493 	/* host object may not be registered */
1494 	if (!(host->registeredFor & HTEvent_BITS(type)))
1495 	    return YES;
1496 	host->registeredFor ^= HTEvent_BITS(type);
1497 
1498 	/* stay registered for READ to catch a socket close */
1499 	/* WRITE and CONNECT can be unregistered, though */
1500 	if ((type == HTEvent_WRITE && isLastInPipe(host, net)) ||
1501 	    type == HTEvent_CONNECT)
1502 	    /* if we are blocked downstream, shut down the whole pipe */
1503 	    HTEvent_unregister(HTChannel_socket(host->channel), type);
1504 	return YES;
1505     }
1506     return NO;
1507 }
1508 
1509 /*
1510 **	The reader tells HostEvent that it's stream did not finish the data
1511 */
HTHost_setRemainingRead(HTHost * host,size_t remaining)1512 PUBLIC BOOL HTHost_setRemainingRead (HTHost * host, size_t remaining)
1513 {
1514     if (host == NULL) return NO;
1515     host->remainingRead = remaining;
1516     HTTRACE(PROT_TRACE, "Host........ %d bytes remaining \n" _ remaining);
1517     if (host->broken_pipe && remaining == 0) {
1518 	HTTRACE(PROT_TRACE, "Host........ Emtied out connection\n");
1519     }
1520     return YES;
1521 }
1522 
HTHost_remainingRead(HTHost * host)1523 PUBLIC size_t HTHost_remainingRead (HTHost * host)
1524 {
1525     return host ? host->remainingRead : (size_t)-1;
1526 }
1527 
HTHost_getSockAddr(HTHost * host)1528 PUBLIC SockA * HTHost_getSockAddr (HTHost * host)
1529 {
1530     if (!host) return NULL;
1531     return &host->sock_addr;
1532 }
1533 
HTHost_setHome(HTHost * host,int home)1534 PUBLIC BOOL HTHost_setHome (HTHost * host, int home)
1535 {
1536     if (!host) return NO;
1537     host->home = home;
1538     return YES;
1539 }
1540 
HTHost_home(HTHost * host)1541 PUBLIC int HTHost_home (HTHost * host)
1542 {
1543     if (!host) return 0;
1544     return host->home;
1545 }
1546 
HTHost_setRetry(HTHost * host,int retry)1547 PUBLIC BOOL HTHost_setRetry (HTHost * host, int retry)
1548 {
1549     if (!host) return NO;
1550     host->retry = retry;
1551     return YES;
1552 }
1553 
HTHost_decreaseRetry(HTHost * host)1554 PUBLIC BOOL HTHost_decreaseRetry (HTHost * host)
1555 {
1556     if (!host) return NO;
1557 
1558     if (host->retry > 0) host->retry--;
1559     return YES;
1560 
1561 }
1562 
HTHost_retry(HTHost * host)1563 PUBLIC int HTHost_retry (HTHost * host)
1564 {
1565     if (!host) return 0;
1566     return host->retry;
1567 }
1568 
1569 #if 0	/* Is a macro right now */
1570 PRIVATE BOOL HTHost_setDNS5 (HTHost * host, HTdns * dns)
1571 {
1572     if (!host) return NO;
1573     host->dns = dns;
1574     return YES;
1575 }
1576 #endif
1577 
HTHost_setChannel(HTHost * host,HTChannel * channel)1578 PUBLIC BOOL HTHost_setChannel (HTHost * host, HTChannel * channel)
1579 {
1580     if (!host) return NO;
1581     host->channel = channel;
1582     return YES;
1583 }
1584 
HTHost_getReadNet(HTHost * host)1585 PUBLIC HTNet * HTHost_getReadNet(HTHost * host)
1586 {
1587     return host ? (HTNet *) HTList_firstObject(host->pipeline) : NULL;
1588 }
1589 
HTHost_getWriteNet(HTHost * host)1590 PUBLIC HTNet * HTHost_getWriteNet(HTHost * host)
1591 {
1592     return host ? (HTNet *) HTList_lastObject(host->pipeline) : NULL;
1593 }
1594 
1595 /*
1596 **	Create the input stream and bind it to the channel
1597 **	Please read the description in the HTIOStream module for the parameters
1598 */
HTHost_getInput(HTHost * host,HTTransport * tp,void * param,int mode)1599 PUBLIC HTInputStream * HTHost_getInput (HTHost * host, HTTransport * tp,
1600 					void * param, int mode)
1601 {
1602     if (host && host->channel && tp) {
1603 	HTChannel * ch = host->channel;
1604 	HTInputStream * input = (*tp->input_new)(host, ch, param, mode);
1605 	HTChannel_setInput(ch, input);
1606 	return HTChannel_getChannelIStream(ch);
1607     }
1608     HTTRACE(CORE_TRACE, "Host Object. Can't create input stream\n");
1609     return NULL;
1610 }
1611 
HTHost_getOutput(HTHost * host,HTTransport * tp,void * param,int mode)1612 PUBLIC HTOutputStream * HTHost_getOutput (HTHost * host, HTTransport * tp,
1613 					  void * param, int mode)
1614 {
1615     if (host && host->channel && tp) {
1616 	HTChannel * ch = host->channel;
1617 	HTOutputStream * output = (*tp->output_new)(host, ch, param, mode);
1618 	HTChannel_setOutput(ch, output);
1619 	return output;
1620     }
1621     HTTRACE(CORE_TRACE, "Host Object. Can't create output stream\n");
1622     return NULL;
1623 }
1624 
HTHost_output(HTHost * host,HTNet * net)1625 PUBLIC HTOutputStream * HTHost_output (HTHost * host, HTNet * net)
1626 {
1627     if (host && host->channel && net) {
1628 	HTOutputStream * output = HTChannel_output(host->channel);
1629 	return output;
1630     }
1631     return NULL;
1632 }
1633 
HTHost_read(HTHost * host,HTNet * net)1634 PUBLIC int HTHost_read(HTHost * host, HTNet * net)
1635 {
1636     HTInputStream * input = HTChannel_input(host->channel);
1637     if (net != HTHost_getReadNet(host)) {
1638 	HTHost_register(host, net, HTEvent_READ);
1639 	return HT_WOULD_BLOCK;
1640     }
1641 
1642     /*
1643     **  If there is no input channel then this can either mean that
1644     **  we have lost the channel or an error occurred. We return
1645     **  HT_CLOSED as this is a sign to the caller that we don't
1646     **  have a channel
1647     */
1648     return input ? (*input->isa->read)(input) : HT_CLOSED;
1649 }
1650 
HTHost_setConsumed(HTHost * host,size_t bytes)1651 PUBLIC BOOL HTHost_setConsumed(HTHost * host, size_t bytes)
1652 {
1653     HTInputStream * input;
1654     if (!host || !host->channel) return NO;
1655     if ((input = HTChannel_input(host->channel)) == NULL)
1656 	return NO;
1657     HTTRACE(CORE_TRACE, "Host........ passing %d bytes as consumed to %p\n" _ bytes _ input);
1658     return (*input->isa->consumed)(input, bytes);
1659 }
1660 
HTHost_hash(HTHost * host)1661 PUBLIC int HTHost_hash (HTHost * host)
1662 {
1663     return host ? host->hash : -1;
1664 }
1665 
HTHost_setWriteDelay(HTHost * host,ms_t delay)1666 PUBLIC BOOL HTHost_setWriteDelay (HTHost * host, ms_t delay)
1667 {
1668     if (host /*&& delay >= 0*/) {
1669 	host->delay = delay;
1670 	return YES;
1671     }
1672     return NO;
1673 }
1674 
HTHost_writeDelay(HTHost * host)1675 PUBLIC ms_t HTHost_writeDelay (HTHost * host)
1676 {
1677     return host ? host->delay : 0;
1678 }
1679 
HTHost_findWriteDelay(HTHost * host,ms_t lastFlushTime,int buffSize)1680 PUBLIC int HTHost_findWriteDelay (HTHost * host, ms_t lastFlushTime, int buffSize)
1681 {
1682 #if 0
1683     unsigned short mtu;
1684     int ret = -1;
1685     int socket = HTChannel_socket(host->channel);
1686 #ifndef WWW_MSWINDOWS
1687     ret = ioctl(socket, 666, (unsigned long)&mtu);
1688 #endif /* WWW_MSWINDOWS */
1689     if ((ret == 0 && buffSize >= mtu) || host->forceWriteFlush)
1690 	return 0;
1691     return host->delay;
1692 #else
1693     return host->forceWriteFlush ? 0 : host->delay;
1694 #endif
1695 }
1696 
HTHost_setDefaultWriteDelay(ms_t delay)1697 PUBLIC BOOL HTHost_setDefaultWriteDelay (ms_t delay)
1698 {
1699   /*    if (delay >= 0) {*/
1700 	WriteDelay = delay;
1701 	HTTRACE(CORE_TRACE, "Host........ Default write delay is %d ms\n" _ delay);
1702 	return YES;
1703         /*    }
1704               return NO;*/
1705 }
1706 
HTHost_defaultWriteDelay(void)1707 PUBLIC ms_t HTHost_defaultWriteDelay (void)
1708 {
1709     return WriteDelay;
1710 }
1711 
HTHost_forceFlush(HTHost * host)1712 PUBLIC int HTHost_forceFlush(HTHost * host)
1713 {
1714     HTNet * targetNet = (HTNet *) HTList_lastObject(host->pipeline);
1715     int ret;
1716     if (targetNet == NULL) return HT_ERROR;
1717     /* 2000/28/07 JK: The following test was proposed by Heiner Kallweit, as there's a problem
1718     ** while using SSL because of a recursive call to this function. We tested the
1719     ** fix and it doesn't seem to introduce any side effects... but one never knows,
1720     ** thus this comment. This seems more like a bug in the SSL code than here.
1721     */
1722     if (host->inFlush) {
1723 	HTTRACE(CORE_TRACE, "Host Event.. FLUSH requested for  `%s\'\n, but ignoring it as we're already processing a flush in this host" _
1724 		HTAnchor_physical(HTRequest_anchor(HTNet_request(targetNet))));
1725 	return HT_OK;
1726     }
1727     HTTRACE(CORE_TRACE, "Host Event.. FLUSH passed to `%s\'\n" _
1728 		HTAnchor_physical(HTRequest_anchor(HTNet_request(targetNet))));
1729     host->forceWriteFlush = YES;
1730     host->inFlush = YES;
1731     ret = (*targetNet->event.cbf)(HTChannel_socket(host->channel), targetNet->event.param, HTEvent_FLUSH);
1732     host->forceWriteFlush = NO;
1733     host->inFlush = NO;
1734     return ret;
1735 }
1736 
1737 /*
1738 ** Context pointer to be used as a user defined context
1739 */
HTHost_setContext(HTHost * me,void * context)1740 PUBLIC void HTHost_setContext (HTHost * me, void * context)
1741 {
1742   if (me) me->context = context;
1743 }
1744 
HTHost_context(HTHost * me)1745 PUBLIC void * HTHost_context (HTHost * me)
1746 {
1747   return me ? me->context : NULL;
1748 }
1749 
HTHost_eventTimeout(void)1750 PUBLIC int HTHost_eventTimeout (void)
1751 {
1752     return EventTimeout;
1753 }
1754 
HTHost_setEventTimeout(int millis)1755 PUBLIC void HTHost_setEventTimeout (int millis)
1756 {
1757     EventTimeout = millis;
1758     HTTRACE(CORE_TRACE, "Host........ Setting event timeout to %d ms\n" _ millis);
1759 }
1760 
HTHost_setMaxPipelinedRequests(int max)1761 PUBLIC BOOL HTHost_setMaxPipelinedRequests (int max)
1762 {
1763     if (max > 1) {
1764 	MaxPipelinedRequests = max;
1765 	return YES;
1766     }
1767     return NO;
1768 }
1769 
HTHost_maxPipelinedRequests(void)1770 PUBLIC int HTHost_maxPipelinedRequests (void)
1771 {
1772     return MaxPipelinedRequests;
1773 }
1774 
HTHost_setActivateRequestCallback(HTHost_ActivateRequestCallback * cbf)1775 PUBLIC void HTHost_setActivateRequestCallback (HTHost_ActivateRequestCallback * cbf)
1776 {
1777     HTTRACE(CORE_TRACE, "HTHost...... Registering %p\n" _ cbf);
1778     ActivateReqCBF = cbf;
1779 }
1780 
HTHost_ActivateRequest(HTNet * net)1781 PRIVATE int HTHost_ActivateRequest (HTNet * net)
1782 {
1783     HTRequest * request = NULL;
1784     if (!ActivateReqCBF) {
1785 	HTTRACE(CORE_TRACE, "HTHost...... No ActivateRequest callback handler registered\n");
1786 	return HT_ERROR;
1787     }
1788     request = HTNet_request(net);
1789     return (*ActivateReqCBF)(request);
1790 }
1791 
HTHost_disable_PendingReqLaunch(void)1792 PUBLIC void HTHost_disable_PendingReqLaunch (void)
1793 {
1794     DoPendingReqLaunch = NO;
1795 }
1796 
HTHost_enable_PendingReqLaunch(void)1797 PUBLIC void HTHost_enable_PendingReqLaunch (void)
1798 {
1799     DoPendingReqLaunch = YES;
1800 }
1801 
1802