Transmitting and Receiving Data


In this section we explain some strategies that we hope will make your life as a developer easier when you handle large or complex data that you have to send via the HTTP client.

Handling Query and Form Parameters

The most common way of sending data through HTTP consists of collections of name /value pairs, passed either as query parameters (part of the URL) or as form parameters (part of the request body).

To show how to use query or form parameters with the HTTP client, we present a small ATL Server request handler without SRF file support that contains the code in Listing 25-1 in the ValidateAndExchange function.

Listing 25.1: ValidateAndExchange Implementation That Iterates the Request Parameters
start example
 m_HttpResponse << "<html><body>";  POSITION pos;  LPCSTR    szName, szValue;  m_HttpResponse << "<H1>Form Variables</H1><br>";  pos = m_HttpRequest.GetFirstFormVar( &szName, &szValue);  while( pos != NULL )  {      m_HttpResponse << szName << "=" << szValue << "<br>";      pos = m_HttpRequest.GetNextFormVar(pos, &szName, &szValue);  }  m_HttpResponse << "<H1>Query Params </H1><br>";  pos = m_HttpRequest.GetFirstQueryParam( &szName, &szValue);  while( pos != NULL )  {      m_HttpResponse << szName << "=" << szValue << "<br>";      pos = m_HttpRequest.GetNextQueryParam(pos, &szName, &szValue);  }  m_HttpResponse << "</body></html>"; 
end example
 

As you can see, the code in Listing 25-1 iterates through both the query and the form parameters and displays them in a user -friendly form.

start sidebar
How Are Parameters Sent in HTTP?

Let s assume that you want to send as parameters values for Name and Age . You can send these values as part of the URL in a request, as follows :

 GET /ClientParams/ClientParams.dll?Name=UserName&Value=25 HTTP/1.1 

Or you can send them as part of the request body, generating an HTTP request like this:

 POST /ClientParams/ClientParams.dll HTTP/1.1  Content-Length: 15  Content-Type: application/x-www-form-urlencoded  Name=foo&Age=25 
end sidebar
 

The format of the name/value collection containing the parameters is the same, both in the URL and in the request body. The differences are in the other headers of the HTTP request. There are also some other differences related to the HTTP server implementation. For instance, usually a URL must be shorter than a certain limit accepted by a Web server. The length of the request body doesn t have to comply with the same restriction.

The client application (in this case, the ATL Server HTTP client application) has to build the string that contains the name/value pairs in the following form:

 <ParameterName>=<ParameterValue> 

followed optionally by other pairs separated by an ampersand (&).

If special characters occur inside the name or the value (the HTTP specification provides a list of all the special characters ”here, we only deal with the = and & characters, which would affect sever-side parsing of the pairs), then these special characters have to be encoded to their ASCII code. Therefore, the = character inside a name or a value should be represented as %3D and the & character should be encoded as %26 . The client application should take care of the encoding rules when creating the parameter s text representation.

Let s see now what the code for sending the Name and Age parameters looks like in the ATL Server HTTP client.

Note  

In all the code snippets presented in this section, we assume three variables declared as follows:

 CAtlNavigateData    navData;      CAtlHttpClient      client;      CUrl                url; 

For sending the parameters as part of the URL, one simple implementation can provide directly the full URL containing the parameters:

 url.CrackUrl(       "http://localhost/ClientParams/ClientParams.dll?Name=UserName&Age=25");      client.Navigate( &url, &navData ); 

Alternately, you can specify the parameters part of the URL separated from the main part, which is convenient when the parameters are generated dynamically:

 url.CrackUrl("http://localhost/ClientParams/ClientParams.dll")      LPCSTR szParamData = "?Name=UserName&Age=25";  url.SetExtraInfo(szParamData);  client.Navigate(&url, &navData); 

The code for sending the same content inside the request body (as form parameters) might look like this:

 url.CrackUrl("http://localhost/ClientParams/ClientParams.dll")      LPCSTR szPOSTData = "Name=UserName&Age=25";      LPCTSTR szContentType = _T("application/x-www-form-urlencoded");  navData.SetMethod(ATL_HTTP_METHOD_POST);  navData.SetPostData((BYTE*)szPOSTData, _tcslen(szPOSTData), szContentType);  client.Navigate(&url, &navData); 

With the preceding code snippets, the problem of sending parameters is reduced to generating the string containing them encoded according to the HTTP standard (name and value separated by = and couples separated by & ).

