# The Host Class

    /*
    **	(c) COPYRIGHT MIT 1995.
    **	Please first read the full copyright statement in the file COPYRIGH.
    */

The Host class manages what we know about a remote host. This can for example be what type of host it is, and what version it is using. Notice that a host object can be used to describe both a server or a client - all information in the Host object can be shared regardless of whether it is to be used in a server application or a client application.

This module is implemented by HTHost.c, and it is a part of the W3C Sample Code Library.

    #ifndef HTHOST_H
    #define HTHOST_H

    typedef struct _HTHost HTHost;
    #define HOST_HASH_SIZE		67

    #include "HTChannl.h"
    #include "HTReq.h"
    #include "HTEvent.h"
    #include "HTProt.h"
    #include "HTTimer.h"

The Host class contains information about the remote host, for example the type (HTTP/1.0, HTTP/1.1, FTP etc.) along with information on how the connections can be used (if it supports persistent connections, interleaved access etc.)

## Creation and Deletion Methods

We keep a cache of information that we know about a remote host. This allows us to be much more detailed in generating requests. Search the host info cache for a host object or create a new one and add it. Examples of host names are

- www.w3.org
- www.foo.com:8000

### Add a Host Object

    extern HTHost * HTHost_new (char * host, u_short u_port);
    extern HTHost * HTHost_newWParse(HTRequest * request, char * url, u_short u_port);
    extern int HTHost_hash (HTHost * host);

### Delete a Host Object

The Host Class contains an automatic garbage collection of Host objects so that we don't keep information around that is stale.

### Find a Host Object

Searches the cache of known hosts to see if we already have information about this host. If not then we return NULL.

    extern HTHost * HTHost_find (char * host);

## Remote Host Information

We keep track of the capabilities of the host in the other end so thatwe may adjust our queries to fit it better

### Remote Host Name

Get the name of the remote host. This is set automatically when a new Host object and can be asked for at any point in time. You can not change the host name but must create a new Host object instead.

    extern char * HTHost_name	(HTHost * host);

### Remote Host Protocol Class and Version

Define the host class of the host at the other end. A class is a generic description of the protocol which is exactly like the access method in a URL, for example "http" etc. The host version is a finer distinction (sub-class) between various versions of the host class, for example HTTP/0.9, HTTP/1.1 etc. The host version is a bit flag that the protocol module can define on its own. That way we don't have to change this module when registering a new protocol module. The host type is a description of whether we can keep the connection persistent or not.

    extern char * HTHost_class	(HTHost * host);
    extern void HTHost_setClass	(HTHost * host, char * s_class);

    extern int  HTHost_version	(HTHost * host);
    extern void HTHost_setVersion	(HTHost * host, int version);

### Public Methods accessible on This Host

A server can inform a client about the supported methods using the `Public` header.

    extern HTMethod HTHost_publicMethods 	(HTHost * me);
    extern void HTHost_setPublicMethods 	(HTHost * me, HTMethod methodset);
    extern void HTHost_appendPublicMethods	(HTHost * me, HTMethod methodset);

### Server Name of Remote Host

A server can send its server application name and version in a HTTP response. We pick up this information and add it to the Host object

    extern char * HTHost_server	(HTHost * host);
    extern BOOL HTHost_setServer	(HTHost * host, const char * server);

### User Agent Name of Remote Host

A client can send the name of the client application in a HTTP request. We pick up this information and add it to the Host Object

    extern char * HTHost_userAgent 	(HTHost * host);
    extern BOOL HTHost_setUserAgent	(HTHost * host, const char * userAgent);

### Range Units Accepted by this Host

Since all HTTP entities are represented in HTTP messages as sequences of bytes, the concept of a byte range is meaningful for any HTTP entity. (However, not all clients and servers need to support byte-range operations.) Byte range specifications in HTTP apply to the sequence of bytes in the entity-body (not necessarily the same as the message-body). A byte range operation may specify a single range of bytes, or a set of ranges within a single entity.

You can also check whether a specific range unit is OK. We always say `YES` except if we have a specific statement from the server that it doesn't understand byte ranges - that is - it has sent "none" in a "Accept-Range" response header

    extern char * HTHost_rangeUnits  (HTHost * host);
    extern BOOL HTHost_setRangeUnits (HTHost * host, const char * units);
    extern BOOL HTHost_isRangeUnitAcceptable (HTHost * host, const char * unit);

## Persistent Connections

We don't want more than MaxSockets-2 connections to be persistent in order to avoid deadlock.

    extern BOOL HTHost_setPersistent (HTHost * host, BOOL persistent,
                                      HTTransportMode mode);
    extern BOOL HTHost_clearChannel (HTHost * host, int status);

    extern HTChannel * HTHost_channel (HTHost * host);

### Is this host Persistent?

Check whether we have a persistent channel or not

    extern BOOL HTHost_isPersistent (HTHost * host);
    extern BOOL HTHost_setCloseNotification (HTHost * host, BOOL mode);
    extern BOOL HTHost_closeNotification (HTHost * host);

### Timing Persistent Channels

Normally we wait for the peer process to close persistent connections but in order not to use up our own resources, we have a timeout on our own. The default value is 1 hour, but you can modify the value using the following methods:

    extern time_t HTHost_persistTimeout (time_t timeout);
    extern void HTHost_setPersistTimeout (time_t timeout);

