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