Symbian3/SDK/Source/GUID-AA2A730E-A7C9-5647-AD42-11C3BAF4C38D.dita
author Dominic Pinkman <dominic.pinkman@nokia.com>
Tue, 20 Jul 2010 12:00:49 +0100
changeset 13 48780e181b38
parent 0 89d6a7a84779
permissions -rw-r--r--
Week 28 contribution of SDK documentation content. See release notes for details. Fixes bugs Bug 1897 and Bug 1522.

<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) 2007-2010 Nokia Corporation and/or its subsidiary(-ies) All rights reserved. -->
<!-- This component and the accompanying materials are made available under the terms of the License 
"Eclipse Public License v1.0" which accompanies this distribution, 
and is available at the URL "http://www.eclipse.org/legal/epl-v10.html". -->
<!-- Initial Contributors:
    Nokia Corporation - initial contribution.
Contributors: 
-->
<!DOCTYPE concept
  PUBLIC "-//OASIS//DTD DITA Concept//EN" "concept.dtd">
<concept xml:lang="en" id="GUID-AA2A730E-A7C9-5647-AD42-11C3BAF4C38D"><title>Filters</title><prolog><metadata><keywords/></metadata></prolog><conbody><p>A filter is used to alter a transaction moving to or from the server. Behaviours can be implemented in filters to modify transactions, for example, change headers, add headers, and remove headers. The transaction body can also be changed to terminate or cancel a transaction, either to resubmit a replacement, or perform other operations on the client device using information from transaction headers or body. </p> <p>HTTP filters can be loaded explicitly (by the client) or implicitly. The way the filter is loaded is dependent on the <codeph>default_data</codeph> setting in the <i>IMPLEMENTATION_INFO</i> section of the filter's resource file. The options are: </p> <ul><li id="GUID-1802931A-BEEB-5D0C-A512-AE884DA30D56"><p> <codeph>HTTP/+FILTERNAME</codeph>: The filter is always loaded </p> </li> <li id="GUID-D9096021-077B-53A3-AFB3-D4F72428246D"><p> <codeph>HTTP/FILTERNAME</codeph>: The filter is loaded unless the client removes it </p> </li> <li id="GUID-CFF97C97-8500-551A-80E8-81CE73999E01"><p> <codeph>HTTP/-FILTERNAME</codeph>: The filter is not loaded unless the client adds it </p> </li> </ul> <p>In the preceding options, <codeph>FILTERNAME</codeph> is the name of your filter. </p> <p>An <xref href="GUID-651801A5-5473-3856-9647-46823598C5C1.dita"><apiname>RHTTPSession</apiname></xref> 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 be registered on the session, providing registration details that specify its triggers. </p> <p>Filter triggers include: particular events, the presence of particular headers, or particular status codes. </p> <p>When an event occurs on a transaction, the event traverses the filter queue either from the client to the server, or from the server back to the client. Priority order determines which filters are visited first when events traverse away from the client (that is, events that originate from the client such as <xref href="GUID-518CC5C3-1CE1-316E-9688-93DB12E58EB3.dita#GUID-518CC5C3-1CE1-316E-9688-93DB12E58EB3/GUID-8E201ED0-7AC9-37D4-A5CD-E26C81A72CC1"><apiname>THTTPEvent::ESubmit</apiname></xref>, which is sent when <xref href="GUID-2E673024-239B-3965-8880-C47B7CC24EF6.dita#GUID-2E673024-239B-3965-8880-C47B7CC24EF6/GUID-F3A8B916-5618-319D-89D8-9B7B04A8A2FC"><apiname>RHTTPTransaction::SubmitL()</apiname></xref> is called). Reverse priority order applies to events that traverse back towards the client (that is, those that originate from the server, such as <xref href="GUID-518CC5C3-1CE1-316E-9688-93DB12E58EB3.dita#GUID-518CC5C3-1CE1-316E-9688-93DB12E58EB3/GUID-0056851B-2933-3701-BFE2-E28162F4EE5B"><apiname>THTTPEvent::EResponseComplete</apiname></xref>). </p> <section><title>Adding, removing and querying filters</title> <p>When an <xref href="GUID-651801A5-5473-3856-9647-46823598C5C1.dita"><apiname>RHTTPSession</apiname></xref> 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: </p> <ul><li id="GUID-7315BBF2-709E-5F18-85D5-9597F340DE21"><p>Redirection filter </p> </li> <li id="GUID-6110EFD0-D9B2-5328-9CE5-BD0A8729D87B"><p>Validation filter </p> </li> </ul> <p>The client may add more filters of its own, or remove filters from the pre-installed set. <xref href="GUID-1B7541CC-9B0B-361C-9BF0-64C027DB6745.dita"><apiname>RHTTPFilterCollection</apiname></xref>, for which a handle is obtained using <xref href="GUID-651801A5-5473-3856-9647-46823598C5C1.dita#GUID-651801A5-5473-3856-9647-46823598C5C1/GUID-E1775A83-8494-3B3F-8DFA-57F4FB64F35A"><apiname>RHTTPSession::FilterCollection()</apiname></xref>, provides facilities for adding and deleting filters, and for querying what filters are installed. <xref href="GUID-2495D233-106D-3223-A84B-2C04EEB1E98F.dita"><apiname>TFilterConfigurationIterator</apiname></xref> allows a client to enumerate and query all available filters, and to install and uninstall a selected one. </p> <p>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 when a session is opened. To help the client in determining the condition later, <xref href="GUID-1B7541CC-9B0B-361C-9BF0-64C027DB6745.dita#GUID-1B7541CC-9B0B-361C-9BF0-64C027DB6745/GUID-C9809478-62A4-37EF-B582-9128DDEA4812"><apiname>RHTTPFilterCollection::CanChangeFilters()</apiname></xref> is provided. </p> <p>The collection of currently installed filters can be queried using an iterator. The following sample code demonstrates the use of the iterator: </p> <codeblock id="GUID-548547E4-408F-5F2A-82B4-92CD23ABB007" xml:space="preserve">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&lt;KMaxFilterNameLength&gt; name;
        name.Copy(iSess.StringPool().StringF(regInfo.iName).DesC().Left(KMaxFilterNameLength));
        TBuf&lt;KMaxHeaderNameLength&gt; header;
        header.Copy(iSess.StringPool().StringF(regInfo.iHeader).DesC().Left(KMaxHeaderNameLength));

        Printf(_L("\n%16S | %4d  | %4d   | %16S |   %3d  | %2d"),
        &amp;name, regInfo.iPosition, regInfo.iEvent.iStatus, &amp;header, regInfo.iStatus, regInfo.iHandle);
        ++iter;
        }
    filtColl.Close();
    }</codeblock> </section> <section><title>Authentication filter</title> <p>The authentication filter provides an easy way of supporting basic and digest authentication, as defined in RFC2617. As it needs to get passwords, it is not installed as standard, but only when a <xref href="GUID-4818EAC3-BE59-3438-A864-E4746FE8DDBC.dita"><apiname>MHTTPAuthenticationCallback</apiname></xref> installs it. To use it, you need to implement a subclass of <xref href="GUID-4818EAC3-BE59-3438-A864-E4746FE8DDBC.dita"><apiname>MHTTPAuthenticationCallback</apiname></xref>, and should refer to the documentation of that class for more details. </p> <p>Users of HTTP authentication should be aware of its security limitations. In particular, basic authentication passes passwords in plaintext. <xref href="GUID-4818EAC3-BE59-3438-A864-E4746FE8DDBC.dita#GUID-4818EAC3-BE59-3438-A864-E4746FE8DDBC/GUID-1B96E431-2572-30D2-AEFD-E1460C477A93"><apiname>MHTTPAuthenticationCallback::GetCredentialsL()</apiname></xref> is told the authentication scheme being used, and applications where plaintext passwords would be an issue must consider rejecting challenges using basic authentication. See section 4 of RFC2617 for more details. </p> <p>The authentication filter supports the following: </p> <ul><li id="GUID-B330D7E7-98A5-5185-9DFF-9826FC2B8668"><p>Basic (Base64) authentication </p> </li> <li id="GUID-D882595D-FB59-5895-B8FF-B1F836EA6A39"><p>Digest authentication using the MD5 algorithm and the 'auth' Quality of Protection. </p> </li> <li id="GUID-D17C8599-6052-5F84-B1B1-5991CF864318"><p>The older RFC2069 style digest authentication for backwards compatibility. </p> </li> </ul> <p>The MD5-sess algorithm is not supported as no major servers support it. The 'auth-int' QoP (Quality of Protection) is not supported as it does not add any real benefit. For integrity checking, SSL is preferred. </p> <p>The filter remembers passwords and attempts to use them for subsequent challenges where appropriate. 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. </p> <p>If an authentication challenge is received which the filter cannot understand, or if the <xref href="GUID-4818EAC3-BE59-3438-A864-E4746FE8DDBC.dita"><apiname>MHTTPAuthenticationCallback</apiname></xref> does not supply credentials (returns <codeph>EFalse</codeph>) the filter effectively does nothing, that is, the client will receive a 401 error response in the same way as other error responses. </p> <p>There are two alternative methods of supplying the username and password with a request. Clients that already know the username and password can consider using these methods and getting their <xref href="GUID-4818EAC3-BE59-3438-A864-E4746FE8DDBC.dita#GUID-4818EAC3-BE59-3438-A864-E4746FE8DDBC/GUID-1B96E431-2572-30D2-AEFD-E1460C477A93"><apiname>MHTTPAuthenticationCallback::GetCredentialsL()</apiname></xref> to always return <codeph>EFalse</codeph>. The first method is to supply a URI of the form <codeph>http://&lt;username&gt;:&lt;password&gt;@host/</codeph>. 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 method is to define transaction properties called 'username' and 'password' containing the username and password. </p> <p>The filter is registered at position <xref href="GUID-2E1C08E2-9024-3269-A1BD-C6B901E78841.dita#GUID-2E1C08E2-9024-3269-A1BD-C6B901E78841/GUID-CD1FDCD5-57B8-3888-8753-55A1F9F31466"><apiname>MHTTPFilter::EStatusCodeHandler</apiname></xref> for handling the '401' return code, and at position <codeph>EStatusCodeHandler</codeph> + 1 for handling submit events. </p> </section> <section><title>Redirection filter</title> <p>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: that is, to make a new request for the resource at the location specified by the server using the 'Location' header. </p> <p>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 resubmit a request on the same transaction. This means that all the headers in the original client request are preserved in the new request. </p> <p>If no 'Location' header is found in the server response, the filter sends a <xref href="GUID-704A16D9-577F-3F63-8CE9-97397BE3318C.dita"><apiname>KErrHttpRedirectNoLocationField</apiname></xref> error to the client. The response body can contain further information about the possible location of the resource. </p> <p>In the case of the HTTP 305 'Use Proxy' status code, the transaction is not resubmitted. Instead, a <xref href="GUID-C02EC58E-DE33-3BA8-BB32-4BFCA19F537A.dita"><apiname>KErrHttpRedirectUseProxy</apiname></xref> 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 <xref href="GUID-651801A5-5473-3856-9647-46823598C5C1.dita"><apiname>RHTTPSession</apiname></xref> as described in <xref href="GUID-F9184A82-A467-5022-B02F-2FDF52258618.dita">Session and transaction properties</xref>. </p> <p>HTTP 304 'Not Modified' responses are not handled by the redirection filter as they are used to indicate that the client contains a valid copy of the resource in the cache. </p> <p>It is possible for the client request to be redirected more than once. To prevent the request getting into an endless loop of redirections, successive redirections that the filter will handle is limited to five. </p> </section> <section id="GUID-8DB0C8B3-4ED6-59D6-A796-F4EA2DE5DC5B"><title>Validation filter</title> <p>The Validation filter has three main roles: </p> <ul><li id="GUID-8B074A32-E937-5179-9DC1-3DE0B89A055A"><p>It checks the presence of a request body to ensure that it is consistent with the HTTP method in use. </p> </li> <li id="GUID-79346A52-732A-5D1D-8AB0-3B58A9C932B9"><p>It validates client requests to ensure that they do not include inappropriate header fields. </p> </li> <li id="GUID-71D96A1F-29EB-5D3E-AF2A-5607A9C7DBF6"><p>It provides a simplified result status for transactions. </p> </li> </ul> <p>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 <xref href="GUID-18F87B91-ADD5-36FE-A772-0481294D1C4A.dita"><apiname>KErrHttpRequestHasBody</apiname></xref> error event to the client. </p> <p>If the request headers contain any fields that are defined by RFC 2616 as response header fields, then the error event <xref href="GUID-5B018652-B72C-397F-B508-E9E61683DC82.dita"><apiname>KErrHttpInvalidHeaderInRequest</apiname></xref> is sent to the client. Invalid fields are removed, and the transaction is allowed to continue. </p> <p>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 <xref href="GUID-5B018652-B72C-397F-B508-E9E61683DC82.dita"><apiname>KErrHttpInvalidHeaderInRequest</apiname></xref> is sent to the client, the fields are removed, and the transaction continues. </p> <p>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 <xref href="GUID-6962B618-0A93-36AE-8E79-C970EEA7A845.dita"><apiname>KErrHttpEntityHeaderMissingContentType</apiname></xref> error event is sent to the client. </p> <p>See also <xref href="GUID-F6CF2920-9095-568B-B332-D5F4A05BD23A.dita">Headers</xref> and the header enumerations in HTTP for further information about different header types. </p> <p>When a transaction response is received, the validation filter will determine from the status code if the transaction has been a success or a failure. The two events <xref href="GUID-518CC5C3-1CE1-316E-9688-93DB12E58EB3.dita#GUID-518CC5C3-1CE1-316E-9688-93DB12E58EB3/GUID-619AC671-5679-3036-82C9-DAAF5B892100"><apiname>THTTPEvent::ESucceeded</apiname></xref> and <xref href="GUID-518CC5C3-1CE1-316E-9688-93DB12E58EB3.dita#GUID-518CC5C3-1CE1-316E-9688-93DB12E58EB3/GUID-6BC33448-9CB9-3488-87DD-EFB0C5435044"><apiname>THTTPEvent::EFailed</apiname></xref> are used to inform the client of this. The client can assume that no further events for that transaction will arrive after it has received either of these two events. </p> </section> <section><title>Configuring and installing filters</title> <p>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. </p> <p>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 <xref href="GUID-4818EAC3-BE59-3438-A864-E4746FE8DDBC.dita"><apiname>MHTTPAuthenticationCallback</apiname></xref> class, and at the time it opens an HTTP session must specify the object that implements the callback to configure the filter. </p> <p>From <filepath>httpexampleclient.h</filepath>: </p> <codeblock id="GUID-45462F17-0A0F-57A2-ACBC-F96FBAAE67D5" xml:space="preserve">class CHttpClient : public CBase, public MHTTPDataSupplier, 
                    public MHTTPAuthenticationCallback
    {
public:
    ...
    // methods inherited from MHTTPAuthenticationCallback
    virtual TBool GetCredentialsL(const TUriC8&amp; aURI, RString aRealm, 
                                 RStringF aAuthenticationType,
                                 RString&amp; aUsername, 
                                 RString&amp; aPassword);
    ...</codeblock> <p>From <filepath>httpexampleclient.cpp</filepath>: </p> <codeblock id="GUID-8B67CD14-96BC-514E-85E8-A762948F4388" xml:space="preserve">void CHttpClient::ConstructL()
    {
    ...

    // Open the RHTTPSession and install this class as the callback for authentication requests
    iSess.OpenL();
    InstallAuthenticationL(iSess);
    ...
    }</codeblock> <p>In this case, <filepath>MHTTPAuthenticationCallback::InstallAuthenticationL()</filepath> is already implemented in the library. It creates and installs the filter on the client's behalf: </p> <codeblock id="GUID-EC6D0967-F3F3-54C0-AAD8-B25700B84AB6" xml:space="preserve">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);
    }</codeblock> <p>However in other cases, the client may have to construct the filter itself and configure it, before installing it on the session, for example: </p> <codeblock id="GUID-79FDC884-B74C-5F22-BD0D-31CD6B1B4050" xml:space="preserve">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()));
    ...
}</codeblock> </section> <section><title>Writing a filter</title> <p>This section provides an overview of writing filters. Refer to the functions mentioned for more details. </p> <p>Filters need to derive from the <xref href="GUID-2E1C08E2-9024-3269-A1BD-C6B901E78841.dita"><apiname>MHTTPFilter</apiname></xref> class. Normally, the constructor or <codeph>NewL()</codeph> of a filter would take a session as a parameter, and would then register itself by calling <xref href="GUID-1B7541CC-9B0B-361C-9BF0-64C027DB6745.dita#GUID-1B7541CC-9B0B-361C-9BF0-64C027DB6745/GUID-B4659DF8-C5DD-30DC-AFF8-D4F22D540386"><apiname>RHTTPFilterCollection::AddFilterL()</apiname></xref>. </p> <p>Filters can often delete themselves automatically. If a filter only registers itself once, it can delete itself simply by overriding <codeph>MHFUnload()</codeph> and <codeph>delete this</codeph> in it. If you register several times, it is probably easiest to overload both <xref href="GUID-2E1C08E2-9024-3269-A1BD-C6B901E78841.dita#GUID-2E1C08E2-9024-3269-A1BD-C6B901E78841/GUID-CFFDC252-CC6F-30F3-87A6-5F1398D5C3CD"><apiname>MHTTPFilter::MHFLoad()</apiname></xref> and <xref href="GUID-2E1C08E2-9024-3269-A1BD-C6B901E78841.dita#GUID-2E1C08E2-9024-3269-A1BD-C6B901E78841/GUID-33452FD8-4AB4-32F8-BD98-0FC9B7E46717"><apiname>MHTTPFilter::MHFUnload()</apiname></xref>, increment a reference count in <codeph>MHFLoad()</codeph>, decrement it in <codeph>MHTTPFilter::MHFUnload()</codeph> and when it reaches zero, <codeph>delete this</codeph>. </p> <p>A filter is notified of events through <xref href="GUID-57216A14-97B8-324C-BB00-63CA880CC779.dita#GUID-57216A14-97B8-324C-BB00-63CA880CC779/GUID-50C6321D-AFEF-3B0F-84DD-5AE0E70274C4"><apiname>MHTTPFilterBase::MHFRunL()</apiname></xref>, similar to client notification. If the <codeph>MHFRunL()</codeph> implementation leaves, the filter must handle the error in <xref href="GUID-57216A14-97B8-324C-BB00-63CA880CC779.dita#GUID-57216A14-97B8-324C-BB00-63CA880CC779/GUID-4ED494E3-6499-3C89-A35A-A672A24FA849"><apiname>MHTTPFilterBase::MHFRunError()</apiname></xref>. There is a potential problem there, in that you may well want to tell the client that something has gone wrong by <xref href="GUID-2E673024-239B-3965-8880-C47B7CC24EF6.dita#GUID-2E673024-239B-3965-8880-C47B7CC24EF6/GUID-7F9EAF08-316A-3641-B145-10534D72A9FB"><apiname>RHTTPTransaction::SendEventL()</apiname></xref>, which can itself leave. If it does leave, you may be forced to call <xref href="GUID-2E673024-239B-3965-8880-C47B7CC24EF6.dita#GUID-2E673024-239B-3965-8880-C47B7CC24EF6/GUID-BAED8ED4-CFC0-3CF4-88E1-784F7C68C219"><apiname>RHTTPTransaction::Fail()</apiname></xref>, which cancels the transaction and sends an <xref href="GUID-518CC5C3-1CE1-316E-9688-93DB12E58EB3.dita#GUID-518CC5C3-1CE1-316E-9688-93DB12E58EB3/GUID-623B9859-6DEC-313F-8D07-0B1F3C93E1BD"><apiname>THTTPEvent::EUnrecoverableError</apiname></xref> message outwards. </p> <p>It is important to note that a filter object is per-session, and so might be shared by several transactions. This means that if you have a per-transaction state that you need to store, it must be stored in the transaction's property set. Do <i>not</i> store any per-transaction information in the filter object. </p> </section> </conbody></concept>