20. HTTP Toolkit
Back to Table of Contents
Overview:
This is the comprehensive reference for Daylight's HTTP Toolkit and is for use by programmers. Covered topics include: 1) the Application Programmers Interface, 2) server & CGI program structure, 3) the essential properties used to construct programs, 4) the entire set of properties, and 5) example routines and program source code demonstrating use of the toolkit.
What is the HTTP Toolkit?
The HTTP Toolkit is a web-based programmer library containing a well-behaved set of entry points and conforms to specifications of RFC 1945 "Hypertext Transfer Protocol -- HTTP/1.0" (available at http://www.ietf.org/rfc/rfc1945.txt). The HTTP Toolkit allows you to receive requests to and send responses from Daylight tools over the web. The HTTP Toolkit is a faster, simplier alternative to Common Gateway Interface (CGI) programming and Apache modules.
Why the HTTP Toolkit?
The HTTP Toolkit was created because most people have a browser, such as Internet Explorer or Netscape Navigator, so the client application is already installed! The HTTP Toolkit leverages the ubiquitity of the internet by enabling programmers to serve information over the web.
Prerequisites
The intended audience for this manual is programmers. A modest knowledge of UNIX and the C programming language are key ingredients for understanding this manual. Familiarity with UNIX commands and the ability to configure the UNIX environment is a must. Experience in C programming is necessary because examples are illustrated using the C language. Further, knowledge of HTTP headers and HTML syntax is helpful.
Daylight Software. You will need to download and install the software from http://www.daylight.com/download/index.html and a license for the SMILES and HTTP Toolkits (for licensing, email info@daylight.com). Source code for programs listed as figures in this manual are available in the "Contrib" area of your Daylight installation ($DY_ROOT/contrib/src subdirectory).
Web Server Configuration. To use a HTTP Toolkit program as a CGI, your local web server will need the LD_LIBRARY_PATH and DY_LICENSEDATA environment variables as well as Daylight's Installation Guide.
Organization
The HTTP Toolkit is not difficult to understand, but you must understand the Application Programmer Interface (API) and how properties control behavior before you start programming.
Section 20.1. The Application Programmer Interface, defines the programmer library entry points, arguments, and behavior.
Section 20.2. The Structure of Programs, illustrates how to use the API to create "run-once" and "run-forever" programs.
Section 20.3. The Essential Properties, shows the key ingredients of receiving a request and sending a response and illustrates the classic example, "Hello World!".
Section 20.4. The Entire Set of Properties, lists a description of all properties and illustrates use of POST data in the example, "Canonical SMILES".
Section 20.5. Working with the Toolkit, illustrates use of the toolkit with programming examples.
Conventions
This manual uses the following typographical conventions:
Italics
Used for URLs, filenames, variables, new terms where they are defined, and emphasis
Constant width
property names, routine names, source code, computer output, and any literal text
Constant width bold
Used in examples to show commands or test that would be typed literally by you
ALL CAPS
Used for object names, environment variables and HTML tags
Scope
Although the intended use of this toolkit is for deployment of Daylight tools, the HTTP Toolkit is a general web server; any kind of information can be deployed using this tool.
20.1 The Application Programmer Interface
The Application Programmer Interface (API) implements the following objects:
HTTP Toolkit Object Classes
|
HTTP
| server object for encapsulation of the HyperText Transfer Protocol
|
TRANSFER
| message transfer object for handling requests & responses
|
and consists of the following entry points:
- dt_alloc_http_server
- dt_http_get
- dt_http_put
That's it! The dt_alloc_http_server entry point is used to create a service on the web. The dt_http_get and dt_http_put entry points are used to receive and respond to requests, respectively.
20.1.1 Create a Service (dt_alloc_http_server)
The entry point for creating a web service is:
dt_Handle dt_alloc_http_server(dt_Integer port);
This allocates a new HTTP object. The port argument is a number in the range of 0 to 65535 and identifies the process on the network. A port number of 0 means that the service behaves as a CGI. In other words, it runs once and terminates. A port number greater than zero means the service listens on that port and behaves as a server. In other words, it runs forever. The return value of this routine is the handle of a new HTTP object or NULL_OB if an error is detected. See the manual page on dt_alloc_http_server for more information.
20.1.2. Receive a Request (dt_http_get)
The entry point for receiving a request is:
dt_Handle dt_http_get(dt_Handle http);
This allocates a new TRANSFER object. The http argument is the HTTP object returned from dt_alloc_http_server. The return value is the handle of a new TRANSFER object or NULL_OB if no request is received or an error is detected. See the manual page on dt_http_get for more information.
20.1.3 Send a Response (dt_http_put)
The entry point for sending a response is:
dt_Boolean dt_http_put(dt_Handle xfer);
This sends a response. The xfer argument is the TRANSFER object returned from dt_http_get. The return value is TRUE if data is successfully transmitted, or FALSE if an error is detected. See the manual page on dt_http_put for more information.
20.2 The Structure of Programs
A HTTP Toolkit program can be designed to function as a CGI service (run once) or a web service (run forever). Also, a program can be designed to function as either one or the other. The following sections contain source code written in the C language for each of the three programs.
The following examples test the API and default behavior of the Toolkit. The TRANSFER object, as returned from dt_http_get, contain properties reflecting the request. Most noteworthy is the Uniform Resource Locator (URL) property. Since the following examples do nothing with request properties and the toolkit initializes the status code to 404 (meaning the URL is not found), we expect each call to dt_http_put to produce a "404 Not Found" response. This behavior is a valid HTTP service, although not useful beyond testing. Nonetheless, it is encouraged that you follow details and reproduce results, so you can proceed with confidence into more complicated aspects of the Toolkit.
20.2.1 Build a CGI
The following source code is an example of a main routine designed to function as a CGI:
Figure 20.2.1-1. Program Source Code: CGI
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
|
#include "dt_smiles.h"
#include "dt_http.h"
int main() {
dt_Handle http, xfer;
dt_Boolean stat;
/* create a service */
http = dt_alloc_http_server(0);
/* receive a request */
xfer = dt_http_get(http);
/* send a response */
stat = dt_http_put(xfer);
return !stat;
}
|
This is a bare bones program that creates a service, receives a request, and sends a response. The Daylight include files are order-depenent (lines 1 & 2). The port argument (line 9) is 0 indicating a CGI service. Nothing is done with the request (line 12), so the response (line 15) is the default response ("404 Not Found"). The program is missing error checking and memory deallocation, but that's okay, the Toolkit can handle it. Let's make a note to tidy it up later and move ahead for now.
This program serves as a fundamental test of HTTP Toolkit CGI service. To test CGI service, save the above code as test-http-cgi.c and make the program (or you can get and make it from the "Contrib" area of Daylight Software).
To compile on Red Hat Linux or SGI Irix systems:
cc -o test-http-cgi test-http-cgi.c -I$DY_ROOT/include -L$DY_ROOT/lib -ldt_http -ldt_smiles
On Sun Solaris systems:
cc -o test-http-cgi test-http-cgi.c -I$DY_ROOT/include -L$DY_ROOT/lib -ldt_http -ldt_smiles -lsocket
On Macintosh Darwin (OSX) systems:
cc -o test-http-cgi test-http-cgi.c -I$DY_ROOT/include -L$DY_ROOT/lib -ldt_http -ldt_smiles << EOF
/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
EOF
Now we've got our HTTP Toolkit CGI application compiled, let's use it. Execute the program with end-of-file markers:
./test-http-cgi << EOF
GET / HTTP/1.0
EOF
You should see output HTML code that contains the following:
404 Not Found
The server cannot find the requested URL.
Daylight/4.81 Server at www.daylight.com Port 0
The program received the request and responded "404 Not Found". In your output, the version (4.81) and host machine (www.daylight.com) will vary and additional header lines (Date, Server, and Content) may appear. If you're not sure if your program output is correct, compare it to test-http-cgi.ref (see "Contrib" area of Daylight Software), which is an example of complete output from the CGI program.
Before we move on, let revisit the CGI program source code and tidy it up:
Figure 20.2.1-2. Program Source Code: CGI (revised)
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
|
#include <stdio.h>
#include "dt_smiles.h"
#include "dt_http.h"
int main() {
dt_Handle http, xfer;
dt_Boolean stat = 1;
/* create a service */
if (NULL_OB == (http = dt_alloc_http_server(0))) {
fprintf(stderr, "dt_alloc_http_server failed\n");
return 1;
}
/* receive a request */
if (NULL_OB != (xfer = dt_http_get(http))) {
/* send a response */
stat = dt_http_put(xfer);
dt_dealloc(xfer);
}
dt_dealloc(http);
return !stat;
}
|
Compared to the original CGI program, the main portion (Figure 20.2.1-1, lines 9, 12 and 15) are logically reworked (lines 10 through 18) to handle errors and memory deallocation. Now, let's move on to a server application.
20.2.2. Build a Server
The following source code is an example of a main routine designed to function as a server.
Figure 20.2.2 Program Source Code: Server
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
|
#include <stdio.h>
#include "dt_smiles.h"
#include "dt_http.h"
int main(int argc, char **argv) {
dt_Handle http, xfer;
dt_Integer port = 0;
if ((2 != argc) || (1 != sscanf(argv[1], "%d", &port))) {
fprintf(stderr, "%s: missing integer argument\n", argv[0]);
return 1;
}
if ((0 >= port) || (65535 < port)) {
fprintf(stderr, "%s: port number out-of-range (1-65535)\n", argv[0]);
return 1;
}
/* create a service */
if (NULL_OB == (http = dt_alloc_http_server(port))) {
fprintf(stderr, "dt_alloc_http_server failed\n");
return 1;
}
for (;;) {
/* receive a request */
xfer = dt_http_get(http);
if (NULL_OB != xfer) {
/* send a response */
dt_http_put(xfer);
dt_dealloc(xfer);
}
}
}
|
The essential difference between this code and the previous CGI example is that the calls to dt_http_get and dt_http_put are within an infinite loop.
This program serves as a fundamental test of HTTP Toolkit web service. To test the web service, save the above code as test-http-server.c and make the program (see the previous section, or the "Contrib" area of Daylight Software):
To compile on Red Hat Linux or SGI Irix systems:
cc -o test-http-server test-http-server.c -I$DY_ROOT/include -L$DY_ROOT/lib -ldt_http -ldt_smiles
On Sun Solaris systems:
cc -o test-http-server test-http-server.c -I$DY_ROOT/include -L$DY_ROOT/lib -ldt_http -ldt_smiles -lsocket
On Macintosh Darwin (OSX) systems:
cc -o test-http-server test-http-server.c -I$DY_ROOT/include -L$DY_ROOT/lib -ldt_http -ldt_smiles << EOF
/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
EOF
Then, execute the program:
./test-http-server 1234
This runs the web service on port 1234. Point your browser to the machine running the service and place a ":1234" at the end of the location, e.g., http://www.daylight.com:1234. This web service produces the same output as the CGI, except for the port number.
Not Found
The requested URL / was not found on this server.
Daylight/4.81 Server at www.daylight.com Port 1234
In your output, the version (4.81) and host machine (www.daylight.com) will vary and additional header lines (Date, Server, and Content) may appear. If you're not sure if your program output is correct, compare it to test-http-server.ref (see "Contrib" area of Daylight Software), which is an example of complete output from the server program.
20.2.3. Build a Dual-Purpose Server & CGI
The following source code is an example of a main routine designed to function as a server or a CGI.
Figure 20.2.3 Program Source Code: Dual-Purpose Server & CGI
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
|
#include <stdio.h>
#include "dt_smiles.h"
#include "dt_http.h"
int main(int argc, char **argv) {
dt_Handle http, xfer;
dt_Integer port = 0;
dt_Boolean stat = 1;
if ((2 == argc) && (1 != sscanf(argv[1], "%d", &port))) {
fprintf(stderr, "%s: missing integer argument\n", argv[0]);
return 1;
}
if ((0 > port) || (65535 < port)) {
fprintf(stderr, "%s: port number out-of-range (0-65535)\n", argv[0]);
return 1;
}
/* create a service */
if (NULL_OB == (http = dt_alloc_http_server(port))) {
fprintf(stderr, "dt_alloc_http_server failed\n");
return 1;
}
for (;;) {
/* receive a request */
xfer = dt_http_get(http);
if (NULL_OB != xfer) {
/* send a response */
stat = dt_http_put(xfer);
dt_dealloc(xfer);
}
/* run-once for CGI */
if (0 == port)
break;
}
dt_dealloc(http);
return !stat;
}
|
The essential difference between this code and the previous example is allowing port 0, breaking out of the loop, and memory deallocation.
This program serves as a fundamental test of HTTP Toolkit web service and CGI service in one program. To test the dual-purpose service, save the above code as test-http-server-cgi.c and make the program (or see "Contrib" area of Daylight Software).
To compile on Red Hat Linux or SGI Irix systems:
cc -o test-http-server-cgi test-http-server-cgi.c -I$DY_ROOT/include -L$DY_ROOT/lib -ldt_http -ldt_smiles
On Sun Solaris systems:
cc -o test-http-server-cgi test-http-server-cgi.c -I$DY_ROOT/include -L$DY_ROOT/lib -ldt_http -ldt_smiles -lsocket
On Macintosh Darwin (OSX) systems:
cc -o test-http-server-cgi test-http-server-cgi.c -I$DY_ROOT/include -L$DY_ROOT/lib -ldt_http -ldt_smiles << EOF
/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
EOF
Now, you can execute the program as a CGI:
./test-http-server-cgi << EOF
GET / HTTP/1.0
EOF
and as a server:
./test-http-server-cgi 1234
You should get the same results as the CGI and server programs in Section 20.2.1 and Section 20.2.2. If you've reproduced these results, you're on your way towards deployment of Daylight tools via a web sevice. Now, let's get into the essential properties of receiving requests and sending responses.
20.3 The Essential Properties
In the Daylight Toolkit, "named properties" associate name/value pairs with objects. In the HTTP Toolkit, named properties are used to get request and set response information associated with the TRANSFER object. For example, the TRANSFER object has a named property called htt_path reflecting the value of the requested URL. Let's suppose the URL is http://www.daylight.com/dayhtml/. If so, the TRANSFER object's named property htt_path (a property name) and would be "/dayhtml/" (its value).
20.3.1 Get a Request
The following lists the name, description, and datatype for the essential properties associated with receiving requests:
Table 20.3.1. Essential Properties of a Request
|
Name
| Description
| Datatype
|
htt_method
| request method
| dt_String
|
htt_path
| URL or path
| dt_String
|
htt_protocol
| protocol and version
| dt_String
|
- htt_method is the request method, for example, "GET", "HEAD", or "POST". Other values include "PUT", "DELETE", "LINK", and "UNLINK".
- htt_path is the URL or path, for example, http://www.daylight.com or /.
- htt_protocol is the protocol and version, for example, "HTTP/1.0" or "HTTP/1.1".
To get the essential request properties:
Figure 20.3.1. Example Source Code: Get a Request Property
- ....
- ....
- ....
|
method = dt_string(&mlen, xfer, 10, "htt_method");
path = dt_string(&plen, xfer, 8, "htt_path");
protocol = dt_string(&rlen, xfer, 12, "htt_protocol");
|
The htt_method_id, htt_path, and htt_protocol are property names and method, path, and protocol contain the property values. For more information, see the manual page for dt_string.
20.3.2 Set a Response
The following table lists the name, description, and datatype for the essential properties associated with sending responses.
Table 20.3.2. Essential Properties of a Response
|
Property Name
| Description
| Datatype
|
htt_code
| status code (200, 404, etc.)
| dt_Integer
|
htt_mime_type
| mime type (text/html, image/gif, etc.)
| dt_String
|
htt_content
| program data (HTML, GIF image, etc.)
| dt_String
|
- htt_code is the response status code and is initialized to 404 (Not Found) when a TRANSFER object is returned from dt_http_get. This property should be set to 200 for response that are "OK".
- htt_mime_type is the MIME type of the content, for example "text/html" or "image/gif", and is initialized to "text/html" when a TRANSFER object is returned from dt_http_get. The syntax conforms to RFC 1521 "MIME (Multipurpose Internet Mail Extensions)" avavilable at http://www.ietf.org/rfc/rfc1520.txt. When this property is not NULL, the value is used with the "Content-Type" header field.
- htt_content is the content of the response with NULL as the initial value. This property is the body of the response sent from the server to the client. When this property is not NULL, the length in bytes of the content is used with the "Content-Length" header field.
To set the essential response properties:
Figure 20.3.2. Example Source Code: Set a Response Property
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
|
dt_setinteger (xfer, 8, "htt_code", 200);
dt_setstring (xfer, 13, "htt_mime_type", 9, "text/html");
dt_setstring (xfer, 11, "htt_content", 12, "Hello World!");
dt_appendstring (xfer, 11, "htt_content", 12, "\n\nMethod: ");
dt_appendstring (xfer, 11, "htt_content", mlen, method);
dt_appendstring (xfer, 11, "htt_content", 11, "\nPath: ");
dt_appendstring (xfer, 11, "htt_content", plen, path);
dt_appendstring (xfer, 11, "htt_content", 11, "\nProtocol: ");
dt_appendstring (xfer, 11, "htt_content", rlen, protocol);
dt_appendstring (xfer, 11, "htt_content", 1, "\n");
|
For more information, see the manual page for dt_setinteger, dt_setstring and dt_appendstring.
20.3.3 Processing the GET Method: Hello World!
Now we have the essential ingredients to build our first program that gets request and sets response properties. Now, let's work through a program for the GET method. Consider the following "Hello World!" program:
Figure 20.3.3. Program Source Code: Hello World!
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
|
#include <stdio.h>
#include "dt_smiles.h"
#include "dt_http.h"
static void my_handler(dt_Handle xfer);
int main(int argc, char **argv) {
dt_Handle http, xfer;
dt_Integer port = 0;
dt_Boolean stat = 1;
if ((2 == argc) && (1 != sscanf(argv[1], "%d", &port))) {
fprintf(stderr, "%s: missing integer argument\n", argv[0]);
return 1;
}
if ((0 > port) || (65535 < port)) {
fprintf(stderr, "%s: port number out-of-range (0-65535)\n", argv[0]);
return 1;
}
/* create a service */
if (NULL_OB == (http = dt_alloc_http_server(port))) {
fprintf(stderr, "dt_alloc_http_server failed\n");
return 1;
}
for (;;) {
/* receive a request */
xfer = dt_http_get(http);
if (NULL_OB != xfer) {
/* process request, prepare response */
my_handler(xfer);
/* send a response */
stat = dt_http_put(xfer);
dt_dealloc(xfer);
}
/* run-once for CGI */
if (0 == port)
break;
}
dt_dealloc(http);
return !stat;
}
static void my_handler(dt_Handle xfer) {
dt_String method, path, protocol;
dt_Integer mlen, plen, rlen;
method = dt_string(&mlen, xfer, 10, "htt_method");
path = dt_string(&plen, xfer, 8, "htt_path");
protocol = dt_string(&rlen, xfer, 12, "htt_protocol");
dt_setinteger (xfer, 8, "htt_code", 200);
dt_setstring (xfer, 13, "htt_mime_type", 9, "text/html");
dt_setstring (xfer, 11, "htt_content", 12, "Hello World!");
dt_appendstring (xfer, 11, "htt_content", 12, "\n\nMethod: ");
dt_appendstring (xfer, 11, "htt_content", mlen, method);
dt_appendstring (xfer, 11, "htt_content", 11, "\nPath: ");
dt_appendstring (xfer, 11, "htt_content", plen, path);
dt_appendstring (xfer, 11, "htt_content", 11, "\nProtocol: ");
dt_appendstring (xfer, 11, "htt_content", rlen, protocol);
dt_appendstring (xfer, 11, "htt_content", 1, "\n");
}
|
The only difference between this code and the previous "Dual-Purpose Server & CGI" example is the htt_handler routine, which gets the method, path, and protocol request properties then sets the return code, mime type, and data response properties.
This program serves as a test of HTTP Toolkit essential properties. To test the "Hello World!" service, save the above code as http-hello-world.c and make the program (or see "Contrib" area of Daylight Software).
To compile on Red Hat Linux or SGI Irix systems:
cc -o http-hello-world http-hello-world.c -I$DY_ROOT/include -L$DY_ROOT/lib -ldt_http -ldt_smiles
On Sun Solaris systems:
cc -o http-hello-world http-hello-world.c -I$DY_ROOT/include -L$DY_ROOT/lib -ldt_http -ldt_smiles -lsocket
On Macintosh Darwin (OSX) systems:
cc -o http-hello-world http-hello-world.c -I$DY_ROOT/include -L$DY_ROOT/lib -ldt_http -ldt_smiles << EOF
/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
EOF
Execute the program as a CGI:
./http-hello-world << EOF
GET / HTTP/1.0
EOF
You should see output HTML code:
Hello World!
Method: GET
Path: /
Protocol: HTTP/1.0
The program received the request, got the URL path and responded "Hello World!" with the request's properties appended to it. If you're not sure if your program output is correct, compare it to http-hello-world.ref (see "Contrib" area of Daylight Software), which contains the complete output from the program.
We've covered the essential properties and worked through a program for the GET method. Now, let's cover the entire set of HTTP Toolkit properties.
20.4. The Entire Set of Properties
Properties are organized by two categories:
Each category is grouped into two subcategories:
- CGI/Server
- Request/Response
CGI/Server properties are associated to the HTTP object. Request/Response properties are associated with the TRANSFER object.
20.4.1. Read-Only Properties of a CGI/Server
Each of the following properties are set when the HTTP server is allocated and do not change, except for htt_status, which changes with each request. The following table lists the named property, description, and datatype for the read-only properties associated with a CGI/Server.
Table 20.4.1. Read-Only Properties of a CGI/Server
|
Name
| Description
| Datatype
|
htt_date
| HTTP object allocation date & time
| dt_String
|
htt_status
| service state
| dt_Integer
|
htt_port
| communication endpoint number
| dt_Integer
|
htt_host
| local host name
| dt_String
|
htt_lips
| local host IP address
| dt_String
|
- htt_date is the date & time of HTTP object allocation, for example, Mon, 02 Dec 2002 07:40:03 GMT. The format conforms to RFC 822 "Standard for the Format of ARPA Internet Text Messages" and RPC 1123 "Requirements for Internet Hosts -- Application and Support" and avavilable at http://www.ietf.org/rfc/rfc822.txt and http://www.ietf.org/rfc/rfc1123.txt.
- htt_status is the state of the HTTP service when a TRANSFER object is returned from dt_http_get and one of following values:
DX_HTT_OK - a request has been received. The TRANSFER object will be equal to NULL_OB if the request was processed internally. Otherwise, the TRANSFER object will be a valid object for processing.
DX_HTT_TIMEOUT - a request has not been received. The TRANSFER object will be equal to NULL_OB.
DX_HTT_ERROR - an error condition exists. The TRANSFER object will be equal to NULL_OB. Use dt_errors to access error information.
DX_HTT_DONE - the same as DX_HTT_OK and also indicates that the service has completed. This status is useful for breaking a CGI service from a dual-purpose Server/CGI loop. Calling dt_http_get again will result in an error.
- htt_port is the port number on which the service is listening. This value is equal to the argument passed to dt_alloc_http_server. Valid values are 0 to 65535 inclusive. Port 0 is interpreted as standard input and causes the service to behave as a CGI.
- htt_host is the name of the local host as viewed from the server using the uname system call, for example, www.daylight.com.
- htt_lips is the IP address of the local host as viewed from the server using gethostbyaddr system call, for example, 207.225.60.130.
20.4.2 Read-Write Properties of a CGI/Server
Each of the following properties can be set anytime after a HTTP server object is allocated and affects the behavior of dt_http_get. The following table lists the named property, description, datatype, and default value for the read-write properties associated with a CGI/Server.
Table 20.4.2. Read-Write Properties of a CGI/Server
|
Name
| Description
| Datatype
| Default
|
htt_access_log
| enable logging of processed requests
| dt_Boolean
| FALSE
|
htt_method_id
| request method identifiers allowed
| dt_Integer
| DX_HTT_METHOD_ALL
|
htt_timeout_ms
| time to listen for a request
| dt_Integer
| 5000
|
- htt_access_log is a boolean to indicate logging of processed requests. When TRUE, successful responses will be written to the error queue at the DX_ERR_NOTE level. Applications should process the queue per request. Valid values for this propery are TRUE and FALSE.
- htt_method_id is a bitwise OR of request method identifiers that are implemented by the service. If a request method received within a call to dt_http_get is not implemented, the toolkit will internally process the request with a "501 Not Implemented" response. This is useful for denying unwanted methods within the toolkit. Valid values for this property are:
DX_HTT_METHOD_HEAD - the service implements the request method HEAD.
DX_HTT_METHOD_GET - the service implements the request method GET.
DX_HTT_METHOD_POST - the service implements the request method POST.
DX_HTT_METHOD_ALL - a bitwise OR of all of the above method identifiers.
DX_HTT_METHOD_ANY - disables toolkit internal processing of unimplemented requests.
- htt_timeout_ms is the time in milliseconds the service polls the listening port for a request by using the select system call. This value determines how long a call to dt_http_get will wait before returning NULL_OB and setting the htt_status property to DX_HTT_TIMEOUT.
20.4.3 Read-Only Properties of a Request
The following table lists the named property, description, and datatype for the read-only properties associated with a request/response.
Table 20.4.3. Read-Only Properties of a Request
|
Name
| Description (HTTP header field)
| Datatype
|
htt_agent
| software information (User-Agent)
| dt_String
|
htt_authorize
| username & password (Authorization)
| dt_String
|
htt_from
| user email address (From)
| dt_String
|
htt_method
| request method
| dt_String
|
htt_method_id
| method identifier
| dt_Integer
|
htt_path
| URL or path
| dt_String
|
htt_post
| POST field & value pairs
| dt_Handle
|
htt_protocol
| protocol and version
| dt_String
|
htt_rawhead
| original HEAD data
| dt_String
|
htt_rawpost
| original POST data
| dt_String
|
htt_recv_
| prefix for additional headers
| dt_String
|
htt_referer
| referral URL (Referer)
| dt_String
|
htt_rips
| remote host IP address
| dt_String
|
htt_since
| date condition (If-Modified-Since)
| dt_String
|
- htt_agent is the information about the software used to make the request, for example, "Mozilla/4.78 [en] (X11; U; Linux 2.4.7-10 i686)"
- htt_authorize is the username & password used to authorize a request.
- htt_from is the email address of the user.
- htt_method is the request method, for example, "GET", "HEAD", or "POST". Other values include "PUT", "DELETE", "LINK", and "UNLINK".
- htt_method_id is the request method identifier of the request. Valid values for this property are:
DX_HTT_METHOD_HEAD - the request method is HEAD.
DX_HTT_METHOD_GET - the request method is GET.
DX_HTT_METHOD_POST - the request method is POST.
DX_HTT_METHOD_NONE - the request method is none of the above implemented methods. In this case, the request method can be determined using the htt_method property.
- htt_path is the URL or path, for example, http://www.daylight.com or /.
- htt_post contains the request posts field/value pairs stored as a handle to a sequence of string objects. Each string object represents one header line from the request. The header field name is the string value of the string object and the header field value is the value of the "htt_value" property of the string object. See the example on how to get a POST value below.
- htt_protocol is the protocol and version, for example, "HTTP/1.0" or "HTTP/1.1".
- htt_rawhead is the original HEAD portion of the data request. This string contains line delimiters (usually \r\n, \n, or \r) and ends with a blank line.
- htt_rawpost is the original POST portion of the data request. This string is NULL for methods other than POST.
- htt_recv_ is the property name prefix for receiving unimplemented request headers and have no predefined property name. For such headers, this prefix is is concatinated with the header name to construct a property to store the header value. For example, the "Cookie" header is not implemented, so when a "Cookie" header is received, the htt_recv_Cookie property will contain the "Cookie" header value. In this way, the toolkit can support any request header information. For information on cookies, see RFC 2965 "HTTP State Management Mechanism" at http://www.ietf.org/rfc/rfc2965.txt.
- htt_referer is the URL of the referring resource.
- htt_rips is the IP address of the remote host as viewed from the server using the accept(3socket) system call. If the service is a CGI, the REMOTE_ADDR environment variable is used.
- htt_since is the conditional date of which the request. The format is the same as the htt_date property. If the resource is older than this date, the resource is not requested.
The following source code shows how to get the value of the POST field named "smiles":
Figure 20.4.3. Example Source Code: Get POST Data
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
|
/* du_http_post - return the string object of a POST named property
** return NULL_OB if the name is not found
*/
dt_Handle du_http_post(dt_Handle xfer, dt_Integer plen, dt_String pname) {
dt_Handle sequence, string;
dt_String propname;
/* get property */
sequence = dt_handle(xfer, 8, "htt_post");
/* loop over sequence, get strings */
while (NULL_OB != (string = dt_next(sequence))) {
/* get propname */
propname = dt_stringvalue(&plen, string);
/* check for match */
if (0 == strncmp(propname, pname, plen))
/* return string value */
return string;
}
/* not found */
return NULL_OB;
}
|
For more information, see dt_handle, dt_next, dt_stringvalue, and dt_string.
20.4.4 Read-Write Properties of a Response
The following table lists the named property, description, datatype, and default value for the read-write properties associated with a request/response:
Table 20.4.4. Read-Write Properties of a Response
|
Name
| Description (HTTP header field in parenthesis)
| Datatype
| Default
|
htt_authenticate
| username & password challenge (WWW-Authenticate)
| dt_String
| NULL
|
htt_code
| status code
| dt_Integer
| 404
|
htt_content
| content body
| dt_String
| NULL
|
htt_date
| date & time (Date)
| dt_String
| NULL
|
htt_encoding
| content compression (Content-Encoding)
| dt_String
| NULL
|
htt_expires
| content expiration date & time (Expires)
| dt_String
| NULL
|
htt_location
| URL redirection (Location)
| dt_String
| NULL
|
htt_method_id
| implemented methods (Allow)
| dt_Integer
| NULL
|
htt_mime_type
| content MIME type (Content-Type)
| dt_String
| text/html
|
htt_modified
| content last change date & time (Last-Modified)
| dt_String
| NULL
|
htt_pragma
| implementation-specific directives (Pragma)
| dt_String
| NULL
|
htt_send_
| prefix for additional headers
| dt_String
| NULL
|
htt_service
| service identification (Server)
| dt_String
| NULL
|
htt_version
| protocol and version
| dt_String
| HTTP/1.0
|
HTML Presentation
|
htm_autohtml
| truthfulness of HTML auto-generation
| dt_Integer
| FALSE
|
htm_body_tag
| <BODY> tag
| dt_String
| NULL
|
htm_body_bg_color
| parameter BGCOLOR within <BODY> tag
| dt_String
| NULL
|
htm_body_bg_image
| parameter BACKGROUND within <BODY> tag
| dt_String
| NULL
|
htm_doctype_tag
| <!DOCTYPE> tag
| dt_String
| NULL
|
htm_favicon
| parameter HREF within <LINK> tag
| dt_String
| NULL
|
htm_head_tag
| <HEAD> tag
| dt_String
| NULL
|
htm_head_title
| <TITLE> tag within <HEAD> tag
| dt_String
| NULL
|
htm_head_script
| <SCRIPT> tag within <HEAD> tag
| dt_String
| NULL
|
htm_head_style
| <STYLE> tag within <HEAD> tag
| dt_String
| NULL
|
htm_prefix
| prefix to content
| dt_String
| NULL
|
htm_postfix
| postfix to content
| dt_String
| NULL
|
- htt_authenticate is the challenge for a client to authenticate itself and is used with status code 401. If this property is not set with status code 401, the toolkit will override the response with "500 Internal Server Error". When this property is not NULL, the value is used with the "WWW-Authenticate" header field.
- htt_code is the response status code and is initialized to 404 (Not Found) when a TRANSFER object is returned from dt_http_get. This property should be set to 200 for response that are "OK".
- htt_content is the content of the response with NULL as the initial value. This property is the body of the response sent from the server to the client. When this property is not NULL, the length in bytes of the content is used with the "Content-Length" header field.
- htt_date is the response date & time, for example, Mon, 02 Dec 2002 07:40:03 GMT. The format conforms to RFC 822 "Standard for the Format of ARPA Internet Text Messages" and RPC 1123 "Requirements for Internet Hosts -- Application and Support" and avavilable at http://www.ietf.org/rfc/rfc822.txt and http://www.ietf.org/rfc/rfc1123.txt.
- htt_encoding is the encoding or compression performed on the content, for example, base64 or gzip. When this property is not NULL, the value is used with the "Content-Encoding" header field.
- htt_expires is the date & time the resource expires, for example, Mon, 02 Dec 2002 07:40:03 GMT. Like the htt_date property, the format conforms to RFC 822 and FPC 1123.. This value is used by caches and proxies to store server responses until it expires. When this property is not NULL, the value is used with the "Expires" header field.
- htt_location is the absolute URL for redirecting a request for a resource and is used with status codes 301 and 302. If this property is not set with status codes 301 and 302, the toolkit will override the response with "500 Internal Server Error". When this property is not NULL, the value is used with the "Location" header field.
- htt_method_id is a bitwise OR of request method identifiers that are implemented by the service for the response content. When this property is not NULL, the value is used with the "Allow" header field. Valid values for this property are:
DX_HTT_METHOD_HEAD - the service implements the request method HEAD.
DX_HTT_METHOD_GET - the service implements the request method GET.
DX_HTT_METHOD_POST - the service implements the request method POST.
DX_HTT_METHOD_ALL - a bitwise OR of all of the above method identifiers.
DX_HTT_METHOD_ANY - disables sending the "Allow" header field.
- htt_mime_type is the MIME type of the content, for example "text/html" or "image/gif", and is initialized to "text/html" when a TRANSFER object is returned from dt_http_get. The syntax conforms to RFC 1521 "MIME (Multipurpose Internet Mail Extensions)" avavilable at http://www.ietf.org/rfc/rfc1520.txt. When this property is not NULL, the value is used with the "Content-Type" header field.
- htt_modified is the date & time the resource was last modified. When this property is not NULL, the value is used with the "Last-Modified" header field.
- htt_pragma is the response implementation-specific directives that may apply to any recipient along the request/response chain. All pragma directives specify optional behavior from the viewpoint of the protocol; however, some systems may require that behavior be consistent with the directives.
- htt_send_ is the property name prefix for sending unimplemented response headers, and have no predefined property name. For such headers, this prefix should be concatinated with the header name to construct a property to store the header value. For example, the "Set-Cookie" header is not implemented, so when a "Set-Cookie" header should be sent, the programmer should set the htt_send_Set-Cookie property to contain the "Set-Cookie" header value. In this way, the toolkit can support any response header information. see RFC 2965 "HTTP State Management Mechanism" at http://www.ietf.org/rfc/rfc2965.txt.
- htt_service is the identifier of the service and is set when an object is returned from dt_http_get. This property is a free-form string and is set to the toolkit software version, for example, "Daylight/4.81". When this property is not NULL, the value is used with the "Server" header field.
- htt_version is the response protocol and version and is set when an object is returned from dt_http_get. The value is initialized to match the request protocol and version (htt_protocol property). So, HTTP/0.9 requests are sent HTTP/0.9 responses (no headers) and HTTP/1.0 requests are sent HTTP/1.0 responses (with headers). Requests that are received using HTTP/1.1 and higher will result in this property being initialized to "HTTP/1.0" within the call to dt_http_get. In order to respond to any other request protocol, the programmer must set this property (for example, to "HTTP/1.1") and add appropiate headers using the htt_send_ property prefix prior to calling dt_http_put.
The following properties pertain to HTML Presentation:
- htm_autohtml is the HTML auto-generation flag. When this property is zero, all of the following properties are ignored.
- htm_body_tag is the body tag. When this property is NULL, no BODY tag will be used unless one or both of htm_background or htm_bgcolor properties are non-NULL. For example:
<BODY BACKGROUND=sunlogo-gray.gif BGCOLOR=e0e0e0>
- htm_body_bg_color is the body tag background color parameter. When this property is NULL, no BGCOLOR parameter will be used within a BODY tag unless specified in the htm_body property, which overrides this property. For example:
e0e0e0
- htm_body_bg_image is the body tag background parameter. When this property is NULL, no BACKGROUND parameter is used within a BODY tag unless specified in the htm_body property, which overrides this property. For example:
sunlogo-gray.gif
- htm_doctype_tag is the document tag. When this property is NULL, the following value is used:
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 1.1//EN">
- htm_favicon is the hypertext reference in the link tag of the header. The reference specifies the location of a custom icon to be used as a thumbnail image in the 'address box window' and 'bookmark listing'. A common value for this parameter is "/favicon.ico". When this parameter is not NULL, a LINK tag within the HEAD field will be output. For example:
<LINK REL="shortcut icon" TYPE="image/x-icon" HREF="/favicon.ico" />
- htm_head_tag is the head tag. When this property is NULL, no HEAD tag is used unless any of htm_title, htm_script, or htm_style properties are non-NULL. For example:
<HEAD>
<TITLE>Auto-HTML Format Demo</TITLE>
<SCRIPT LANGUAGE=JavaScript SRC=/application/x-javascript/loadJavaGrins.js></SCRIPT>
<STYLE TYPE=text/css></STYLE>
</HEAD>
- htm_head_title is the title tag. When this property is NULL, no TITLE tag is used unless specified in the htm_head property, which overrides this property. For example:
<TITLE>Auto-HTML Format Demo</TITLE>
- htm_head_script is the script tag. When this property is NULL, no SCRIPT tag is used unless specified in the htm_head property, which overrides this property. For example:
<SCRIPT LANGUAGE=JavaScript SRC=/application/x-javascript/loadJavaGrins.js></SCRIPT>
- htm_head_style is the style tag. When this property is NULL, no STYLE is used unless specified in the htm_head property, which overrides this property. For example:
<STYLE TYPE=text/css></STYLE>
- htm_prefix is the content that prefixes the htt_content property in the response. This is used to specify a banner at the top of each page. For example:
<TABLE BORDER CELLSPACING CELLPADDING=4 WIDTH=100%%><TR>
<TD ALIGN=CENTER WIDTH=100%%><A HREF=/>Auto-HTML Format Demo</A>
</TABLE>
- htm_postfix is the content that postfixes the htt_content property in the response. This is used to specify a banner at the bottom of each page. For example:
<TABLE BORDER CELLSPACING CELLPADDING=4 WIDTH=100%%><TR>
<TD ALIGN=CENTER><A HREF=mailto:info@daylight.com>
info@daylight.com</A>
<TD ALIGN=CENTER VALIGN=MIDDLE WIDTH=100%%>
<A HREF=http://www.daylight.com>
Daylight Chemical Information Systems, Inc.</A>
<TD ALIGN=CENTER VALIGN=MIDDLE><I>Daylight<BR>HTTP Toolkit</I>
</TABLE>
We've covered all properties of the toolkit. Now, let's start using these properties to build various kinds of programs and control the behavior of the toolkit.
20.5 Working With the Toolkit
We've constructed an example program to respond to GET requests ("Hello World!", Figure 20.3.3f). In order to respond to GET request in general, you would parse the htt_path property and set the htt_code, htt_mime_type, and htt_content properties. Let's move onto processing POST requests.
20.5.1 Processing the POST Method
Two kinds of POST data content types are supported by the toolkit:
- URL-encoding (application/x-www-form-urlencoding)
- multipart media (multipart/form-data)
Both kinds of POST data can be obtained from a HTML form that is created using the following source code:
Figure 20.5.1. Example Source Code: Set a POST form
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
|
/* du_http_form - set form into response
*/
void du_http_form(dt_Handle xfer, char *name, char *type, char *encoding,
char *action, char *button, int vlen, dt_String value) {
/* begin form */
dt_appendstring(xfer, 11, "htt_content", 35,
"<FORM METHOD=POST NAME=form ACTION=");
/* action */
dt_appendstring(xfer, 11, "htt_content", strlen(action), action);
/* encoding */
dt_appendstring(xfer, 11, "htt_content", 9, " ENCTYPE=");
dt_appendstring(xfer, 11, "htt_content", strlen(encoding), encoding);
dt_appendstring(xfer, 11, "htt_content", 2, ">\n");
/* label */
dt_appendstring(xfer, 11, "htt_content", strlen(name), name);
/* name */
dt_appendstring(xfer, 11, "htt_content", 14, ": <INPUT NAME=");
dt_appendstring(xfer, 11, "htt_content", strlen(name), name);
/* type */
dt_appendstring(xfer, 11, "htt_content", 6, " TYPE=");
dt_appendstring(xfer, 11, "htt_content", strlen(type), type);
/* value */
dt_appendstring(xfer, 11, "htt_content", 7, " VALUE=");
if(NULL != value)
dt_appendstring(xfer, 11, "htt_content", vlen, value);
dt_appendstring(xfer, 11, "htt_content", 2, ">\n");
/* button */
if (NULL != button) {
dt_appendstring(xfer, 11, "htt_content", 19, "<INPUT TYPE=BUTTON ");
dt_appendstring(xfer, 11, "htt_content", strlen(button), button);
dt_appendstring(xfer, 11, "htt_content", 2, ">\n");
}
/* end form */
dt_appendstring(xfer, 11, "htt_content", 40,
"<INPUT TYPE=SUBMIT NAME=button>\n</FORM>\n");
}
|
This routine accepts several arguments:
- name is an indentification label.
- type is the input field type, for example, "TEXT" or "FILE".
- encoding is the content type, for example, "application/x-www-form-urlencoding" or "multipart/form-data".
- action is the target URL.
- button is for specifying an additional button.
- value is used to initialize the input field.
Each argument is NULL-terminated, except the value argument, which is length-terminated with the vlen argument.
The du_http_form routine, along with the source code from du_http_post (Figure 20.4.3) and the Dual-Purpose Server & CGI (Figure 20.2.3) can be reused in other example program. This code is available in the "Contrib" area of Daylight Software as du_http_main.c. The main routine is renamed to du_http_main so the library won't have a execution entry point, which calls my_handler(xfer) just like the "Hello World!" program. A header file for prototypes is called du_http.h. Also, a "makefile" is available for use with the system make utility. To compile this code for reuse in other programs, make the libdu.a library, for example,
cd $DY_ROOT/contrib/src
make
All programs in this manual can built using make, for example,
cd $DY_ROOT/contrib/src/http
make http-hello-world
This builds the "Hello World!" program from Figure 20.3.3. Now, let's construct an example program that uses POST data and our new link library libdu.a.
20.5.1.1 URL-Encoding (application/x-www-form-urlencoded)
Let's construct an example program that uses POST data using a MIME type called "application/x-www-form-urlencoded". Consider the following "Canonical SMILES" program:
Figure 20.5.1.1. Program Source Code: Canonical SMILES
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
|
#include <stdio.h>
#include "dt_smiles.h"
#include "dt_http.h"
#include "du_http.h"
/* my_handler - get POST data and set response
*/
static void my_handler(dt_Handle xfer) {
dt_Handle string;
dt_String value = NULL;
dt_Integer vlen = 0;
char *name = "SMILES", *type = "TEXT";
char *encoding = "application/x-www-form-urlencoding";
/* set return code and mime type */
dt_setinteger(xfer, 8, "htt_code", 200);
dt_setstring (xfer, 8, "htt_mime_type", 9, "text/html");
/* get POST data */
if (NULL_OB != (string = du_http_post(xfer, strlen(name), name)))
value = dt_string(&vlen, string, 9, "htt_value");
/* write form */
du_http_form(xfer, name, type, encoding, "/", NULL, vlen, value);
/* parse smiles and canonicalize */
if (NULL != value ) {
dt_Handle molecule;
dt_String cansmiles = "invalid";
dt_Integer clen = 7;
if (NULL_OB != (molecule = dt_smilin(vlen, value)))
cansmiles = dt_cansmiles(&clen, molecule, 1);
/* set response */
dt_appendstring(xfer, 11, "htt_content", 22, "\n<P>Canonical SMILES: ");
dt_appendstring(xfer, 11, "htt_content", clen, cansmiles);
dt_appendstring(xfer, 11, "htt_content", 1, "\n");
dt_dealloc(molecule);
}
}
/* main - calls dual-purpose server & CGI
*/
int main(int argc, char **argv) {
return du_http_main(argc,argv);
}
|
The key aspects of the my_handler routine are calls to:
- du_http_post to get a SMILES from POST data
- du_http_form to set a POST form of type TEXT and encoding application/x-www-form-urlencoding
- dt_smilin and dt_cansmiles to get the canonical form of the SMILES
We set "SMILES" to be the name of the text input in the form, then used that name to get to POST data. Then we used the htt_value property to get the SMILES string. This code is available as http-cansmi.c in the "Contrib" area of Daylight Software and is made with the command:
cd $DY_ROOT/contrib/src/http
make http-cansmi
Now, let's execute the the program as a CGI:
./http-cansmi << EOF
POST / HTTP/1.0
Content-type: application/x-www-form-urlencoded
Content-length: 30
SMILES=OCC&button=Submit+Query
EOF
You should see output HTML code that contains:
Canonical SMILES: CCO
The program received the request, got the SMILES from POST data, called the SMILES Toolkit to the get the canonical SMILES and responded "Canonical SMILES: CCO". If you're not sure if your program output is correct, compare it to http-cansmi.ref (see "Contrib" area of Daylight Software). This is the complete output from the program.
Now, let's construct an example program that uses multipart/form-data POST data.
20.5.1.2 Multipart Data (multipart/form-data)
Let's construct an example program that uses POST data of a MIME type called "multipart/form-data". Consider the following "Upload File" program:
Figure 20.5.1.2. Program Source Code: Upload File
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
|
#include <stdio.h>
#include "dt_smiles.h"
#include "dt_http.h"
#include "du_http.h"
/* my_handler - get POST data and set response
*/
static void my_handler(dt_Handle xfer) {
dt_Handle string;
dt_String value = NULL;
dt_Integer vlen = 0;
char *name = "UPLOAD", *type = "FILE";
char *encoding = "multipart/form-data";
dt_String filename = NULL, mimetype = NULL;
dt_Integer flen = 0, mlen = 0;
/* set return code */
dt_setinteger(xfer, 8, "htt_code", 200);
/* get file from POST data */
if (NULL_OB != (string = du_http_post(xfer, strlen(name), name))) {
value = dt_string(&vlen, string, 9, "htt_value");
filename = dt_string(&flen, string, 12, "htt_filename");
mimetype = dt_string(&mlen, string, 13, "htt_mime_type");
}
/* set MIME type */
if (NULL != mimetype) {
dt_setstring (xfer, 8, "htt_mime_type", mlen, mimetype);
/* set file content into response and return if the MIME type is not HTML */
if (0 != strncmp("text/html", mimetype, mlen)) {
dt_appendstring(xfer, 11, "htt_content", vlen, value);
return;
}
}
/* write form */
du_http_form(xfer, name, type, encoding, "/", NULL, flen, filename);
/* filename */
if (NULL != filename) {
dt_appendstring(xfer, 11, "htt_content", 15, "\n<P>Filename: ");
dt_appendstring(xfer, 11, "htt_content", flen, filename);
}
/* MIME type */
if (NULL != mimetype) {
dt_appendstring(xfer, 11, "htt_content", 15, "\n<P>MIME type: ");
dt_appendstring(xfer, 11, "htt_content", mlen, mimetype);
}
/* file contents */
if (NULL != value) {
dt_appendstring(xfer, 11, "htt_content", 15, "\n<P>Content: ");
dt_appendstring(xfer, 11, "htt_content", vlen, value);
}
dt_appendstring (xfer, 11, "htt_content", 1, "\n");
}
/* main - calls dual-purpose server & CGI
*/
int main(int argc, char **argv) {
return du_http_main(argc,argv);
}
|
This program source code is similar to the previous "Canonical SMILES" program; both use dt_http_main, dt_http_post, and dt_http_form from the libdu.a library. A key differences are calls from the my_handler routines to:
- du_http_post to get a file from POST data
- du_http_form to set a POST form of type FILE and encoding multipart/form-data
This code is available as http-upload.c in the "Contrib" area of Daylight Software and is made with the command:
cd $DY_ROOT/contrib/src/http
make http-upload
Now, let's execute the the program as a CGI:
./http-upload << EOF
POST / HTTP/1.0
Content-type: multipart/form-data; boundary=---------------------------33163136917725
Content-Length: 300
-----------------------------33163136917725
Content-Disposition: form-data; name="UPLOAD"; filename="foo.html"
Content-Type: text/html
bar
-----------------------------33163136917725
Content-Disposition: form-data; name="button"
Submit Query
-----------------------------33163136917725--
EOF
You should see output HTML code that contains:
Filename: foo.html
MIME type: text/html
Content: bar
The program received the request, got the filename, MIME type, and content from POST data, then responded with the value of those properties. If you're not sure if your program output is correct, compare it to http-upload.ref (see "Contrib" area of Daylight Software), which contains the complete output from the program.
The arguments to du_http_form reflect the difference between application/x-www-form-urlencoded and multipart/form-data POST data. We set "UPLOAD" to be the name of the file input in the form, then used that name to get POST data and the htt_value property to get the content of the file. Further, multipart/form-data POST data can have additional properties for an uploaded file, such as htt_filename and htt_mime_type which define the name of the file and the MIME type of the content. The my_handler routine sets the MIME type of the response to the htt_mime_type property of the file and sends the form with the file contents only if the MIME type is "text/html". In this way, we can upload any kind of MIME type, such as "image/gif", and get the form along with the file if the file is HTML.
20.5.2. Authenticating Access
The basic form of user authentication is base64-encoding. When a client (browser) attempts unauthorized access for a protected URL, the server sends a "401 Unauthorized" response. This response normally invokes the client to "pop-up" a window prompting the user for a username and password. Upon entering a username and password, the client browser encodes the data using the base64 algorithm. Although the algorithm is simple and can easily be decoded with paper and pencil, base64-encoding obfuscates text beyond easy recognition. Then the client sends the request for the protected URL along with the base64-encoding data to the server. In order for the server to validate the username and password, the base64-encoded data must be decoded. For example:
Figure 20.5.2-1. Example Source Code: Base64 Routines
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
|
/* FUNCTION: du_http_base64dechar - decode a base 64 character to a 6-bit digit
*/
static char du_http_base64dechar(char c) {
if ((96 < c) && (123 > c))
return c - 71; /* a-z */
else if ((65 < c) && (91 > c))
return c - 65; /* A-Z */
else if ((47 < c) && (58 > c))
return c + 4; /* 0-9 */
else if ('+' == c)
return 62;
else if ('/' == c)
return 63;
else /* if ('=' == c) */
return 0;
}
/* FUNCTION: du_http_base64decode - decode an ASCII string using base 64
*/
char* du_http_base64decode(int len, char *base64, char *ascii) {
int i, j, k;
char a, b, c, d;
for(i=j=0; i/*convert base 64 character to 6-bit digit */
a = du_http_base64dechar(*(base64 + i++));
b = (i < len) ? du_http_base64dechar(*(base64 + i++)) : 0;
c = (i < len) ? du_http_base64dechar(*(base64 + i++)) : 0;
d = (i < len) ? du_http_base64dechar(*(base64 + i++)) : 0;
/* join four 6-bit digits into 24-bit integer */
k = (a << 18) + (b << 12) + (c << 6) + d;
/* decompose 24-bit integer into three 8-bit characters */
*(ascii + j++) = k >> 16 & 255;
*(ascii + j++) = k >> 8 & 255;
*(ascii + j++) = k & 255;
}
ascii[j] = '\0';
return ascii;
}
|
This source code is part of the libdu.a library.
Now, let's construct an example program that uses base64-decoding to authenticate a username and password:
Figure 20.5.2-2. Program Source Code: Basic Authentication
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
|
#include <stdio.h>
#include "dt_smiles.h"
#include "dt_http.h"
#include "du_http.h"
/* my_handler - basic authentication
*/
void my_handler(dt_Handle xfer) {
dt_String value;
dt_Integer vlen, klen;
char *key, *ptr = "Basic realm=\"Daylight Contrib\"";
/* check for file protected with user/passwd mug/coffee */
if (NULL == (value = dt_string(&vlen, xfer, 13, "htt_authorize"))) {
dt_setstring(xfer, 16, "htt_authenticate", strlen(ptr), ptr);
dt_setinteger(xfer, 8, "htt_code", 401);
return;
}
/* check that encoding is base64 ("Basic"), else respond 401 (Authenticate) */
if (0 != memcmp(value, "Basic ", 6)) {
dt_setstring(xfer, 16, "htt_authenticate", strlen(ptr), ptr);
dt_setinteger(xfer, 8, "htt_code", 401);
return;
}
value += 6;
vlen -= 6;
/* allocate memory for key, else respond 500 (Internal Server Error) */
klen = (vlen*3)/4;
if(NULL == (key = (char*)malloc(klen*sizeof(char)))) {
fprintf(stderr, "out-of-memory (malloc(%d) failed)", klen);
dt_setinteger(xfer, 8, "htt_code", 500);
return;
}
/* decode using base64 */
du_http_base64decode(vlen, value, key);
/* authenticate username/pasword */
if(0 != memcmp(key, "mug:coffee", 10)) {
/* incorrect username/password, respond 401 (Authenticate) again */
ptr = "Basic realm=\"Daylight Toolkit\"";
dt_setstring(xfer, 16, "htt_authenticate", strlen(ptr), ptr);
dt_setinteger(xfer, 8, "htt_code", 401);
} else {
/* correct username/password, respond OK */
dt_setinteger(xfer, 8, "htt_code", 200);
dt_appendstring(xfer, 11, "htt_content", 19, "Base64 decoding of ");
dt_appendstring(xfer, 11, "htt_content", vlen, value);
dt_appendstring(xfer, 11, "htt_content", 4, " is ");
dt_appendstring(xfer, 11, "htt_content", klen, key);
dt_appendstring(xfer, 11, "htt_content", 1, "\n");
}
/* deallocate key */
free(key);
}
/* main - calls entry point for dual-purpose server & CGI
*/
int main(int argc, char **argv) {
return du_http_main(argc,argv);
}
|
This code has several features worth mentioning:
- Line 14: The client must send the "WWW-Authorize" header, which is stored in the htt_authorize property.
- Line 21: The authentication form must be "Basic", which means base64-encoding.
- Line 38: The server calls the base64-decoding routine (Figure 20.5.2-1).
- Line 41: The username and password must match "mug:coffee". The colon (":") character is inserted between the username and password by the client, and so appears in the string that the server decodes.
This code is available as http-base64.c in the "Contrib" area of Daylight Software and made with the command:
cd $DY_ROOT/contrib/src/http
make http-base64
Now, let's execute the the program as a CGI:
./http-base64 << EOF
GET / HTTP/1.0
EOF
Status: 401 Unauthorized
Date: Tue, 31 Dec 2002 04:52:34 GMT
Server: Daylight/4.81
WWW-Authenticate: Basic realm="Daylight Contrib"
Here we attempted access and the server responded "401 Unauthorized. Included in the response is the WWW-Authenticate header, telling the client that the server expects Basic (base64) encoding. The realm parameter "Daylight Contrib" appears on the window that "pops-up" prompting for the username and password. This is intended to help the user determine which username and password is appropiate. Now, let's simulate that the user entered the username "mug" and password "coffee":
./http-base64 << EOF
GET / HTTP/1.0
Authorization: Basic bXVnOmNvZmZlZQ==
EOF
Date: Tue, 31 Dec 2002 05:24:04 GMT
Server: Daylight/4.81
Content-Length: 52
Content-Type: text/html
Base64 decoding of bXVnOmNvZmZlZQ== is mug:coffee
The base64-encoded string "bXVnOmNvZmZlZQ==" decodes to "mug:coffee" and the client is authorized to access the server resources. Using base64-encoding, you can protect resources using a basic form of authentication.
20.5.3 Serving Files from Disk
As with all other Daylight toolkits, the HTTP Toolkit does not support explicit file I/O. The initial thought on server resources is to have all content built-in. This has the advantage of independence from access problems and hardware faults, as well as the disadvantage of not being able to update data without updating the server. Also, serving files from disk has the problem of configuration (and security) for data that should (and shouldn't) be served. There's no reasonable solution deserving implementation within the toolkit. Therefore, this section describes a program that serves files from disk that is built on top of the toolkit. For exanmple:
Figure 20.5.3-1. Example Source Code: Disk Access Routine
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
|
/* du_http_month - return month number given 3-letter month (Jan=0, Feb=1, etc.)
*/
int du_http_month(char *str) {
if (0 == memcmp(str, "Jan", 3)) return 0;
else if (0 == memcmp(str, "Feb", 3)) return 1;
else if (0 == memcmp(str, "Mar", 3)) return 2;
else if (0 == memcmp(str, "Apr", 3)) return 3;
else if (0 == memcmp(str, "May", 3)) return 4;
else if (0 == memcmp(str, "Jun", 3)) return 5;
else if (0 == memcmp(str, "Jul", 3)) return 6;
else if (0 == memcmp(str, "Aug", 3)) return 7;
else if (0 == memcmp(str, "Sep", 3)) return 8;
else if (0 == memcmp(str, "Oct", 3)) return 9;
else if (0 == memcmp(str, "Nov", 3)) return 10;
else /* Dec */ return 11;
}
/* du_http_file - disk access routine
*/
dt_Integer du_http_file(dt_Handle xfer) {
dt_String path, value;
dt_Integer plen, vlen;
char buf[1024], dmy[4], month[4], tz[4];
char *ptr, *dy_root, *docroot, filename[1024];
int fd, rlen, year, mday, hour, min, sec;
size_t len;
ssize_t bytes, tbytes=0;
struct stat fdstat;
struct tm gmt, since;
/* set document root directory */
if (NULL == (dy_root = getenv("DY_ROOT"))) {
dt_setstring (xfer, 11, "htt_content", 15, "DY_ROOT not set");
dt_setinteger(xfer, 8, "htt_code", 500);
return 500;
}
docroot = "/contrib/src/data/http";
path = dt_string(&plen, xfer, 8, "htt_path");
/* check for access outside document root */
if (NULL != strstr(path, "../")) {
dt_setstring (xfer, 11, "htt_content", 15, "filename contains ../");
dt_setinteger(xfer, 8, "htt_code", 404);
return 404;
}
/* check for overflow */
if (1024 < strlen(dy_root) + strlen(docroot) + plen + 1) {
dt_setstring (xfer, 11, "htt_content", 17, "filename too long");
dt_setinteger(xfer, 8, "htt_code", 500);
return 500;
}
/* construct absolute filename (ignore arguments) */
sprintf(filename, "%s%s%.*s", dy_root, docroot, plen, path);
if (NULL != (ptr = strchr(filename, '+')))
*ptr = '\0';
/* open file or respond "Not Found" */
if (-1 == (fd = open(filename, O_RDONLY, 0))) {
dt_setinteger(xfer, 8, "htt_code", 404);
return 404;
}
/* get file statistics */
if (-1 == (fstat(fd, &fdstat))) {
len = sprintf(buf, "error signal %d (%s)", errno, strerror(errno));
dt_setstring(xfer, 11, "htt_content", len, buf);
dt_setinteger(xfer, 8, "htt_code", 500);
close(fd);
return 500;
}
/* check for content */
if (0 == fdstat.st_size) {
dt_setinteger(xfer, 8, "htt_code", 204);
close(fd);
return 204;
}
/* get time-and-date of last modification in RFC 1123 format*/
if (NULL != gmtime_r(&(fdstat.st_mtime), &gmt))
if ( 0 < (len = strftime(buf, 32, "%a, %d %b %Y %H:%M:%S GMT", &gmt)))
dt_setstring(xfer, 12, "htt_modified", len, buf);
/* check for If-Modified-Since */
if (NULL != (value = dt_string(&vlen, xfer, 9, "htt_since"))) {
sscanf(value, "%3s, %2d %3s %4d %2d:%2d:%2d %3s",
dmy, &mday, month, &year, &hour, &min, &sec, tz);
/* check date for GMT timezone (RFC 1123 conformance) */
if (0 != memcmp(tz, "GMT", 3)) {
len = sprintf(buf, "Invalid date format (%.*s)", vlen, value);
dt_setstring(xfer, 11, "htt_content", len, buf);
dt_setinteger(xfer, 8, "htt_code", 400);
close(fd);
return 400;
}
/* compare file date to "If-Modified-Since" value
** if file is older, respond "Not Modified" */
if ((gmt.tm_year+1900 < year) ||
(gmt.tm_mon < du_http_month(month)) ||
(gmt.tm_mday < mday) ||
(gmt.tm_hour < hour) ||
(gmt.tm_min < min) ||
(gmt.tm_sec <= sec)) {
dt_setinteger(xfer, 8, "htt_code", 304);
close(fd);
return 304;
}
}
/* read file, set content */
do {
bytes = read(fd, buf, len);
if (0 < bytes)
dt_appendstring(xfer, 11, "htt_content", bytes, buf);
tbytes += bytes;
} while ((0 < bytes) || ((-1 == bytes) && (EINTR == errno)));
/* close file descriptor */
close(fd);
/* check for error signal */
if (-1 == bytes) {
len = sprintf(buf, "error signal %d (%s)", errno, strerror(errno));
dt_setstring(xfer, 11, "htt_content", len, buf);
dt_setinteger(xfer, 8, "htt_code", 500);
return 500;
}
/* get MIME type from path */
if(NULL != (ptr = strchr(path+1, '/')))
if(NULL != (ptr = strchr(ptr+1, '/')))
dt_setstring(xfer, 13, "htt_mime_type", ptr-path-1, path+1);
/* set status code to success */
dt_setinteger(xfer, 8, "htt_code", 200);
return 200;
}
|
This code has some features worth mentioning:
- Line 32: The getenv system call must succeed, otherwise the response is set to 500 Internal Server Error".
- Line 37: The document root directory is set relative to the DY_ROOT environment variable.
- Line 41: The URL path must not contain "../", otherwise the response is set to "404 Not Found". This is a basic security check on the filename to prevent access outside of the document root directory.
- Line 48: The filename local variable must be long enough to contain the name of the file, otherwise the response is set to "500 Internal Server Error". Changing the local variable memory from static to dynamic is a reasonable alternative, as it supports very long filenames provided that the system can allocate the memory space.
- Line 55: The absolute filename is constructed from the DY_ROOT environment variable, document root directory and the URL path. Any arguments after the filename are ignored.
- Line 66: The fstat system call must succeed, otherwise the response is set to "500 Internal Server Error".
- Line 75: If the file size empty, the response is set to "204 No Content".
- Line 82: If the gmtime_r and strfime system calls succeed, the htt_modified property is set, and the "Last-Modified" header is sent in the response.
- Line 87: If the htt_since property is not NULL (corresponding to the "If-Modified-Since" header), the value of the htt_since property is compared to the date of the file.
- Line 91: If the date value doesn't specify GMT as the timezone, the response is set to "400 Client Error". This is a minimal check on the data for conformance to the RFC 1123 specification.
- Line 100: If the file is older than the date specified by the htt_since property, the response is set to "304 Not Modified".
- Line 113: The interrupt signal is ignored while the file is read and set into the htt_content property.
- Line 124: If a signal other than he interrupt signal ocurrs during reading, the response is set to "500 Internal Server Error".
- Line 134: The MIME type of the file is implied by the first two subdirectories of the URL path. For example, if the path is text/html/plain-index.html, the (implied) MIME type is text/html.
- Line 137: If execution reaches the end of the routine, the response is set to "200 OK".
This source code is part of the libdu.a library.
Now, let's construct an example program that uses the du_http_file routine to serve files from disk:
Figure 20.5.3-2. Program Source Code: File I/O
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
|
#include <stdio.h>
#include "dt_smiles.h"
#include "dt_http.h"
#include "du_http.h"
/* my_handler - serve files from disk
*/
void my_handler(dt_Handle xfer) {
du_http_file(xfer, fd);
}
/* main - calls entry point for dual-purpose server & CGI
*/
int main(int argc, char **argv) {
return du_http_main(argc,argv);
}
|
This code is available as http-file.c in the "Contrib" area of Daylight Software and made with the command:
cd $DY_ROOT/contrib/src/http
make http-file
Now, let's execute the the program as a CGI:
./http-file << EOF
GET /text/html/index-plain.html HTTP/1.0
EOF
You should see output HTML code that contains:
<H2>Index of /text/plain Subdirectory</H2>
<UL>
<LI><A HREF=/text/plain/AsImplemented.txt>
The Original HTTP as defined in 1991 </A> (HTTP 0.9)
<LI><A HREF=/text/plain/draft-coar-cgi-v11-03.txt>
The WWW Common Gateway Interface Version 1.1 </A> (CGI 1.1)
<LI><A HREF=/text/plain/rfc822.txt>
RFC 822: Standard for the Format of ARPA Internet Text Messages </A> (date for
mat)
<LI><A HREF=/text/plain/rfc1123.txt>
RFC 1123: Requirements for Internet Hosts -- Application and Support </A> (dat
e format)
<LI><A HREF=/text/plain/rfc1520.txt>
RFC 1521: MIME (Multipurpose Internet Mail Extensions) </A>
<LI><A HREF=/text/plain/rfc1945.txt>
RFC 1945: Hypertext Transfer Protocol -- HTTP/1.0 </A>
<LI><A HREF=/text/plain/rfc2616.txt>
RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1 </A>
<LI><A HREF=/text/plain/rfc2965.txt>
RFC 2965: HTTP State Management Mechanism </A> (cookies)
The program received the request, got the file (/text/html/index-plain.html), set the MIME type as implied from the URL path (text/html), and responded with the contents of the file. The file is a prepared index of the /text/plain subdirectory, which contains links to public documents pertaining to HTTP specifications used to develop the HTTP Toolkit. If you're not sure if your program output is correct, compare it to http-file.ref (see "Contrib" area of Daylight Software), which contains the complete output from the program.
20.5.4. Scripting with Java
As the name implies, JavaScript is a scripting language for Java. One way to use JavaScript in HTML is to specify a <SCRIPT> tag that defines the script and an <INPUT> button to invoke the script. Using the toolkit, we can do this by setting the htm_head_script property and specifying the button argument to the du_http_form call:
Figure 20.5.4. Program Source Code: Canonical JavaGrins
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
|
#include <stdio.h>
#include "dt_smiles.h"
#include "dt_http.h"
#include "du_http.h"
/* my_handler - get POST data and set response
*/
static void my_handler(dt_Handle xfer) {
dt_Handle string;
dt_String value = NULL;
dt_Integer vlen = 0;
char *name = "SMILES", *type = "TEXT";
char *encoding = "application/x-www-form-urlencoding";
char *script = "LANGUAGE=JavaScript SRC=/application/x-javascript/loadJavaGrins.js";
char *button = "VALUE=JavaGrins ONCLICK=makeNewWindow('smilesForm.smiles')";
/* serve files from disk, except for special POST URL ("/") */
path = dt_string(&plen, xfer, 8, "htt_path");
if (1 < plen) {
du_http_file(xfer);
return;
}
/* set return code and mime type */
dt_setinteger(xfer, 8, "htt_code", 200);
dt_setstring (xfer, 8, "htt_mime_type", 9, "text/html");
/* set script and button */
dt_setinteger(xfer, 12, "htm_autohtml", 1);
dt_setstring (xref, 15, "htm_head_script", strlen(script), script);
/* get POST data */
if (NULL_OB != (string = du_http_post(xfer, strlen(name), name)))
value = dt_string(&vlen, string, 9, "htt_value");
/* write form */
du_http_form(xfer, name, type, encoding, "/", button, vlen, value);
/* parse smiles and canonicalize */
if (NULL != value ) {
dt_Handle molecule;
dt_String cansmiles = "invalid";
dt_Integer clen = 7;
if (NULL_OB != (molecule = dt_smilin(vlen, value)))
cansmiles = dt_cansmiles(&clen, molecule, 1);
/* set response */
dt_appendstring(xfer, 11, "htt_content", 22, "\n<P>Canonical SMILES: ");
dt_appendstring(xfer, 11, "htt_content", clen, cansmiles);
dt_appendstring(xfer, 11, "htt_content", 1, "\n");
dt_dealloc(molecule);
}
}
/* main - calls dual-purpose server & CGI
*/
int main(int argc, char **argv) {
return du_http_main(argc,argv);
}
|
This program adapted from "Canonical SMILES" (Figure 20.5.1.1). The key additions are:
- Line 14: The JavaScript language and source are defined.
- Line 15: The invocation button is defined.
- Line 18: URLs other than "/" are processed as a file from disk.
- Line 29: The htm_autohtml property is turned on.
- Line 30: The htm_head_script property is set.
- Line 37: The invocation button is a parameter to du_http_form.
This code is available as http-javagrins.c in the "Contrib" area of Daylight Software and is made with the command:
cd $DY_ROOT/contrib/src/http
make http-javagrins
Since JavaGrins is a graphical drawing tool, testing this program requires interaction with a browser. When you click on the button labeled "JavaGRINS", the drawing tool should "pop-up". You will need the dayutilserver license to convert drawings to SMILES. For more information on JavaGrins or the dayutilserver, see Daylight's JavaGrins User Guide at http://www.daylight.com/dayhtml/doc/java/javagrins.html.
Nongraphically, we can test that the JavaScript language, script, and invocation button are included in the HTML output. This will test that the htm_autohtml and htm_head_script properties and that the button parameter to the du_http_form call works properly. So, let's execute the the program as a CGI:
./http-javagrins << EOF
GET / HTTP/1.0
EOF
You should see output HTML code that contains:
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 1.1//EN">
<HTML>
<HEAD>
<SCRIPT LANGUAGE=JavaScript SRC=/application/x-javascript/loadJavaGrins.js></SCRIPT>
</HEAD>
<BODY>
<P><FORM METHOD=POST NAME=form ACTION=/ ENCTYPE=application/x-www-form-urlencoding>
SMILES: <INPUT NAME=SMILES TYPE=TEXT VALUE=>
<INPUT TYPE=BUTTON VALUE=JavaGrins ONCLICK=makeNewWindow('smilesForm.smiles')>
<INPUT TYPE=SUBMIT NAME=button>
</FORM>
</BODY>
</HTML>
We see that a non-zero htm_autohtml property value makes the toolkit output HTML tags before and after the form. The htm_head_script property value is in the SCRIPT tag and the button parameter to the du_http_form call is in the form. If you're not sure if your program output is correct, compare it to http-javagrins.ref (see "Contrib" area of Daylight Software), which contains the complete output from the program.
20.5.5 Formatting With Automatic HTML
Section 20.4.4 describes HTML presentation properties. In this section, we'll demonstrate use of the properties and look at the HTML output that is produced:
Figure 20.5.5. Program Source Code: Automatic HTML
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
- ....
|
#include <stdio.h>
#include "dt_smiles.h"
#include "dt_http.h"
#include "du_http.h"
/* my_handler - demonstration of automatic HTML formatting
*/
void my_handler(dt_Handle xfer) {
dt_String method, path, protocol;
dt_Integer mlen, plen, rlen;
method = dt_string(&mlen, xfer, 10, "htt_method");
path = dt_string(&plen, xfer, 8, "htt_path");
protocol = dt_string(&rlen, xfer, 12, "htt_protocol");
dt_setinteger (xfer, 8, "htt_code", 200);
dt_setstring (xfer, 13, "htt_mime_type", 9, "text/html");
dt_setstring (xfer, 11, "htt_content", 12, "Hello World!");
dt_appendstring (xfer, 11, "htt_content", 12, "\n\nMethod: ");
dt_appendstring (xfer, 11, "htt_content", mlen, method);
dt_appendstring (xfer, 11, "htt_content", 11, "\nPath: ");
dt_appendstring (xfer, 11, "htt_content", plen, path);
dt_appendstring (xfer, 11, "htt_content", 11, "\nProtocol: ");
dt_appendstring (xfer, 11, "htt_content", rlen, protocol);
dt_appendstring (xfer, 11, "htt_content", 1, "\n");
/* auto-HTML formatting */
dt_setinteger (xfer, 12, "htm_autohtml", 1);
dt_setstring (xfer, 17, "htm_body_bg_color", 6, "e0e0e0");
dt_setstring (xfer, 14, "htm_head_title", 21, "Auto-HTML Format Demo");
dt_setstring (xfer, 10, "htm_prefix", 191,
"<TABLE BORDER CELLSPACING CELLPADDING=4 WIDTH=100%><TR>\n"
"<TD ALIGN=CENTER WIDTH=100%>\n"
" <A HREF=/>Auto-HTML Format Demo</A>\n"
"<TD ALIGN=CENTER>\n"
" <A HREF=mailto:info@daylight.com>\n"
" info@daylight.com</A>\n"
"<TD ALIGN=CENTER VALIGN=MIDDLE WIDTH=100%>\n"
" <A HREF=http://www.daylight.com>\n"
" Daylight Chemical Information Systems, Inc.</A>\n"
"<TD ALIGN=CENTER VALIGN=MIDDLE>\n"
" <I>Daylight<BR>");
ver = dt_info(&vlen, NULL_OB, "toolkit_version");
dt_appendstring (xfer, 11, "htm_postfix", vlen, ver);
dt_appendstring (xfer, 11, "htm_postfix", 13, "</I>\n</TABLE>");
}
/* main - calls entry point for dual-purpose server & CGI
*/
int main(int argc, char **argv) {
return du_http_main(argc,argv);
}
|
This program is adapted from "Hello World!" (Figure 20.3.3). The key additions are:
- Line 27: Automatic HTML formatting is turned on.
- Line 28: The background color is set to light gray.
- Line 29: The title is set to "Auto-HTML Format Demo".
- Line 30: The page content is prefixed with a table.
- Line 34: The page content is postfixed with a table.
This code is available as http-autohtml.c in the "Contrib" area of Daylight Software and is made with the command:
cd $DY_ROOT/contrib/src/http
make http-autohtml
Now, let's execute the the program as a CGI:
./http-autohtml << EOF
GET / HTTP/1.0
EOF
You should see output HTML code that contains:
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 1.1//EN">
<HTML>
<HEAD>
<TITLE>Auto-HTML Format Demo</TITLE>
</HEAD>
<BODY BACKGROUND=image/gif/sunlogo-gray.gif>
<P><TABLE BORDER CELLSPACING CELLPADDING=4 WIDTH=100%><TR>
<TD ALIGN=CENTER WIDTH=100%>
<A HREF=/>Auto-HTML Format Demo</A>
<TD ALIGN=CENTER>
<A HREF=mailto:info@daylight.com>
info@daylight.com</A>
<TD ALIGN=CENTER VALIGN=MIDDLE WIDTH=100%>
<A HREF=http://www.daylight.com>
Daylight Chemical Information Systems, Inc.</A>
<TD ALIGN=CENTER VALIGN=MIDDLE>
<I>Daylight<BR>4.81</I>
|