Click here to Skip to main content
Click here to Skip to main content

A Simple HTTP Proxy with the ACE Library

By , 6 Nov 2008
Rate this:
Please Sign up or sign in to vote.

Introduction

I'm a newbie in network programming. So, I tried to goal my learning into making a very simple proxy server. This one is very simple, an HTTP proxy server using lib ACE.

Background

You need to know socket and multi-thread programming, and also about lib ACE. I use lib ACE for the socket and the logger. Actually, I started with an asynchronous proxy server (single thread with non-blocking socket) but I ended up with a very slow proxy server. That's why I am using this library. So, it is better for you to download lib ACE first and build the library both for the debug and release versions. Lib ACE is, however, a very nice library for network programming. I have used lib ACE as a dynamic library here.

Basic Theory

Well, here is a little theory:

A proxy server is a server (a computer system or an application program) that services the requests of its clients by forwarding requests to other servers. A client connects to the proxy server, requesting some service, such as a file, connection, Web page, or other resource, available from a different server. The proxy server provides the resource by connecting to the specified server and requesting the service on behalf of the client (Quoted from Wikipedia.org).

image002.gif

So, how will we do it? Here is the plan.

Create a TCP socket to the client, your Web browser I mean. Bind on port 5060, and start listening for incoming requests from the client.

  1. Soon after a connection from the client is accepted, the proxy will store its socket data into a buffer variable. This happens in an infinite loop.
  2. A thread, called mother thread, will always run, checking the buffer. If the buffer is not empty, then it will create another thread, called daughter thread.
  3. A daughter thread will receive requests from the client based on its stored socket data.
  4. The proxy will process this request and determine the destination IP address and port number (commonly 80 for HTTP).
  5. Then, the request is sent to the destination Web server.
  6. Soon, the Web server will reply, sending back the content of the requested pages.
  7. The proxy will keep receiving this data and then send it back to the client until one of them (client or server) closes the connection or until all the page contents have been sent.

img4.JPG

The Code

Everything starts here. There is an infinite loop that will accept any incoming connection and put its socket information into a buffer list. At the same time, mother thread is running, watching on that buffer list.

int CPROXY::Run(int nPort)
{
	ACE_DEBUG((LM_DEBUG, ACE_TEXT("%t: Running!\n")));

	DWORD thid;
	HANDLE hMotherThread = CreateThread(NULL, 0, MotherThread, this, 0, &thid);
	if (!hMotherThread)
		return -1;

	ACE_SOCK_Stream client_stream;
	ACE_INET_Addr addr;
	addr.set(nPort, addr.get_ip_address());

	int e = client_acceptor.open(addr);
	if (e == INVALID_SOCKET)
		return -1;

	while(true)
	{
		int e = client_acceptor.accept(client_stream);
		if (e == INVALID_SOCKET)
			continue;

		//Store in a buffer.
		EnterCriticalSection(&guard);
		queue.push_back(client_stream);
		LeaveCriticalSection(&guard);
	}
	
	return 0;
}

Besides mother thread, we also have daughter thread. The real things happen in the daughter. The mother will only do request buffer checking.

int CPROXY::MotherThreadWorker()
{
	isMotherThreadRunning = true;

	while (isMotherThreadRunning)
	{
		EnterCriticalSection(&guard);
		bool isEmpty = queue.empty();
		LeaveCriticalSection(&guard);

		if (!isEmpty){
			DWORD thid;
			HANDLE hDaughterThread = CreateThread(NULL, 0,
					DaughterThread, this, 0, &thid);
			if (!hDaughterThread)
				continue;

			WaitForSingleObject(wait, INFINITE);
		}
	}
	return 0;
}

int CPROXY::DaughterThreadWorker()
{
    char buf1[BUFSIZE];
    char cServerAddress[256];
    int nServerPort;

    EnterCriticalSection(&guard);
    ACE_SOCK_Stream client_stream = queue.front();
    queue.pop_front();
    LeaveCriticalSection(&guard);

    SetEvent(wait);

    const ACE_Time_Value t(2,0);

    size_t nLen = client_stream.recv(buf1, sizeof(buf1), &t);
    if(sizeof(buf1) > nLen)
    buf1[nLen+1] = '\0';
    if (nLen <= 0){
        ACE_DEBUG((LM_DEBUG, ACE_TEXT("%t: Connection closed by " 
                   "browser client while receiving requests.\n")));
        return -1;
    }

    //Parse the received packet from client.
    GetAddressAndPort(buf1, cServerAddress, &nServerPort);

    //Attempt to connect to remote server
    ACE_INET_Addr addr;
    addr.set(nServerPort, cServerAddress);
    ACE_SOCK_Stream server_stream;

    if (server_connector.connect(server_stream, addr) == INVALID_SOCKET){
        ACE_DEBUG((LM_DEBUG, ACE_TEXT("%t: Connection closed " 
                   "by remote server while connecting.\n")));
        return -1;
    }

    //Send request to server.
    if (server_stream.send(buf1, nLen) == INVALID_SOCKET){
        ACE_DEBUG((LM_DEBUG, ACE_TEXT("%t: Connection closed by remote" 
                   " server while sending requests.\n")));
        return -1;
    }

    //Retrieve responses from server and send to client.
    char buf2[BUFSIZE];
    while(1){
       nLen =server_stream.recv(buf2, sizeof(buf2), &t);
       if (nLen <= 0){
           ACE_DEBUG((LM_DEBUG, ACE_TEXT("%t: Connection closed by remote " 
                      "server while receiving responses.\n")));
           break;
       }

       if (client_stream.send((buf2), nLen) == SOCKET_ERROR){
           ACE_DEBUG((LM_DEBUG, ACE_TEXT("%t: Connection closed by browser " 
                      "client while sending responses.\n")));
           break;
       }
    }

    server_stream.close();
    client_stream.close();

    return 0;
}

Well, from the source code, we can see that the proxy will only do bypassing requests from the client (Internet browser) to the Web server and then send back the response from the Web server to the client.

Points of Interest

Well, actually this is simple but very nice. Very good for a newbie like me to learn. Looking forward to comments...

History

  • 2nd November, 2008: Initial post
  • 3rd November, 2008: Put in more detailed description to make it easier to understand

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

auralius manurung
Gyeongsang National University, South Korea
Indonesia Indonesia
from Indonesia with love... Smile | :)

Comments and Discussions

 
QuestionPlease add instructions to build this on Linux PinmemberNarendra Kumar.S.S4-Dec-11 22:35 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140415.2 | Last Updated 6 Nov 2008
Article Copyright 2008 by auralius manurung
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid