/* HTNet.c ** HTNet Class ** ** (c) COPYRIGHT MIT 1995. ** Please first read the full copyright statement in the file COPYRIGH. ** @(#) $Id: HTNet.c,v 2.93 1998/05/04 19:37:05 frystyk Exp $ ** ** This is the implementation of the internal library multithreading ** functions. This includes an interrupt handler and a event loop. ** ** History: ** 12 June 94 Written by Henrik Frystyk, frystyk@w3.org ** 31 May 95 Charlie Brooks cbrooks@osf.org ** */ /* Implemention dependent include files */ #include "wwwsys.h" /* Library include files */ #include "WWWUtil.h" #include "HTProt.h" #include "HTError.h" #include "HTAlert.h" #include "HTParse.h" #include "HTTrans.h" #include "HTHost.h" #include "HTReq.h" #include "HTEvent.h" #include "HTStream.h" #include "HTHstMan.h" #include "HTIOStream.h" #include "HTNetMan.h" /* Implemented here */ #ifndef HT_MAX_SOCKETS #define HT_MAX_SOCKETS 25 #endif #define HASH_SIZE 599 typedef struct _BeforeFilter { HTNetBefore * before; /* Filter function */ char * tmplate; /* URL template for when to call filter */ int order; /* Relative execution order */ void * param; /* Local context */ } BeforeFilter; typedef struct _AfterFilter { HTNetAfter * after; /* Filter function */ char * tmplate; /* URL template for when to call filter */ int order; /* Relative execution order */ void * param; /* Local context */ int status; /* Status of load for when to call filter */ } AfterFilter; struct _HTStream { const HTStreamClass * isa; /* ... */ }; struct _HTInputStream { const HTInputStreamClass * isa; /* ... */ }; PRIVATE HTList * HTBefore = NULL; /* List of global BEFORE filters */ PRIVATE HTList * HTAfter = NULL; /* List of global AFTER filters */ PRIVATE int MaxActive = HT_MAX_SOCKETS; /* Max active requests */ PRIVATE int Active = 0; /* Counts open sockets */ PRIVATE int Persistent = 0; /* Counts persistent sockets */ PRIVATE HTList ** NetTable = NULL; /* List of net objects */ PRIVATE int HTNetCount = 0; /* Counting elements in table */ /* ------------------------------------------------------------------------- */ /* GENERIC BEFORE and AFTER filter Management */ /* ------------------------------------------------------------------------- */ PRIVATE int HTBeforeOrder (const void * a, const void * b) { return ((BeforeFilter *) b)->order - ((BeforeFilter *) a)->order; } PRIVATE int HTAfterOrder (const void * a, const void * b) { return ((AfterFilter *) b)->order - ((AfterFilter *) a)->order; } PRIVATE int check_order (HTFilterOrder order) { return (orderHT_FILTER_LAST) ? HT_FILTER_LAST : order; } /* ** Register a BEFORE filter in the list provided by the caller. ** Several filters can be registered in which case they are called ** with the filter ordering in mind. */ PUBLIC BOOL HTNetCall_addBefore (HTList * list, HTNetBefore * before, const char * tmplate, void * param, HTFilterOrder order) { if (list && before) { BeforeFilter * me; if ((me = (BeforeFilter *) HT_CALLOC(1, sizeof(BeforeFilter)))==NULL) HT_OUTOFMEM("HTNetCall_addBefore"); me->before = before; if (tmplate) StrAllocCopy(me->tmplate, tmplate); me->order = check_order(order); me->param = param; if (CORE_TRACE) HTTrace("Net Before.. Add %p with order %d tmplate `%s\' context %p\n", before, me->order, tmplate ? tmplate : "", param); return (HTList_addObject(list, me) && HTList_insertionSort(list, HTBeforeOrder)); } return NO; } /* ** Unregister all instances of a BEFORE filter from a list. */ PUBLIC BOOL HTNetCall_deleteBefore (HTList * list, HTNetBefore * before) { if (CORE_TRACE) HTTrace("Net Before.. Delete %p\n", before); if (list && before) { HTList * cur = list; BeforeFilter * pres; while ((pres = (BeforeFilter *) HTList_nextObject(list))) { if (pres->before == before) { HTList_removeObject(list, (void *) pres); HT_FREE(pres->tmplate); HT_FREE(pres); cur = list; } } } return NO; } /* ** Deletes all BEFORE filters in list */ PUBLIC BOOL HTNetCall_deleteBeforeAll (HTList * list) { if (CORE_TRACE) HTTrace("Net Before. Delete All filters\n"); if (list) { HTList * cur = list; BeforeFilter * pres; while ((pres = (BeforeFilter *) HTList_nextObject(cur))) { HT_FREE(pres->tmplate); HT_FREE(pres); } HTList_delete(list); return YES; } return NO; } /* ** Call all the BEFORE filters in the order specified at registration ** time. We also check for any template and whether it matches or not. ** If a filter returns other than HT_OK then stop and return immediately. ** Otherwise return what the last filter returns. */ PUBLIC int HTNetCall_executeBefore (HTList * list, HTRequest * request) { HTParentAnchor * anchor = HTRequest_anchor(request); char * url = HTAnchor_physical(anchor); char * addr = url ? url : HTAnchor_address((HTAnchor *) anchor); int ret = HT_OK; int mode = 0; if (list && request && addr) { BeforeFilter * pres; while ((pres = (BeforeFilter *) HTList_nextObject(list))) { if (!pres->tmplate || (pres->tmplate && HTStrMatch(pres->tmplate, addr))) { if (CORE_TRACE) HTTrace("Net Before.. calling %p (request %p, context %p)\n", pres->before, request, pres->param); ret = (*pres->before)(request, pres->param, mode); if (ret != HT_OK) break; /* ** Update the address to match against if the filter changed ** the physical address. */ if ((url = HTAnchor_physical(anchor))) addr = url; } } } if (!url) HT_FREE(addr); return ret; } /* ** Register a AFTER filter in the list provided by the caller. ** Several filters can be registered in which case they are called ** with the filter ordering in mind. */ PUBLIC BOOL HTNetCall_addAfter (HTList * list, HTNetAfter * after, const char * tmplate, void * param, int status, HTFilterOrder order) { if (list && after) { AfterFilter * me; if ((me = (AfterFilter *) HT_CALLOC(1, sizeof(AfterFilter)))==NULL) HT_OUTOFMEM("HTNetCall_addAfter"); me->after = after; if (tmplate) StrAllocCopy(me->tmplate, tmplate); me->order = check_order(order); me->param = param; me->status = status; if (CORE_TRACE) HTTrace("Net After... Add %p with order %d tmplate `%s\' code %d context %p\n", after, me->order, tmplate ? tmplate : "", status, param); return (HTList_addObject(list, me) && HTList_insertionSort(list, HTAfterOrder)); } return NO; } /* ** Unregister all instances of an AFTER filter from a list. */ PUBLIC BOOL HTNetCall_deleteAfter (HTList * list, HTNetAfter * after) { if (CORE_TRACE) HTTrace("Net After... Delete %p\n", after); if (list && after) { HTList * cur = list; AfterFilter * pres; while ((pres = (AfterFilter *) HTList_nextObject(cur))) { if (pres->after == after) { HTList_removeObject(list, (void *) pres); HT_FREE(pres->tmplate); HT_FREE(pres); cur = list; } } } return NO; } /* ** Unregister all filters registered for a given status. */ PUBLIC BOOL HTNetCall_deleteAfterStatus (HTList * list, int status) { if (CORE_TRACE) HTTrace("Net After... Delete all with status %d\n",status); if (list) { HTList * cur = list; AfterFilter * pres; while ((pres = (AfterFilter *) HTList_nextObject(cur))) { if (pres->status == status) { HTList_removeObject(list, (void *) pres); HT_FREE(pres->tmplate); HT_FREE(pres); cur = list; } } return YES; } return NO; } /* ** Deletes all AFTER filters in list */ PUBLIC BOOL HTNetCall_deleteAfterAll (HTList * list) { if (CORE_TRACE) HTTrace("Net After. Delete All filters\n"); if (list) { HTList * cur = list; AfterFilter * pres; while ((pres = (AfterFilter *) HTList_nextObject(cur))) { HT_FREE(pres->tmplate); HT_FREE(pres); } HTList_delete(list); return YES; } return NO; } /* ** Call all the AFTER filters in the order specified at registration ** time and if it has the right status code and it's not HT_IGNORE. ** We also check for any template and whether it matches or not. ** If a filter returns other than HT_OK then stop and return immediately. ** Otherwise return what the last filter returns. */ PUBLIC int HTNetCall_executeAfter (HTList * list, HTRequest * request, int status) { int ret = HT_OK; if (status != HT_IGNORE) { HTParentAnchor * anchor = HTRequest_anchor(request); char * url = HTAnchor_physical(anchor); char * addr = url ? url : HTAnchor_address((HTAnchor *) anchor); HTResponse * response = HTRequest_response(request); if (list && request && addr) { AfterFilter * pres; while ((pres = (AfterFilter *) HTList_nextObject(list))) { if ((pres->status == status || pres->status == HT_ALL) && (!pres->tmplate || (pres->tmplate && HTStrMatch(pres->tmplate, addr)))) { if (CORE_TRACE) HTTrace("Net After... calling %p (request %p, response %p, status %d, context %p)\n", pres->after, request, response, status, pres->param); ret = (*pres->after)(request, response, pres->param, status); if (ret != HT_OK) break; /* ** Update the address to match against if the filter changed ** the physical address. */ if ((url = HTAnchor_physical(anchor))) addr = url; } } } if (!url) HT_FREE(addr); } return ret; } /* ------------------------------------------------------------------------- */ /* GLOBAL BEFORE and AFTER filter Management */ /* ------------------------------------------------------------------------- */ /* ** Global set of callback functions BEFORE the request is issued ** list can be NULL */ PUBLIC BOOL HTNet_setBefore (HTList *list) { HTBefore = list; return YES; } PUBLIC HTList * HTNet_before (void) { return HTBefore; } PUBLIC BOOL HTNet_addBefore (HTNetBefore * before, const char * tmplate, void * param, HTFilterOrder order) { if (!HTBefore) HTBefore = HTList_new(); return HTNetCall_addBefore(HTBefore, before, tmplate, param, order); } PUBLIC BOOL HTNet_deleteBefore (HTNetBefore * cbf) { return HTNetCall_deleteBefore(HTBefore, cbf); } /* ** Call both the local and the global BEFORE filters (if any) */ PUBLIC int HTNet_executeBeforeAll (HTRequest * request) { int ret; BOOL override = NO; HTList * befores; if ((befores = HTRequest_before(request, &override))) { if ((ret = HTNetCall_executeBefore(befores, request)) != HT_OK) return ret; } return override ? HT_OK : HTNetCall_executeBefore(HTBefore, request); } /* ** Global set of callback functions AFTER the request is issued ** list can be NULL */ PUBLIC BOOL HTNet_setAfter (HTList *list) { HTAfter = list; return YES; } PUBLIC HTList * HTNet_after (void) { return HTAfter; } PUBLIC BOOL HTNet_addAfter (HTNetAfter * after, const char * tmplate, void * param, int status, HTFilterOrder order) { if (!HTAfter) HTAfter = HTList_new(); return HTNetCall_addAfter(HTAfter, after, tmplate, param, status, order); } PUBLIC BOOL HTNet_deleteAfter (HTNetAfter * cbf) { return HTNetCall_deleteAfter(HTAfter, cbf); } PUBLIC BOOL HTNet_deleteAfterStatus (int status) { return HTNetCall_deleteAfterStatus(HTAfter, status); } /* ** Call both the local and the global AFTER filters (if any) */ PUBLIC int HTNet_executeAfterAll (HTRequest * request, int status) { int ret; BOOL override = NO; HTList * afters; if ((afters = HTRequest_after(request, &override))) { if ((ret = HTNetCall_executeAfter(afters, request, status)) != HT_OK) return ret; } return override ? HT_OK : HTNetCall_executeAfter(HTAfter, request, status); } /* ------------------------------------------------------------------------- */ /* Socket Management */ /* ------------------------------------------------------------------------- */ PUBLIC int HTNet_maxSocket (void) { return MaxActive; } PUBLIC BOOL HTNet_setMaxSocket (int newmax) { if (newmax > 0) { MaxActive = newmax; return YES; } return NO; } PUBLIC void HTNet_increaseSocket (void) { Active++; if (CORE_TRACE) HTTrace("Net Manager. Increasing active sockets to %d, %d persistent sockets\n", Active, Persistent); } PUBLIC void HTNet_decreaseSocket (void) { if (--Active < 0) Active = 0; if (CORE_TRACE) HTTrace("Net Manager. Decreasing active sockets to %d, %d persistent sockets\n", Active, Persistent); } PUBLIC int HTNet_availableSockets (void) { int available = MaxActive - Active; return available > 0 ? available : 0; } PUBLIC void HTNet_increasePersistentSocket (void) { Persistent++; if (CORE_TRACE) HTTrace("Net Manager. %d active sockets, increasing persistent sockets to %d\n", Active, Persistent); } PUBLIC void HTNet_decreasePersistentSocket (void) { if (--Persistent < 0) Persistent = 0; if (CORE_TRACE) HTTrace("Net Manager. %d active sockets, decreasing persistent sockets to %d\n", Active, Persistent); } PUBLIC int HTNet_availablePersistentSockets (void) { int available = MaxActive - 2 - Persistent; return available > 0 ? available : 0; } /* ** Returns whether there are any Net objects pending or active */ PUBLIC BOOL HTNet_isIdle (void) { return (HTNetCount > 0); } PUBLIC BOOL HTNet_isEmpty (void) { return (HTNetCount <= 0); } PUBLIC int HTNet_count (void) { return HTNetCount; } /* ------------------------------------------------------------------------- */ /* Creation and deletion methods */ /* ------------------------------------------------------------------------- */ PRIVATE HTNet * create_object (void) { static int net_hash = 0; HTNet * me = NULL; /* Create new object */ if ((me = (HTNet *) HT_CALLOC(1, sizeof(HTNet))) == NULL) HT_OUTOFMEM("HTNet_new"); me->hash = net_hash++ % HASH_SIZE; /* Insert into hash table */ if (!NetTable) { if ((NetTable = (HTList **) HT_CALLOC(HASH_SIZE, sizeof(HTList *))) == NULL) HT_OUTOFMEM("create_object"); } if (!NetTable[me->hash]) NetTable[me->hash] = HTList_new(); HTList_addObject(NetTable[me->hash], (void *) me); HTNetCount++; if (CORE_TRACE) HTTrace("Net Object.. %p created with hash %d\n",me, me->hash); return me; } /* HTNet_duplicate ** --------------- ** Creates a new HTNet object as a duplicate of the same request. ** Returns YES if OK, else NO ** BUG: We do not check if we have a socket free! */ PUBLIC HTNet * HTNet_dup (HTNet * src) { if (src) { HTNet * me; int hash; if ((me = create_object()) == NULL) return NULL; hash = me->hash; if (CORE_TRACE) HTTrace("Net Object.. Duplicated %p\n", src); memcpy((void *) me, src, sizeof(HTNet)); me->hash = hash; /* Carry over hash entry */ return me; } return NULL; } PUBLIC BOOL HTNet_execute (HTNet * net, HTEventType type) { if (net && net->event.cbf && net->request) { if (CORE_TRACE) HTTrace("Net Object.. %p calling %p with event type %d and context %p\n", net, net->event.cbf, type, net->event.param); (*(net->event.cbf))(HTNet_socket(net), net->event.param, type); return YES; } return NO; } /* ** Start a Net obejct by calling the protocol module. */ PUBLIC BOOL HTNet_start (HTNet * net) { if (net && net->event.cbf && net->request) { if (CORE_TRACE) HTTrace("Net Object.. Launching %p\n", net); (*(net->event.cbf))(HTNet_socket(net), net->event.param, HTEvent_BEGIN); return YES; } return NO; } /* HTNet_new ** --------- ** This function creates a new HTNet object and assigns the socket number ** to it. This is intended to be used when you are going to listen on a ** socket using the HTDoListen() function in HTTCP.c. The function do NOT ** call any of the callback functions. ** Returns new object or NULL on error */ PUBLIC HTNet * HTNet_new (HTRequest * request) { HTNet * me; if ((me = create_object()) == NULL) return NULL; me->preemptive = HTRequest_preemptive(request); HTNet_setEventPriority(me, HTRequest_priority(request)); me->request = request; HTRequest_setNet(request, me); return me; } /* HTNet_newServer ** --------------- ** Create a new HTNet object as a new request to be handled. If we have ** more than MaxActive connections already then return NO. ** Returns YES if OK, else NO */ PUBLIC BOOL HTNet_newServer (HTRequest * request, HTNet * net, char * access) { HTProtocol * protocol; HTTransport * tp = NULL; /* added JTD:5/28/96 */ HTProtCallback * cbf; if (!request) return NO; /* Find a protocol object for this access scheme */ protocol = HTProtocol_find(request, access); /* added - JTD:5/28/96 */ /* Find a transport object for this protocol */ tp = HTTransport_find(request, HTProtocol_transport(protocol)); if (tp == NULL) { if (CORE_TRACE) HTTrace("Net Object.. NO TRANSPORT OBJECT\n"); return NO; } /* end of additions - JTD:5/28/96 */ /* Fill out the net object and bind it to the request object */ net->preemptive = (HTProtocol_preemptive(protocol) || HTRequest_preemptive(request)); #if 0 net->protocol = protocol; net->transport = tp; /* added - JTD:5/28/96 */ #endif net->event.priority = HTRequest_priority(request); net->request = request; HTRequest_setNet(request, net); if (!(cbf = HTProtocol_server(protocol))) { if (CORE_TRACE) HTTrace("Net Object.. NO CALL BACK FUNCTION!\n"); return NO; } /* Increase the number of retrys for this download */ HTRequest_addRetry(request); /* Start the server request */ if (CORE_TRACE) HTTrace("Net Object.. starting SERVER request %p with net object %p\n", request, net); (*(cbf))(HTNet_socket(net), request); return YES; } /* HTNet_new ** --------- ** Create a new HTNet object as a new request to be handled. If we have ** more than MaxActive connections already then put this into the ** pending queue, else start the request by calling the call back ** function registered with this access method. ** Returns YES if OK, else NO */ PUBLIC BOOL HTNet_newClient (HTRequest * request) { HTParentAnchor * anchor = HTRequest_anchor(request); HTNet * me = NULL; HTProtocol * protocol = NULL; HTTransport * tp = NULL; char * physical = NULL; int status; HTProtCallback * cbf; if (!request) return NO; /* ** First we do all the "BEFORE" callbacks in order to see if we are to ** continue with this request or not. If we receive a callback status ** that is NOT HT_OK then jump directly to the after callbacks and return */ if ((status = HTNet_executeBeforeAll(request)) != HT_OK) { HTNet_executeAfterAll(request, status); return YES; } /* ** If no translation was provided by the filters then use the anchor ** address directly */ if (!(physical = HTAnchor_physical(anchor))) { char * addr = HTAnchor_address((HTAnchor *) anchor); if (CORE_TRACE) HTTrace("Net Object.. Using default address\n"); HTAnchor_setPhysical(anchor, addr); physical = HTAnchor_physical(anchor); HT_FREE(addr); } /* Find a protocol object for this access scheme */ { char * proxy = HTRequest_proxy(request); char * access = HTParse(proxy ? proxy : physical, "", PARSE_ACCESS); if ((protocol = HTProtocol_find(request, access)) == NULL) { if (CORE_TRACE) HTTrace("Net Object.. NO PROTOCOL OBJECT\n"); HT_FREE(access); return NO; } HT_FREE(access); } /* Find a transport object for this protocol */ tp = HTTransport_find(request, HTProtocol_transport(protocol)); if (tp == NULL) { if (CORE_TRACE) HTTrace("Net Object.. NO TRANSPORT OBJECT\n"); return NO; } /* Create new net object and bind it to the request object */ if ((me = create_object()) == NULL) return NO; me->preemptive = (HTProtocol_preemptive(protocol) || HTRequest_preemptive(request)); #if 0 me->priority = HTRequest_priority(request); #endif HTNet_setEventPriority(me, HTRequest_priority(request)); me->protocol = protocol; me->transport = tp; me->request = request; HTRequest_setNet(request, me); if (!(cbf = HTProtocol_client(protocol))) { if (CORE_TRACE) HTTrace("Net Object.. NO CALL BACK FUNCTION!\n"); HT_FREE(me); return NO; } /* Increase the number of retrys for this download */ HTRequest_addRetry(request); /* ** Check if we can start the request, else put it into pending queue ** If so then call the call back function associated with the anchor. ** We use the INVSOC as we don't have a valid socket yet! */ if (CORE_TRACE) HTTrace("Net Object.. starting request %p (retry=%d) with net object %p\n", request, HTRequest_retrys(request), me); (*(cbf))(INVSOC, request); return YES; } /* delete_object ** ------------- ** Deletes an HTNet object ** Return YES if OK, else NO */ PRIVATE BOOL delete_object (HTNet * net) { if (CORE_TRACE) HTTrace("Net Object.. Remove object %p\n", net); if (net) { /* Close socket */ /* ** As we may have a socket available we check for whether ** we can start any pending requests. We do this by asking for ** pending Host objects. If none then use the current object */ /* (*net->input->isa->consumed)(net->input, net->header_length + net->bytes_read); */ HTHost_launchPending(net->host); /* ** Break the link to the request and free the Net object */ HTRequest_setNet(net->request, NULL); HT_FREE(net); return YES; } return NO; } PRIVATE BOOL remove_net (HTNet * net) { if (net && NetTable) { HTList * list = NetTable[net->hash]; if (list) { HTList_removeObject(list, (void *) net); delete_object(net); HTNetCount--; return YES; } } return NO; } /* ** Clears the contents of the Net object so that we can use it again. */ PUBLIC BOOL HTNet_clear (HTNet * net) { if (net) { net->host->channel = NULL; net->readStream = NULL; net->bytesRead = 0; net->bytesWritten = 0; net->headerLength = 0; return YES; } return NO; } /* HTNet_delete ** ------------ ** Deletes the HTNet object from the list of active requests and calls ** any registered call back functions IF not the status is HT_IGNORE. ** This is used if we have internal requests that the app doesn't know ** about. We also see if we have pending requests that can be started ** up now when we have a socket free. ** The callback functions are called in the reverse order of which they ** were registered (last one first) ** Return YES if OK, else NO */ PUBLIC BOOL HTNet_delete (HTNet * net, int status) { if (CORE_TRACE) HTTrace("Net Object.. Delete %p and call AFTER filters\n", net); if (net) { HTRequest * request = net->request; /* ** If we have a premature close then recover the request. Otherwise ** break the link to the Host object and continue deleting the net ** object */ if (net->host) { HTHost_unregister (net->host, net, HTEvent_READ); HTHost_unregister (net->host, net, HTEvent_WRITE); if (status == HT_RECOVER_PIPE) { HTNet_clear(net); if (CORE_TRACE) HTTrace("Net Object.. Restarting request %p (retry=%d) with net object %p\n", request, HTRequest_retrys(request), net); return YES; } HTHost_free(net->host, status); HTHost_deleteNet(net->host, net); if (HTHost_doRecover(net->host)) HTHost_recoverPipe(net->host); } /* Remove object from the table of Net Objects */ remove_net(net); /* Call AFTER filters */ HTNet_executeAfterAll(request, status); return YES; } return NO; } /* HTNet_deleteAll ** --------------- ** Deletes all HTNet object that might either be active or pending ** We DO NOT call the AFTER filters - A crude way of saying goodbye! */ PUBLIC BOOL HTNet_deleteAll (void) { if (CORE_TRACE) HTTrace("Net Object.. Remove all Net objects, NO filters\n"); if (NetTable) { HTList * cur = NULL; HTNet * pres = NULL; int cnt; for (cnt=0; cntevent.cbf) { (*(net->event.cbf))(HTNet_socket(net), net->event.param, HTEvent_CLOSE); return YES; } return remove_net(net); } if (CORE_TRACE) HTTrace("Net Object.. No object to kill\n"); return NO; } /* HTNet_killAll ** ------------- ** Kills all registered net objects by calling the call ** back function with a request for closing the connection. We do not ** remove the HTNet object as it is done by HTNet_delete(). ** Returns OK if success, NO on error */ PUBLIC BOOL HTNet_killAll (void) { if (CORE_TRACE) HTTrace("Net Object.. Kill ALL Net objects!!!\n"); if (NetTable) { HTList * cur = NULL; HTNet * pres = NULL; int cnt; for (cnt=0; cntevent.priority : HT_PRIORITY_INV); } /* HTNet_setPriority ** ----------------- ** Set the current priority of the Net object ** This will change the priority next time the thread is blocked */ PUBLIC BOOL HTNet_setPriority (HTNet * net, HTPriority priority) { if (net) { net->event.priority = priority; return YES; } return NO; } /* HTNet_Persistent ** ---------------- ** Check whether the net object handles persistent connections ** If we have a DNS entry then check that as well. */ PUBLIC BOOL HTNet_persistent (HTNet * net) { return (net && HTHost_isPersistent(net->host)); } /* HTNet_persistent ** ---------------- ** Set the net object to handle persistent connections ** If we also have a DNS entry then update that as well */ PUBLIC BOOL HTNet_setPersistent (HTNet * net, BOOL persistent, HTTransportMode mode) { if (net) { BOOL result = HTHost_setPersistent(net->host, persistent, mode); if (CORE_TRACE) HTTrace("Net Object.. Persistent connection set %s %s\n", persistent ? "ON" : "OFF", result ? "succeeded" : "failed"); return result; } return NO; } /* ** Context pointer to be used in context call back function */ PUBLIC BOOL HTNet_setContext (HTNet * net, void * context) { if (net) { net->context = context; return YES; } return NO; } PUBLIC void * HTNet_context (HTNet * net) { return net ? net->context : NULL; } /* ** Get and set the socket number */ PUBLIC BOOL HTNet_setSocket (HTNet * net, SOCKET sockfd) { if (net && net->host && net->host->channel) { HTChannel_setSocket(net->host->channel, sockfd); return YES; } return NO; } PUBLIC SOCKET HTNet_socket (HTNet * net) { return (net && net->host && net->host->channel ? HTChannel_socket(net->host->channel) : INVSOC); } /* ** Get and set the HTRequest object */ PUBLIC BOOL HTNet_setRequest (HTNet * net, HTRequest * request) { if (net && request) { net->request = request; return YES; } return NO; } PUBLIC HTRequest * HTNet_request (HTNet * net) { return (net ? net->request : NULL); } /* ** Get and set the HTChannel object */ PUBLIC BOOL HTNet_setChannel (HTNet * net, HTChannel * channel) { return (net && channel) ? HTHost_setChannel(net->host, channel) : NO; } PUBLIC HTChannel * HTNet_channel (HTNet * net) { return net ? HTHost_channel(net->host) : NULL; } /* ** Get and set the HTHost object */ PUBLIC BOOL HTNet_setHost (HTNet * net, HTHost * host) { if (net && host) { net->host = host; return YES; } return NO; } PUBLIC HTHost * HTNet_host (HTNet * net) { return (net ? net->host : NULL); } /* ** Get and set the HTdns object */ PUBLIC BOOL HTNet_setDns (HTNet * net, HTdns * dns) { if (net && dns) { net->host->dns = dns; return YES; } return NO; } PUBLIC HTdns * HTNet_dns (HTNet * net) { return (net ? net->host->dns : NULL); } PUBLIC BOOL HTNet_setProtocol (HTNet * net, HTProtocol * protocol) { if (net && protocol) { net->protocol = protocol; return YES; } return NO; } PUBLIC HTProtocol * HTNet_protocol (HTNet * net) { return (net ? net->protocol : NULL); } PUBLIC BOOL HTNet_setTransport (HTNet * net, HTTransport * tp) { if (net && tp) { net->transport = tp; return YES; } return NO; } PUBLIC HTTransport * HTNet_transport (HTNet * net) { return (net ? net->transport : NULL); } PUBLIC BOOL HTNet_preemptive (HTNet * net) { return (net ? net->preemptive : NO); } /* ** Create the output stream and bind it to the channel ** Please read the description in the HTIOStream module on the parameters */ PUBLIC HTOutputStream * HTNet_getOutput (HTNet * me, void * param, int mode) { if (me && me->host && me->host->channel && me->transport) { HTTransport * tp = me->transport; HTChannel * ch = me->host->channel; HTOutputStream * output = (*tp->output_new)(me->host, ch, param, mode); HTChannel_setOutput(ch, output); return output; } if (CORE_TRACE) HTTrace("Host Object.. Can't create output stream\n"); return NULL; } PUBLIC HTEvent * HTNet_event (HTNet * net) { return net ? &net->event : NULL; } PUBLIC BOOL HTNet_setEventParam (HTNet * net, void * eventParam) { if (net) return HTEvent_setParam(&net->event, eventParam); return NO; } PUBLIC void * HTNet_eventParam (HTNet * net) { return net ? net->event.param : NULL; } PUBLIC BOOL HTNet_setEventCallback(HTNet * net, HTEventCallback * cbf) { if (net) return HTEvent_setCallback(&net->event, cbf); return NO; } PUBLIC HTEventCallback * HTNet_eventCallback(HTNet * net) { return net->event.cbf; } PUBLIC BOOL HTNet_setEventPriority(HTNet * net, HTPriority priority) { if (net) return HTEvent_setPriority(&net->event, priority); return NO; } PUBLIC HTPriority HTNet_eventPriority(HTNet * net) { return net->event.priority; } PUBLIC HTStream * HTNet_readStream(HTNet * net) { if (!net) return NULL; return net->readStream; } PUBLIC BOOL HTNet_setReadStream (HTNet * net, HTStream * stream) { if (net) { net->readStream = stream; return YES; } return NO; }