< Asynchronous HTTP & Misc. | Main | C# Asynchronous Http Get Request Program Example >


 

Chapter 10 part 9:

HTTP with .NET

 

 

What do we have in this chapter 10 Part 9?

  1. C++ Asynchronous Http Get Request Program Example

 

C++ Asynchronous Http Get Request Program Example

 

Create a new CLR console application project and you might want to use AsyncHttpGetRequestCP as the solution name and project name as shown in the following Figure.

 

C++ Asynchronous Http Get Request Program Example - creating a new CLR console application project in Visual Studio 2008 IDE

 

 

 

Add/edit the following using directives. You may discard the comments.

 

// This sample illustrates the asynchronous BeginGetResponseStream() method of the HttpWebRequest

// class. This sample posts an asynchronous GET request for the given URI with a timeout value.

// When the delegate is invoked, the response stream is retrieved and an asynchronous stream

// read is posted. A single asynchronous stream read is posted at a time until the entire stream

// has been received. For each stream read, a DataBuffer object is created which contains the

// data read on the stream. Once the operation completes it is added to a list and an event is

// signaled. The main thread waits on this event and when signaled walks the list of completed

// DataBuffer objects and then writes them to disk. This is done to prevent a blocking write

// call from occurring in the asynchronous delegate for the stream read call.

//

// Usage:

//      usage: Executable_file_name [-u URI] [-p proxy]

//           -u URI          URI to download (along with linked content)

//           -p proxy        Name of proxy server

//

// Sample usage:

//      Executable_file_name -u http://www.microsoft.com -p http://myproxy/

 

using namespace System;

using namespace System::IO;

using namespace System::Net;

using namespace System::Text;

using namespace System::ComponentModel;

using namespace System::Collections;

using namespace System::Threading;

 

Add the DataBuffer class

 

public ref class DataBuffer

{

public:

    static const int BufferSize = 4096;

    array<Byte>^ byteBuffer;

    int bytesRead;

    FileStream^ saveFile;

    bool done;

 

    /// <summary>

    /// Initialize the DataBuffer object by allocating the byte array.

    /// </summary>

public:

    DataBuffer()

    {

        byteBuffer = gcnew array<Byte>(BufferSize);

        done = false;

    }

};

 

Next, add the HttpRequestState class.

 

/// <summary>

/// This class maintains the context information for each asynchronous web request.

/// </summary>

public ref class HttpRequestState

{

public:

    HttpWebRequest^ httpRequest;

    HttpWebResponse^ httpResponse;

    Stream^ httpResponseStream;

    DataBuffer^ readBuffer;

    ArrayList^ bufferList;

    ManualResetEvent^ bufferEvent;

    Uri^ getUri;

    FileStream^ saveFile;

    String^ localSavePath;

    int readCount;

    int requestTimeout;

 

public:

    HttpRequestState(HttpWebRequest^ httpObj, ArrayList^ list, ManualResetEvent^ dataEvent)

    {

        httpRequest = httpObj;

        httpResponse = nullptr;

        httpResponseStream = nullptr;

        bufferList = list;

        bufferEvent = dataEvent;

        readBuffer = gcnew DataBuffer();

        readCount = 0;

    }

};

 

Then, add the AsyncGetClass class.

 

ref class AsyncGetClass

