Annotation of libwww/Library/src/HTHost.c, revision 2.8

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.8     ! frystyk     6: **     @(#) $Id: HTHost.c,v 2.7 1996/08/05 17:22:31 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 */
                     16: #include "sysdep.h"
                     17: #include "WWWUtil.h"
                     18: #include "HTParse.h"
                     19: #include "HTAlert.h"
                     20: #include "HTError.h"
                     21: #include "HTNetMan.h"
                     22: #include "HTTrans.h"
                     23: #include "HTHost.h"                                     /* Implemented here */
                     24: 
                     25: #define HOST_TIMEOUT           43200L       /* Default host timeout is 12 h */
                     26: #define TCP_TIMEOUT            3600L           /* Default TCP timeout i 1 h */
                     27: #define HASH_SIZE              67
                     28: 
                     29: /* Type definitions and global variables etc. local to this module */
                     30: struct _HTHost {
                     31:     char *             hostname;            /* name of host + optional port */
                     32:     time_t             ntime;                              /* Creation time */
                     33:     char *             type;                                   /* Peer type */
                     34:     int                version;                             /* Peer version */
2.6       frystyk    35:     HTMethod           methods;                /* Public methods (bit-flag) */
                     36:     char *             server;                               /* Server name */
                     37:     char *             user_agent;                            /* User Agent */
2.8     ! frystyk    38:     HTTransportMode    mode;                              /* Supported mode */
2.1       frystyk    39:     HTChannel *                channel;                       /* Persistent channel */
2.8     ! frystyk    40:     HTList *           pipeline;                /* Pipe line of net objects */
        !            41:     HTList *           pending;              /* List of pending Net objects */
2.1       frystyk    42:     time_t             expires;          /* Persistent channel expires time */
                     43: };
                     44: 
                     45: PRIVATE time_t HostTimeout = HOST_TIMEOUT;       /* Timeout on host entries */
                     46: PRIVATE time_t TCPTimeout = TCP_TIMEOUT;  /* Timeout on persistent channels */
                     47: 
2.8     ! frystyk    48: PRIVATE HTList ** HostTable = NULL;
        !            49: PRIVATE HTList * PendHost = NULL;          /* List of pending host elements */
2.1       frystyk    50: 
                     51: /* ------------------------------------------------------------------------- */
                     52: 
                     53: PRIVATE void free_object (HTHost * me)
                     54: {
                     55:     if (me) {
                     56:        HT_FREE(me->hostname);
                     57:        HT_FREE(me->type);
2.3       eric       58:        if (me->channel) {
2.5       eric       59:            HTChannel_delete(me->channel, HT_OK);
2.3       eric       60:            me->channel = NULL;
                     61:        }
2.8     ! frystyk    62:        HTList_delete(me->pipeline);
        !            63:        HTList_delete(me->pending);
2.1       frystyk    64:        HT_FREE(me);
                     65:     }
                     66: }
                     67: 
                     68: PRIVATE BOOL delete_object (HTList * list, HTHost * me)
                     69: {
2.2       frystyk    70:     if (CORE_TRACE) HTTrace("Host info... object %p from list %p\n", me, list);
2.1       frystyk    71:     HTList_removeObject(list, (void *) me);
                     72:     free_object(me);
                     73:     return YES;
                     74: }
                     75: 
                     76: /*
                     77: **     Search the host info cache for a host object or create a new one
                     78: **     and add it. Examples of host names are
                     79: **
                     80: **             www.w3.org
                     81: **             www.foo.com:8000
                     82: **             18.52.0.18
                     83: **
                     84: **     Returns Host object or NULL if error. You may get back an already
                     85: **     existing host object - you're not guaranteed a new one each time.
                     86: */
                     87: PUBLIC HTHost * HTHost_new (char * host)
                     88: {
                     89:     HTList * list = NULL;                          /* Current list in cache */
                     90:     HTHost * pres = NULL;
                     91:     if (!host) {
2.2       frystyk    92:        if (CORE_TRACE) HTTrace("Host info... Bad argument\n");
2.1       frystyk    93:        return NULL;
                     94:     }
                     95:     
                     96:     /* Find a hash for this host */
                     97:     {
                     98:        int hash = 0;
                     99:        char *ptr;
                    100:        for (ptr=host; *ptr; ptr++)
                    101:            hash = (int) ((hash * 3 + (*(unsigned char *) ptr)) % HASH_SIZE);
                    102:        if (!HostTable) {
                    103:            if ((HostTable = (HTList **) HT_CALLOC(HASH_SIZE,
                    104:                                                   sizeof(HTList *))) == NULL)
                    105:                HT_OUTOFMEM("HTHost_find");
                    106:        }
                    107:        if (!HostTable[hash]) HostTable[hash] = HTList_new();
                    108:        list = HostTable[hash];
                    109:     }
                    110: 
                    111:     /* Search the cache */
                    112:     {
                    113:        HTList * cur = list;
                    114:        while ((pres = (HTHost *) HTList_nextObject(cur))) {
                    115:            if (!strcmp(pres->hostname, host)) {
2.8     ! frystyk   116:                if (HTHost_isIdle(pres) && time(NULL)>pres->ntime+HostTimeout){
2.2       frystyk   117:                    if (CORE_TRACE)
2.1       frystyk   118:                        HTTrace("Host info... Collecting host info %p\n",pres);
                    119:                    delete_object(list, pres);
                    120:                    pres = NULL;
                    121:                }
                    122:                break;
                    123:            }
                    124:        }
                    125:     }
                    126: 
2.8     ! frystyk   127:     /* If not found then create new Host object, else use existing one */
2.1       frystyk   128:     if (pres) {
                    129:        if (pres->channel) {
                    130:            if (pres->expires < time(NULL)) {      /* Cached channel is cold */
2.2       frystyk   131:                if (CORE_TRACE)
2.1       frystyk   132:                    HTTrace("Host info... Persistent channel %p gotten cold\n",
                    133:                            pres->channel);
2.5       eric      134:                HTChannel_delete(pres->channel, HT_OK);
2.1       frystyk   135:                pres->channel = NULL;
                    136:            } else {
2.2       frystyk   137:                if (CORE_TRACE)
2.1       frystyk   138:                    HTTrace("Host info... REUSING CHANNEL %p\n",pres->channel);
                    139:            }
                    140:        }
                    141:     } else {
                    142:        if ((pres = (HTHost *) HT_CALLOC(1, sizeof(HTHost))) == NULL)
                    143:            HT_OUTOFMEM("HTHost_add");
                    144:        StrAllocCopy(pres->hostname, host);
                    145:        pres->ntime = time(NULL);
2.8     ! frystyk   146:        pres->mode = HT_TP_SINGLE;
2.2       frystyk   147:        if (CORE_TRACE) 
2.1       frystyk   148:            HTTrace("Host info... added `%s\' to list %p\n", host, list);
                    149:        HTList_addObject(list, (void *) pres);
                    150:     }
                    151:     return pres;
                    152: }
                    153: 
                    154: /*
2.8     ! frystyk   155: **     Get and set the hostname of the remote host
        !           156: */
        !           157: PUBLIC char * HTHost_name (HTHost * host)
        !           158: {
        !           159:      return host ? host->hostname : NULL;
        !           160: }
        !           161: 
        !           162: /*
2.1       frystyk   163: **     Get and set the type class of the remote host
                    164: */
                    165: PUBLIC char * HTHost_class (HTHost * host)
                    166: {
                    167:      return host ? host->type : NULL;
                    168: }
                    169: 
                    170: PUBLIC void HTHost_setClass (HTHost * host, char * s_class)
                    171: {
                    172:     if (host && s_class) StrAllocCopy(host->type, s_class);
                    173: }
                    174: 
                    175: /*
                    176: **     Get and set the version of the remote host
                    177: */
                    178: PUBLIC int HTHost_version (HTHost *host)
                    179: {
                    180:      return host ? host->version : 0;
                    181: }
                    182: 
                    183: PUBLIC void HTHost_setVersion (HTHost * host, int version)
                    184: {
                    185:     if (host) host->version = version;
                    186: }
                    187: 
                    188: /*
                    189: **     Get and set the cache timeout for persistent entries.
                    190: **     The default value is TCP_TIMEOUT
                    191: */
                    192: PUBLIC void HTHost_setPersistTimeout (time_t timeout)
                    193: {
                    194:     TCPTimeout = timeout;
                    195: }
                    196: 
                    197: PUBLIC time_t HTHost_persistTimeout (time_t timeout)
                    198: {
                    199:     return TCPTimeout;
                    200: }
                    201: 
                    202: /*     Persistent Connection Expiration
                    203: **     --------------------------------
                    204: **     Should normally not be used. If, then use calendar time.
                    205: */
                    206: PUBLIC void HTHost_setPersistExpires (HTHost * host, time_t expires)
                    207: {
                    208:     if (host) host->expires = expires;
                    209: }
                    210: 
                    211: PUBLIC time_t HTHost_persistExpires (HTHost * host)
                    212: {
                    213:     return host ? host->expires : -1;
                    214: }
                    215: 
                    216: /*
2.6       frystyk   217: **     Public methods for this host
                    218: */
                    219: PUBLIC HTMethod HTHost_publicMethods (HTHost * me)
                    220: {
                    221:     return me ? me->methods : METHOD_INVALID;
                    222: }
                    223: 
                    224: PUBLIC void HTHost_setPublicMethods (HTHost * me, HTMethod methodset)
                    225: {
                    226:     if (me) me->methods = methodset;
                    227: }
                    228: 
                    229: PUBLIC void HTHost_appendPublicMethods (HTHost * me, HTMethod methodset)
                    230: {
                    231:     if (me) me->methods |= methodset;
                    232: }
                    233: 
                    234: /*
                    235: **     Get and set the server name of the remote host
                    236: */
                    237: PUBLIC char * HTHost_server (HTHost * host)
                    238: {
                    239:      return host ? host->server : NULL;
                    240: }
                    241: 
                    242: PUBLIC BOOL HTHost_setServer (HTHost * host, const char * server)
                    243: {
                    244:     if (host && server) {
                    245:        StrAllocCopy(host->server, server);
                    246:        return YES;
                    247:     }
                    248:     return NO;
                    249: }
                    250: 
                    251: /*
                    252: **     Get and set the userAgent name of the remote host
                    253: */
                    254: PUBLIC char * HTHost_userAgent (HTHost * host)
                    255: {
                    256:      return host ? host->user_agent : NULL;
                    257: }
                    258: 
                    259: PUBLIC BOOL HTHost_setUserAgent (HTHost * host, const char * userAgent)
                    260: {
                    261:     if (host && userAgent) {
                    262:        StrAllocCopy(host->user_agent, userAgent);
                    263:        return YES;
                    264:     }
                    265:     return NO;
                    266: }
                    267: 
2.1       frystyk   268: /*     HTHost_catchClose
                    269: **     -----------------
                    270: **     This function is registered when the socket is idle so that we get
                    271: **     a notification if the socket closes at the other end. At this point
                    272: **     we can't use the request object as it might have been freed a long
                    273: **     time ago.
                    274: */
                    275: PUBLIC int HTHost_catchClose (SOCKET soc, HTRequest * request, SockOps ops)
                    276: {
2.2       frystyk   277:     if (CORE_TRACE)
2.1       frystyk   278:        HTTrace("Catch Close. called with socket %d with ops %x\n",
                    279:                soc, (unsigned) ops);
                    280:     if (ops == FD_READ) {
                    281:        HTChannel * ch = HTChannel_find(soc);     /* Find associated channel */
2.8     ! frystyk   282:        HTHost * host = HTChannel_host(ch);           /* and associated host */
2.1       frystyk   283:        if (ch && host) {           
2.2       frystyk   284:            if (CORE_TRACE) HTTrace("Catch Close. CLOSING socket %d\n", soc);
2.8     ! frystyk   285:            HTHost_clearChannel(host, HT_OK);
2.1       frystyk   286:        } else {
2.2       frystyk   287:            if (CORE_TRACE) HTTrace("Catch Close. socket %d NOT FOUND!\n",soc);
2.1       frystyk   288:        }
                    289:     }
2.4       eric      290:     HTEvent_unregister(soc, (SockOps) FD_ALL);
2.1       frystyk   291:     return HT_OK;
                    292: }
                    293: 
                    294: /*
                    295: **     As soon as we know that this host accepts persistent connections,
                    296: **     we associated the channel with the host. 
                    297: **     We don't want more than MaxSockets-2 connections to be persistent in
                    298: **     order to avoid deadlock.
                    299: */
2.8     ! frystyk   300: PUBLIC BOOL HTHost_setChannel (HTHost *                host,
        !           301:                               HTChannel *      channel,
        !           302:                               HTTransportMode  mode)
2.1       frystyk   303: {
2.6       frystyk   304:     if (!host || !channel) return NO;
2.2       frystyk   305:     if (host->channel) {
                    306:        if (CORE_TRACE) HTTrace("Host info... %p already persistent\n", host);
                    307:        return YES;
                    308:     } else {
2.1       frystyk   309:        SOCKET sockfd = HTChannel_socket(channel);
2.8     ! frystyk   310:        if (sockfd != INVSOC && HTNet_availablePersistentSockets() > 0) {
2.1       frystyk   311:            host->channel = channel;
2.8     ! frystyk   312:            host->mode = mode;
2.1       frystyk   313:            host->expires = time(NULL) + TCPTimeout;      /* Default timeout */
2.8     ! frystyk   314:            HTChannel_setHost(channel, host); 
        !           315:            HTNet_increasePersistentSocket();
2.2       frystyk   316:            if (CORE_TRACE)
2.1       frystyk   317:                HTTrace("Host info... added host %p as persistent\n", host);
                    318:            return YES;
                    319:        } else {
2.2       frystyk   320:            if (CORE_TRACE)
                    321:                HTTrace("Host info... no room for persistent socket %d\n",
2.7       frystyk   322:                        sockfd);
2.1       frystyk   323:        }
                    324:     }
                    325:     return NO;
                    326: }
                    327: 
                    328: /*
                    329: **     Find persistent channel associated with this host.
                    330: */
                    331: PUBLIC HTChannel * HTHost_channel (HTHost * host)
                    332: {
                    333:     return host ? host->channel : NULL;
                    334: }
                    335: 
                    336: /*
                    337: **     Clear the persistent entry by deleting the channel object. Note that
                    338: **     the channel object is only deleted if it's not used anymore.
                    339: */
2.8     ! frystyk   340: PUBLIC BOOL HTHost_clearChannel (HTHost * host, int status)
2.1       frystyk   341: {
                    342:     if (host && host->channel) {
2.8     ! frystyk   343:        HTChannel_setHost(host->channel, NULL);
        !           344:        HTChannel_delete(host->channel, status);
2.1       frystyk   345:        host->expires = 0;
                    346:        host->channel = NULL;
2.8     ! frystyk   347:        HTNet_decreasePersistentSocket();
2.2       frystyk   348:        if (CORE_TRACE)
                    349:            HTTrace("Host info... removed host %p as persistent\n", host);
2.1       frystyk   350:        return YES;
                    351:     }
                    352:     return NO;
                    353: }
                    354: 
                    355: /*
                    356: **     Check whether we have a persistent channel or not
                    357: */
                    358: PUBLIC BOOL HTHost_isPersistent (HTHost * host)
                    359: {
                    360:     return host && host->channel;
2.8     ! frystyk   361: }
        !           362: 
        !           363: /*
        !           364: **     Handle the connection mode. The mode may change mode in the 
        !           365: **     middle of a connection.
        !           366: */
        !           367: PUBLIC HTTransportMode HTHost_mode (HTHost * host, BOOL * active)
        !           368: {
        !           369:     return host ? host->mode : HT_TP_SINGLE;
        !           370: }
        !           371: 
        !           372: /*
        !           373: **     If the new mode is lower than the old mode then adjust the pipeline
        !           374: **     accordingly. That is, if we are going into single mode then move
        !           375: **     all entries in the pipeline and move the rest to the pending
        !           376: **     queue. They will get launched at a later point in time.
        !           377: */
        !           378: PUBLIC BOOL HTHost_setMode (HTHost * host, HTTransportMode mode)
        !           379: {
        !           380:     if (host) {
        !           381:        /*
        !           382:        **  Check the new mode and see if we must adjust the queues.
        !           383:        */
        !           384:        if (mode == HT_TP_SINGLE && host->mode > mode) {
        !           385:            int piped = HTList_count(host->pipeline);
        !           386:            if (piped > 0) {
        !           387:                int cnt;
        !           388:                if (CORE_TRACE)
        !           389:                    HTTrace("Host info... Moving %d Net objects from pipe line to pending queue\n", piped);
        !           390:                if (!host->pending) host->pending = HTList_new();
        !           391:                for (cnt=0; cnt<piped; cnt++) {
        !           392:                    HTNet * net = HTList_removeFirstObject(host->pipeline);
        !           393:                    HTList_appendObject(host->pending, net);
        !           394:                }
        !           395:            }
        !           396:        }
        !           397:        host->mode = mode;
        !           398:        return YES;
        !           399:     }
        !           400:     return NO;
        !           401: }
        !           402: 
        !           403: /*
        !           404: **     Check whether a host is idle meaning if it is ready for a new
        !           405: **     request which depends on the mode of the host. If the host is 
        !           406: **     idle, i.e. ready for use then return YES else NO. If the host supports
        !           407: **     persistent connections then still only return idle if no requests are
        !           408: **     ongoing. 
        !           409: */
        !           410: PUBLIC BOOL HTHost_isIdle (HTHost * host)
        !           411: {
        !           412:     return (host && HTList_count(host->pipeline) <= 0);
        !           413: }
        !           414: 
        !           415: /*
        !           416: **     Add a net object to the host object. If the host
        !           417: **     is idle then add to active list (pipeline) else add
        !           418: **     it to the pending list
        !           419: **     Return HT_PENDING if we must pend, HT_OK, or HT_ERROR
        !           420: */
        !           421: PUBLIC int HTHost_addNet (HTHost * host, HTNet * net)
        !           422: {
        !           423:     if (host && net) {
        !           424:        int status = HT_OK;
        !           425: 
        !           426:        /* Check to see if we can get a socket */
        !           427:        if (HTNet_availableSockets() <= 0) {
        !           428:            if (!PendHost) PendHost = HTList_new();
        !           429:            if (CORE_TRACE)
        !           430:                HTTrace("Host info... Add Host %p as pending\n", host);
        !           431:            HTList_addObject(PendHost, host);
        !           432:            status = HT_PENDING;
        !           433:        }
        !           434: 
        !           435:        /* Add to either active or pending queue */
        !           436:        if (HTHost_isIdle(host)) {
        !           437:            if (CORE_TRACE) HTTrace("Host info... Add Net %p to pipeline of host %p\n", net, host);
        !           438:            if (!host->pipeline) host->pipeline = HTList_new();
        !           439:            HTList_addObject(host->pipeline, net);
        !           440:            
        !           441:            /*
        !           442:            **  We have been idle and must hence unregister our catch close
        !           443:            **  event handler
        !           444:            */
        !           445:            if (host->channel) {
        !           446:                SOCKET sockfd = HTChannel_socket(host->channel);
        !           447:                HTEvent_unregister(sockfd, (SockOps) FD_CLOSE);
        !           448:            }
        !           449:        } else {
        !           450:            if (CORE_TRACE) HTTrace("Host info... Add Net %p as pending\n", net);
        !           451:            if (!host->pending) host->pending = HTList_new();
        !           452:            HTList_addObject(host->pending, net);
        !           453:            status = HT_PENDING;
        !           454:        }
        !           455:        return status;
        !           456:     }
        !           457:     return HT_ERROR;
        !           458: }
        !           459: 
        !           460: PUBLIC BOOL HTHost_deleteNet (HTHost * host, HTNet * net)
        !           461: {
        !           462:     if (host && net) {
        !           463:        if (CORE_TRACE)
        !           464:            HTTrace("Host info... Remove Net %p from pipe line\n", net);
        !           465:        HTList_removeObject(host->pipeline, net);
        !           466:        HTList_removeObject(host->pending, net);
        !           467:        return YES;
        !           468:     }
        !           469:     return NO;
        !           470: }
        !           471: 
        !           472: /*
        !           473: **     Handle pending host objects.
        !           474: **     There are two ways we can end up with pending reqyests:
        !           475: **      1) If we are out of sockets then register new host objects as pending.
        !           476: **      2) If we are pending on a connection then register new net objects as
        !           477: **         pending
        !           478: **     This set of functions handles pending host objects and can start new
        !           479: **     requests as resources get available
        !           480: */
        !           481: 
        !           482: /*
        !           483: **     Check this host object for any pending requests and return the next
        !           484: **     registered Net object.
        !           485: */
        !           486: PUBLIC HTNet * HTHost_nextPendingNet (HTHost * host)
        !           487: {
        !           488:     HTNet * net = NULL;
        !           489:     if (host && host->pending && host->pipeline) {
        !           490:        if ((net = (HTNet *) HTList_removeFirstObject(host->pending)) != NULL)
        !           491:            if (PROT_TRACE)
        !           492:                HTTrace("Host info... Popping %p from pending net queue\n",
        !           493:                        net);
        !           494:        HTList_addObject(host->pipeline, net);
        !           495:     }
        !           496:     return net;
        !           497: }
        !           498: 
        !           499: /*
        !           500: **     Return the current list of pending host obejcts waiting for a socket
        !           501: */
        !           502: PUBLIC HTHost * HTHost_nextPendingHost (void)
        !           503: {
        !           504:     HTHost * host = NULL;
        !           505:     if (PendHost) {
        !           506:        if ((host = (HTHost *) HTList_removeFirstObject(PendHost)) != NULL)
        !           507:            if (PROT_TRACE)
        !           508:                HTTrace("Host info... Poping %p from pending host queue\n",
        !           509:                        host);
        !           510:     }
        !           511:     return host;
        !           512: }
        !           513: 
        !           514: /*
        !           515: **     Start the next pending request if any. First we look for pending
        !           516: **     requests for the same host and then we check for any other pending
        !           517: **     hosts
        !           518: */
        !           519: PUBLIC BOOL HTHost_launchPending (HTHost * host)
        !           520: {
        !           521:     int available = HTNet_availableSockets();
        !           522: 
        !           523:     if (!host) {
        !           524:        if (PROT_TRACE) HTTrace("Host info... Bad arguments\n");
        !           525:        return NO;
        !           526:     }
        !           527: 
        !           528:     /*
        !           529:     **  Check if we do have resources available for a new request
        !           530:     **  This can either be reusing an existing connection or opening a new one
        !           531:     */
        !           532:     if (available > 0 || host->mode >= HT_TP_PIPELINE) {
        !           533: 
        !           534:        /*
        !           535:        **  Check the current Host obejct for pending Net objects
        !           536:        */
        !           537:        if (host) {
        !           538:            HTNet * net = HTHost_nextPendingNet(host);
        !           539:            if (net) return HTNet_start(net);
        !           540:        }
        !           541: 
        !           542:        /*
        !           543:        **  Check for other pending Host objects
        !           544:        */
        !           545:        {
        !           546:            HTHost * pending = HTHost_nextPendingHost();
        !           547:            if (pending) {
        !           548:                HTNet * net = HTHost_nextPendingNet(pending);
        !           549:                if (net) return HTNet_start(net);
        !           550:            }
        !           551:        }
        !           552: 
        !           553:        /*
        !           554:        **  If nothing pending then register our catch close event handler to
        !           555:        **  have something catching the socket if the remote server closes the
        !           556:        **  connection, for example due to timeout.
        !           557:        */
        !           558:        if (PROT_TRACE) HTTrace("Host info... Nothing pending\n");
        !           559:        if (host->channel) {
        !           560:            SOCKET sockfd = HTChannel_socket(host->channel);
        !           561:            HTEvent_register(sockfd, 0, (SockOps) FD_CLOSE,
        !           562:                             HTHost_catchClose,  HT_PRIORITY_MAX);
        !           563:        }
        !           564:     } else
        !           565:        if (PROT_TRACE) HTTrace("Host info... No available sockets\n");
        !           566:     return NO;
2.1       frystyk   567: }
                    568: 

Webmaster