TCP and UDP are both protocols used by the Internet. The most commonly used is TCP, which stands for Transmission Control Protocol. It is used to connect two computers and to reliably transfer data between them. The data itself is transmitted as packets, which are small units of data that can theoretically arrive at the host out of sequence. TCP ensures that they are placed in sequence and arrive reliably. It is used by the most commonly used Internet applicationthe World Wide Web and email. SocketCore ClassThe SocketCore class is an abstract class, meaning that it is never instantiated itself. It serves as the parent class to the classes that you will use when writing programs that interact with networks. The class provides some of the basic features that will be needed by all the subclasses in order to work. For starters, one of the first things you will need to do when creating a network connection is to specify which port to listen to or which port to try to connect to. In the case of the World Wide Web and the HTTP protocol, the standard port number is 80, but that's not the only port that's available. Port numbers can range from 0 to 65535 and are divided into three tiers. So-called Well-Known Ports are those from 0 to 1023. On most systems, you need to have superuser status to use these ports. The next tier is called Registered Ports, and these number from 1024 to 49151. Finally, Dynamic Ports are numbered from 49152 to 65535. A list of commonly used ports follows: FTP 21 SSH 22 TELNET 23 SMTP 25 HTTP 80 HTTPS 443 POP3 110 HTTP-Alternate 8080 Although Registered Ports do have a semiformal status, there's no reason you can't use port numbers in that range, especially when you know that it is available. REALbasic provides TCPSocket subclasses for HTTP, SMTP, and POP3, and these subclasses will default to the port that is appropriate for the given protocol. To find out which port to connect to, or bind to, check the following property: SocketCore.Port as Integer Exactly what this means depends on the context. When two computers connect through a network, one plays the role of the server and the other of the client. The server is said to listen on a port, or that it is bound to a port. A client connects to the server through a particular port. As a consequence, if the SocketCore subclass you are working with is a server and is waiting to receive data, the Port property says which port it is listening to. Otherwise, it says which port it is going to try to connect to. Sending and Receiving DataBecause the SocketCore class is an abstract class, it doesn't implement all the features required to make a network connection. One of the most important things that is missing is a way to read and write data. Rest assured that this functionality is implemented in the subclasses, but you won't find it here. What you will find, however, is something that is common to all the subclasses, and that is the event that gets triggered whenever data arrives: SocketCore.DataAvailable Whenever a socket receives data, the DataAvailable event is triggered. This does not mean, however, that all the data is available. In fact, there is no guarantee that all the required information has been retrieved. Because all the data doesn't come at once, the socket classes all implement two internal buffers, one for data that is coming in and one for data that is being sent out. The process you use to access these buffers differs depending on which subclass you are using. I will look at the buffers in more detail in the section on the TCPSocket class. In addition to receiving data, sockets can send data. The SocketCore class doesn't implement a way to actually send data. For that, you'll also need to look at the specific subclass in question. However, sending data works like receiving data does in the sense that it does not necessarily get sent all at once. As a consequence, the socket classes all have the following event, which is triggered when all the data in the send buffer has been sent: SocketCore.SendComplete(UserAborted as Boolean) When an error is encountered, the Error event is triggered: SocketCore.Error To find out which error caused the event to be triggered, you need to check the LastErrorCode property: SocketCore.LastErrorCode as Integer The following class constants describe the cause of the error that was just triggered: SocketCore.OpenDriverError = 100 SocketCore.LostConnection = 102 SocketCore.NameResolutionError = 103 SocketCore.AddressInUseError = 105 SocketCore.InvalidStateError = 106 SocketCore.InvalidPortError = 107 SocketCore.OutOfMemoryError = 108 SocketCore PropertiesEvery socket is given a unique identifier, which is accessible through the following property: SocketCore.Handle as Integer Being able to access the Handle for each socket can be convenient because in many cases, you will have more than one socket open at a time. In the web server sample application, you will see that several sockets are open at any given time, and you can use the Handle to identify which socket is performing any activity. The following properties perform some basic tasks, which are mostly self-explanatory. To see if you are connected to another socket: SocketCore.IsConnected as Boolean To find out what the local IP address is for the machine your application is running on, check SocketCore.LocalAddress as String The following property identifies which NetworkInterface your socket is using: SocketCore.NetworkInterface as NetworkInterface SocketCore MethodsSockets open their connection by calling the Connect method, and they can close it by calling the Close method. The subclasses that will be reviewed next often provide a more graceful means of opening and closing sockets. SocketCore.Close SocketCore.Connect If you want to purge the internal send buffer, call the following: SocketCore.Purge One important thing to remember when dealing with sockets is that the Internet protocol is big endian, like PowerPC Macintosh, so you will need to pay attention to endianness when using sockets. TCPSocketThings start to get interesting when you use the TCPSocket class. This is the one that I will be using in the sample web server application. TCPSocket EventsAlthough SocketCore has the isConnected property that you can use to check to see if your socket is connected to another socket, the TCPSocket adds an additional layer of functionality by triggering an event when a connection is first established: TCPSocket.Connected The SendProgress event is also added to this class, which allows you to track how much of a document has been downloaded: TCPSocket.SendProgress(BytesSent as Integer, BytesLeft as Integer) as Boolean If you want to cancel the send, you can return TRue from this event. The following property tells you what address you are trying to connect to: TCPSocket.Address as String The address you actually are connected to after you have connected is not necessarily the same as the one you tried to connect to in the first place. Therefore, REALbasic lets you check the remote address you are connected to using this property: TCPSocket.RemoteAddress as String TCPSocket MethodsYou can use TCPSocket to both send and receive data. If you are going to be receiving data, you will need to tell TCPSocket to Listen on a given port for any requests using the following method: TCPSocket.Listen If a connection has already been established, you can close it using the following: TCPSocket.Disconnect The SocketCore class itself did not provide any methods for reading or writing data. Because the whole point of using sockets is to send and receive data, every subclass provides its own methods for doing so. The TCPSocket provides the following three methods for reading data in the receive buffer: TCPSocket.Read(Bytes as Integer, [Encoding as TextEncoding]) as String TCPSocket.ReadAll([Encoding as TextEncoding]) as String TCPSocket.Lookahead([Encoding as TextEncoding]) as String Under normal circumstances, when you read the buffer, the data is cleared from the buffer, regardless of whether you call Read or ReadAll. There are times when you want to see what's in the buffer without deleting the contents of the buffer, and that is when you call the Lookahead method. You'll see an example of this in the web server code supplied later in this chapter. In addition to reading data, you can write data as well, simply by calling the following method: TCPSocket.Write(Data as String) Keep in mind that you can send a string of any size to the Write method, but if the String is particularly long, it will not get sent all at once and will take some time to be completely sent. In the meantime, your application can check the data passed in the SendProgress event to monitor progress. HTTPSocket Inherits TCPSocketThe HTTPSocket class is used to handle the client side of an HTTP connection. Because HTTP (Hypertext Transfer Protocol) is the protocol used by the World Wide Web, in simpler terms this means that the HTTPSocket class performs the role of the web browserin the sense that it can request files from a web server and receive them. Any kind of file can be retrieved this way, but html files are most commonly associated with the web. Note that the HTTPSocket class is a networking class. It cannot display html pages; that's what the HTMLViewer control is for. HTTPSocket is a subclass of TCPSocket and does all the same things that TCPSocket does. The layer of functionality it adds is that of creating HTTP requests, which have a specific format, and it handles retrieving the data that is sent back as a result of the request. The HTTP protocol supports several types of requests, but I will concern myself with two in particular, a GET request and a POST request. The most common kind of request is a GET, which does what the name impliesit makes a request for a certain file and the HTTP server returns the file. Here is an example of the body of a GET request: GET / HTTP/1.1 Accept: */* Accept-Language: en-us Accept-Encoding: gzip, deflate Cookie: photo_display=thumbnail; nde-textsize=16px; SessionID=12345 User-Agent: Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/412 (KHTML, like Gecko) Safari/412 Connection: keep-alive Host: localhost:8080 The first line of the request tells you what kind of request it is and what the request is for. It consists of a single line that is delimited by two spaces, like so: GET<space>/path/to/file<space>HTTP/1.1 Following the initial line are a series of key/value pairs that provide basic information about the request, and at the very end of the request (not visible in the example) is a blank line that signifies the end of the request. This is important! A line ending according to the HTTP protocol consists of a carriage return and a linefeed, which is the same as the value for EndOfLine.Windows. A POST request is similar, except that it also sends additional information. The term "POST" refers to the idea of POSTING information and is used when the user has filled out a form and submitted it in the web browser. A sample of a POST request can be seen in Listing 9.2.
The first line is different, as you can see, because it designates the request as a POST. You will also notice that there are some additional key/value pairs that are being sent, the most important of which are Content-Type and Content-Length. Both of these are always available on POST requests. Just as in GET, the main request is ended with a blank line, but in addition to the blank line, a POST also attaches additional information. In the preceding example, the post has appended data from a form that the user has filled out. Even though a POST is intended to be used for sending data to the web server, it is not the only way. You can use a GET request, too. The only difference is that the encoded data is not sent in the header; it's appended to the URL. The following GET request shows what a GET request looks like:
In the first line of the request, everything that follows the "?" in the string of characters in /form.html?textfield=Mark&Submit=Submit is the data that is sent by a POST in the body of the request. As you will see in the example, when you create a form using html, you can specify which kind of request that form should generate, either a GET or a POST. POST /form.html HTTP/1.1 Host: localhost:8080 User-Agent: Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.6) Gecko/20040113 Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9, text/plain;q=0.8,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1 Accept-Language: en-us,en;q=0.5 Accept-Encoding: gzip,deflate Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Keep-Alive: 300 Connection: keep-alive Referer: http://localhost:8080/fileform.html Content-Type: multipart/form-data; boundary=myBoundaryValue Content-Length: 59628 --myBoundaryValue Content-Disposition: form-data; name="datafile"; filename="05-Threading.doc" Content-Type: application/msword <lots of data> The example I have provided will accommodate both kinds of POST requests. When submitting a file, the file is submitted according to its MIME type. This is designated in the Content-Type field. There are two values submitted in Content-Type. The value "multipart/form-data" indicates that a file is being uploaded and that "boundary=..." is a value used to delimit the file. The uploaded file starts with the string "" followed by the data that was passed as the boundary value. The file can be divided up into multiple parts, and each part is separated by this string of characters, which in the preceding example is --myBoundaryValue The end of the document is signified by the boundary value again, this time with "" on either side, like so: --myBoundaryValue-- Figure 9.2 is an example of a html form: Figure 9.2. Sample form.You can code this form in html either as a POST or as a GET. Either way, the form looks the same. To create it as a POST, you would write the following: <form name="form1" method="post" action="http://localhost:8080/form.html"> <p> <input type="text" name="textfield"> </p> <p> <input type="submit" name="Submit" value="Submit"> </p> </form> The request would be the POST request listed previously. To create it as a GET, it would look like this: <form name="form1" method="get" action="http://localhost:8080/form.html"> <p> <input type=" text" name="textfield"> </p> <p> <input type="submit" name="Submit" value="Submit"> </p> </form> The headers for this request would be like the GET request I just listed. You can use the HTTPSocket class to perform any kind of request. You can even use it to mimic having filled out a form. You can use either a GET or a POST request. An html form passes a series of key/value pairs, and the HTTPSocket class makes it easy to submit the values needed using a dictionary. If you are going to use a GET request, you can use the following method to take the values in a dictionary and return a URL-encoded String: HTTPSocket.EncodeFormData(Form as Dictionary) as String Because this returns a String, you would use this by appending a "?" following by the returned String to the URL, something like this: Dim aForm as New Dictionary aForm.Value("textfield") = "Mark" HTTPSocket1.Get("http://localhost:8080/form.html" + "?" + _ HTTPSocket1.EncodeFormData(aForm) You can do something similar with a POST, using the following method: HTTPSocket.SetFormData(Form as Dictionary) The code would look like the following: Dim aForm as New Dictionary aForm.Value("textfield") = "Mark" HTTPSocket1.SetFormData(aForm) HTTPSocket1.Post("http://localhost:8080/form.html") In addition to posting data from html forms, POST requests can also be used to upload files. The HTTPSocket class has a method to do this: HTTPSocket.SetPostContent(Content as String, ContentType as String) AuthenticationThe HTTP protocol defines two means of authenticating users: Basic Authentication and Digest Authentication. The most widely used method is Basic Authentication, which is what REALbasic supports, with the following event:
It works like this: When you make a request for a web page, the web server checks to see if authentication is required. If it is, it sends back a notice saying that it needs to get a username and a password. When this happens, the HTTPSocket class triggers the AuthenticationRequired event and passes along the headers so that you can glean any necessary information from them. As you can see, it also passes a Name and Password variable ByRef. Although this is an unusual occurrence, it means that you should assign a value for the Name and Password parameters. When you do, the server will be sent the username and password to be authenticated. Get, Post, and GetHeaders MethodsThere are several request types that are legal HTTP requests, but the HTTPSocket class implements the three that are required: GET, POST, and HEAD, represented by the Get, Post, and GetHeaders methods. Get and Post each come in three forms, as follows: HTTPSocket.Get(URL as String) HTTPSocket.Get(URL as String, File as FolderItem) HTTPSocket.Get(URL as String, Timeout as Integer) as String HTTPSocket.Post(URL as String) HTTPSocket.Post(URL as String, File as FolderItem) HTTPSocket.Post(URL as String, Timeout as Integer) as String HTTPSocket.GetHeaders(URL as String) HTTPSocket.GetHeaders(URL as String, Timeout as Integeer) as String If you want to get the home page of REAL software, you would execute the request like so: httpsocket1.Get "http://www.realsoftware.com/" The first two method signatures are subroutines because they do not return a value. Because of the asynchronous nature of network communications, an event is triggered when the data is returned. Two events are relevant:
The event to use is contingent on which form of the Get and Post methods is used. You have the option of passing a FolderItem when calling the methods, and if you do, the data that you have requested will be made available in the DowndloadComplete event. If you do not pass a FolderItem, the content will be made available as a String in the PageReceived event. The general rule is this: if you are requesting an html or XML file, do not pass a FolderItem. If you are downloading a binary file, such as a Microsoft Word document or a JPEG image, pass a FolderItem in the method. GetHeaders works like a GET request, expect that only the headers are returned, not the requested file. You also have the option of passing a "Timeout" value in the Get, Post, and GetHeaders methods, which makes the request run in synchronous mode, which means that the methods are functions. REALbasic will block and wait for a result, which will be in the form of a String. When using the class in synchronous mode, you can also set the following parameter to true to yield time that allows other events to be triggered: HTTPSocket.Yield as Boolean If you are using synchronous mode, you can access headers using the following property: HTTPSocket.PageHeaders as InternetHeader TCPSocket has a SendProgress property, and HTTPSocket adds a ReceiveProgress property to the mix: HTTPSocket.ReceiveProgress(BytesReceived as Integer, TotalBytes as Integer) Handles Event You have seen this used in an earlier example with a ProgressBar control. You have already seen examples of HTTP headers, but you haven't seen all the different variations yet (at least not in this book). The great thing about the HTTP protocol is that it is very flexible. One thing it allows you to do is to create your own headers. This can be a very powerful tool in your programming belt if you want to develop custom HTTP services. You can arbitrarily set any header using the following method: HTTPSocket.SetRequestHeader(Name as String, Value as String) You can also clear all your headers by calling HTTPSocket.ClearRequestHeaders HTTPSocket.HeadersReceived(Headers as InternetHeaders, HTTPStatus as Integer) Handles Event When you make an HTTP request and get the results, the HTTP server has to send back a status code. Hopefully, the status code will be 200, which means that everything is okay. If you are using the HTTPSocket class in synchronous mode, you can find out what the status code was by checking the following property: HTTPSocket.HTTPStatusCode as String Here is an abbreviated list of sample codes: 200 - OK 201 - Created 202 - Accepted 203 - Non-Authoritative Information 204 - No Content 205 - Reset Content 206 - Partial Content 300 - Multiple Choices 301 - Moved Permanently 302 - Found 303 - See Other 304 - Not Modified 305 - Use Proxy 307 - Temporary Redirect 400 - Bad Request 401 - Not Authorised 403 - Forbidden 404 - Not Found 408 - Request Timeout 409 - Conflict 410 - Gone 411 - Length Required 412 - Precondition Failed 414 - Request URI Too Long 415 - Unsupported Media Type 500 - Internal Server Error 501 - Not Implemented 502 - Bad Gateway 503 - Service Unavailable 504 - Gateway Timeout 505 - HTTP Version Not Supported There are some situations where users sit behind a firewall and aren't given direct access to the Internet. This is often done at businesses as a security measure. HTTPSocket.HTTPProxyAddress HTTPSocket.HTTPProxyPort InternetHeaders ClassThe InternetHeaders class is basically an array of key/value pairs, plus some methods that make it easier to add and delete individual pairs in the list. From the example given earlier, the source of the header data looks like this: Accept-Language: en-us Accept-Encoding: gzip, deflate Cookie: photo_display=thumbnail; nde-textsize=16px; SessionID=12345 User-Agent: Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/412 (Khtml, like Gecko) Safari/412 Connection: keep-alive Host: localhost:8080 You can access the raw source by calling the following method: InternetHeaders.Source as String Using the same header data just listed, I could get access to the "Connection" value like this: Dim s as String s = Headers.Value("Connection") I can use the following method to find out how many headers there are: InternetHeaders.Count as Integer This means that I can get access to the value of any given header by using either the name, the index, or both, using the following methods: InternetHeaders.Value(Name as String) as String InternetHeaders.Value(Name as String, Index as Integer) as String InternetHeaders.Value(Index as Integer) as String InternetHeaders.Name(Index as Integer) as String Likewise, I can delete individual headers using either the name or the name and the index: InternetHeaders.Delete(Name as String) InternetHeaders.Delete(Name as String, Index as Integer) InternetHeaders.DeleteAllHeaders If I want to add an entirely new header to the object, I call the AppendHeader method: InternetHeaders.AppendHeader(Name as String, Value as String) If I want to modify an existing header, or add a new one if it doesn't exist, I can call the SetHeader method: InternetHeaders.SetHeader(Name as String, Value as String) From CGI to HTTPServerOne of the challenges of working with sockets is the fundamentally asynchronous nature of computer networks. There is almost always some kind of network latencya period of time between when a request is made and when an answer is received. More importantly, a request may not arrive complete. Rather, it will arrive in batches and your program needs to know what to expect and how to gather up all the data prior to acting on it. You've seen an example of the HTTPSocket class in the RSSReader application, but we cannot use it for a web server because the HTTPSocket class plays the role of the client. To create a web server, you will need to use the TCPSocket class, which is the super class of HTTPSocket. A web server is used to view web pages, and a web page is created using html. Here's an example of a page that I will view in the sample application:
Figure 9.3 shows what this page looks like rendered in a web browser: Figure 9.3. Web browser view.One thing that you should notice about this file is that the references to images include URLs to the images, not the image itself. This means that when you access a page, you generate a request for the page itself, plus requests for all the images that are referenced within the html of the page request. When the browser gets the source html page, it then goes through and requests all the individual images. This means that a web server needs to be prepared to handle a lot of concurrent requests. If I wrote the server using TCPSocket alone, I would run into a problem because each new request needs a new socket connection (the HTTP/1.1 protocol lets you keep the socket connection open and make multiple requests through the same connection, but this sample closes the socket connection after each individual request for simplicity's sake). To accommodate this, you need a way to manage a pool of sockets that are available for requests, a way to hand off requests to these sockets when they come in, and a way to create new sockets if all the current sockets are being used. Fortunately, REALbasic provides a class that does just that for you. ServerSocket ClassThe ServerSocket class manages a pool of sockets for you, and this is what I will use in the web server. The ServerSocket class is one of those classes that will save you hours of coding if you do much with sockets. Not only does it handle one of the most challenging aspects of working with sockets, which is managing a pool of them to handle multiple requests, it also does it in a way that is remarkably intuitive and easy to use. To use it, the ServerSocket instance must be persistent. You can use it like a Control by dragging it onto a Window, or create it as a property of a Window or App object. Whichever you do, the important part is that it persists because this one object will be listening for requests and then spinning them off throughout the time the server is running. There are a few properties to set at first. The first, which should come as no surprise, is setting the port to listen to ServerSocket.Port as Integer Then you also need to tell the object some information about how to handle socket creation. Because some overhead is associated with instantiating a new socket when a request is made, it's a good idea to have some sockets ready and waiting for the next request. You can set the following property to determine the minimum number of sockets to be instantiated when listening for requests: ServerSocket.MinimumSocketsAvailable as Integer The flip side of this is that you also want to set a limit as to how many sockets can get created. If all of a sudden the server is handling thousands of requests, you could quickly run out of memory from spinning off too many sockets. You can set this limit with the following property: ServerSocket.MaximumSocketsConnected as Integer There are two other properties that can be useful: ServerSocket.IsListening as Boolean ServerSocket.LocalAddress as String If you want to check to see if the ServerSocket instance is listening for connections, check the IsListening property. Also, if you want to know what the local IP address is, you can check LocalAddress. After the necessary properties are set, you can tell the ServerSocket instance to start listening for connections by calling the Listen method: ServerSocket.Listen As you might expect, you can tell it to stop by calling this method: ServerSocket.StopListening Everything that I've covered so far composes the "easy part." All the heavy lifting is done in the AddSocket event: ServerSocket.AddSocket() as TCPSocket Handles Event "Heavy lifting" is actually overstating things a bit. The AddSocket event is triggered when a request comes in on the port that the ServerSocket is listening to. You need to write the code in this event that defines how you respond to the request. The response is to instantiate a socket of some sort and then go back to listening for more new requests. The socket you instantiate at this point is the socket that will respond to the request. If something goes wrong in the process, the ServerSocket error event is triggered: ServerSocket.Error(ErrorCode as Integer) Handles Event The HTTPServer ApplicationTo implement the web server, I will create a subclass of ServerSocket called HTTPServer and create a subclass of TCPSocket called HTTPConnection. This will be the socket I instantiate when a new request comes in. First, take a look at the HTTPServer class to see how new instances of HTTPConnection are handled. Class HTTPServer Inherits ServerSocketThe HTTPServer class is simple. There is one property: DocRoot As FolderItem This property refers to the root directory that will be used by the web server. When a request for a page is received, the socket will look for the file within the DocRoot FolderItem. In addition to the DocRoot property, the subclass also includes an implementation of the AddSocket event, which follows. Listing 9.1. Function AddSocket() As TCPSocket Handles Event
In the sample application, the HTTPServer instance is a property of Window1. Window1 has an EditField, where messages about the current state or activity of the server are displayed. It also has a PushButton labeled Request that will be used to stimulate several simultaneous requests, but don't worry about that at this point. When the HTTPServer class is first instantiated, it opens up a pool of sockets. In the sample application, each time a socket is instantiated, it is displayed in the Window. You can see an example in the following figure: Figure 9.4. Pool of sockets.
In the Open event of Window1, the HTTPServer instance is instantiated and assigned to the Window1.Server property. In addition to being instantiated, the values are set for the minimum and maximum number of sockets that are available. The port is set to 8080, which is commonly used as an alternative to the standard port 80 for web servers. A lot of applications that are servers use 8080, so a person can try out the application without interfering with a real web server running on that computer. Listing 9.2. Sub Window1.Open() Handles Event
In addition to the Open event, Window1 also implements the AppendString method, which is a simple way of displaying output to the window as the application is running. Listing 9.3. Sub Window1.AppendString(s as String)
Handling GET RequestsSo far, you've seen what happens when the application is first run and the initial sockets are created. What is more interesting is to see how the application responds to actual HTTP requests. We do not need a web browser to generate those requests because we can do it programmatically with REALbasic. In the sample application, there are four HTTPSockets that can be called to make HTTP requests against the web server. All I did was drag them onto Window1. If you click the Request button, they are all triggered as close to simultaneously as possible. Figure 9.5 shows which socket received which request, and is identified by the TCPSocket.Handle property: Figure 9.5. Request button output.
Control Window1.PushButton1:Listing 9.4. Sub PushButton1.Action() Handles Event
Now if you start the application and click the Request button, this is the output that you will see: At this point, you don't really need to worry too much about what's happening, except to understand that whenever a socket receives a request, the DataAvailable event is triggered. In the HTTPConnection class, I have this line of code that displays in the Window the Handle for each socket that has received a request: Window1.AppendString("Socket " + str(me.Handle) + ": Data available") In the previous figure, clicking the Request button and generating four simultaneous events caused four different sockets to respond with handles 20, 22, 24, and 21, respectively. With this information, you can be assured that the HTTPServer is properly getting requests and handing them off to different sockets. At the beginning of this section, I shared a sample html page and showed what the page would look like when displayed in a web browser. If I call this page index.html and place it as a text file in the DocRoot FolderItem (which is a property of the HTTPServer object), I can access it using my web browser by typing in the following URL: http://localhost:8080/ When I try to access this page through my browser, the browser shows the page, and the web server application displays the following information: Socket 16: Data available Path: / Socket 16: Data available Path: /images/REALbasicBook.jpg You can see that this page generated two requests. The first request was for the path / and the second was for /images/REALbasicBook.jpg. The / is the root file, and the default name for that file is index.html, so the application automatically looks for a file named index.html if there is no file listed. If you go back and examine the original html file, you will see the following in the code: <img src="/books/2/97/1/html/2/images/REALbasicBook.jpg" align="left" /> This is the html tag that is responsible for the second request. As you might imagine, a single page can generate quite a few requests, depending on how many images are on the page. This example is interesting because the same socket handled both requests. At this point, you may want to understand just how the HTTPConnection socket sent the html page and the image to the web browser, but before I go into the code details, I want to show you a few other examples as well. Handling POST Requests: Posting Forms and Transferring FilesFollowing is an example of how the application handles a form. Here is the html used to create the form (I have named the file form.html and saved it in the DocRoot): <!DOCTYPE html PUBLIC "-//W3C//DTD html 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>HTTPServer Form</title> </head> <body> <p><b>Tell me your name:</b></p> <form name="form1" method="post" action="http://localhost:8080/form.html"> <p> <input type="text" name="textfield"> </p> <p> <input type="submit" name="Submit" value="Submit"> </p> </form> </body> </html> When displayed, it looks like Figure 9.6: Figure 9.6. Form.If you fill out this form using your web browser and click Submit, the page shown in Figure 9.7 is returned as the result: Figure 9.7. Hello, Mark results.The form generated a POST request, which was processed by the web server application. The value that was filled in on the form was used to compose the output, which in this case was "Hello, Mark". Although a POST is the most common way to handle form data, you can also use a GET request as well. When you do, instead of having the POST data appended to the request, the data from the form is appended to the URL itself. The following example shows what happens when the following URL is used to access the form.html document: http://localhost:8080/form.html?dog=lucy The sample app takes the data after the "?" character, parses it, and responds thus: Figure 9.8. Get with an extended URL.In both of these examplesthe POST and the GET requestthe sample application checks the values that are sent to it and responds accordingly. When you read through the sample code later on in the chapter, you will see exactly how it does this. Uploading a FileThe previous two examples used form data of some sort to generate a request. You can also use a POST to upload a file. Here is the html for a page that will let you upload a file to the web server application: <!DOCTYPE html PUBLIC "-//W3C//DTD html 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>HTTPServer Form</title> </head> <body> <p><b>Please specify a file, or a set of files:</b></p> <form name="form1" method="post" enctype="multipart/form-data" action="http://localhost:8080/form.html"> <p> <input type="file" name="datafile" size="40"> </p> <p> <input type="submit" value="Send"> </p> </form> </body> </html> In the examples, I have named this file fileform.html. There are two distinguishing characteristics to the html used to create this form. First, there is a value of "multipart/form-data" for the enctype attribute. This tells the server that a file is being uploaded and how to handle it. There is also an input element whose type is "file" in contrast to "text" as in the previous example. Figure 9.9 shows you what this form looks like when viewed in a web browser: Figure 9.9. Form for uploading a file.Next is an excerpt from the request that is generated when this form is filled out:
In this example, I uploaded a Microsoft Word document called Threading.doc. All the data in the document isn't shown; for brevity's sake I replaced it with the text <lots of data>. Now you have seen examples of several kinds of requests and how the web server application handles them. The interesting part is seeing exactly how it gets done. There are two additional classes HTTPConnection, which is a subclass of TCPSocket, and HTTPRequest, which is a subclass of Dictionary, that are used to process the request. The hard part is that a request does not come in all at once. You may get it in small segments, with each instance triggering a DataAvailable event in the HTTPConnection object. Not only do you need to wait until you have the entire request, you also need to see what kind of request it is and respond accordingly. To make this happen, take the following steps:
All of this is managed by the HTTPConnection class, instances of which are the sockets spun off by the HTTPServer object. Class HTTPConnection Inherits TCPSocketThe HTTPConnection class is a subclass of TCPSocket rather than the HTTPSocket class because the HTTPSocket class is designed to be the client, or requesting socket, rather than the serving socket. It has three properties: Request As HTTPRequest Stream As BinaryStream Protected DocRoot As FolderItem The request object holds the values that are passed in the header, including data uploaded from forms. The response is written to the client using a BinaryStream object, and the DocRoot FolderItem has already been discussed. This is the root folder that all requests are considered relative to. Listing 9.5. Sub HTTPConnection.Constructor(aDocRoot as FolderItem)
It is in the DataAvailable event that everything happens. The code is heavily commented, so you can read through it to see what is happening. Listing 9.6. Sub HTTPConnection.DataAvailable() Handles Event
Listing 9.7. Sub HTTPConnection.SendComplete(userAborted as Boolean) Handles Event
Listing 9.8. Sub HTTPConnection.parseHeader(data as String)
Class HTTPRequest Inherits DictionaryListing 9.9. HTTPRequest Properties
Listing 9.10. Sub HTTPRequest.Constructor(aDocRoot as FolderItem)
URL encoded data can be passed either in the body of a POST request or in the URL of a GET request. Either way, the same encoding is used, and the following method parses it and assigns the key/value pairs to the HTTPRequest object. Listing 9.11. Sub HTTPRequest.getQueryString(QueryString as String)
The path to the requested file is sent as part of the first line of the header. The following function calculated the total path relative to the DocRoot FolderItem. If the file requested is a directory, the code looks for a default filename, such as index.html, and returns a reference to that FolderItem. Listing 9.12. Function HTTPRequest.getResponseFileFromPath(aPath as String) as FolderItem
SSLSocketThe Secure Sockets Layer (SSL) and Transport Layer Security (TLS) are protocols for transmitting encrypted data over the Internet. SSL versions range from 1.0 to 3.0, and TLS is considered the successor to SSL, and is currently at version 1.0 (version 1.1 may be out by the time you read this). The SSLSocket class is the secure version of the TCPSocket class and with a few exceptions, they both work the same way. There is one important caveat: SSLSocket cannot be used to listen for requests securely; it can only make connections to other secure sockets (like that of a secure web server). There are a few properties you need to set (or read) when working with SSLSocket. To specify what type of secure connection to use, use the following property: SSLSocket.ConnectionType as Integer
You also do not have to use the SSLSocket class exclusively for secure sockets. Set the following value to true if you want to establish a secure connection alse if you do not want to establish a secure connection: SSLSocket.Secure as Boolean The following read-only properties will tell you if you are currently connected to another socket, or if you are in the process of connecting, respectively. SSLSocket.SSLConnected as Boolean(Read Only) SSLSocket.SSLConnecting as Boolean (Read Only) |