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