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