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

  1. ....
  2. ....
  3. ....
  4. ....
  5. ....
  6. ....
  7. ....
  8. ....
  9. ....
  10. ....
  11. ....
  12. ....
  13. ....
  14. ....
  15. ....
  16. ....
  17. ....
  18. ....
#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)

  1. ....
  2. ....
  3. ....
  4. ....
  5. ....
  6. ....
  7. ....
  8. ....
  9. ....
  10. ....
  11. ....
  12. ....
  13. ....
  14. ....
  15. ....
  16. ....
  17. ....
  18. ....
  19. ....
  20. ....
  21. ....
  22. ....
  23. ....
  24. ....
#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

  1. ....
  2. ....
  3. ....
  4. ....
  5. ....
  6. ....
  7. ....
  8. ....
  9. ....
  10. ....
  11. ....
  12. ....
  13. ....
  14. ....
  15. ....
  16. ....
  17. ....
  18. ....
  19. ....
  20. ....
  21. ....
  22. ....
  23. ....
  24. ....
  25. ....
  26. ....
  27. ....
  28. ....
  29. ....
  30. ....
  31. ....
  32. ....
  33. ....
  34. ....
#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

  1. ....
  2. ....
  3. ....
  4. ....
  5. ....
  6. ....
  7. ....
  8. ....
  9. ....
  10. ....
  11. ....
  12. ....
  13. ....
  14. ....
  15. ....
  16. ....
  17. ....
  18. ....
  19. ....
  20. ....
  21. ....
  22. ....
  23. ....
  24. ....
  25. ....
  26. ....
  27. ....
  28. ....
  29. ....
  30. ....
  31. ....
  32. ....
  33. ....
  34. ....
  35. ....
  36. ....
  37. ....
  38. ....
  39. ....
  40. ....
  41. ....
#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

  1. ....
  2. ....
  3. ....
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

To set the essential response properties:

Figure 20.3.2. Example Source Code: Set a Response Property

  1. ....
  2. ....
  3. ....
  4. ....
  5. ....
  6. ....
  7. ....
  8. ....
  9. ....
  10. ....
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!

  1. ....
  2. ....
  3. ....
  4. ....
  5. ....
  6. ....
  7. ....
  8. ....
  9. ....
  10. ....
  11. ....
  12. ....
  13. ....
  14. ....
  15. ....
  16. ....
  17. ....
  18. ....
  19. ....
  20. ....
  21. ....
  22. ....
  23. ....
  24. ....
  25. ....
  26. ....
  27. ....
  28. ....
  29. ....
  30. ....
  31. ....
  32. ....
  33. ....
  34. ....
  35. ....
  36. ....
  37. ....
  38. ....
  39. ....
  40. ....
  41. ....
  42. ....
  43. ....
  44. ....
  45. ....
  46. ....
  47. ....
  48. ....
  49. ....
  50. ....
  51. ....
  52. ....
  53. ....
  54. ....
  55. ....
  56. ....
  57. ....
  58. ....
  59. ....
  60. ....
  61. ....
  62. ....
  63. ....
  64. ....
  65. ....
#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:

  • Read-Only
  • Read-Write

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

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

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

  1. ....
  2. ....
  3. ....
  4. ....
  5. ....
  6. ....
  7. ....
  8. ....
  9. ....
  10. ....
  11. ....
  12. ....
  13. ....
  14. ....
  15. ....
  16. ....
  17. ....
  18. ....
  19. ....
  20. ....
  21. ....
  22. ....
  23. ....
