March 97 - Using Newton Internet Enabler to Create a Web Server
Using Newton Internet Enabler to Create a Web
Ray Rischpater
With Newton Internet Enabler (NIE), a whole host of TCP/IP-based
applications become possible on the Newton. Internet clients for
e-mail, Web browsers, and other common services are springing up,
and developers are using NIE to provide portable access to legacy
systems and databases. But how do you get started? This article
will explore the details of using NIE by presenting a sample
Internet application -- a working Web server.
The long-awaited Newton Internet Enabler (NIE for short) opens up the Internet to
Newton users -- the personal communications platform can now speak TCP/IP to the
rest of the world. With so many possible applications that could use it, programming
under NIE is not only useful, it's sure to be a lot of fun!
I've chosen to introduce NIE by looking at a sample application called nHTTPd -- a
Newton-based Web server that implements the HTTP protocol. Throughout the article,
I'll assume you're familiar with the basics of Newton software development and
TCP/IP; see the Newton Programmer's Guide and the NIE documentation for reference.
SAMPLE APPLICATION BASICS
Before delving into the sample application, let's take a moment to see what's included
in NIE. NIE provides support for three distinct entities. There's a Link Controller for
sharing an underlying link (that is, the low-level PPP or SLIP connection) between
multiple applications and performing expect-response scripting. It also provides a
Domain Name Service (DNS) to map back and forth between host names and IP
addresses. Finally, of course, there's an implementation of TCP and UDP through
NewtonScript's endpoints. For details of how the different parts of NIE work, see "NIE
in a Nutshell.
______________________________
NIE IN A NUTSHELL
NIE's Link Controller is responsible for establishing the link and passing a
prepared link to the SLIP or PPP manager for the TCP/IP stack to use. In NIE
1.0, authentication is necessary before the stream is switched to PPP --
there is no support for PAP or CHAP. The only Internet links supported in NIE
1.0 are PPP or SLIP over serial or modem links. (NIE 1.1 adds support for
PAP, CHAP, and interactive authentication.) There's a setup utility for users
to enter information about their Internet service provider, such as the access
number, link-level protocol, and a connection script.
The NIE 1.0 DNS is admittedly austere. You can map host names to IP addresses
and the reverse, or you can look up a mail server for a particular host, but
less common kinds of queries are not supported. Name resolution is
nonrecursive -- NIE won't resubmit queries based on earlier responses.
Mappings are cached for a period of time to minimize repeated network
queries. If your application requires more general domain name functionality,
you'll find yourself writing your own, but for most applications this shouldn't
be necessary.
Access to TCP or UDP sockets is available through the standard endpoint API,
with new options to support the different things you may want to do with such
an endpoint.
______________________________
Our sample application (which accompanies this article on this issue's CD anddevelop's
Web site) is a bare-bones HTTP version 0.9 server. To help you understand the code,
here are the basics of the HTTP 0.9 protocol:
• Objects (usually Web pages) are referred to by a URL, which is a
concatenation of the protocol being used (http), the host name serving the
document (such as www.lothlorien.com), and the path and filename of the
document being served (such as "/" for the root document). A typical URL is
http://www.lothlorien.com/.
• Objects are fetched with the GET instruction. A request comes in
containing the word GET followed by the partial URL (the path and filename,
but not the protocol or host name) of the object being fetched, followed by a
carriage return and linefeed pair. For example:
GET /
• Form data can be retrieved with an extension of the URL: the URL is
followed by a question mark and some text. Within this text, field names may
be declared to the left of an equal sign (=), with the data in the field to the
right of the equal sign, although data is not required. Multiple field
declarations are separated by an ampersand (&). A form with two fields might
look like this:
GET /myapp/search.html?field1=hello&field2=world
• In response, the HTTP server dynamically creates a response object that's
returned to the client.
For the latest version of the sample application, check out the latest
CD and develop's Web site, as this code is perpetually evolving.*
HANDLING REQUESTS
Since the Newton doesn't have conventional directories or files, we need to make some
compromises to translate directory- and file-oriented specifications into something
more meaningful on a Newton. My implementation uses a registry to correlate Newton
data (such as application soup entries) with translators capable of creating Web
objects from Newton data. These translators convert frames or soup entries to HTML
or pure text, and nHTTPd has an API with three methods to allow other programs to
register as translators:
• nHTTPd:RegTranslator(inAppSym, inPathStr, inCallbackSpec) registers
the callback specification frame inCallbackSpec for your application (whose
application symbol is inAppSym) and the partial URL path indicated by
inPathStr. (More about the callback specification frame in a moment.) It's
strongly urged that you use a path beginning with your application symbol to
prevent collisions in the path name space.
• nHTTPd:UnRegTranslator(inAppSym, inPathStr) removes the callback
specification frame for the partial URL path indicated by inPathStr.
• nHTTPd:UnRegTranslators(inAppSym) removes all registered translators
for the application whose symbol is inAppSym.
The callback specification frame provides a mechanism for nHTTPd to invoke a
translator and has the same format as a regular communications callback specification.
The only required slot is a CompletionScript slot (since all calls are asynchronous),
containing a function that is passed, in order, these three arguments:
• inEp, the endpoint associated with the request
• inData, a frame containing the data received from the client
• inErr, an error code, or nil if the request began with no errors
The inData frame has at least four slots:
• raw is the original partial URL.
• data is the partial URL without the path.
• path is the path of the partial URL without the filename.
• tag is the partial URL with neither path nor any form data.
A formslot, if present, contains slots named for fields in the request; each slot contains a string bearing the value of the named field. Listing 1 shows what our second
GET example above would expand to.
______________________________
Listing 1. A processed URL frame
raw: "/myApp/search.html?field1=hello&field2=world",
data: "search.html?field1=hello&field2=world",
path: "myApp",
tag: "search.html",
form: {
field1: "hello",
field2: "world
}
}
______________________________
We'll probably also want to include a _parent slot in the callback specification, so
that it can inherit from a useful context within the translator (see Listing 2).
______________________________
Listing 2. The translator callback specification frame
CompletionScript := func(inEp, inData, inErr)
begin
inEp:Output("Why did you want " & inData.tag & "?", nil, {
async: true,
completionScript: func(inEp, inOpt, inErr)
begin
// Do something useful!
...
end,
}
);
inEp:Close();
end,
}
______________________________
OUR STATE MACHINE
Like most communications programs, our application can be represented as a state
machine. The application can be in one of a finite number of states: while in one state,
the application waits for an event, and the functionality of the program is encapsulated
in the actions performed during a state change. The nHTTPd state machine is shown in
Figure 1. The arrows between states indicate transitions -- where the program
actually does something. For clarity, my implementation of this state machine uses
symbols to denote each state, but using integer constants would save memory.
Figure 1. The nHTTPd server's state machine
Using a state machine provides many advantages during application development.
Primarily, it's an organizational tool, allowing us to accurately plan and visualize
what's going on inside our application. There's something very soothing (even if you're
as bad a mechanic as I am) in imagining an application running as a set of gears
clanking around, and it's a lot easier to follow than a spaghetti of method calls. There's
also the benefit that most existing protocols are specified and implemented as state
machines, which makes porting somebody else's code a little easier. While the Newton
is generally a difficult target for porting, porting from a state machine is significantly
easier than porting from most other kinds of implementation.
Our state machine is a little odd, because there are really two separate machines
running at once. We use a protoTCPServer (discussed in detail later) to manage the
Internet link and provide endpoints listening on port 80 (the standard HTTP server
port number). This state machine invokes an instance of protoHTTPServer, which
itself is a smaller state machine that invokes your application.
GETTING A REQUEST
nHTTPd's protoTCPServer deals with all the details of establishing a link, as well as
instantiating and listening on a TCP socket. The protoTCPServer has two methods:
• Start requests the link, instantiates an endpoint, and awaits a connection.
When a connection is made, it's accepted, and the callback specification's
CompletionScript is called with the resulting endpoint. The callback passed to
Start is invoked whenever nHTTPd receives a connection on the monitored
port.
• Stop cancels any open endpoints, disconnects and destroys them, and
releases the open link. When we're done with the protoTCPServer, we use the
Stop method to shut down all pending connections and close down the link. The
callback for Stop is called when the teardown is complete and the link is
released.
USING THE LINK CONTROLLER
Looking at the Start method gives a good idea of what's involved in using the Link
Controller for the average application. The Link Controller APIs are all asynchronous,
so as part of the arguments you must provide a context frame and the symbol of the
completion script to be invoked (we'll see the details in a bit). Generally, you'll
perform the following steps:
1. Your application will give the user a chance to configure the link by
calling InetOpenConnectionSlip.
2. Your application will call InetGrabLink to request a link from the system.
3. Periodically as the link is established, the callback you provided to
InetGrabLink will be invoked with status information. In your callback, you'll
call InetDisplayStatus to notify the user of the progress of the link
establishment.