![Symbian Developer Library](../../../../a_stock/images/mainheading.gif)
![]() |
![]() |
|
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.
HTTP filters can be loaded explicitly (by the client) or implicitly. The
way the filter is loaded is dependent on the default_data
setting
in the IMPLEMENTATION_INFO section of the filter's resource file.
The options are:
HTTP/+FILTERNAME
: The filter is always loaded
HTTP/FILTERNAME
: The filter is loaded unless the client
removes it
HTTP/-FILTERNAME
: The filter is not loaded unless the
client adds it
In the preceding options, FILTERNAME
is the name of your
filter.
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 be
registered on the session, providing registration details that specify its
triggers.
Filter triggers 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 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 THTTPEvent::ESubmit
, which is sent when
RHTTPTransaction::SubmitL()
is called). Reverse priority
order applies to events that traverse back towards the client (that is, those
that originate from the server, such as
THTTPEvent::EResponseComplete
).
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:
Redirection filter
Validation filter
The client may add more 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
when a session is opened. To help the client in determining the condition
later, 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;
}
filtColl.Close();
}
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
MHTTPAuthenticationCallback
installs it. To use it, you
need to implement a subclass of
MHTTPAuthenticationCallback
, and should refer to the
documentation of that class for more details.
Users of HTTP authentication should be aware of its security
limitations. In particular, basic authentication passes passwords in plaintext.
MHTTPAuthenticationCallback::GetCredentialsL()
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.
The authentication filter supports the following:
Basic (Base64) authentication
Digest authentication using the MD5 algorithm and the 'auth' Quality of Protection.
The older RFC2069 style digest authentication for backwards compatibility.
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.
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.
If an authentication challenge is received which the filter cannot
understand, or if the MHTTPAuthenticationCallback
does not
supply credentials (returns EFalse
) the filter effectively does
nothing, that is, the client will receive a 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
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 method 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.
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.
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.
If no 'Location' header is found in the server response, the filter
sends a KErrHttpRedirectNoLocationField
error to the
client. The response body can 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 as they are used to indicate that the client contains a valid copy of the resource in the cache.
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.
The Validation filter has three main roles:
It checks the presence of a request body to ensure that it is consistent with the HTTP method in use.
It validates client requests to ensure that they do not include inappropriate header fields.
It provides a simplified result status for transactions.
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, then the error event
KErrHttpInvalidHeaderInRequest
is sent to the client.
Invalid 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
KErrHttpEntityHeaderMissingContentType
error event 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 if the transaction has been a success or a
failure. The two events THTTPEvent::ESucceeded
and
THTTPEvent::EFailed
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.
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()
is already
implemented in the library. It creates and installs 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, for example:
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()));
...
}
This section provides 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 is 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()
, similar to 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 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 not store any per-transaction information in the filter object.