/* 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

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

  1. ....
  2. ....
  3. ....
  4. ....
  5. ....
  6. ....
  7. ....
  8. ....
  9. ....
  10. ....
  11. ....
  12. ....
  13. ....
  14. ....
  15. ....
  16. ....
  17. ....
  18. ....
  19. ....
  20. ....
  21. ....
  22. ....
  23. ....
  24. ....
  25. ....
  26. ....
  27. ....
  28. ....
  29. ....
  30. ....
  31. ....
  32. ....
  33. ....
  34. ....
  35. ....
  36. ....
  37. ....

/* 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:

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

  1. ....
  2. ....
  3. ....
  4. ....
  5. ....
  6. ....
  7. ....
  8. ....
  9. ....
  10. ....
  11. ....
  12. ....
  13. ....
  14. ....
  15. ....
  16. ....
  17. ....
  18. ....
  19. ....
  20. ....
  21. ....
  22. ....
  23. ....
  24. ....
  25. ....
  26. ....
  27. ....
  28. ....
  29. ....
  30. ....
  31. ....
  32. ....
  33. ....
  34. ....
  35. ....
  36. ....
  37. ....
  38. ....
  39. ....
  40. ....
  41. ....
  42. ....
  43. ....
  44. ....
  45. ....
  46. ....
#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:

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

  1. ....
  2. ....
  3. ....
  4. ....
  5. ....
  6. ....
  7. ....
  8. ....
  9. ....
  10. ....
  11. ....
  12. ....
  13. ....
  14. ....
  15. ....
  16. ....
  17. ....
  18. ....
  19. ....
  20. ....
  21. ....
  22. ....
  23. ....
  24. ....
  25. ....
  26. ....
  27. ....
  28. ....
  29. ....
  30. ....
  31. ....
  32. ....
  33. ....
  34. ....
  35. ....
  36. ....
  37. ....
  38. ....
  39. ....
  40. ....
  41. ....
  42. ....
  43. ....
  44. ....
  45. ....
  46. ....
  47. ....
  48. ....
  49. ....
  50. ....
  51. ....
  52. ....
  53. ....
  54. ....
  55. ....
  56. ....
  57. ....
  58. ....
  59. ....
  60. ....
  61. ....
  62. ....
#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:

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

  1. ....
  2. ....
  3. ....
  4. ....
  5. ....
  6. ....
  7. ....
  8. ....
  9. ....
  10. ....
  11. ....
  12. ....
  13. ....
  14. ....
  15. ....
  16. ....
  17. ....
  18. ....
  19. ....
  20. ....
  21. ....
  22. ....
  23. ....
  24. ....
  25. ....
  26. ....
  27. ....
  28. ....
  29. ....
  30. ....
  31. ....
  32. ....
  33. ....
  34. ....
  35. ....
  36. ....
  37. ....
  38. ....
/* 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

  1. ....
  2. ....
  3. ....
  4. ....
  5. ....
  6. ....
  7. ....
  8. ....
  9. ....
  10. ....
  11. ....
  12. ....
  13. ....
  14. ....
  15. ....
  16. ....
  17. ....
  18. ....
  19. ....
  20. ....
  21. ....
  22. ....
  23. ....
  24. ....
  25. ....
  26. ....
  27. ....
  28. ....
  29. ....
  30. ....
  31. ....
  32. ....
  33. ....
  34. ....
  35. ....
  36. ....
  37. ....
  38. ....
  39. ....
  40. ....
  41. ....
  42. ....
  43. ....
  44. ....
  45. ....
  46. ....
  47. ....
  48. ....
  49. ....
  50. ....
  51. ....
  52. ....
  53. ....
  54. ....
  55. ....
  56. ....
  57. ....
  58. ....
  59. ....
  60. ....
  61. ....
  62. ....
  63. ....
  64. ....
#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:

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

  1. ....
  2. ....
  3. ....
  4. ....
  5. ....
  6. ....
  7. ....
  8. ....
  9. ....
  10. ....
  11. ....
  12. ....
  13. ....
  14. ....
  15. ....
  16. ....
  17. ....
  18. ....
  19. ....
  20. ....
  21. ....
  22. ....
  23. ....
  24. ....
  25. ....
  26. ....
  27. ....
  28. ....
  29. ....
  30. ....
  31. ....
  32. ....
  33. ....
  34. ....
  35. ....
  36. ....
  37. ....
  38. ....
  39. ....
  40. ....
  41. ....
  42. ....
  43. ....
  44. ....
  45. ....
  46. ....
  47. ....
  48. ....
  49. ....
  50. ....
  51. ....
  52. ....
  53. ....
  54. ....
  55. ....
  56. ....
  57. ....
  58. ....
  59. ....
  60. ....
  61. ....
  62. ....
  63. ....
  64. ....
  65. ....
  66. ....
  67. ....
  68. ....
  69. ....
  70. ....
  71. ....
  72. ....
  73. ....
  74. ....
  75. ....
  76. ....
  77. ....
  78. ....
  79. ....
  80. ....
  81. ....
  82. ....
  83. ....
  84. ....
  85. ....
  86. ....
  87. ....
  88. ....
  89. ....
  90. ....
  91. ....
  92. ....
  93. ....
  94. ....
  95. ....
  96. ....
  97. ....
  98. ....
  99. ....
  100. ....
  101. ....
  102. ....
  103. ....
  104. ....
  105. ....
  106. ....
  107. ....
  108. ....
  109. ....
  110. ....
  111. ....
  112. ....
  113. ....
  114. ....
  115. ....
  116. ....
  117. ....
  118. ....
  119. ....
  120. ....
  121. ....
  122. ....
  123. ....
  124. ....
  125. ....
  126. ....
  127. ....
  128. ....
  129. ....
  130. ....
  131. ....
  132. ....
  133. ....
  134. ....
  135. ....
  136. ....
  137. ....
  138. ....
  139. ....
/* 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:

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

  1. ....
  2. ....
  3. ....
  4. ....
  5. ....
  6. ....
  7. ....
  8. ....
  9. ....
  10. ....
  11. ....
  12. ....
  13. ....
  14. ....
  15. ....
  16. ....
#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

  1. ....
  2. ....
  3. ....
  4. ....
  5. ....
  6. ....
  7. ....
  8. ....
  9. ....
  10. ....
  11. ....
  12. ....
  13. ....
  14. ....
  15. ....
  16. ....
  17. ....
  18. ....
  19. ....
  20. ....
  21. ....
  22. ....
  23. ....
  24. ....
  25. ....
  26. ....
  27. ....
  28. ....
  29. ....
  30. ....
  31. ....
  32. ....
  33. ....
  34. ....
  35. ....
  36. ....
  37. ....
  38. ....
  39. ....
  40. ....
  41. ....
  42. ....
  43. ....
  44. ....
  45. ....
  46. ....
  47. ....
  48. ....
  49. ....
  50. ....
  51. ....
  52. ....
  53. ....
  54. ....
  55. ....
  56. ....
  57. ....
  58. ....
  59. ....
#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

  1. ....
  2. ....
  3. ....
  4. ....
  5. ....
  6. ....
  7. ....
  8. ....
  9. ....
  10. ....
  11. ....
  12. ....
  13. ....
  14. ....
  15. ....
  16. ....
  17. ....
  18. ....
  19. ....
  20. ....
  21. ....
  22. ....
  23. ....
  24. ....
  25. ....
  26. ....
  27. ....
  28. ....
  29. ....
  30. ....
  31. ....
  32. ....
  33. ....
  34. ....
  35. ....
  36. ....
  37. ....
  38. ....
  39. ....
  40. ....
  41. ....
  42. ....
  43. ....
  44. ....
  45. ....
  46. ....
  47. ....
  48. ....
  49. ....
  50. ....
  51. ....
  52. ....
  53. ....
  54. ....
  55. ....
  56. ....
  57. ....
#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>

If you're not sure if your program output is correct, compare it to http-autohtml.ref (see "Contrib" area of Daylight Software), which contains the complete output from the program.

20.5.6 Adding Color to GIF Images

Here's a neat trick for adding color to GIF images. This is useful for using one image in many HTTP services and distinguishing them by the color of its image. This technique uses red, green, and blue arguments from the URL and works on GIF images that are of type grayscale:

Figure 20.5.6-1. Example Source Code: gif2rgb

  1. ....
  2. ....
  3. ....
  4. ....
  5. ....
  6. ....
  7. ....
  8. ....
  9. ....
  10. ....
  11. ....
/* du_http_gif2rgb - add color to a GIF image
*/
void du_http_gif2rgb(char *gif, int red, int green, int blue) {
  int i, byte[7] = { 19, 25, 22, 13, 28, 16, 31 };

  for (i = 0; i < 7; i++) {
    gif[byte[i]]   += red;
    gif[byte[i]+1] += green;
    gif[byte[i]+2]  = blue;
  }
}

