Handling request body data

HTTP methods POST and PUT both require a request body to be specified by the client before a transaction is submitted. Also required in the request is a Content-Type header, to inform the HTTP server of the body data's media (MIME) type.

If the body or Content-Type header are absent, an error will be returned as an event. See Validation filter for details of errors that can be sent.

Specifying a request data supplier

The following code fragment from HTTPEXAMPLECLIENT demonstrates a request body being set:

void CHttpClient::InvokeHttpMethodL(const TDesC8& aUri, RStringF aMethod)
{
...
iTrans = iSess.OpenTransactionL(uri, *iTransObs, aMethod);
RHTTPHeaders hdr = iTrans.Request().GetHeaderCollection();
...
// Add headers and body data for methods that use request bodies
    if (iHasARequestBody)
        {
        // Content type header
        TBuf8<KMaxContentTypeSize> contTypeBuf;
        contTypeBuf.Copy(iReqBodyContentType);
        RStringF contTypeStr = iSess.StringPool().OpenFStringL(contTypeBuf);
        CleanupClosePushL(contTypeStr);
    THTTPHdrVal contType(contTypeStr);
        hdr.SetFieldL(iSess.StringPool().StringF(HTTP::EContentType,RHTTPSession::GetTable()), contType);

        MHTTPDataSupplier* dataSupplier = this;
        if (iManualPost)
            dataSupplier = iFormEncoder;
        iTrans.Request().SetBody(*dataSupplier);
        CleanupStack::PopAndDestroy(&contTypeStr);
        }

    // submit the transaction
    iTrans.SubmitL();

The example uses two alternative alterative implementations of MHTTPDataSupplier: the first method, not described here, is the CHTTPFormEncoder class, a utility that encodes a series of field name-value pairs into the URI-encoded form, as used by Web/WAP browsers to send data the user has entered into text boxes.

The second method sets the request body data supplier to be the CHttpClient object itself. From the header file httpexampleclient.h:

class CHttpClient : public CBase, public MHTTPDataSupplier, ...
    {
    ...
public: // methods inherited from MHTTPDataSupplier
    virtual TBool GetNextDataPart(TPtrC8& aDataPart);
    virtual void ReleaseData();
    virtual TInt OverallDataSize();
    ...
    };

The client must implement the three pure-virtual methods of MHTTPDataSupplier to allow HTTP to obtain the body data it needs as it assembles an HTTP request.

  • GetNextDataPart(): when this method is invoked, the client must set the aDataChunk parameter to point at an 8-bit data buffer that is populated with the next piece of body data to be transmitted. If this buffer holds the last part of the request body, the method must return ETrue.

  • ReleaseData(): when this method is invoked, the client can free the data buffer it last returned from GetDataChunk(), and can start to assemble the next buffer for transmission.

  • OverallDataSize(): when this method is invoked, the client should return the size of the whole request body data, if it is known. If unknown, it should return KErrNotFound.

There is no need for the client to set a 'Content-Length' header. The API does this automatically, based on what it gets from calling OverallDataSize().

Signalling new body data

After ReleaseData() has been invoked, the client must prepare the next buffer of data for transmission. Since the API cannot predict when each buffer will be ready, it will pause the request transmission until an event is sent by the client to notify the availability of new data. This is done using the RHTTPTransaction::NotifyNewRequestBodyPartL() method.

void CHttpClient::ReleaseData()
    {
    // Clear out the submit buffer
    TPtr8 buff = iReqBodySubmitBuffer->Des();
    buff.Zero();
    // Notify HTTP of more data available immediately, since it's being read from file
    TRAPD(err, iTrans.NotifyNewRequestBodyPartL());
    if (err != KErrNone)
        User::Panic(KHttpExampleClientPanic, KCouldntNotifyBodyDataPart);
    }

When THTTPEvent::ENotifyNewRequestBodyPart is processed by the API, it resumes transmission of body data and calls the client's GetNextDataPart() method again.

Streaming body data

If a client application is using HTTP POST to stream data to a server, then it is unlikely to know at the start of the transaction how much data is to be sent.

As described previously, the client's OverallDataSize() method should return KErrNotFound to indicate this. The result is that the API will automatically switch over to use the 'chunked' transfer encoding. The API automatically generates the 'Transfer-Encoding: chunked' header and will not send a Content-Length header.