The CHttpRequestParams class in atlisapi.h provides the implementation for a collection of name/value pairs, plus methods for both rendering the HTTP-encoded string for these pairs and parsing such a string. This class is used by the request handlers to parse the request parameters, and it can be used on the client side in generating the string containing the request parameters.

So, the new code for adding Name and Age might look Listing 25-2.

Listing 25.2: Using CHttpRequestParams for Generating a Collection of HTTP POST Parameters
start example
 CHttpRequestParams    requestParams;  requestParams.SetAt("Name", "UserName");  requestParams.SetAt("Age", "25");  char    *szPOSTData = NULL;  DWORD    dwSize = 0;  // Get the number of actual characters  requestParams.Render(szPOSTData, &dwSize);  szPOSTData = new char[dwSize+1]; // leave room for trailing 
 CHttpRequestParams requestParams; requestParams.SetAt("Name", "UserName"); requestParams.SetAt("Age", "25"); char *szPOSTData = NULL; DWORD dwSize = 0; // Get the number of actual characters requestParams.Render(szPOSTData, &dwSize); szPOSTData = new char[dwSize+1]; // leave room for trailing \0 dwSize += 1; // mark the room for trailing \0 requestParams.Render(szPOSTData, &dwSize); // render the string navData.SetMethod(ATL_HTTP_METHOD_POST); navData.SetPostData((BYTE*)szPOSTData, _tcslen(szPOSTData), szContentType); client.Navigate(&url, &navData); 
dwSize += 1; // mark the room for trailing
 CHttpRequestParams requestParams; requestParams.SetAt("Name", "UserName"); requestParams.SetAt("Age", "25"); char *szPOSTData = NULL; DWORD dwSize = 0; // Get the number of actual characters requestParams.Render(szPOSTData, &dwSize); szPOSTData = new char[dwSize+1]; // leave room for trailing \0 dwSize += 1; // mark the room for trailing \0 requestParams.Render(szPOSTData, &dwSize); // render the string navData.SetMethod(ATL_HTTP_METHOD_POST); navData.SetPostData((BYTE*)szPOSTData, _tcslen(szPOSTData), szContentType); client.Navigate(&url, &navData); 
requestParams.Render(szPOSTData, &dwSize); // render the string navData.SetMethod(ATL_HTTP_METHOD_POST); navData.SetPostData((BYTE*)szPOSTData, _tcslen(szPOSTData), szContentType); client.Navigate(&url, &navData);
end example
 

The CHttpRequestParams class has a limitation: It isn t able to render the ? separator required for sending parameters as part of the URL. So, to send the parameters as part of the URL s extra information (the HTTP GET method), replace the last part of the code in Listing 25-2 with the code in Listing 25-3.

Listing 25.3: Using CHttpRequestParams for Generating a Collection of HTTP GET Parameters
start example
 // Get the number of actual characters  requestParams.Render(szPOSTData, &dwSize);  szPOSTData = new char[dwSize+2];   // leave room for ? prefix trailing 
 // Get the number of actual characters requestParams.Render(szPOSTData, &dwSize); szPOSTData = new char[dwSize+2]; // leave room for ? prefix trailing \0 and ? szPOSTData[0] = '?'; // add the URL parameters separator dwSize += 1; // mark the room for trailing \0 requestParams.Render(szPOSTData + 1, &dwSize); // render the string url.SetExtraInfo(szPOSTData); 
and ? szPOSTData[0] = '?'; // add the URL parameters separator dwSize += 1; // mark the room for trailing
 // Get the number of actual characters requestParams.Render(szPOSTData, &dwSize); szPOSTData = new char[dwSize+2]; // leave room for ? prefix trailing \0 and ? szPOSTData[0] = '?'; // add the URL parameters separator dwSize += 1; // mark the room for trailing \0 requestParams.Render(szPOSTData + 1, &dwSize); // render the string url.SetExtraInfo(szPOSTData); 
requestParams.Render(szPOSTData + 1, &dwSize); // render the string url.SetExtraInfo(szPOSTData);
end example
 
Note  

By default, CHttpRequestParams is implemented as a hash table ( CAtlMap , mapping strings to strings). In this implementation, assigning for the second time a value to the same parameter will override the initial value. However, you can use it for multiple parameters with the same name. To do so, you have to define ATL_HTTP_PARAM_MULTIMAP before you include atlisapi.h. When you define ATL_HTTP_PARAM_MULTIMAP , CHttpRequestParams is no longer a CAtlMap derivative; rather, it s a CRBMultiMap derivative.