{

public:

            Uri^              ResourceUri;

            IWebProxy^         HttpProxy;

            String^           LocalSavePath;

            bool             RecursiveGet;

            ArrayList^        RetrievedUri;

            ArrayList^        PendingUri;

            ArrayList^        PendingBuffer;

            ManualResetEvent^ BufferEvent;

            int              RequestTimeout;

 

public:

AsyncGetClass()

{

    // Change the uri to other for testing...

    ResourceUri = gcnew Uri("http://www.google.com/intl/en/images/about_logo.gif");

    // ResourceUri = gcnew Uri("http://labs.google.com/index.html");

    HttpProxy = WebRequest::DefaultWebProxy;

    LocalSavePath = ".";

    RecursiveGet = false;

    RetrievedUri = gcnew ArrayList();

    PendingUri = gcnew ArrayList();

    PendingBuffer = gcnew ArrayList();

    BufferEvent = gcnew ManualResetEvent( false );

    RequestTimeout = 10 * 1000; // 10 second timeout

}

 

/// <summary>

/// Displays simple usage information for the application.

/// </summary>

static void usage()

{

     Console::WriteLine("Usage: Executable_file_name [-u URI] [-p proxy]");

     Console::WriteLine("Available options:");

     Console::WriteLine("     -u URI          URI to download (along with linked content)");

     Console::WriteLine("     -p proxy        Name of proxy server");

     Console::WriteLine();

}

 

/// <summary>

/// Creates any subdirectories and opens the file for writing to. The path is stripped

/// from the URI to build the local path to save the file to. This path is appended

/// to the local save path supplied. A file is then opened with the same name as

/// the retrieved file and the FileStream to it is returned.

/// </summary>

/// <param name="localSavePath">Local path to append saved resources paths to</param>

/// <param name="uriName">URI of destination resource being saved</param>

public:

static FileStream^ CreateFile( String^ localSavePath, Uri^ uriName )

{

    FileStream^  localFile = nullptr;

    String^ fileName;

 

    try

    {

        array<String^>^   uriSegments = uriName->Segments;

        String^      localDirs = "";

 

        // Retrieve the directory path to the file

        Console::WriteLine("Retrieving the directory path to the file...");

        for(int i=0; i < uriSegments->Length-1 ;i++)

        {

            localDirs += uriSegments[i];

        }

       

        if ( uriSegments->Length > 1 )

        {               

            // Replace the forward slashes with back slashes

            Console::WriteLine("Replacing the forward slashes with back slashes...");

            localDirs = localDirs->Replace("/", "\\");

 

            // Remove the escaped spaces

            Console::WriteLine("Removing the escaped spaces...");

            String^ temp = localDirs->Replace("%20", " ");

            if ( temp != nullptr )

                localDirs = temp;

            Console::WriteLine("  Creating directory: {0}", localSavePath + "\\" + localDirs );

            Directory::CreateDirectory( localSavePath + "\\" + localDirs );

            fileName = uriSegments[uriSegments->Length - 1];

        }

        else

        {

            Console::WriteLine("Using default file name...");

            localDirs = "\\";

            fileName = "default.html";

        }

 

        // Open the file to write to

        Console::WriteLine("Opening the file to write to...");

        String^ saveFileName = localSavePath + localDirs + fileName;

        localFile = File::Open(

                saveFileName,

                IO::FileMode::Create,

                IO::FileAccess::Write,

                IO::FileShare::None

            );

        Console::WriteLine("Created File: {0}", saveFileName);

    }

    catch ( Exception^ ex )

    {

        Console::WriteLine("WriteHttpContentToFile failed: {0}", ex->Message);

        Console::WriteLine("Stack:\n{0}", ex->StackTrace);

        if ( localFile != nullptr )

            localFile->Close();

    }

    return localFile;

}

 

private:

static void RequestTimeoutCallback( Object^ state, bool timedOut )

{

    if ( timedOut )

    {

        Console::WriteLine("RequestTimeoutCallback timed out!");

        HttpRequestState^ httpState = (HttpRequestState^)state;

       

        if ( ( httpState != nullptr ) && ( httpState->httpRequest != nullptr ) )

        {

             httpState->httpRequest->Abort();

        }

    }

}

 

private:

static  void HttpStreamReadCallback( IAsyncResult^ ar )

{

    HttpRequestState^  httpState = (HttpRequestState^) ar->AsyncState;

   

    try

    {

         httpState->readBuffer->bytesRead = httpState->httpResponseStream->EndRead( ar );

         Console::WriteLine("HttpStreamReadCallback() returned: {0} bytes read (content length = {1})",

         httpState->readBuffer->bytesRead, httpState->httpResponse->ContentLength );

         // Set the FileStream in the DatBuffer object

         Console::WriteLine("Setting the FileStream in the DatBuffer object...");

         httpState->readBuffer->saveFile = httpState->saveFile;

 

        if ( httpState->readBuffer->bytesRead > 0 )

        {

            httpState->bufferList->Add( httpState->readBuffer );

            httpState->bufferEvent->Set();

            httpState->readBuffer = gcnew DataBuffer();

            PostResponseRead( httpState );

        }

        else

        {

            httpState->httpResponseStream->Close();

            httpState->httpResponse->Close();

            httpState->readBuffer->done = true;

            httpState->bufferList->Add( httpState->readBuffer );

            httpState->bufferEvent->Set();

        }

    }

    catch(WebException^ e)

    {

            Console::WriteLine("\nReadCallBack() Exception raised!");

            Console::WriteLine("\nMessage:{0}",e->Message);

            Console::WriteLine("\nStatus:{0}",e->Status);

    }

}

 

private:

static void HttpResponseCallback(IAsyncResult^ ar)

{

    HttpRequestState^    httpState = (HttpRequestState^) ar->AsyncState;

    Console::WriteLine("HttpResponseCallback() invoked...");

 

    try

    {

        // Complete the asynchronous request

        Console::WriteLine("Completing the asynchronous request...");

        httpState->httpResponse = (HttpWebResponse^) httpState->httpRequest->EndGetResponse( ar );

        // Read the response into a Stream object.

        Console::WriteLine("Reading the response into a Stream object...");

        httpState->httpResponseStream = httpState->httpResponse->GetResponseStream();

        // Create the file where resource is to be saved

        Console::WriteLine("Creating the file where resource is to be saved...");

        httpState->saveFile = CreateFile( httpState->localSavePath, httpState->getUri );

        PostResponseRead( httpState );

        Console::WriteLine("BeginRead() invoked on response stream...");

    }

    catch(WebException^ e)

    {

        Console::WriteLine("\nRespCallback() Exception raised!");

        Console::WriteLine("\nMessage:{0}",e->Message);

        Console::WriteLine("\nStatus:{0}",e->Status);

    }

}

 

public:

static void PostResponseRead( HttpRequestState^ state )

{

    Monitor::Enter( state );

    // state.readBuffer.Count = Interlocked.Increment( ref state.readCount );

    // Begin the Reading of the contents of the HTML page and print it to the console.

    Console::WriteLine("Begin the Reading of the contents of the HTML page and print it to the console...");

    IAsyncResult^ asyncStreamRead = state->httpResponseStream->BeginRead(

                        state->readBuffer->byteBuffer,

                        0,

                        DataBuffer::BufferSize,

                        gcnew AsyncCallback( HttpStreamReadCallback ),

                        state

                        );

    Monitor::Exit( state );

}

 

/// <summary>

/// Retrieve the resource specified by the string URI address. The HTTP proxy

/// can be specified if needed to go outside the local network.

/// </summary>

/// <param name="resourceName">String URI to retrieve</param>

/// <param name="proxy">Proxy server to use to access resource</param>

/// <param name="localSavePath">Local path to append saved resources paths to</param>

/// <returns></returns>

public:

void GetResource(Uri^ getUri, bool recurse)

{

    HttpWebRequest^ httpRequest = nullptr;

    HttpWebResponse^ httpResponse = nullptr;

    FileStream^ localFile = nullptr;

 

    try

    {

        RetrievedUri->Add(getUri);

        Console::WriteLine("Retrieving: {0}", getUri->AbsoluteUri);

        // Create the HTTP request object

        Console::WriteLine("Creating the HTTP request object...");

        httpRequest = (HttpWebRequest^)WebRequest::Create(getUri->AbsoluteUri);

        // Set some HTTP specific headers

        Console::WriteLine("Setting some HTTP specific headers...");

        httpRequest->UserAgent = "My User Agent/1.0";

        // If a proxy was specified create an instance of the WebProxy with it

        Console::WriteLine("If a proxy was specified create an instance of the WebProxy with it...");

        httpRequest->Proxy = HttpProxy;

        HttpRequestState^ getState = gcnew HttpRequestState(httpRequest, this->PendingBuffer, this->BufferEvent);

        // Set the local save path in the context block

        Console::WriteLine("Setting the local save path in the context block...");

        getState->localSavePath = this->LocalSavePath;

        getState->getUri = getUri;

        // Get the response object

        Console::WriteLine("Getting the response object...");

        IAsyncResult^ ar = httpRequest->BeginGetResponse(gcnew AsyncCallback(HttpResponseCallback), getState);

            ThreadPool::RegisterWaitForSingleObject(

                    ar->AsyncWaitHandle,

                    gcnew WaitOrTimerCallback(RequestTimeoutCallback),

                    getState,

                    RequestTimeout,

                    true);

    }

    catch (WebException^ wex)

    {

        Console::WriteLine("Exception occurred on request: {0}", wex->Message);

        Console::WriteLine("Status code: {0}", wex->Status);

 

        if (wex->Status == WebExceptionStatus::ProtocolError)

        {

            // If there was a protocol error then the response object is

            //    valid but there was an error retrieving the response.

            httpResponse = (HttpWebResponse^)wex->Response;

            Console::WriteLine("\nThe protocol returned was: {0}", httpResponse->StatusCode.ToString());

            httpResponse->Close();

            httpResponse = nullptr;

        }

    }

    finally

    {

        // Close the resources if still open

        if (localFile != nullptr)

            localFile->Close();

        if (httpResponse != nullptr)

            httpResponse->Close();

    }

}

};

 

 

 

 

