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