Web Server in VB6 – Part 1

Yes, you read that right 🙂

Back in 2007 or so, I was trying to understand how web servers work — coming from a desktop application background.

All my programming life, I write desktop applications both standalone and networked (like a chat using raw sockets or using MSMQ for reliable message transport). The web was new to me and I started getting curious about it.

A smart friend told me the basics of the web server and I challenged myself to write one, even a simple one. Yeah, that’s how we spend our extra time — making crazy challenges — LOL.

Long story short, I was able to build one — in Visual Basic 6.0. Why, you might ask?

Cause VB6 was the only programming language I was using that time. I didn’t know Python, PHP or VB.Net.

So if you’re wondering why in the world someone would build one (and in VB6) — well, the goal was to understand how it works (under the hood so to speak) and the best way to do that is to write one so you can see everything that happens. That was a learning sandbox for me and it also helped tremendously when I started learning “real” web development using “real” web servers like PHP and Django.

How I did it

Let me share how I did it back then and how it still works today (even in newer browsers)

Adding WinSock component

Web Servers sit or runs on top of sockets (TCP/IP) and since I already have a working knowledge of it (when I created a chat application), I used that to setup a web server. All I had to know was which port it listens to.

To do this, we need to add the Winsock (Windows Socket) control to our VB6 project:

Creating Winsock Instance in our Form

Now add the Winsock control to our Form. It’s unfortunate that socket in VB6 is an OCX and not a DLL. This meant you need a form to use it. I don’t know why Microsoft didn’t just make it a DLL so we can use it without a form (say writing a daemon).

If you’re from Microsoft reading this blog, do let me know in the comments section below 🙂

Setting properties of Winsock

It’s always a good idea to name your controls, variables, with something descriptive. So we’ll name this “sckServer” since it makes more sense that “Winsock1”. We also need to set the Index property to zero (0), and I’ll explain why (next).

Make an array of sockets

VB6 Control Arrays

In VB6, if you want to make array of controls during design, you set the Index property to zero (0) to make it the first array item. You can copy then paste that control and VB6 IDE will create a new one with the same name but an index of 1. You repeat that and you’ll get index 2, 3, etc. You may also do it programmatically later which is what I did.

The next section below will explain why we need to make the socket control an array.

Web

Client-Server model

Here’s how a typical web client interacts with a web server (based on my research and testing):

  1. Client makes a connection to Server
  2. Server accepts the Client connection request
  3. Client sends a (resource) request to Server
  4. Server processes the request and returns a response
  5. Server closes the connection once the response has been sent completely
  6. Client knows it has received the full response

Browsers

Web Browsers typically makes about 2 requests (1 for the URL you type, and the other for “favicon.ico”). That means 2 separate requests, served by 2 separate connections (considering the client-server model discussed above).
This is the reason why we had to set the Winsock control Index to zero — so we can handle multiple connections.

Core Logic

Receive request

Once we have the server up and running (listening on port 80 by default), we process incoming requests from clients (in this case, a modern web browser). The request can be captured from within the “DataArrival” event of the Winsock control.

Here’s the code screenshot of how the data is captured:

Sample Http Request

Here’s what my browser sent to my web server when I tested it:

GET /favicon.ico HTTP/1.1
Host: localhost
Connection: keep-alive
sec-ch-ua: "Microsoft Edge";v="105", " Not;A Brand";v="99", "Chromium";v="105"
sec-ch-ua-mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36 Edg/105.0.1343.42
sec-ch-ua-platform: "Windows"
Accept: image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: no-cors
Sec-Fetch-Dest: image
Referer: http://localhost/
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9

Processing request and returning response

You can directly process the request there, but believe me it’s better to compartmentalize your logic (as you can see from that snippet where I delegated the processing to a global function).

Here’s what that function looks like:

First we have to decode the complex request into parts so we can easily process it.

Once the request is decoded, we can process it and then send a response. In my example, I simply returned a serialized version of the CHttpRequest object, but from here you can send whatever response you want, say call a function and use the return value as the HttpResponse.

GET Method

Here’s the result of testing the GET method using my web server:

That’s the REPR (representation) of the CHttpRequest object and you can see how it successfully mapped the request to properties.

POST Method

And here’s a test using POST method, to show that it works on both (and also properly gets the data):

Okay, this blog took longer than I expected so I’ll just end it here.

I’ll probably write a follow up on this if this topic gets a lot of interests.

Leave a Comment