Symbian
Symbian OS Library

SYMBIAN OS V9.3

[Index] [Spacer] [Previous] [Next]



Filters

A filter is best thought of as manipulating a transaction on its way either to or from the server. Behaviours can be implemented in filters that modify the transaction, e.g. change headers, add or remove headers, transform the body; that terminate or cancel a transaction, perhaps resubmitting a replacement; or that perform other operations on the client device using information obtained from transaction headers or body.

An RHTTPSession contains a queue that can hold zero or more filters, which are arranged in a priority order. The filter objects are shared amongst all transactions. To join the filter queue, a filter must register itself on the session, providing registration details that specify its triggers.

Filter triggers may include: particular events, the presence of particular headers or particular status codes.

When an event occurs on a transaction, the event traverses the filter queue in one of two directions: from client to server, or from server back to the client. Priority order determines which filters are visited first when events traverse away from the client (i.e. events that originate from the client such as THTTPEvent::ESubmit, which is sent when RHTTPTransaction::SubmitL() is called); reverse priority order applies to events that traverse back towards the client (i.e. those that originate from the server, such as THTTPEvent::EResponseComplete).


Adding, removing and querying filters

When an RHTTPSession is opened, a standard set of filters is pre-installed. The client does not need to do anything further if these filters are acceptable. The standard set includes:

The client may add further filters of its own, or remove filters from the pre-installed set. RHTTPFilterCollection, for which a handle is obtained using RHTTPSession::FilterCollection(), provides facilities for adding and deleting filters, and for querying what filters are installed. TFilterConfigurationIterator allows a client to enumerate and query all available filters, and to install and uninstall a selected one.

Filters may only be added to, or removed from, a session when no transactions are opened on that session. This is easy to determine immediately after the session itself is opened; to aid the client in determining the condition later on, RHTTPFilterCollection::CanChangeFilters() is provided.

The collection of currently-installed filters can be queried using an iterator. The following sample code demonstrates the use of the iterator:

void CHttpClient::ShowFilters()
    {
    RHTTPFilterCollection filtColl = iSess.FilterCollection();
    THTTPFilterIterator iter = filtColl.Query();

    THTTPFilterRegistration regInfo;
    iter.First();
    TInt lines = 0;
    while (!iter.AtEnd())
        {
        // Get next filter registration info
        regInfo = iter();

        TBuf<KMaxFilterNameLength> name;
        name.Copy(iSess.StringPool().StringF(regInfo.iName).DesC().Left(KMaxFilterNameLength));
        TBuf<KMaxHeaderNameLength> header;
        header.Copy(iSess.StringPool().StringF(regInfo.iHeader).DesC().Left(KMaxHeaderNameLength));

        Printf(_L("\n%16S | %4d  | %4d   | %16S |   %3d  | %2d"),
        &name, regInfo.iPosition, regInfo.iEvent.iStatus, &header, regInfo.iStatus, regInfo.iHandle);
        ++iter;
        }
    }

[Top]


Authentication filter

The authentication filter provides an easy way of supporting basic and digest authentication as defined in RFC2617. As it needs a way of getting hold of passwords, it is not installed as standard, but only when a MHTTPAuthenticationCallback installs it. To use it, you will need to implement a subclass of MHTTPAuthenticationCallback, and should see the documentation for that class for further details.

Anyone using HTTP authentication should be aware of its security limitations. In particular, basic authentication essentialy involves passing passwords around in plaintext. MHTTPAuthenticationCallback::GetCredentialsL() is told the authentication scheme being used, and applications where plaintext passwords would be a problem should consider rejecting challenges using basic authentication. See section 4 of RFC2617 for more details.

The authentication filter supports the following:

The MD5-sess algorithm is not supported as no major servers seem to support it. The 'auth-int' qop is not supported as it does not seem to add any real benefit: for integrity checking, ssl would be preferred.

The filter remembers passwords and will attempt to use them for subsequent challenges where they appear to be apropriate. It will forget them if they turn out to be wrong. Currently there is no facility to persist the passwords. For basic authentication, an attempt is made to supply the username and password with the first request if the URI suggests that a previously stored username and password are applicable. This is not done for digest, as that introduces extra complications into the digest algorithm.

If an authentication challenge is received which the filter can't understand or if the MHTTPAuthenticationCallback does not supply credentials (returns EFalse) the filter effectively does nothing, meaning that the client will receive the 401 error response in the same way as other error responses.

There are two alternative methods of supplying the username and password with a request. Clients that already know the username and password might can consider using these methods and getting their MHTTPAuthenticationCallback::GetCredentialsL() to always return EFalse. The first method is to supply a URI of the form http://<username>:<password>@host/. In this case the username and the password are removed from the URI when it is submitted, but will be used for any subsequent authentication challenge. The other way is to define transaction properties called 'username' and 'password' containing the username and password.

The filter is registered at position MHTTPFilter::EStatusCodeHandler for handling the '401' return code, and at position EStatusCodeHandler + 1 for handling submit events.

[Top]


Redirection filter

The Redirection filter handles 300-series status codes from HTTP servers. These are used to tell the client of the correct location of a resource that has moved. Most clients will want this situation to be handled transparently: i.e. to make a new request for the resource at the location specified by the server using the 'Location' header.

When an HTTP response is received that includes the status codes 300, 301, 302, 303 or 307, the Redirection filter cancels the current transaction, and uses the URI from the 'Location' response header to make a resubmitted request on the same transaction. This means that all the headers in the original client request are preserved in the new request.