This source code is part of the libdu.a library.

Now, let's construct an example program that uses the du_http_gif2rgb routine to add color to a GIf image:

Figure 20.5.6-2. Program Source Code: Color GIF

  1. ....
  2. ....
  3. ....
  4. ....
  5. ....
  6. ....
  7. ....
  8. ....
  9. ....
  10. ....
  11. ....
  12. ....
  13. ....
  14. ....
  15. ....
  16. ....
  17. ....
  18. ....
  19. ....
  20. ....
  21. ....
  22. ....
  23. ....
  24. ....
  25. ....
  26. ....
  27. ....
  28. ....
  29. ....
  30. ....
  31. ....
  32. ....
  33. ....
  34. ....
  35. ....
  36. ....
  37. ....
  38. ....
  39. ....
  40. ....
  41. ....
  42. ....
#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) {
  dt_String path, type, args, gif;
  dt_Integer len;
  int red, green, blue;

  du_http_file(xfer);

  /* check for GIf */
  type = dt_string(&len, xfer, 13, "htt_mime_type");
  if ((9 == len) && (0 == memcmp("image/gif", type, 9))) {

    /* check for RGB argument in URL */
    path = dt_string(&len, xfer, 8, "htt_path");
    for (args = path; args <= path+len-4; args++)
      if (0 == memcmp(args, "+rgb=", 5))
	break;

    if (args <= path+len-5) {
      /* get RGB 8-bit values */
      sscanf(args+5, "%2x%2x%2x", &red, &green, &blue);
      if (0 != (red + green + blue)) {
	/* get GIf image */
	if (NULL != (gif = dt_string(&len, xfer, 11, "htt_content")))
	  /* add color */
	  du_http_gif2rgb(gif, red, green, blue);
      }
    }
  }
}