Sending and Receiving Large Payloads

In this section we discuss methods exposed by CAtlNavigateData for sending or receiving large payloads. ATL Server provides multiple ways of customizing the transfer of such content. First, we briefly look at how you can customize the read/write buffer size and the implications this might have on the behavior of the client application.

The CAtlNavigateData class, an instance of which you can use for controlling a Navigate invocation, allows setting the size of the buffers used for socket operations. You can do this by invoking the following methods:

 DWORD SetReadBlockSize(DWORD dwBlockSize ) throw( );  DWORD SetSendBlockSize(DWORD dwBlockSize ) throw( ); 

The default size of the socket buffers is ATL_HTTP_DEFAULT_BLOCK_SIZE (4096), which proved an appropriate number for the most common scenarios.

Now, it s possible that the data becomes available faster than the socket code can process it. In this case, the bottleneck for the application performance is the socket operation. If this is the case, increasing the size of the buffer used in the socket operation will improve the performance of the application. You can obtain the current values for the buffer size by invoking the similar Get methods.

You can use the block size functions only when the CAtlNavigateData object has the ATL_HTTP_FLAG_SEND_BLOCKS flag set (which is on by default).

Reading or sending a large amount of content takes an accordingly long time. Therefore, it s important to have some way of recording the progress of the operation, especially in applications that provide a user interface. Using a common practice for this kind of scenario, CAtlNavigateData allows the usage of callback functions for recording the progress of reading and writing requests . These two callbacks (one for reading and one for writing) have to be implemented by the client application and they should have the following prototype:

 typedef bool (WINAPI *PFNATLSTATUSCALLBACK)( DWORD dwBytesSent,                             DWORD_PTR dwParam); 

Once you ve implemented the callbacks, you can attach them to the CAtlNavigateData object with the following code:

 PFNATLSTATUSCALLBACK SetReadStatusCallback(     PFNATLSTATUSCALLBACK pfn,     DWORD_PTR dwData ) throw( );  PFNATLSTATUSCALLBACK SetSendStatusCallback(     PFNATLSTATUSCALLBACK pfn,     DWORD_PTR dwData ) throw( ); 

Both functions will return the previously used callback function ( NULL , by default). The dwData parameter will be passed to the callback functions as the dwParam parameter. This parameter is intended to serve as a cookie when using the same callback function with multiple instances of the CAtlHttpClient class (by identifying the instance that generated a specific progress notification). It s of type DWORD_PTR , a type that allows you to safely store either a numeric value or a pointer, both on 32-bit and 64-bit systems.

Alternately, you may decide to use chunked-transfer encoding of the request content, which allows you to deal with blocks of variable size. For this, you should set the ATL_HTTP_FLAG_SEND_CALLBACK flag on the CAtlNavigateData object. This flag and the one for using blocks are mutually exclusive. Also, you should use the CAtlHttpClient class s NavigateChunked method.

With chunked content, the application can successively send blocks of different sizes. This proves useful when you don t know the total size of the request before you start the request. The code for socket operations communicates with the rest of the application by using a callback mechanism for passing the blocks to and from the socket. To do this, you ll have to implement callback methods defined as follows:

 typedef bool (WINAPI *PFNATLCHUNKEDCB)(BYTE** ppData, DWORD *pdwSize,   DWORD_PTR dwParam); 

Once you ve implemented a chunked callback, you can attach it to a CAtlNavigateData object by calling

 PFNATLCHUNKEDCB SetChunkCallback(     PFNATLCHUNKEDCB pfn,     DWORD_PTR dwParam ) throw( ); 

The MSDN documentation that comes with Visual Studio .NET mistakenly doesn t include the dwParam parameter in the definition of the PFNATLCHUNKEDCB prototype. It s actually a required parameter of the callback function, and it has the same role there as in the callbacks for progress notifications: It allows you to use the same callback function for multiple instances of the CAtlHttpClient class.

In the next section we cover security-related issues and describe the authentication objects that you can use within ATL Server HTTP client applications. We also offer suggestions for further transport security enhancements.




ATL Server. High Performance C++ on. NET
Observing the User Experience: A Practitioners Guide to User Research
ISBN: B006Z372QQ
EAN: 2147483647
Year: 2002
Pages: 181

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net