If no 'Location' header was found in the server response, the filter will send a KErrHttpRedirectNoLocationField error to the client. The response body may contain further information about the possible location of the resource.

In the case of the HTTP 305 'Use Proxy' status code, the transaction is not resubmitted. Instead, a KErrHttpRedirectUseProxy error is sent to the client. The 'Location' header will contain the address of a proxy to which the client must send the request for that URL. In practice, this means the client should modify the properties of their current RHTTPSession as described in Session and transaction properties.

HTTP 304 'Not Modified' responses are not handled by the redirection filter since they are used to indicate that the client contains a valid copy of the resource in cache.

It is possible for the client request to be redirected more than once. To prevent the request becoming stuck in an endless loop of redirections, the number of successive redirections that the filter will handle is limited to 5.

[Top]


Validation filter

The Validation filter has three main roles:

Request bodies are only allowed for HTTP methods POST and PUT. If GET, HEAD or TRACE requests contain a body the filter will cancel the transaction and send a KErrHttpRequestHasBody error event to the client.

If the request headers contain any fields that are defined by RFC 2616 as response header fields, the error event KErrHttpInvalidHeaderInRequest is sent to the client. Offending fields are removed, and the transaction is allowed to continue.

If the request does not have a body, and the request headers contain any fields that are defined by RFC 2616 as entity header fields, then KErrHttpInvalidHeaderInRequest is sent to the client, the fields are removed, and the transaction continues.

If the request has a body, and the 'Content-Type' header is not present in the request headers, then the transaction will be cancelled and the error event KErrHttpEntityHeaderMissingContentType is sent to the client.

See also Headers and the header enumerations in HTTP for further information about different header types.

When a transaction response is received, the validation filter will determine from the status code whether the transaction has ultimately been a success or a failure. The two events THTTPEvent::ESucceeded and THTTPEvent::EFailed are used to inform the client of this. The client may assume that no further events for that transaction will arrive once it has received either of these two.

[Top]


Configuring and installing filters

Apart from the standard set of filters, the client must manually install any other filter it wishes to use. Some filters may need to be configured before they can be installed; this should be done when the filter is instantiated.

An example of where filter configuration is needed is the authentication filter, which requires a callback to gather user credentials from the client. The client must implement the MHTTPAuthenticationCallback class, and at the time it opens an HTTP session must specify the object that implements the callback to configure the filter.

From httpexampleclient.h:

class CHttpClient : public CBase, public MHTTPDataSupplier, public MHTTPAuthenticationCallback
    {
public:
    ...
    // methods inherited from MHTTPAuthenticationCallback
    virtual TBool GetCredentialsL(const TUriC8& aURI, RString aRealm, 
    RStringF aAuthenticationType,
 RString& aUsername, 
    RString& aPassword);
    ...

From httpexampleclient.cpp:

void CHttpClient::ConstructL()
    {
    ...

    // Open the RHTTPSession and install this class as the callback for authentication requests
    iSess.OpenL();
    InstallAuthenticationL(iSess);
    ...
    }

In this case, MHTTPAuthenticationCallback::InstallAuthenticationL() already is implemented in the library. It does the job of creating and installing the filter on the client's behalf:

void MHTTPAuthenticationCallback::InstallAuthenticationL(RHTTPSession aSession)
    {
    // Create an authentication filter. This will install itself, and
    // will delete itself when uninstalled, so we don't need to keep
    // track of it at all.
    CAuthenticationFilter::NewL(*this, aSession);
    }

However in other cases, the client may have to construct the filter itself, and configure it, before installing it on the session, e.g.:

void CAuthenticationFilter::ConstructL(RHTTPSession aSession)
    {
    ...
    // Register for WWW-Authenticate headers and 401 status codes
    aSession.FilterCollection().AddFilterL(*this, 
        THTTPEvent::EGotResponseHeaders, 
        iStringPool.StringF(HTTP::EWWWAuthenticate,RHTTPSession::GetTable()), 
        401, 
        EStatusCodeHandler, 
        iStringPool.StringF(HTTP::EAuthentication,RHTTPSession::GetTable()));
    ...
}

[Top]


Writing a filter

This section provide an overview of writing filters. Refer to the functions mentioned for more details.

Filters need to derive from the MHTTPFilter class. Normally, the constructor or NewL() of a filter would take a session as a parameter, and would then register itself by calling RHTTPFilterCollection::AddFilterL().

Filters can often delete themselves automatically. If a filter only registers itself once, it can delete itself simply by overriding MHFUnload() and delete this in it. If you register several times, it's probably easiest to overload both MHTTPFilter::MHFLoad() and MHTTPFilter::MHFUnload(), increment a reference count in MHFLoad(), decrement it in MHTTPFilter::MHFUnload() and when it reaches zero, delete this.

A filter is notified of events through MHTTPFilterBase::MHFRunL(), just like client notification. If the MHFRunL() implementation leaves, the filter must handle the error in MHTTPFilterBase::MHFRunError(). There is a potential problem there, in that you may well want to tell the client that something has gone wrong by RHTTPTransaction::SendEventL(), which can itself leave. If it does leave, you may be forced to call RHTTPTransaction::Fail(), which cancels the transaction and sends an THTTPEvent::EUnrecoverableError message outwards.

It's important to note that a filter object is per-session, and so might be shared by several transactions. This means that if you have per-transaction state that you need to store, it must be stored in the transaction's property set. Do not store any per-transaction information in the filter object.