/* 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-color.c in the "Contrib" area of Daylight Software and made with the command:

cd $DY_ROOT/contrib/src/http
make http-color

To invoke RGB coloring of a GIF, add "+rgb=" and a 6-character hexidecimal string to a GIF URL. The first 2 character are for red, the next two for green, and the last two for blue. For example, the URL /image/gif/sunlogo-gray.gif+rgb=08010f adds 8 parts red, 1 part green, and 15 parts blue to make a faint purple image.

20.5.7 Avoiding Run-Time Problems With Compiler Definitions

The consequence of a mispelled literal at run-time often costs more time to solve than at compile-time. This section is dedicated to a simple, yet effective, technique that uses property name definitions to enable the compiler to detect a misspelling instead of experiencing a run-time problem.

Given the numerous named properties in this toolkit, the probability of misspelling a string literal, i.e., "htt_data" instead of "htt_date" is quite high. From the compiler point-of-view, a misspelled literal is syntactically correct, so no problem is detected. At run-time, a problem arises from use of a misspelled property name:

This returns NULL (and length equal to -1). Lack of return value checking (as is often the case) can make this problem difficult to solve (I've actually misspelled htt_date a couple times). If you're good, you'll find the problem quickly, otherwise, this misspelling problem may cost many minutes or perhaps hours to characterize and correct. In dt_http.h, each property name is defined:

Uuse of the definition (DX_HTT_DATE) instead of the literal ("htt_date") effectively enables the compiler to detect a misspelling:

This produces a compiler error such as:


"my_http.c", line 11: undefined symbol: DX_HTT_DATA

Using property name definitions can save significant time during development of HTTP Toolkit programs. Further, with some compilers (i.e., GCC), the strlen call is substituted with the actual length of the string literal at compile-time, so there's no cost in including strlen as an argument to dt_string. Further, specifying the string length and literal in a dt_string call (strlen(DX_HTT_DATA), DX_HTT_DATA) creates the possibility that the two interdependent arguments may be inconsistent:

Defining a macro like:

Using it in calls to dt_string:

This averts the problem, or at least makes programming with property names a bit more convenient.

20.5.8 Technical Specifications, Methods, Headers, and Status Codes

This toolkit is compliant with HTTP/1.0 and adheres to specifications in the the following references:
  1. Gourley, D.; Totty, B., "HTTP: The Definitive Guide", First Edition, O'Reilly & Associates, Inc., September 2002.
  2. Coar, K., "The WWW Common Gateway Interface Version 1.1", Internet Draft, June 1999. (http://cgi-spec.golux.com/draft-coar-cgi-v11-03.txt)
  3. Berners-Lee, T.; Fielding, R.; Frystyk, H., "Hypertext Transfer Protocol -- HTTP/1.0", Request for Comments: 1945, May 1996. (http://www.ietf.org/rfc/rfc1945.txt)
  4. Gundavaram, S., "CGI Programming on the World Wide Web", First Edition, O'Reilly & Associates, Inc., March 1996.
  5. Berners-Lee, T., "The Original HTTP as defined in 1991", W3C, 1991. (http://www.w3.org/Protocols/HTTP/AsImplemented.html)
The following methods have an integer definition in dt_http.h:

The following headers have a named property defined in dt_http.h:

The following status codes are recognized in dt_http_put:

Back to Table of Contents
Go to previous chapter Reentrant Toolkit Interface