Write a Webserver in 100 Lines of Code or Less

Network programming can be cumbersome for even the most advanced developers. REALbasic, a rapid application development (RAD) environment and language, simplifies networking yet provides the power that developers expect from any modern object-oriented language. I'll show you how this powerful environment enables professional developers and parttime hobbyists alike to quickly create a full-featured webserver.

Because you may be new to REALbasic's unique features and language, it's a good idea to start with something simple. The goal of writing a webserver in REALbasic is not to replace Apache. Instead, this server will demonstrate how to use REALbasic to handle multiple connections using an object-oriented design.

Let's start with some preliminary information about how networking in REALbasic works, as well as a quick walk-through of the HTTP protocol.

REALbasic Networking Classes

REALbasic includes several networking classes that make development easier, including the TCPSocket, UDPSocket, IPCSocket, and ServerSocket, and they are all event-based. This means that you can implement events in your code that will run when certain things happen, such as when an error occurs or when data is received. This saves you the trouble of having to constantly check the states of sockets.

HTTP is a simple protocol that is used to request information (generally files). The HTTP protocol works by having the client, such as a browser, send a request to the server. The server will evaluate the request and reply with either an error or the information requested.

The syntax for a request is:

(GET | POST) + Space + RESOURCE_PATH + Space + HTTPVersion + CRLF

As with most text-based protocols, the ending of a line is a carriage-return and a line-feed concatenated together. This is called a CRLF. An example request that a browser would send for the root file on the server would be:

The server can send more data after the end of the two CRLFs, if desired. That is where, for example, the page requested would be written. After all the data is sent, the connection is closed by the server.

Building the Server

REALbasic provides two classes which will be utilized for the majority of the work. The ServerSocket class automatically handles accepting connections without dropping any, even if they are done simultaneously. The TCPSocket provides an easy mechanism for communicating via TCP. Together, they allow for extremely easy creation of servers.

Let's get started by launching REALbasic 5.5. (Get a 10-day free trial here.) Once open, you will be presented with a few windows. To the left is a "Controls" palette, which contains commonly used GUI controls. To the right is the Properties window, which allows you to visually set properties on objects such as windows, controls, or classes. The window that contains the three items, Window1, MenuBar1, and App, is the project window.

To utilize the ServerSocket and TCPSocket, they need to be subclassed. When you subclass another class, you gain access to events that the superclass exposes. For example, the TCPSocket itself doesn't actually know what to do with the data once it has been received. So, you subclass the TCPSocket and implement the DataAvailable event so that you can do something when data is received.

First, create a new class named HTTPServerSocket whose superclass is ServerSocket. Do this by choosing "New Class" from the File menu. The project window will now have a new item named Class1. Click on it, and notice how the properties window updates to reflect what item is selected. Rename the class to HTTPServerSocket, and select "ServerSocket" from the Super popup menu.

While we're here, go ahead and add another class. Rename it to HTTPConnection, and set its superclass to TCPSocket. You can click on the superclass field to the left of the popup arrow, and REALbasic allows you to type in the class name. Start typing TCP, and notice how autocomplete kicks in. Press tab to let autocomplete finish the rest of the name for you.

Now, you have the two classes that are needed to make the HTTP server. The HTTPServerSocket class is responsible for creating HTTPConnections, and the HTTPConnection class is responsible for handling the HTTP communications.

This is a good time to save the project. Remember where you saved the project at because at the end of this article you will need to put another file beside it.

Double click on the HTTPServerSocket class. This will open a code editor window. On the left is the code browser, which helps navigate between methods, events, properties, and constants. All we need to do in the HTTPServerSocket is implement an event. Expand the "Events" section, and select "AddSocket."

The AddSocket event is fired when the ServerSocket needs more sockets in its pool. The way servers work is that there is generally a "pool" or collection of sockets that are sitting around. When a connection occurs, the server will hand off the connection to one of the sockets in the pool. The ServerSocket takes care of all the tricky TCP code required to create a server that doesn't drop connections for us. All you need to do is add this code to the AddSocket event:

returnnew HTTPConnection

That's all that is needed. Close that code editor, and double click on the HTTPConnection class we wrote. Here is a diagram of what will happen once the server receives a request:

Each of these tasks will be handled by different methods that we will create. The first task is to determine what resource is being requested. The server will have a string that contains the entire request, and this method will extract a string that represents the path to the resource. The next task will be to take that string and locate that file. The server will then either report an error or send the file. When done sending all the data, the server will close the connection.

To accomplish these tasks, three methods will be created. Add a method to the HTTPConnection class by choosing "Edit->New Method..." Create the method as shown:

The next method you'll need to create is one that takes the path returned from GetPathForRequest and returns the actual file to us. In REALbasic, files are dealt with through the FolderItem class rather than string paths. This is a huge benefit because not only is this same object used across all platforms, but it is so easy to use. Create a method named GetFolderItemForPath that takes "path as String" and returns a FolderItem. The method's scope can be private because the method only pertains to this class.

The last method you need to create is just a convenience method. No matter what type of response the server receives, the response will always have a similar header. To help eliminate repeated code, define one last method named WriteResponseHeader that takes the parameters "statusCode as Integer, response as String" with no return value. This method can also be private.

In the code browser, expand the Events section. You will see Connected, DataAvailable, Error, SendComplete, and SendProgress. With HTTP, there isn't anything the server needs to do in the connected event. The client will be sending a request, so the server needs to wait until we have data available. The DataAvailable event is very important because of that. That event fires whenever data has been received. That event is where most of the logic will reside. The Error event is fired when there is a socket-related error, but for this example, socket-related errors will be ignored. Finally, SendComplete and SendProgress can be used to keep the send buffer full without eating up too much memory.

Since this server is going to be memory-efficient, you need to keep one variable around. Variables that are on the class level and not created inside a method are called properties. The property that is needed is to store a reference to an already open file so that we can write more data from the file when needed. In REALbasic, there is a class called BinaryStream that is used to read and manipulate files. Although there are other classes for accessing files, the BinaryStream class will do what is needed. Create a property by choosing "Edit->New Property..." In the Declaration field, type "stream as BinaryStream" and change its scope to Private. Click OK when done.