Finally, add the main() code.

 

/// <summary>

/// This is the main function which parses the command line, initializes the proxy

/// if present, and calls the routine to retrieve the specified URI.

/// </summary>

/// <param name="args">Arguments passed to application</param>

int main(array<System::String ^> ^args)

{

     AsyncGetClass^    httpGet = gcnew AsyncGetClass();

    // Parse the command line

    if(args->Length != 0)

    {

       for(int i=0; i < args->Length ;i++)

       {

           try

           {

               if ( ( args[i][0] == '-' ) || ( args[i][0] == '/' ) )

               {

                  switch ( Char::ToLower( args[i][1] ) )

                  {

                    case 'u':

                        // URI to download (get)

                        httpGet->ResourceUri = gcnew Uri( args[ ++i ] );

                        break;

                    case 'p':

                        // Name of proxy server to use

                        httpGet->HttpProxy = gcnew WebProxy( args[ ++i ] );

                        break;

                    case 'r':

                        // Retrieve all referenced images and text on the same host

                        httpGet->RecursiveGet = true;

                        break;

                    case 's':

                        // Local save path to append to retrieved resources

                        httpGet->LocalSavePath = args[ ++i ];

                        break;

                    default:

                        AsyncGetClass::usage();

                        return 0;

                }

            }

        }

        catch(Exception^ err)

        {

            Console::WriteLine("Error: " + err->Message);

            AsyncGetClass::usage();

            return 0;

        }

    }

  }

  else

  {

        AsyncGetClass::usage();

        return 0;

   }

 

    // Initialize the proxy server and retrieve the resources

    Console::WriteLine("Initializing the proxy server and retrieve the resources...");

    try

    {

        bool getCompleted = false;

        httpGet->GetResource( httpGet->ResourceUri, httpGet->RecursiveGet );

        while ( getCompleted == false )

        {

            if ( httpGet->BufferEvent->WaitOne( 5000, true ) == true )

            {

                Console::WriteLine("Buffer event signaled...");

                while ( httpGet->PendingBuffer->Count > 0 )

                {

                    DataBuffer^ data = (DataBuffer^) httpGet->PendingBuffer[0];

                    httpGet->PendingBuffer->RemoveAt(0);

 

                    if ( data->done == true )

                    {

                        data->saveFile->Close();

                        Console::WriteLine("End of stream, closing file...");

                        getCompleted = true;

                        break;

                    }

                    else if ( data->byteBuffer->Length > 0 )

                    {                           

                        data->saveFile->Write( data->byteBuffer, 0, data->bytesRead );

                    }

                }

                httpGet->BufferEvent->Reset();

            }

            else

            {

                Console::WriteLine("Buffer event timed out");

            }

        }

    }

    catch( Exception^ ex )

    {

         Console::WriteLine("Exception occurred: {0}", ex->Message);

    }

    return 0;

}

 

Build and run the project.

 

C++ Asynchronous Http Get Request Program Example - building a project

 

C++ Asynchronous Http Get Request Program Example - running the project  

 

The following is the sample output.

 

C++ Asynchronous Http Get Request Program Example - a sample output without argument

 

The following is the sample output when run from the command prompt.

 

C++ Asynchronous Http Get Request Program Example - a sample output when run from the command prompt with argument values

 

The downloaded file should be saved as Default.html.

 

C++ Asynchronous Http Get Request Program Example - the saved downloaded file

 

 

 


 

< Asynchronous HTTP & Misc. | Main | C# Asynchronous Http Get Request Program Example >