Each persistent connection has an absolute value of when this connection most likely will expire. If the peer process does not inform us, we use our own timeout.

    extern void HTHost_setPersistExpires (HTHost * host, time_t expires);
    extern time_t HTHost_persistExpires (HTHost * host);
    extern void HTHost_setReqsPerConnection (HTHost * host, int reqs);
    extern int HTHost_reqsPerConnection (HTHost * host);
    extern void HTHost_setReqsMade (HTHost * host, int reqs);
    extern int HTHost_reqsMade (HTHost * host);

## Transport Mode

Handle the connection mode. The mode may change mode in the middle of a connection If the new mode is lower than the old mode then adjust the pipeline accordingly. That is, if we are going into single mode then move all entries in the pipeline and move the rest to the pending queue. They will get launched at a later point in time.

    extern HTTransportMode HTHost_mode (HTHost * host, BOOL * active);
    extern BOOL HTHost_setMode (HTHost * host, HTTransportMode mode);

## Queuing Requests

Requests are queued in the Host object until we have resources to start them. The request is in the form of a Net object as we may have multiple socket requests per Request object. This is for example the case with FTP which uses two connections.

    extern int HTHost_addNet     (HTHost * host, HTNet * net);
    extern BOOL HTHost_free      (HTHost * host, int status);
    extern BOOL HTHost_deleteNet (HTHost * host, HTNet * net);
    extern HTList * HTHost_net   (HTHost * host);

### Is the Host Idle?

Before we can start a new connection to the host we must be sure that the host is idle. That is, if it can accept a new connection. We have several modes describing how and when a host is idle. This is a function of the Transport Object

    extern BOOL HTHost_isIdle (HTHost * host);

### Handling Pending Requests

There are two ways we can end up with pending reqyests:

1. If we are out of sockets then register new host objects as pending.
2. If we are pending on a connection then register new net objects as pending

This set of functions handles pending host objects and can start new requests as resources get available. The first function checks the host object for any pending Net objects and return the first of these Net objects.

    extern HTNet * HTHost_nextPendingNet (HTHost * host);

The second checks the list of pending host objects waiting for a socket and returns the first of these Host objects.

    extern HTHost * HTHost_nextPendingHost (void);

Start the next pending request if any. First we look for pending requests for the same host and then we check for any other pending hosts. If nothing pending then register a catch close event handler to have something catching the socket if the remote server closes the connection, for example due to timeout.

    extern BOOL HTHost_launchPending (HTHost * host);

    extern int HTHost_connect (HTHost * host, HTNet * net, char * url,
                               HTProtocolId port);

HTHost clients can use the host for all IO and take advantage of host multiplexing and pipelining.

## Event Management

    extern int HTHost_register(HTHost * host, HTNet * net, HTEventType type);
    extern int HTHost_unregister(HTHost * host, HTNet * net, HTEventType type);
    extern int HTHost_tickleFirstNet(HTHost * host, HTEventType type);

    extern BOOL HTHost_setRemainingRead(HTHost * host, size_t remainaing);
    extern size_t HTHost_remainingRead (HTHost * host);

    extern SockA * HTHost_getSockAddr(HTHost * host);

### Control the Delayed Flush Timer

These methods can control how long we want to wait for a flush on a given host.

    extern BOOL HTHost_setWriteDelay (HTHost * host, ms_t delay);
    extern ms_t HTHost_writeDelay (HTHost * host);
    extern int HTHost_findWriteDelay(HTHost * host, ms_t lastFlushTime, int buffSize);

It is also possible to explicitly require a flush using the following method

    extern int HTHost_forceFlush(HTHost * host);

You can also set the global value so that all new host objects will inherit this value instead of setting it individually.

    extern BOOL HTHost_setDefaultWriteDelay (ms_t delay);
    extern ms_t HTHost_defaultWriteDelay (void);

### Event Timeouts

Events can be assigned a timeout which causes the event to be triggered if the timeout happens before other action is available on the socket. You can assign a global timeout for all host object using the following methods

    extern int HTHost_eventTimeout (void);
    extern void HTHost_setEventTimeout (int millis);

### Get the Next Net object for Reading and Writing

    extern HTNet * HTHost_firstNet     (HTHost * host);
    extern HTNet * HTHost_getReadNet  (HTHost * host);
    extern HTNet * HTHost_getWriteNet (HTHost * host);

### Automatic Connection Recovery

In case a pipeline is broken then we have to recover it and start again. This is handled automatically by the host object

    extern BOOL HTHost_recoverPipe (HTHost * host);
    extern BOOL HTHost_doRecover (HTHost * host);

### Get input and output Streams for this Host

    extern HTInputStream * HTHost_getInput (HTHost * host, HTTransport * transport,
    				        void * param, int mode);

    extern HTOutputStream * HTHost_getOutput (HTHost * host, HTTransport * tp,
    					  void * param, int mode);

### The Channel Associated with this Host

    extern BOOL HTHost_setChannel (HTHost * host, HTChannel * channel);
    extern HTChannel * HTHost_channel (HTHost * host);

### Read Management

    extern int HTHost_read(HTHost * host, HTNet * net);
    extern BOOL HTHost_setConsumed(HTHost * host, size_t bytes);

## Multi homed Host Management

We also keep track of if a host has multiple IP addresses. If so then it is called a multi-homed host. This is used for two things: finding the fastest host with this name and as a backup if one or more of the hosts are down.

    extern BOOL HTHost_setHome (HTHost * host, int home);
    extern int HTHost_home (HTHost * host);

    extern BOOL HTHost_setRetry (HTHost * host, int retry);
    extern int HTHost_retry (HTHost * host);
    extern BOOL HTHost_decreaseRetry (HTHost * host);

    #endif /* HTHOST_H */