1 /*								     HTNet.c
2 **	HTNet Class
3 **
4 **	(c) COPYRIGHT MIT 1995.
5 **	Please first read the full copyright statement in the file COPYRIGH.
6 **	@(#) $Id$
7 **
8 **	This is the implementation of the internal library multithreading
9 **	functions. This includes an interrupt handler and a event loop.
10 **
11 ** History:
12 **  	12 June 94	Written by Henrik Frystyk, frystyk@w3.org
13 **      31 May  95      Charlie Brooks cbrooks@osf.org
14 **
15 */
16 
17 /* Implemention dependent include files */
18 #include "wwwsys.h"
19 
20 /* Library include files */
21 #include "WWWUtil.h"
22 #include "HTProt.h"
23 #include "HTError.h"
24 #include "HTAlert.h"
25 #include "HTParse.h"
26 #include "HTTrans.h"
27 #include "HTHost.h"
28 #include "HTReq.h"
29 #include "HTEvent.h"
30 #include "HTStream.h"
31 #include "HTHstMan.h"
32 #include "HTIOStream.h"
33 #include "HTNetMan.h"					 /* Implemented here */
34 
35 #ifndef HT_MAX_SOCKETS
36 #define HT_MAX_SOCKETS	25
37 #endif
38 
39 typedef struct _BeforeFilter {
40     HTNetBefore *	before;				  /* Filter function */
41     char *		tmplate;     /* URL template for when to call filter */
42     int			order;			 /* Relative execution order */
43     void *		param;				    /* Local context */
44 } BeforeFilter;
45 
46 typedef struct _AfterFilter {
47     HTNetAfter *	after;				  /* Filter function */
48     char *		tmplate;     /* URL template for when to call filter */
49     int			order;			 /* Relative execution order */
50     void *		param;				    /* Local context */
51     int			status;	   /* Status of load for when to call filter */
52 } AfterFilter;
53 
54 struct _HTStream {
55     const HTStreamClass *	isa;
56     /* ... */
57 };
58 
59 struct _HTInputStream {
60     const HTInputStreamClass *	isa;
61     /* ... */
62 };
63 
64 typedef struct _HTFilterEvent {
65     HTRequest *		request;
66     int			status;
67     HTTimer *		timer;
68 } HTFilterEvent;
69 
70 PRIVATE HTList * HTBefore = NULL;	    /* List of global BEFORE filters */
71 PRIVATE HTList * HTAfter = NULL;	     /* List of global AFTER filters */
72 
73 PRIVATE int MaxActive = HT_MAX_SOCKETS;  	      /* Max active requests */
74 PRIVATE int Active = 0;				      /* Counts open sockets */
75 PRIVATE int Persistent = 0;		        /* Counts persistent sockets */
76 
77 PRIVATE HTList ** NetTable = NULL;		      /* List of net objects */
78 PRIVATE int HTNetCount = 0;		       /* Counting elements in table */
79 
80 /* ------------------------------------------------------------------------- */
81 /*		   GENERIC BEFORE and AFTER filter Management		     */
82 /* ------------------------------------------------------------------------- */
83 
HTBeforeOrder(const void * a,const void * b)84 PRIVATE int HTBeforeOrder (const void * a, const void * b)
85 {
86     return ((BeforeFilter *) b)->order - ((BeforeFilter *) a)->order;
87 }
88 
HTAfterOrder(const void * a,const void * b)89 PRIVATE int HTAfterOrder (const void * a, const void * b)
90 {
91     return ((AfterFilter *) b)->order - ((AfterFilter *) a)->order;
92 }
93 
check_order(HTFilterOrder order)94 PRIVATE int check_order (HTFilterOrder order)
95 {
96     return (order<HT_FILTER_FIRST) ? HT_FILTER_FIRST :
97 	(order>HT_FILTER_LAST) ? HT_FILTER_LAST : order;
98 }
99 
100 /*
101 **	Register a BEFORE filter in the list provided by the caller.
102 **	Several filters can be registered in which case they are called
103 **	with the filter ordering in mind.
104 */
HTNetCall_addBefore(HTList * list,HTNetBefore * before,const char * tmplate,void * param,HTFilterOrder order)105 PUBLIC BOOL HTNetCall_addBefore (HTList * list, HTNetBefore * before,
106 				 const char * tmplate, void * param,
107 				 HTFilterOrder order)
108 {
109     if (list && before) {
110 	BeforeFilter * me;
111 	if ((me = (BeforeFilter *) HT_CALLOC(1, sizeof(BeforeFilter)))==NULL)
112 	    HT_OUTOFMEM("HTNetCall_addBefore");
113 	me->before = before;
114 	if (tmplate) StrAllocCopy(me->tmplate, tmplate);
115 	me->order = check_order(order);
116 	me->param = param;
117 	HTTRACE(CORE_TRACE, "Net Before.. Add %p with order %d tmplate `%s\' context %p\n" _
118 		    before _ me->order _ tmplate ? tmplate : "<null>" _ param);
119 	return (HTList_addObject(list, me) &&
120 		HTList_insertionSort(list, HTBeforeOrder));
121     }
122     return NO;
123 }
124 
125 /*
126 **	Unregister all instances of a BEFORE filter from a list.
127 */
HTNetCall_deleteBefore(HTList * list,HTNetBefore * before)128 PUBLIC BOOL HTNetCall_deleteBefore (HTList * list, HTNetBefore * before)
129 {
130     HTTRACE(CORE_TRACE, "Net Before.. Delete %p\n" _ before);
131     if (list && before) {
132 	HTList * cur = list;
133 	BeforeFilter * pres;
134 	while ((pres = (BeforeFilter *) HTList_nextObject(cur))) {
135 	    if (pres->before == before) {
136 		HTList_removeObject(list, (void *) pres);
137 		HT_FREE(pres->tmplate);
138 		HT_FREE(pres);
139 		cur = list;
140 	    }
141 	}
142     }
143     return NO;
144 }
145 
146 /*
147 **	Deletes all BEFORE filters in list
148 */
HTNetCall_deleteBeforeAll(HTList * list)149 PUBLIC BOOL HTNetCall_deleteBeforeAll (HTList * list)
150 {
151     HTTRACE(CORE_TRACE, "Net Before. Delete All filters\n");
152     if (list) {
153 	HTList * cur = list;
154 	BeforeFilter * pres;
155 	while ((pres = (BeforeFilter *) HTList_nextObject(cur))) {
156 	    HT_FREE(pres->tmplate);
157 	    HT_FREE(pres);
158 	}
159 	HTList_delete(list);
160 	return YES;
161     }
162     return NO;
163 }
164 
165 /*
166 **	Call all the BEFORE filters in the order specified at registration
167 **	time. We also check for any template and whether it matches or not.
168 **	If a filter returns other than HT_OK then stop and return immediately.
169 **	Otherwise return what the last filter returns.
170 */
HTNetCall_executeBefore(HTList * list,HTRequest * request)171 PUBLIC int HTNetCall_executeBefore (HTList * list, HTRequest * request)
172 {
173     HTParentAnchor * anchor = HTRequest_anchor(request);
174     char * url = HTAnchor_physical(anchor);
175     char * addr = url ? url : HTAnchor_address((HTAnchor *) anchor);
176     int ret = HT_OK;
177     int mode = 0;
178     if (list && request && addr) {
179 	BeforeFilter * pres;
180 	while ((pres = (BeforeFilter *) HTList_nextObject(list))) {
181 	    if (!pres->tmplate ||
182 		(pres->tmplate && HTStrMatch(pres->tmplate, addr))) {
183 		HTTRACE(CORE_TRACE, "Net Before.. calling %p (request %p, context %p)\n" _
184 					pres->before _
185 					request _ pres->param);
186 		ret = (*pres->before)(request, pres->param, mode);
187 		if (ret != HT_OK) break;
188 
189 		/*
190 		**  Update the address to match against if the filter changed
191 		**  the physical address.
192 		*/
193 		if ((url = HTAnchor_physical(anchor))) addr = url;
194 	    }
195 	}
196     }
197     if (!url) HT_FREE(addr);
198     return ret;
199 }
200 
201 /*
202 **	Register an AFTER filter in the list provided by the caller.
203 **	Several filters can be registered in which case they are called
204 **	with the filter ordering in mind.
205 */
HTNetCall_addAfter(HTList * list,HTNetAfter * after,const char * tmplate,void * param,int status,HTFilterOrder order)206 PUBLIC BOOL HTNetCall_addAfter (HTList * list, HTNetAfter * after,
207 				const char * tmplate, void * param,
208 				int status, HTFilterOrder order)
209 {
210     if (list && after) {
211 	AfterFilter * me;
212 	if ((me = (AfterFilter *) HT_CALLOC(1, sizeof(AfterFilter)))==NULL)
213 	    HT_OUTOFMEM("HTNetCall_addAfter");
214 	me->after = after;
215 	if (tmplate) StrAllocCopy(me->tmplate, tmplate);
216 	me->order = check_order(order);
217 	me->param = param;
218 	me->status = status;
219 	HTTRACE(CORE_TRACE, "Net After... Add %p with order %d tmplate `%s\' code %d context %p\n" _
220 		    after _ me->order _ tmplate ? tmplate : "<null>" _ status _ param);
221 	return (HTList_addObject(list, me) &&
222 		HTList_insertionSort(list, HTAfterOrder));
223     }
224     return NO;
225 }
226 
227 /*
228 **	Unregister all instances of an AFTER filter from a list.
229 */
HTNetCall_deleteAfter(HTList * list,HTNetAfter * after)230 PUBLIC BOOL HTNetCall_deleteAfter (HTList * list, HTNetAfter * after)
231 {
232     HTTRACE(CORE_TRACE, "Net After... Delete %p\n" _ after);
233     if (list && after) {
234 	HTList * cur = list;
235 	AfterFilter * pres;
236 	while ((pres = (AfterFilter *) HTList_nextObject(cur))) {
237 	    if (pres->after == after) {
238 		HTList_removeObject(list, (void *) pres);
239 		HT_FREE(pres->tmplate);
240 		HT_FREE(pres);
241 		cur = list;
242 	    }
243 	}
244     }
245     return NO;
246 }
247 
248 /*
249 **	Unregister all filters registered for a given status.
250 */
HTNetCall_deleteAfterStatus(HTList * list,int status)251 PUBLIC BOOL HTNetCall_deleteAfterStatus (HTList * list, int status)
252 {
253     HTTRACE(CORE_TRACE, "Net After... Delete all with status %d\n" _ status);
254     if (list) {
255 	HTList * cur = list;
256 	AfterFilter * pres;
257 	while ((pres = (AfterFilter *) HTList_nextObject(cur))) {
258 	    if (pres->status == status) {
259 		HTList_removeObject(list, (void *) pres);
260 		HT_FREE(pres->tmplate);
261 		HT_FREE(pres);
262 		cur = list;
263 	    }
264 	}
265 	return YES;
266     }
267     return NO;
268 }
269 
270 /*
271 **	Deletes all AFTER filters in list
272 */
HTNetCall_deleteAfterAll(HTList * list)273 PUBLIC BOOL HTNetCall_deleteAfterAll (HTList * list)
274 {
275     HTTRACE(CORE_TRACE, "Net After. Delete All filters\n");
276     if (list) {
277 	HTList * cur = list;
278 	AfterFilter * pres;
279 	while ((pres = (AfterFilter *) HTList_nextObject(cur))) {
280 	    HT_FREE(pres->tmplate);
281 	    HT_FREE(pres);
282 	}
283 	HTList_delete(list);
284 	return YES;
285     }
286     return NO;
287 }
288 
289 /*
290 **	Call all the AFTER filters in the order specified at registration
291 **	time and if it has the right status code and it's not HT_IGNORE.
292 **	We also check for any template and whether it matches or not.
293 **	If a filter returns other than HT_OK then stop and return immediately.
294 **	Otherwise return what the last filter returns.
295 */
HTNetCall_executeAfter(HTList * list,HTRequest * request,int status)296 PUBLIC int HTNetCall_executeAfter (HTList * list, HTRequest * request,
297 				   int status)
298 {
299     int ret = HT_OK;
300     if (status != HT_IGNORE) {
301 	HTParentAnchor * anchor = HTRequest_anchor(request);
302 	char * url = HTAnchor_physical(anchor);
303 	char * addr = url ? url : HTAnchor_address((HTAnchor *) anchor);
304 	HTResponse * response = HTRequest_response(request);
305 	if (list && request && addr) {
306 	    AfterFilter * pres;
307 	    while ((pres = (AfterFilter *) HTList_nextObject(list))) {
308 		if ((pres->status == status || pres->status == HT_ALL) &&
309 		    (!pres->tmplate ||
310 		     (pres->tmplate && HTStrMatch(pres->tmplate, addr)))) {
311 		    HTTRACE(CORE_TRACE, "Net After... calling %p (request %p, response %p, status %d, context %p)\n" _
312 				pres->after _ request _ response _
313 				status _ pres->param);
314 		    ret = (*pres->after)(request, response, pres->param, status);
315 		    if (ret != HT_OK) break;
316 
317 		    /*
318 		    **  Update the address to match against if the filter changed
319 		    **  the physical address.
320 		    */
321 		    if ((url = HTAnchor_physical(anchor))) addr = url;
322 		}
323 	    }
324 	}
325 	if (!url) HT_FREE(addr);
326     }
327     return ret;
328 }
329 
330 /* ------------------------------------------------------------------------- */
331 /*		   GLOBAL BEFORE and AFTER filter Management		     */
332 /* ------------------------------------------------------------------------- */
333 
334 /*
335 **	Global set of callback functions BEFORE the request is issued
336 **	list can be NULL
337 */
HTNet_setBefore(HTList * list)338 PUBLIC BOOL HTNet_setBefore (HTList *list)
339 {
340     HTBefore = list;
341     return YES;
342 }
343 
HTNet_before(void)344 PUBLIC HTList * HTNet_before (void)
345 {
346     return HTBefore;
347 }
348 
HTNet_addBefore(HTNetBefore * before,const char * tmplate,void * param,HTFilterOrder order)349 PUBLIC BOOL HTNet_addBefore (HTNetBefore * before, const char * tmplate,
350 			     void * param, HTFilterOrder order)
351 {
352     if (!HTBefore) HTBefore = HTList_new();
353     else HTNet_deleteBefore(before); /* Ensure not listed twice */
354     return HTNetCall_addBefore(HTBefore, before, tmplate, param, order);
355 }
356 
HTNet_deleteBefore(HTNetBefore * cbf)357 PUBLIC BOOL HTNet_deleteBefore (HTNetBefore * cbf)
358 {
359     return HTNetCall_deleteBefore(HTBefore, cbf);
360 }
361 
362 /*
363 **  Call both the local and the global BEFORE filters (if any)
364 */
HTNet_executeBeforeAll(HTRequest * request)365 PUBLIC int HTNet_executeBeforeAll (HTRequest * request)
366 {
367     int ret;
368     BOOL override = NO;
369     HTList * befores;
370     if ((befores = HTRequest_before(request, &override))) {
371 	if ((ret = HTNetCall_executeBefore(befores, request)) != HT_OK)
372 	    return ret;
373     }
374     return override ? HT_OK : HTNetCall_executeBefore(HTBefore, request);
375 }
376 
377 /*
378 **	Global set of callback functions AFTER the request is issued
379 **	list can be NULL
380 */
HTNet_setAfter(HTList * list)381 PUBLIC BOOL HTNet_setAfter (HTList *list)
382 {
383     HTAfter = list;
384     return YES;
385 }
386 
HTNet_after(void)387 PUBLIC HTList * HTNet_after (void)
388 {
389     return HTAfter;
390 }
391 
HTNet_addAfter(HTNetAfter * after,const char * tmplate,void * param,int status,HTFilterOrder order)392 PUBLIC BOOL HTNet_addAfter (HTNetAfter * after, const char * tmplate,
393 			    void * param, int status, HTFilterOrder order)
394 {
395     if (!HTAfter) HTAfter = HTList_new();
396     else HTNet_deleteAfter(after); /* Ensure not listed twice */
397     return HTNetCall_addAfter(HTAfter, after, tmplate, param, status, order);
398 }
399 
HTNet_deleteAfter(HTNetAfter * cbf)400 PUBLIC BOOL HTNet_deleteAfter (HTNetAfter * cbf)
401 {
402     return HTNetCall_deleteAfter(HTAfter, cbf);
403 }
404 
HTNet_deleteAfterStatus(int status)405 PUBLIC BOOL HTNet_deleteAfterStatus (int status)
406 {
407     return HTNetCall_deleteAfterStatus(HTAfter, status);
408 }
409 
410 /*
411 **  Call both the local and the global AFTER filters (if any)
412 */
HTNet_executeAfterAll(HTRequest * request,int status)413 PUBLIC int HTNet_executeAfterAll (HTRequest * request, int status)
414 {
415     int ret;
416     BOOL override = NO;
417     HTList * afters;
418     if ((afters = HTRequest_after(request, &override))) {
419 	if ((ret = HTNetCall_executeAfter(afters, request, status)) != HT_OK)
420 	    return ret;
421     }
422     return override ? HT_OK : HTNetCall_executeAfter(HTAfter, request, status);
423 }
424 
425 /* ------------------------------------------------------------------------- */
426 /*			      Socket Management				     */
427 /* ------------------------------------------------------------------------- */
428 
HTNet_maxSocket(void)429 PUBLIC int HTNet_maxSocket (void)
430 {
431     return MaxActive;
432 }
433 
HTNet_setMaxSocket(int newmax)434 PUBLIC BOOL HTNet_setMaxSocket (int newmax)
435 {
436     if (newmax > 0) {
437 	MaxActive = newmax;
438 	return YES;
439     }
440     return NO;
441 }
442 
HTNet_increaseSocket(void)443 PUBLIC void HTNet_increaseSocket (void)
444 {
445     Active++;
446     HTTRACE(CORE_TRACE, "Net Manager. Increasing active sockets to %d, %d persistent sockets\n" _
447 		Active _ Persistent);
448 }
449 
HTNet_decreaseSocket(void)450 PUBLIC void HTNet_decreaseSocket (void)
451 {
452     if (--Active < 0) Active = 0;
453     HTTRACE(CORE_TRACE, "Net Manager. Decreasing active sockets to %d, %d persistent sockets\n" _
454 		Active _ Persistent);
455 }
456 
HTNet_availableSockets(void)457 PUBLIC int HTNet_availableSockets (void)
458 {
459     int available = MaxActive - Active;
460     return available > 0 ? available : 0;
461 }
462 
HTNet_increasePersistentSocket(void)463 PUBLIC void HTNet_increasePersistentSocket (void)
464 {
465     Persistent++;
466     HTTRACE(CORE_TRACE, "Net Manager. %d active sockets, increasing persistent sockets to %d\n" _
467 		Active _ Persistent);
468 }
469 
HTNet_decreasePersistentSocket(void)470 PUBLIC void HTNet_decreasePersistentSocket (void)
471 {
472     if (--Persistent < 0) Persistent = 0;
473     HTTRACE(CORE_TRACE, "Net Manager. %d active sockets, decreasing persistent sockets to %d\n" _
474 		Active _ Persistent);
475 }
476 
HTNet_availablePersistentSockets(void)477 PUBLIC int HTNet_availablePersistentSockets (void)
478 {
479     int available = MaxActive - 2 - Persistent;
480     return available > 0 ? available : 0;
481 }
482 
483 /*
484 **	Returns whether there are any Net objects pending or active
485 */
HTNet_isIdle(void)486 PUBLIC BOOL HTNet_isIdle (void)
487 {
488     return (HTNetCount > 0);
489 }
490 
HTNet_isEmpty(void)491 PUBLIC BOOL HTNet_isEmpty (void)
492 {
493     return (HTNetCount <= 0);
494 }
495 
HTNet_count(void)496 PUBLIC int HTNet_count (void)
497 {
498     return HTNetCount;
499 }
500 
501 /* ------------------------------------------------------------------------- */
502 /*			  Creation and deletion methods  		     */
503 /* ------------------------------------------------------------------------- */
504 
AfterFilterEvent(HTTimer * timer,void * param,HTEventType type)505 PRIVATE int AfterFilterEvent (HTTimer * timer, void * param, HTEventType type)
506 {
507     HTFilterEvent * fe = (HTFilterEvent *) param;
508     if (fe) {
509 	HTRequest * request = fe->request;
510 	int status = fe->status;
511 	if (timer != fe->timer)
512 	    HTDEBUGBREAK("Net timer. %p not in sync\n" _ timer);
513 	HTTRACE(CORE_TRACE, "HTNet....... Continuing calling AFTER filters %p with timer %p\n" _
514 		    fe _ timer);
515 
516 	/* Delete the event context */
517 	HT_FREE(fe);
518 
519 	/* Now call the remaining AFTER filters */
520 	return HTNet_executeAfterAll(request, status);
521     }
522     return HT_ERROR;
523 }
524 
createAfterFilterEvent(HTRequest * request,int status)525 PRIVATE BOOL createAfterFilterEvent (HTRequest * request, int status)
526 {
527     HTFilterEvent * me = NULL;
528     if ((me = (HTFilterEvent *) HT_CALLOC(1, sizeof(HTFilterEvent))) == NULL)
529         HT_OUTOFMEM("createAfterFilterEvent");
530     me->request = request;
531     me->status = status;
532     me->timer = HTTimer_new(NULL, AfterFilterEvent, me, 1, YES, NO);
533     return YES;
534 }
535 
create_object(void)536 PRIVATE HTNet * create_object (void)
537 {
538     static int net_hash = 0;
539     HTNet * me = NULL;
540 
541     /* Create new object */
542     if ((me = (HTNet *) HT_CALLOC(1, sizeof(HTNet))) == NULL)
543         HT_OUTOFMEM("HTNet_new");
544     me->hash = net_hash++ % HT_XL_HASH_SIZE;
545 
546     /* Insert into hash table */
547     if (!NetTable) {
548 	if ((NetTable = (HTList **) HT_CALLOC(HT_XL_HASH_SIZE, sizeof(HTList *))) == NULL)
549 	    HT_OUTOFMEM("create_object");
550     }
551     if (!NetTable[me->hash]) NetTable[me->hash] = HTList_new();
552     HTList_addObject(NetTable[me->hash], (void *) me);
553     HTNetCount++;
554     HTTRACE(CORE_TRACE, "Net Object.. %p created with hash %d\n" _ me _ me->hash);
555     return me;
556 }
557 
558 /*	HTNet_duplicate
559 **	---------------
560 **	Creates a new HTNet object as a duplicate of the same request.
561 **	Returns YES if OK, else NO
562 **	BUG: We do not check if we have a socket free!
563 */
HTNet_dup(HTNet * src)564 PUBLIC HTNet * HTNet_dup (HTNet * src)
565 {
566     if (src) {
567         HTNet * me;
568 	int hash;
569 	if ((me = create_object()) == NULL) return NULL;
570 	hash = me->hash;
571 	HTTRACE(CORE_TRACE, "Net Object.. Duplicated %p\n" _ src);
572         memcpy((void *) me, src, sizeof(HTNet));
573 	me->hash = hash;			/* Carry over hash entry */
574 	return me;
575     }
576     return NULL;
577 }
578 
HTNet_execute(HTNet * net,HTEventType type)579 PUBLIC BOOL HTNet_execute (HTNet * net, HTEventType type)
580 {
581     if (net && net->event.cbf && net->request) {
582 	HTTRACE(CORE_TRACE, "Net Object.. %p calling %p with event type %d and context %p\n" _
583 		    net _ net->event.cbf _ type _ net->event.param);
584 	(*(net->event.cbf))(HTNet_socket(net), net->event.param, type);
585 	return YES;
586     }
587     return NO;
588 }
589 
590 /*
591 **	Start a Net obejct by calling the protocol module.
592 */
HTNet_start(HTNet * net)593 PUBLIC BOOL HTNet_start (HTNet * net)
594 {
595     if (net && net->event.cbf && net->request) {
596 	HTTRACE(CORE_TRACE, "Net Object.. Launching %p\n" _ net);
597 	(*(net->event.cbf))(HTNet_socket(net), net->event.param, HTEvent_BEGIN);
598 	return YES;
599     }
600     return NO;
601 }
602 
603 /*	HTNet_new
604 **	---------
605 **	This function creates a new HTNet object and assigns the socket number
606 **	to it. This is intended to be used when you are going to listen on a
607 **	socket using the HTDoListen() function in HTTCP.c. The function do NOT
608 **	call any of the before or after filter functions.
609 **	Returns new object or NULL on error
610 */
HTNet_new(HTHost * host)611 PUBLIC HTNet * HTNet_new (HTHost * host)
612 {
613     if (host) {
614 	HTNet * me;
615 	HTTRACE(CORE_TRACE, "Net Object.. Creating listen object for host %p\n" _ host);
616 	me = create_object();
617 	me->host = host;
618 	return me;
619     }
620     return NULL;
621 }
622 
623 /*      HTNet_newServer
624 **      ---------------
625 **      Create a new HTNet object as a new request to be handled. If we have
626 **      more than MaxActive connections already then return NO.
627 **      Returns YES if OK, else NO
628 */
HTNet_newServer(HTRequest * request)629 PUBLIC BOOL HTNet_newServer (HTRequest * request)
630 {
631     HTParentAnchor * anchor = HTRequest_anchor(request);
632     HTNet * me = NULL;
633     HTProtocol * protocol;
634     HTTransport * tp = NULL;    	/* added JTD:5/28/96 */
635     char * physical = NULL;
636     int status;
637     HTProtCallback * cbf;
638 
639     if (!request) return NO;
640 
641     /*
642     ** First we do all the "BEFORE" callbacks in order to see if we are to
643     ** continue with this request or not. If we receive a callback status
644     ** that is NOT HT_OK then jump directly to the after callbacks and return
645     */
646     if ((status = HTNet_executeBeforeAll(request)) != HT_OK) {
647 
648  	/*
649 	**  If in non-blocking mode then return here and call AFTER
650 	**  filters from a timer event handler. As Olga Antropova
651 	**  points out, otherwise, the stack can grow if new requests
652 	**  are started directly from the after filters
653 	*/
654 	if (HTEvent_isCallbacksRegistered() && !HTRequest_preemptive(request))
655 	    createAfterFilterEvent(request, status);
656 	else
657 	    HTNet_executeAfterAll(request, status);
658 	return YES;
659     }
660 
661     /*
662     ** If no translation was provided by the filters then use the anchor
663     ** address directly
664     */
665     if (!(physical = HTAnchor_physical(anchor))) {
666 	char * addr = HTAnchor_address((HTAnchor *) anchor);
667 	HTTRACE(CORE_TRACE, "Net Object.. Using default address\n");
668 	HTAnchor_setPhysical(anchor, addr);
669 	physical = HTAnchor_physical(anchor);
670 	HT_FREE(addr);
671     }
672 
673     /* Find a protocol object for this access scheme */
674     {
675 	char * access = HTParse(physical, "", PARSE_ACCESS);
676 	if ((protocol = HTProtocol_find(request, access)) == NULL) {
677 	    HTTRACE(CORE_TRACE, "Net Object.. NO PROTOCOL Object found for URI scheme `%s\'\n" _ access);
678 	    HT_FREE(access);
679 	    return NO;
680 	}
681 	if (!(cbf = HTProtocol_server(protocol))) {
682 	    HTTRACE(CORE_TRACE, "Net Object.. NO SERVER HANDLER for URI scheme `%s\'\n" _ access);
683 	    HT_FREE(access);
684 	    HT_FREE(me);
685 	    return NO;
686 	}
687 	HT_FREE(access);
688     }
689 
690     /* Find a transport object for this protocol */
691     if ((tp = HTTransport_find(request, HTProtocol_transport(protocol))) == NULL) {
692         HTTRACE(CORE_TRACE, "Net Object.. NO TRANSPORT found for protocol `%s\'\n" _ HTProtocol_name(protocol));
693         return NO;
694     }
695 
696     /* Create new net object and bind to request object */
697     if ((me = create_object()) == NULL) return NO;
698     me->preemptive = (HTProtocol_preemptive(protocol) || HTRequest_preemptive(request));
699     HTNet_setEventPriority(me, HTRequest_priority(request));
700     me->protocol = protocol;
701     me->transport = tp; 		/* added - JTD:5/28/96 */
702     me->request = request;
703     HTRequest_setNet(request, me);
704 
705     /* Start the server request */
706     HTTRACE(CORE_TRACE, "Net Object.. starting SERVER request %p and net object %p\n" _ request _ me);
707     (*(cbf))(INVSOC, request);
708     return YES;
709 }
710 
711 /*	HTNet_newClient
712 **	---------------
713 **	Create a new HTNet object as a new request to be handled. If we have
714 **	more than MaxActive connections already then put this into the
715 **	pending queue, else start the request by calling the call back
716 **	function registered with this access method.
717 **	Returns YES if OK, else NO
718 */
HTNet_newClient(HTRequest * request)719 PUBLIC BOOL HTNet_newClient (HTRequest * request)
720 {
721     HTParentAnchor * anchor = HTRequest_anchor(request);
722     HTNet * me = NULL;
723     HTProtocol * protocol = NULL;
724     HTTransport * tp = NULL;
725     char * physical = NULL;
726     int status;
727     HTProtCallback * cbf;
728 
729     if (!request) return NO;
730 
731     /*
732     ** First we do all the "BEFORE" callbacks in order to see if we are to
733     ** continue with this request or not. If we receive a callback status
734     ** that is NOT HT_OK then jump directly to the after callbacks and return
735     */
736     if ((status = HTNet_executeBeforeAll(request)) != HT_OK) {
737 
738  	/*
739 	**  If in non-blocking mode then return here and call AFTER
740 	**  filters from a timer event handler. As Olga Antropova
741 	**  points out, otherwise, the stack can grow if new requests
742 	**  are started directly from the after filters
743 	*/
744 	if (HTEvent_isCallbacksRegistered() && !HTRequest_preemptive(request))
745 	    createAfterFilterEvent(request, status);
746 	else
747 	    HTNet_executeAfterAll(request, status);
748 	return YES;
749     }
750 
751     /*
752     ** If no translation was provided by the filters then use the anchor
753     ** address directly
754     */
755     if (!(physical = HTAnchor_physical(anchor))) {
756 	char * addr = HTAnchor_address((HTAnchor *) anchor);
757 	HTTRACE(CORE_TRACE, "Net Object.. Using default address\n");
758 	HTAnchor_setPhysical(anchor, addr);
759 	physical = HTAnchor_physical(anchor);
760 	HT_FREE(addr);
761     }
762 
763     /* Find a protocol object for this access scheme */
764     {
765 	char * proxy = HTRequest_proxy(request);
766 	char * access = HTParse(proxy ? proxy : physical, "", PARSE_ACCESS);
767 	if ((protocol = HTProtocol_find(request, access)) == NULL) {
768 	    HTTRACE(CORE_TRACE, "Net Object.. NO PROTOCOL Object found for URI scheme `%s\'\n" _ access);
769 	    HT_FREE(access);
770 	    return NO;
771 	}
772 	if (!(cbf = HTProtocol_client(protocol))) {
773 	    HTTRACE(CORE_TRACE, "Net Object.. NO CLIENT HANDLER for URI scheme `%s\'\n" _ access);
774 	    HT_FREE(access);
775 	    HT_FREE(me);
776 	    return NO;
777 	}
778 	HT_FREE(access);
779     }
780 
781     /* Find a transport object for this protocol */
782     tp = HTTransport_find(request, HTProtocol_transport(protocol));
783     if (tp == NULL) {
784 	HTTRACE(CORE_TRACE, "Net Object.. NO TRANSPORT found for protocol `%s\'\n" _ HTProtocol_name(protocol));
785 	return NO;
786     }
787 
788     /* Create new net object and bind it to the request object */
789     if ((me = create_object()) == NULL) return NO;
790     me->preemptive = (HTProtocol_preemptive(protocol) || HTRequest_preemptive(request));
791 #if 0
792     me->priority = HTRequest_priority(request);
793 #endif
794     HTNet_setEventPriority(me, HTRequest_priority(request));
795     me->protocol = protocol;
796     me->transport = tp;
797     me->request = request;
798     HTRequest_setNet(request, me);
799 
800     /* Increase the number of retrys for this download */
801     HTRequest_addRetry(request);
802 
803     /*
804     ** Check if we can start the request, else put it into pending queue
805     ** If so then call the call back function associated with the anchor.
806     ** We use the INVSOC as we don't have a valid socket yet!
807     */
808     HTTRACE(CORE_TRACE, "Net Object.. starting request %p (retry=%d) with net object %p\n" _
809 	        request _ HTRequest_retrys(request) _ me);
810     (*(cbf))(INVSOC, request);
811     return YES;
812 }
813 
814 /*
815 **      Check whether we have any pending HTNet objects and if so
816 **	then start the next one.
817 **	Return YES if OK, else NO
818 */
check_pending(HTNet * net)819 PRIVATE BOOL check_pending (HTNet * net)
820 {
821     HTTRACE(CORE_TRACE, "Net Object.. Check for pending Net objects\n");
822     if (net) {
823 
824 	/*
825 	**  As we may have a socket available we check for whether
826 	**  we can start any pending requests. We do this by asking for
827 	**  pending Host objects. If none then use the current object
828 	*/
829         HTHost_launchPending(net->host);
830 
831         return YES;
832     }
833     return NO;
834 }
835 
free_net(HTNet * net)836 PRIVATE BOOL free_net (HTNet * net)
837 {
838     HTTRACE(CORE_TRACE, "Net Object.. Freeing object %p\n" _ net);
839     if (net) {
840         if (net == HTRequest_net(net->request)) HTRequest_setNet(net->request, NULL);
841         HT_FREE(net);
842 	return YES;
843     }
844     return NO;
845 }
846 
847 /*
848 **  Unregister the net object from the global list
849 **  and see if we can start a new pending request.
850 */
unregister_net(HTNet * net)851 PRIVATE BOOL unregister_net (HTNet * net)
852 {
853     if (net && NetTable) {
854 	HTList * list = NetTable[net->hash];
855 	if (list) {
856 	    HTList_removeObject(list, (void *) net);
857 	    check_pending(net);
858 	    HTNetCount--;
859 	    return YES;
860 	}
861     }
862     return NO;
863 }
864 
865 /*
866 **	Clears the contents of the Net object so that we can use it again.
867 */
HTNet_clear(HTNet * net)868 PUBLIC BOOL HTNet_clear (HTNet * net)
869 {
870     if (net) {
871 	net->host->channel = NULL;
872 	net->readStream = NULL;
873 	net->bytesRead = 0;
874 	net->headerBytesRead = 0;
875 	net->bytesWritten = 0;
876 	net->headerBytesWritten = 0;
877 	return YES;
878     }
879     return NO;
880 }
881 
882 /*	HTNet_delete
883 **	------------
884 **	Deletes the HTNet object from the list of active requests and calls
885 **	any registered call back functions IF not the status is HT_IGNORE.
886 **	This is used if we have internal requests that the app doesn't know
887 **	about. We also see if we have pending requests that can be started
888 **	up now when we have a socket free.
889 **	The callback functions are called in the reverse order of which they
890 **	were registered (last one first)
891 **	Return YES if OK, else NO
892 */
HTNet_delete(HTNet * net,int status)893 PUBLIC BOOL HTNet_delete (HTNet * net, int status)
894 {
895     HTTRACE(CORE_TRACE, "Net Object.. Delete %p and call AFTER filters\n" _ net);
896     if (net) {
897 	HTRequest * request = net->request;
898 
899 	/*
900 	** If we have a premature close then recover the request. Otherwise
901 	** break the link to the Host object and continue deleting the net
902 	** object
903 	*/
904 	if (net->host) {
905 	    HTHost_unregister (net->host, net, HTEvent_READ);
906 	    HTHost_unregister (net->host, net, HTEvent_WRITE);
907 	    if (status == HT_RECOVER_PIPE) {
908 		HTNet_clear(net);
909 		HTTRACE(CORE_TRACE, "Net Object.. Restarting request %p (retry=%d) with net object %p\n" _
910 			    request _ HTRequest_retrys(request) _ net);
911 		return YES;
912 	    }
913             HTHost_deleteNet(net->host, net, status);
914 	    if (HTHost_doRecover(net->host)) HTHost_recoverPipe(net->host);
915         }
916 
917         /* Remove object from the table of Net Objects */
918 	unregister_net(net);
919         free_net(net);
920 
921     	/* Call AFTER filters */
922 	if (status != HT_IGNORE) HTNet_executeAfterAll(request, status);
923 
924 	/*
925 	** Truely delete the HTNet object. Thanks to Mikhail Grouchinski
926 	** we now do this after having called the after filters so that
927 	** these filters can use the information in the Net object
928 	*/
929 
930 	return YES;
931     }
932     return NO;
933 }
934 
HTNet_deleteDup(HTNet * dup)935 PUBLIC BOOL HTNet_deleteDup (HTNet * dup)
936 {
937     return dup ? (unregister_net(dup) && free_net(dup)) : NO;
938 }
939 
940 /*	HTNet_deleteAll
941 **	---------------
942 **	Deletes all HTNet object that might either be active or pending
943 **	We DO NOT call the AFTER filters - A crude way of saying goodbye!
944 */
HTNet_deleteAll(void)945 PUBLIC BOOL HTNet_deleteAll (void)
946 {
947     HTTRACE(CORE_TRACE, "Net Object.. Remove all Net objects, NO filters\n");
948     if (NetTable) {
949 	HTList * cur = NULL;
950         HTNet * pres = NULL;
951 	int cnt;
952 	for (cnt=0; cnt<HT_XL_HASH_SIZE; cnt++) {
953 	    if ((cur = NetTable[cnt])) {
954 		while ((pres = (HTNet *) HTList_nextObject(cur)) != NULL)  {
955 		    check_pending(pres);
956                     free_net(pres);
957 		}
958 	    }
959 	    HTList_delete(NetTable[cnt]);
960 	}
961 	HT_FREE(NetTable);
962 	HTNetCount = 0;
963 	return YES;
964     }
965     return NO;
966 }
967 
968 /*
969 **	When pipelining, it is not possible to kill a single request
970 **	as we then loose track of where we are in the pipe. It is
971 **	therefore necessary to kill the whole pipeline.
972 */
HTNet_killPipe(HTNet * net)973 PUBLIC BOOL HTNet_killPipe (HTNet * net)
974 {
975     return (net && net->host) ? HTHost_killPipe(net->host) : NO;
976 }
977 
978 /*	HTNet_kill
979 **	----------
980 **	Kill the request by calling the call back function with a request for
981 **	closing the connection. Does not remove the object. This is done by
982 **	HTNet_delete() function which is called by the load routine.
983 **	Returns OK if success, NO on error
984 */
HTNet_kill(HTNet * net)985 PUBLIC BOOL HTNet_kill (HTNet * net)
986 {
987     if (net) {
988         HTAlertCallback * cbf = HTAlert_find(HT_PROG_INTERRUPT);
989         if (cbf) (*cbf)(net->request, HT_PROG_INTERRUPT, HT_MSG_NULL, NULL, NULL, NULL);
990 	HTTRACE(CORE_TRACE, "Net Object.. Killing %p\n" _ net);
991 	if (net->event.cbf) {
992 	    (*(net->event.cbf))(HTNet_socket(net), net->event.param, HTEvent_CLOSE);
993 	    return YES;
994 	}
995 	return unregister_net(net) && free_net(net);
996     }
997     HTTRACE(CORE_TRACE, "Net Object.. No object to kill\n");
998     return NO;
999 }
1000 
1001 /*	HTNet_killAll
1002 **	-------------
1003 **	Kills all registered net objects by calling the call
1004 **	back function with a request for closing the connection. We do not
1005 **	remove the HTNet object as it is done by HTNet_delete().
1006 **	Returns OK if success, NO on error
1007 */
HTNet_killAll(void)1008 PUBLIC BOOL HTNet_killAll (void)
1009 {
1010     HTTRACE(CORE_TRACE, "Net Object.. Kill ALL Net objects!!!\n");
1011     if (NetTable) {
1012 	HTList * cur = NULL;
1013         HTNet * pres = NULL;
1014 	int cnt;
1015 	for (cnt=0; cnt<HT_XL_HASH_SIZE; cnt++) {
1016 	    if ((cur = NetTable[cnt])) {
1017 		while ((pres = (HTNet *) HTList_lastObject(cur)) != NULL) {
1018 		    HTNet_kill(pres);
1019 		    cur = NetTable[cnt];
1020 		}
1021 	    }
1022 	}
1023 	return YES;
1024     }
1025     HTTRACE(CORE_TRACE, "Net Object.. No objects to kill\n");
1026     return NO;
1027 }
1028 
1029 /* ------------------------------------------------------------------------- */
1030 /*			    Connection Specifics 			     */
1031 /* ------------------------------------------------------------------------- */
1032 
1033 /*	HTNet_priority
1034 **	--------------
1035 **	Get the current priority of the Net object
1036 */
HTNet_priority(HTNet * net)1037 PUBLIC HTPriority HTNet_priority (HTNet * net)
1038 {
1039     return (net ? net->event.priority : HT_PRIORITY_INV);
1040 }
1041 
1042 /*	HTNet_setPriority
1043 **	-----------------
1044 **	Set the current priority of the Net object
1045 **	This will change the priority next time the thread is blocked
1046 */
HTNet_setPriority(HTNet * net,HTPriority priority)1047 PUBLIC BOOL HTNet_setPriority (HTNet * net, HTPriority priority)
1048 {
1049     if (net) {
1050 	net->event.priority = priority;
1051 	return YES;
1052     }
1053     return NO;
1054 }
1055 
1056 /*	HTNet_Persistent
1057 **	----------------
1058 **	Check whether the net object handles persistent connections
1059 **	If we have a DNS entry then check that as well.
1060 */
HTNet_persistent(HTNet * net)1061 PUBLIC BOOL HTNet_persistent (HTNet * net)
1062 {
1063     return (net && HTHost_isPersistent(net->host));
1064 }
1065 
1066 /*	HTNet_persistent
1067 **	----------------
1068 **	Set the net object to handle persistent connections
1069 **	If we also have a DNS entry then update that as well
1070 */
HTNet_setPersistent(HTNet * net,BOOL persistent,HTTransportMode mode)1071 PUBLIC BOOL HTNet_setPersistent (HTNet *		net,
1072 				 BOOL			persistent,
1073 				 HTTransportMode	mode)
1074 {
1075     if (net) {
1076 	BOOL result = HTHost_setPersistent(net->host, persistent, mode);
1077 	HTTRACE(CORE_TRACE, "Net Object.. Persistent connection set %s %s\n" _
1078 		    persistent ? "ON" : "OFF" _
1079 		    result ? "succeeded" : "failed");
1080 	return result;
1081     }
1082     return NO;
1083 }
1084 
1085 /*
1086 **	Context pointer to be used in context call back function
1087 */
HTNet_setContext(HTNet * net,void * context)1088 PUBLIC BOOL HTNet_setContext (HTNet * net, void * context)
1089 {
1090     if (net) {
1091 	net->context = context;
1092 	return YES;
1093     }
1094     return NO;
1095 }
1096 
HTNet_context(HTNet * net)1097 PUBLIC void * HTNet_context (HTNet * net)
1098 {
1099     return net ? net->context : NULL;
1100 }
1101 
1102 /*
1103 **  Get and set the socket number
1104 */
HTNet_setSocket(HTNet * net,SOCKET sockfd)1105 PUBLIC BOOL HTNet_setSocket (HTNet * net, SOCKET sockfd)
1106 {
1107     if (net && net->host && net->host->channel) {
1108 	HTChannel_setSocket(net->host->channel, sockfd);
1109 	return YES;
1110     }
1111     return NO;
1112 }
1113 
HTNet_socket(HTNet * net)1114 PUBLIC SOCKET HTNet_socket (HTNet * net)
1115 {
1116     return (net && net->host && net->host->channel ? HTChannel_socket(net->host->channel) : INVSOC);
1117 }
1118 
1119 /*
1120 **  Get and set the HTRequest object
1121 */
HTNet_setRequest(HTNet * net,HTRequest * request)1122 PUBLIC BOOL HTNet_setRequest (HTNet * net, HTRequest * request)
1123 {
1124     if (net && request) {
1125 	net->request = request;
1126 	return YES;
1127     }
1128     return NO;
1129 }
1130 
HTNet_request(HTNet * net)1131 PUBLIC HTRequest * HTNet_request (HTNet * net)
1132 {
1133     return (net ? net->request : NULL);
1134 }
1135 
1136 /*
1137 **  Get and set the HTChannel object
1138 */
HTNet_setChannel(HTNet * net,HTChannel * channel)1139 PUBLIC BOOL HTNet_setChannel (HTNet * net, HTChannel * channel)
1140 {
1141     return (net && channel) ? HTHost_setChannel(net->host, channel) : NO;
1142 }
1143 
HTNet_channel(HTNet * net)1144 PUBLIC HTChannel * HTNet_channel (HTNet * net)
1145 {
1146     return net ? HTHost_channel(net->host) : NULL;
1147 }
1148 
1149 /*
1150 **  Get and set the HTHost object
1151 */
HTNet_setHost(HTNet * net,HTHost * host)1152 PUBLIC BOOL HTNet_setHost (HTNet * net, HTHost * host)
1153 {
1154     if (net && host) {
1155 	net->host = host;
1156 	return YES;
1157     }
1158     return NO;
1159 }
1160 
HTNet_host(HTNet * net)1161 PUBLIC HTHost * HTNet_host (HTNet * net)
1162 {
1163     return (net ? net->host : NULL);
1164 }
1165 
1166 /*
1167 **  Get and set the HTdns object
1168 */
HTNet_setDns(HTNet * net,HTdns * dns)1169 PUBLIC BOOL HTNet_setDns (HTNet * net, HTdns * dns)
1170 {
1171     if (net && dns) {
1172 	net->host->dns = dns;
1173 	return YES;
1174     }
1175     return NO;
1176 }
1177 
HTNet_dns(HTNet * net)1178 PUBLIC HTdns * HTNet_dns (HTNet * net)
1179 {
1180     return (net ? net->host->dns : NULL);
1181 }
1182 
HTNet_setProtocol(HTNet * net,HTProtocol * protocol)1183 PUBLIC BOOL HTNet_setProtocol (HTNet * net, HTProtocol * protocol)
1184 {
1185     if (net && protocol) {
1186 	net->protocol = protocol;
1187 	return YES;
1188     }
1189     return NO;
1190 }
1191 
HTNet_protocol(HTNet * net)1192 PUBLIC HTProtocol * HTNet_protocol (HTNet * net)
1193 {
1194     return (net ? net->protocol : NULL);
1195 }
1196 
HTNet_setTransport(HTNet * net,HTTransport * tp)1197 PUBLIC BOOL HTNet_setTransport (HTNet * net, HTTransport * tp)
1198 {
1199     if (net && tp) {
1200 	net->transport = tp;
1201 	return YES;
1202     }
1203     return NO;
1204 }
1205 
HTNet_transport(HTNet * net)1206 PUBLIC HTTransport * HTNet_transport (HTNet * net)
1207 {
1208     return (net ? net->transport : NULL);
1209 }
1210 
HTNet_preemptive(HTNet * net)1211 PUBLIC BOOL HTNet_preemptive (HTNet * net)
1212 {
1213     return (net ? net->preemptive : NO);
1214 }
1215 
1216 /*
1217 **	Create the output stream and bind it to the channel
1218 **	Please read the description in the HTIOStream module on the parameters
1219 */
HTNet_getOutput(HTNet * me,void * param,int mode)1220 PUBLIC HTOutputStream * HTNet_getOutput (HTNet * me, void * param, int mode)
1221 {
1222     if (me && me->host && me->host->channel && me->transport) {
1223 	HTTransport * tp = me->transport;
1224 	HTChannel * ch = me->host->channel;
1225 	HTOutputStream * output = (*tp->output_new)(me->host, ch, param, mode);
1226 	HTChannel_setOutput(ch, output);
1227 	return output;
1228     }
1229     HTTRACE(CORE_TRACE, "Host Object.. Can't create output stream\n");
1230     return NULL;
1231 }
1232 
HTNet_event(HTNet * net)1233 PUBLIC HTEvent * HTNet_event (HTNet * net)
1234 {
1235     return net ? &net->event : NULL;
1236 }
1237 
HTNet_setEventParam(HTNet * net,void * eventParam)1238 PUBLIC BOOL HTNet_setEventParam (HTNet * net, void * eventParam)
1239 {
1240     if (net) return HTEvent_setParam(&net->event, eventParam);
1241     return NO;
1242 }
1243 
HTNet_eventParam(HTNet * net)1244 PUBLIC void * HTNet_eventParam (HTNet * net)
1245 {
1246     return net ? net->event.param : NULL;
1247 }
1248 
HTNet_setEventCallback(HTNet * net,HTEventCallback * cbf)1249 PUBLIC BOOL HTNet_setEventCallback(HTNet * net, HTEventCallback * cbf)
1250 {
1251     if (net) return HTEvent_setCallback(&net->event, cbf);
1252     return NO;
1253 }
1254 
HTNet_eventCallback(HTNet * net)1255 PUBLIC HTEventCallback * HTNet_eventCallback(HTNet * net)
1256 {
1257     return net->event.cbf;
1258 }
1259 
HTNet_setEventPriority(HTNet * net,HTPriority priority)1260 PUBLIC BOOL HTNet_setEventPriority(HTNet * net, HTPriority priority)
1261 {
1262     if (net) return HTEvent_setPriority(&net->event, priority);
1263     return NO;
1264 }
1265 
HTNet_eventPriority(HTNet * net)1266 PUBLIC HTPriority HTNet_eventPriority(HTNet * net)
1267 {
1268     return net->event.priority;
1269 }
1270 
HTNet_readStream(HTNet * net)1271 PUBLIC HTStream * HTNet_readStream(HTNet * net)
1272 {
1273     if (!net) return NULL;
1274     return net->readStream;
1275 }
1276 
HTNet_setReadStream(HTNet * net,HTStream * stream)1277 PUBLIC BOOL HTNet_setReadStream (HTNet * net, HTStream * stream)
1278 {
1279     if (net) {
1280 	net->readStream = stream;
1281 	return YES;
1282     }
1283     return NO;
1284 }
1285 
1286 /*
1287 **	Should we do raw byte count at the network or later?
1288 **	Normally it is later but in cases like FTP we need it
1289 **	in the raw form
1290 */
HTNet_setRawBytesCount(HTNet * net,BOOL mode)1291 PUBLIC BOOL HTNet_setRawBytesCount (HTNet * net, BOOL mode)
1292 {
1293     if (net) {
1294 	net->countRawBytes = mode;
1295 	return YES;
1296     }
1297     return NO;
1298 }
1299 
HTNet_rawBytesCount(HTNet * net)1300 PUBLIC BOOL HTNet_rawBytesCount (HTNet * net)
1301 {
1302     return (net && net->countRawBytes);
1303 }
1304