This edition applies to:
and to all subsequent releases and modifications until otherwise indicated in new editions.
Order publications through your IBM representative or through the IBM branch office serving your locality.
This section describes the purpose, organization, and conventions of this document, the WebSphere(R) Application Server Programming Guide for Edge Components.
This book describes the application programming interfaces (APIs) that are available for customizing the Edge components of WebSphere Application Server, Version 6.0.2. This information is intended for programmers who write plug-in applications and make other customizations. Network designers and system administrators also might be interested in this information as an indication of the types of customization that are possible.
Using the information in this book requires understanding of programming procedures using the Java(TM) or C programming languages, depending on the API that you plan to use. The methods and structures available in each exposed interface are documented, but you must know how to construct your own application, compile it for your system, and test it. Sample code is provided for some interfaces, but the samples are provided only as examples for constructing your own application.
This documentation uses the following typographical and keying conventions.
Convention | Meaning |
---|---|
Bold | When referring to graphical user interfaces (GUIs), bold face indicates menus, menu items, labels, buttons, icons, and folders. It also can be used to emphasize command names that otherwise might be confused with the surrounding text. |
Monospace | Indicates text you must enter at a command prompt. Monospace also indicates screen text, code examples, and file excerpts. |
Italics | Indicates variable values that you must provide (for example, you supply the name of a file for fileName). Italics also indicates emphasis and the titles of books. |
Ctrl-x | Where x is the name of a key, indicates a control-character sequence. For example, Ctrl-c means hold down the Ctrl key while you press the c key. |
Return | Refers to the key labeled with the word Return, the word Enter, or the left arrow. |
% | Represents the Linux and UNIX(R) command-shell prompt for a command that does not require root privileges. |
# | Represents the Linux and UNIX command-shell prompt for a command that requires root privileges. |
C:\ | Represents the Windows command prompt. |
Entering commands | When instructed to "enter" or "issue" a command, type the command and then press Return. For example, the instruction "Enter the ls command" means type ls at a command prompt and then press Return. |
[ ] | Enclose optional items in syntax descriptions. |
{ } | Enclose lists from which you must choose an item in syntax descriptions. |
| | Separates items in a list of choices enclosed in { }(braces) in syntax descriptions. |
... | Ellipses in syntax descriptions indicate that you can repeat the preceding item one or more times. Ellipses in examples indicate that information was omitted from the example for the sake of brevity. |
Accessibility features help a user who has a physical disability, such as restricted mobility or limited vision, to use software products successfully. These are the major accessibility features in WebSphere Application Server, Version 6.0.2:
Your feedback is important in helping to provide the most accurate and high-quality information. If you have any comments about this book or any other documentation about the Edge components of WebSphere Application Server:
This book discusses the application programming interfaces (APIs) provided for the Edge components of WebSphere Application Server. (The Edge components of WebSphere Application Server include Caching Proxy and Load Balancer.) Several interfaces are provided that enable administrators to customize their installations, to alter how the Edge components interact with each other, or to enable interaction with other software systems.
IMPORTANT: Caching Proxy is available on all Edge component installations, with the following exceptions:
The APIs in this document address several categories.
The Caching Proxy has several interfaces written into its processing sequence where custom processing can be added or substituted for standard processing. Customizations that can be executed include altering or augmenting tasks like the following:
Custom application programs, which are also known as Caching Proxy plug-ins, are called at predetermined points in the proxy server's processing sequence.
The Caching Proxy API has been used to implement certain system features. For example, the proxy server's LDAP support is implemented as a plug-in.
The Caching Proxy API describes the interface in detail and includes steps for configuring the proxy server to use plug-in programs.
The Load Balancer can be customized by writing your own advisors. Advisors perform the actual load measurement on the servers. With a custom advisor, you can use a method that you provide and that is relevant to your system to measure the load. This is especially important if you have customized or proprietary Web server systems.
Custom advisors provides detailed information about writing and using custom advisors. It includes sample advisor code.
Sample code for these APIs is included on the Edge Components CD-ROM, in the samples directory. Additional code samples are available from the WebSphere Application Server Web site, www.ibm.com/software/webservers/appserv/
This section discusses the Caching Proxy application programming interface (API): what it is, why it is useful, and how it works.
IMPORTANT: Caching Proxy is available on all Edge component installations, with the following exceptions:
The API is an interface to the Caching Proxy that enables you to extend the proxy server's base functions. You can write extensions, or plug-ins, to do customized processing, including the following examples:
The Caching Proxy API provides the following benefits:
Before writing your Caching Proxy plug-in programs, you need to understand how the proxy server works. The behavior of the proxy server can be divided into several distinct processing steps. For each of these steps, you can supply your own customized functions using the API. For example, do you want to do something after a client request is read but before performing any other processing? Or maybe you want to perform special routines during authentication and then again after the requested file is sent.
A library of predefined functions is provided with the API. Your plug-in programs can call the predefined API functions in order to interact with the proxy server process (for example, to manipulate requests, to read or write request headers, or to write to the proxy server's logs). These functions should not be confused with the plug-in functions that you write, which are called by the proxy server. The predefined functions are described in Predefined functions and macros.
You instruct the proxy server to call your plug-in functions at the appropriate steps by using the corresponding Caching Proxy API directives in your server configuration file. These directives are described in Caching Proxy configuration directives for API steps.
This document includes the following:
You can use these components and procedures to write your own Caching Proxy plug-in programs.
The basic operation of the proxy server can be broken up into steps based on the type of processing that the server performs during that phase. Each step includes a juncture at which a specified part of your program can run. By adding API directives to your Caching Proxy configuration file (ibmproxy.conf), you indicate which of your plug-in functions you want to be called during a particular step. You can call several plug-in functions during a particular process step by including more than one directive for that step.
Some steps are part of the server request process. In other words, the proxy server executes these steps each time it processes a request. Other steps are performed independently of request processing; that is, the server executes these steps regardless of whether a request is being processed.
Your compiled program resides in a shared object, for example, a DLL or .so file, depending on your operating system. As the server proceeds through its request process steps, it calls the plug-in functions associated with each step until one of the functions indicates that it has handled the request. If you specify more than one plug-in function for a particular step, the functions are called in the order in which their directives appear in the configuration file.
If the request is not handled by a plug-in function (either you did not include a Caching Proxy API directive for that step, or your plug-in function for that step returned HTTP_NOACTION), the server performs its default action for that step.
Note: This is true for all steps except the Service step; the Service step does not have a default action.
Figure 1 depicts the steps of the proxy server process and defines the processing order for the steps that are related to request processing.
Four of the steps on the diagram are executed independently from the processing of any client request. These steps are related to the running and maintenance of the proxy server. They include the following:
The following list explains the purpose of each step pictured in Figure 1. Note that not all steps are guaranteed to be called for a particular request.
Performs processing after a request is read but before anything else is done.
If this step returns an indication that the request was processed (HTTP_OK), the server bypasses the other steps in the request process and performs only the Transmogrifier, Log, and PostExit steps.
Uses stored security tokens to check the physical path for protections, ACLs, and other access controls, and generates the WWW-Authenticate headers required for basic authentication. If you write your own plug-in function to replace this step, you must generate these headers yourself.
See Authentication and authorization for more information.
Decodes, verifies, and stores security tokens.
See Authentication and authorization for more information.
Performs processing after authorization and object location but before the request is satisfied.
If this step returns an indication that the request was processed (HTTP_OK), the server bypasses the other steps in the request process and performs only the Transmogrifier, Log, and PostExit steps.
On AIX(R) systems, you need an export file (for example, libmyapp.exp) that lists your plug-in functions, and you must link with the Caching Proxy API import file, libhttpdapi.exp.
On Linux, HP-UX, and Solaris systems, you must link with the libhttpdapi and libc libraries.
On Windows(R) systems, you need a module definition file (.def) that lists your plug-in functions, and you must link with HTTPDAPI.LIB.
Be sure to include HTAPI.h and to use the HTTPD_LINKAGE macro in your function definitions. This macro ensures that all the functions use the same calling conventions.
Use the following compile and link commands as a guideline.
cc_r -c -qdbxextra -qcpluscmt foo.c
cc_r -bM:SRE -bnoentry -o libfoo.so foo.o -bI:libhttpdapi.exp -bE:foo.exp
(This command is shown on two lines for readability only.)
cc -Ae -c +Z +DAportable
aCC +Z -mt -c +DAportable
gcc -c foo.c
ld -G -Bsymbolic -o libfoo.so foo.o -lhttpdapi -lc
cc -mt -Bsymbolic -c foo.c
cc -mt -Bsymbolic -G -o libfoo.so foo.o -lhttpdapi -lc
cl /c /MD /DWIN32 foo.c
link httpdapi.lib foo.obj /def:foo.def /out:foo.dll /dll
To specify exports, use one of these methods:
Follow the syntax presented in Plug-in function prototypes to write your own program functions for the defined request processing steps.
Each of your functions must fill in the return code parameter with a value that indicates what action was taken:
The function prototypes for each Caching Proxy step show the format to use and explain the type of processing they can perform. Note that the function names are not predefined. You must give your functions unique names, and you can choose your own naming conventions. For ease of association, this document uses names that relate to the server's processing steps.
In each of these plug-in functions, certain predefined API functions are valid. Some predefined functions are not valid for all steps. The following predefined API functions are valid when called from all of these plug-in functions:
Additional valid or invalid API functions are noted in the function prototype descriptions.
The value of the handle parameter sent to your functions can be passed as the first argument to the predefined functions. Predefined API functions are described in Predefined functions and macros.
void HTTPD_LINKAGE ServerInitFunction ( unsigned char *handle, unsigned long *major_version, unsigned long *minor_version, long *return_code )
A function defined for this step is called once when your module is loaded during server initialization. It is your opportunity to perform initialization before any requests have been accepted.
Although all server initialization functions are called, a error return code from a function in this step causes the server to ignore all other functions configured in the same module as the function that returned the error code. (That is, any other functions contained in the same shared object as the function that returned the error are not called.)
The version parameters contain the proxy server's version number; these are supplied by the Caching Proxy.
void HTTPD_LINKAGE PreExitFunction ( unsigned char *handle, long *return_code )
A function defined for this step is called for each request after the request has been read but before any processing has occurred. A plug-in at this step can be used to access the client's request before it is processed by the Caching Proxy.
Valid return codes for the preExit function are the following:
Other return codes must not be used.
If this function returns HTTP_OK, the proxy server assumes that the request has been handled. All subsequent request processing steps are bypassed, and only the response steps (Transmogrifier, Log, and PostExit) are performed.
All predefined API functions are valid during this step.
void HTTPD_LINKAGE MidnightFunction ( unsigned char *handle, long *return_code )
A function defined for this step runs daily at midnight and contains no request context. For example, it can be used to invoke a child process to analyze logs. (Note that extensive processing during this step can interfere with logging.)
void HTTPD_LINKAGE AuthenticationFunction ( unsigned char *handle, long *return_code )
A function defined for this step is called for each request based on the request's authentication scheme. This function can be used to customize verification of the security tokens that are sent with a request.
void HTTPD_LINKAGE NameTransFunction ( unsigned char *handle, long *return_code )
A function defined for this step is called for each request. A URL template can be specified in the configuration file directive if you want the plug-in function to be called only for requests that match the template. The Name Translation step occurs before the request is processed and provides a mechanism for mapping URLs to objects such as file names.
void HTTPD_LINKAGE AuthorizationFunction ( unsigned char *handle, long *return_code )
A function defined for this step is called for each request. A URL template can be specified in the configuration file directive if you want the plug-in function to be called only for requests that match the template. The Authorization step occurs before the request is processed and can be used to verify that the identified object can be returned to the client. If you are doing basic authentication, you must generate the required WWW-Authenticate headers.
void HTTPD_LINKAGE ObjTypeFunction ( unsigned char *handle, long *return_code )
A function defined for this step is called for each request. A URL template can be specified in the configuration file directive if you want the plug-in function to be called only for requests that match the template. The Object Type step occurs before the request is processed and can be used to check whether the object exists, and to perform object typing.
void HTTPD_LINKAGE PostAuthFunction ( unsigned char *handle, long *return_code )
A function defined for this step is called after the request has been authorized but before any processing has occurred. If this function returns HTTP_OK, the proxy server assumes that the request has been handled. All subsequent request steps are bypassed, and only the response steps (Transmogrifier, Log, and PostExit) are performed.
All server predefined functions are valid during this step.
void HTTPD_LINKAGE ServiceFunction ( unsigned char *handle, long *return_code )
A function defined for this step is called for each request. A URL template can be specified in the configuration file directive if you want the plug-in function to be called only for requests that match the template. The Service step satisfies the request, if it was not satisfied in the PreExit or PostAuthorization steps.
All server predefined functions are valid during this step.
Refer to the Enable directive in the WebSphere Application Server Caching Proxy Administration Guide for information on configuring your Service function to be executed based on the HTTP method rather than on the URL.
For this step, you must implement the following four functions. (Your function names do not need to match these names.)
void * HTTPD_LINKAGE openFunction ( unsigned char *handle, long *return_code )
The open function performs any initialization (such as buffer allocation) required to process the data for this stream. Any return code other than HTTP_OK causes this filter to abort (the write and close functions are not called). Your function can return a void pointer so that you can allocate space for a structure and have the pointer passed back to you in the correlator parameter of the subsequent functions.
void HTTPD_LINKAGE writeFunction ( unsigned char *handle, unsigned char *data, /* response data sent by the origin server */ unsigned long *length, /* length of response data */ void *correlator, /* pointer returned by the 'open' function */ long *return_code )
The write function processes the data and can call the server's predefined HTTPD_write() function with the new or changed data. The plug-in must not attempt to free the buffer passed to it or expect the server to free the buffer it receives.
If you decide not to change the data during the scope of your write function, you still must call the HTTPD_write() function during the scope of either your open, write, or close function in order to pass the data for the response to the client. The correlator argument is the pointer to the data buffer that was returned in your open routine.
void HTTPD_LINKAGE closeFunction ( unsigned char *handle, void *correlator, long *return_code )
The close function performs any clean-up actions (such as flushing and freeing the correlator buffer) required to complete processing the data for this stream. The correlator argument is the pointer to the data buffer that was returned in your open routine.
void HTTPD_LINKAGE errorFunction ( unsigned char *handle, void *correlator, long *return_code )
The error function enables performance of clean-up actions, such as flushing or freeing the buffered data (or both) before an error page is sent. At this point, your open, write, and close functions are called to process the error page. The correlator argument is the pointer to the data buffer that was returned in your open routine.
Notes:
void HTTPD_LINKAGE GCAdvisorFunction ( unsigned char *handle, long *return_code )
A function defined for this step is called for each file in the cache during garbage collection. This function enables you to influence which files are kept and which files are discarded. For more information, see the GC_* variables.
void HTTPD_LINKAGE ProxyAdvisorFunction ( unsigned char *handle, long *return_code )
A function defined for this step is invoked during service of each proxy request. For example, it can be used to set the USE_PROXY variable.
void HTTPD_LINKAGE LogFunction ( unsigned char *handle, long *return_code )
A function defined for this step is called for each request after the request has been processed and the communication to the client has been closed. A URL template can be specified in the configuration file directive if you want the plug-in function to be called only for requests that match the template. This function is called regardless of the success or failure of the request processing. If you do not want your log plug-in to override the default log mechanism, set your return code to HTTP_NOACTION instead of HTTP_OK.
void HTTPD_LINKAGE ErrorFunction ( unsigned char *handle, long *return_code )
A function defined for this step is called for each request that fails. A URL template can be specified in the configuration file directive if you want the plug-in function to be called only for failed requests that match the template. The Error step provides an opportunity for you to customize the error response.
void HTTPD_LINKAGE PostExitFunction ( unsigned char *handle, long *return_code )
A function defined for this step is called for each request, regardless of the success or failure of the request. This step enables you to do clean-up tasks for any resources allocated by your plug-in to process the request.
void HTTPD_LINKAGE ServerTermFunction ( unsigned char *handle, long *return_code )
A function defined for this step is called when an orderly shutdown of the server occurs. It enables you to clean up resources allocated during the Server Initialization step. Do not call any HTTP_* functions in this step (the results are unpredictable). If you have more than one Caching Proxy API directive in your configuration file for Server Termination, they will all be called.
These return codes follow the HTTP 1.1 specification, RFC 2616, published by the World Wide Web Consortium (www.w3.org/pub/WWW/Protocols/). Your plug-in functions must return one of these values.
Value | Return code |
0 | HTTP_NOACTION |
100 | HTTP_CONTINUE |
101 | HTTP_SWITCHING_PROTOCOLS |
200 | HTTP_OK |
201 | HTTP_CREATED |
202 | HTTP_ACCEPTED |
203 | HTTP_NON_AUTHORITATIVE |
204 | HTTP_NO_CONTENT |
205 | HTTP_RESET_CONTENT |
206 | HTTP_PARTIAL_CONTENT |
300 | HTTP_MULTIPLE_CHOICES |
301 | HTTP_MOVED_PERMANENTLY |
302 | HTTP_MOVED_TEMPORARILY |
302 | HTTP_FOUND |
303 | HTTP_SEE_OTHER |
304 | HTTP_NOT_MODIFIED |
305 | HTTP_USE_PROXY |
307 | HTTP_TEMPORARY_REDIRECT |
400 | HTTP_BAD_REQUEST |
401 | HTTP_UNAUTHORIZED |
403 | HTTP_FORBIDDEN |
404 | HTTP_NOT_FOUND |
405 | HTTP_METHOD_NOT_ALLOWED |
406 | HTTP_NOT_ACCEPTABLE |
407 | HTTP_PROXY_UNAUTHORIZED |
408 | HTTP_REQUEST_TIMEOUT |
409 | HTTP_CONFLICT |
410 | HTTP_GONE |
411 | HTTP_LENGTH_REQUIRED |
412 | HTTP_PRECONDITION_FAILED |
413 | HTTP_ENTITY_TOO_LARGE |
414 | HTTP_URI_TOO_LONG |
415 | HTTP_BAD_MEDIA_TYPE |
416 | HTTP_BAD_RANGE |
417 | HTTP_EXPECTATION_FAILED |
500 | HTTP_SERVER_ERROR |
501 | HTTP_NOT_IMPLEMENTED |
502 | HTTP_BAD_GATEWAY |
503 | HTTP_SERVICE_UNAVAILABLE |
504 | HTTP_GATEWAY_TIMEOUT |
505 | HTTP_BAD_VERSION |
You can call the server's predefined functions and macros from your own plug-in functions. You must use their predefined names and follow the format described below. In the parameter descriptions, the letter i indicates an input parameter, the letter o indicates an output parameter, and i/o indicates that a parameter is used for both input and output.
Each of these functions returns one of the HTTPD return codes, depending on the success of the request. These codes are described in Return codes from predefined functions and macros.
Use the handle provided to your plug-in as the first parameter when calling these functions. Otherwise, the function returns an HTTPD_PARAMETER_ERROR error code. NULL is not accepted as a valid handle.
void HTTPD_LINKAGE HTTPD_authenticate ( unsigned char *handle, /* i; handle */ long *return_code /* o; return code */ )
void HTTPD_LINKAGE HTTPD_cacheable_url ( unsigned char *handle, /* i; handle */ unsigned char *url, /* i; URL to check */ unsigned char *req_method, /* i; request method for the URL */ long *retval /* o; return code */ )
The return value HTTPD_SUCCESS indicates that the URL content is cacheable; HTTPD_FAILURE indicates the content is not cacheable. HTTPD_INTERNAL_ERROR also is a possible return code for this function.
void HTTPD_LINKAGE HTTPD_close ( unsigned char *handle, /* i; handle */ long *return_code /* o; return code */ )
void HTTPD_LINKAGE HTTPD_exec ( unsigned char *handle, /* i; handle */ unsigned char *name, /* i; name of script to run */ unsigned long *name_length, /* i; length of the name */ long *return_code /* o; return code */ )
void HTTPD_LINKAGE HTTPD_extract ( unsigned char *handle, /* i; handle */ unsigned char *name, /* i; name of variable to extract */ unsigned long *name_length, /* i; length of the name */ unsigned char *value, /* o; buffer in which to put the value */ unsigned long *value_length, /* i/o; buffer size */ long *return_code /* o; return code */ )
If this function returns the code HTTPD_BUFFER_TOO_SMALL, the buffer size you requested was not big enough for the extracted value. In this case, the function does not use the buffer but updates the value_length parameter with the buffer size that you need in order to successfully extract this value. Retry the extraction with a buffer that is at least as big as the returned value_length.
void HTTPD_LINKAGE HTTPD_file ( unsigned char *handle, /* i; handle */ unsigned char *name, /* i; name of file to send */ unsigned long *name_length, /* i; length of the name */ long *return_code /* o; return code */ )
const unsigned char * /* o; value of variable */ HTTPD_LINKAGE httpd_getvar( unsigned char *handle, /* i; handle */ unsigned char *name, /* i; variable name */ unsigned long *n /* i; index number for the array containing the header */ )
The index for the array containing the header begins with 0. To obtain the first item in the array, use the value 0 for n; to obtain the fifth item, use the value 4 for n.
void HTTPD_LINKAGE HTTPD_log_access ( unsigned char *handle, /* i; handle */ unsigned char *value, /* i; data to write */ unsigned long *value_length, /* i; length of the data */ long *return_code /* o; return code */ )
Note that escape symbols are not required when writing the percent symbol (%) in server access logs.
void HTTPD_LINKAGE HTTPD_log_error ( unsigned char *handle, /* i; handle */ unsigned char *value, /* i; data to write */ unsigned long *value_length, /* i; length of the data */ long *return_code /* o; return code */ )
Note that escape symbols are not required when writing the percent symbol (%) in server error logs.
void HTTPD_LINKAGE HTTPD_log_event ( unsigned char *handle, /* i; handle */ unsigned char *value, /* i; data to write */ unsigned long *value_length, /* i; length of the data */ long *return_code /* o; return code */ )
Note that escape symbols are not required when writing the percent symbol (%) in server event logs.
void HTTPD_LINKAGE HTTPD_log_trace ( unsigned char *handle, /* i; handle */ unsigned char *value, /* i; data to write */ unsigned long *value_length, /* i; length of the data */ long *return_code /* o; return code */ )
Note that escape symbols are not required when writing the percent symbol (%) in server trace logs.
void HTTPD_LINKAGE HTTPD_open ( unsigned char *handle, /* i; handle */ long *return_code /* o; return code */ )
void HTTPD_LINKAGE HTTPD_proxy ( unsigned char *handle, /* i; handle */ unsigned char *url_name, /* i; URL for the proxy request */ unsigned long *name_length, /* i; length of URL */ void *request_body, /* i; body of request */ unsigned long *body_length, /* i; length of body */ long *return_code /* o; return code */ )
void HTTPD_LINKAGE HTTPD_read ( unsigned char *handle, /* i; handle */ unsigned char *value, /* i; buffer for data */ unsigned long *value_length, /* i/o; buffer size (data length) */ long *return_code /* o; return code */ )
void HTTPD_LINKAGE HTTPD_restart ( long *return_code /* o; return code */ )
Note that you can also create variables with this function. Variables that you create are subject to the conventions for HTTP_ and PROXY_ prefixes, which are described in Variables. If you create a variable that begins with HTTP_, it is sent as a header in the response to the client, without the HTTP_ prefix. For example, to set a Location header, use HTTPD_set() with the variable name HTTP_LOCATION. Variables created with a PROXY_ prefix are sent as headers in the request to the content server. Variables created with a CGI_ prefix are passed to CGI programs.
This function is valid in all steps; however, not all variables are valid in all steps.
void HTTPD_LINKAGE HTTPD_set ( unsigned char *handle, /* i; handle */ unsigned char *name, /* i; name of value to set */ unsigned long *name_length, /* i; length of the name */ unsigned char *value, /* i; buffer with value */ unsigned long *value_length, /* i; length of value */ long *return_code /* o; return code */ )
long /* o; return code */ HTTPD_LINKAGE httpd_setvar ( unsigned char *handle, /* i; handle */ unsigned char *name, /* i; variable name */ unsigned char *value, /* i; new value */ unsigned long *addHdr /* i; add header or replace it */ )
The addHdr parameter has four possible values:
These values are defined in HTAPI.h.
void HTTPD_LINKAGE httpd_variant_insert ( unsigned char *handle, /* i; handle */ unsigned char *URI, /* i; URI of this object */ unsigned char *dimension, /* i; dimension of variation */ unsigned char *variant, /* i; value of the variant */ unsigned char *filename, /* i; file containing the object */ long *return_code /* o; return code */ )
Notes:
Mozilla 4.0 (compatible; BatBrowser 94.1.2; Bat OS)
void HTTPD_LINKAGE httpd_variant_lookup ( unsigned char *handle, /* i; handle */ unsigned char *URI, /* URI of this object */ unsigned char *dimension, /* i; dimension of variation */ unsigned char *variant, /* i; value of the variant */ long *return_code); /* o; return code */
If you do not set the content type before calling this function for the first time, the server assumes that you are sending a CGI data stream.
void HTTPD_LINKAGE HTTPD_write ( unsigned char *handle, /* i; handle */ unsigned char *value, /* i; data to send */ unsigned char *value_length, /* i; length of the data */ long *return_code); /* o; return code */
The server will set the return code parameter to one of these values, depending on the success of the request:
Value | Status code | Explanation |
---|---|---|
-1 | HTTPD_UNSUPPORTED | The function is not supported. |
0 | HTTPD_SUCCESS | The function succeeded, and the output fields are valid. |
1 | HTTPD_FAILURE | The function failed. |
2 | HTTPD_INTERNAL_ERROR | An internal error was encountered and processing for this request cannot continue. |
3 | HTTPD_PARAMETER_ERROR | One or more invalid parameters was passed. |
4 | HTTPD_STATE_CHECK | The function is not valid in this process step. |
5 | HTTPD_READ_ONLY | (Returned only by HTTPD_set and httpd_setvar.) The variable is read-only and cannot be set by the plug-in. |
6 | HTTPD_BUFFER_TOO_SMALL | (Returned by HTTPD_set, httpd_setvar, and HTTPD_read.) The buffer provided was too small. |
7 | HTTPD_AUTHENTICATE_FAILED | (Returned only by HTTPD_authenticate.) The authentication failed. Examine the HTTP_RESPONSE and HTTP_REASON variables for more information. |
8 | HTTPD_EOF | (Returned only by HTTPD_read.) Indicates the end of the request body. |
9 | HTTPD_ABORT_REQUEST | The request was aborted because the client provided an entity tag that did not match the condition specified by the request. |
10 | HTTPD_REQUEST_SERVICED | (Returned by HTTPD_proxy.) The function that was called completed the response for this request. |
11 | HTTPD_RESPONSE_ALREADY_COMPLETED | The function failed because the response for that request has already been completed. |
12 | HTTPD_WRITE_ONLY | The variable is write-only and cannot be read by the plug-in. |
Each step in the request process has a configuration directive that you use to indicate which of your plug-in functions you want to call and execute during that step. You can add these directives to your server's configuration file (ibmproxy.conf) by manually editing and updating it, or by using the API Request Processing form in the Caching Proxy Configuration and Administration forms.
This means that the server processes the Service, NameTrans, Exec, Fail, Map, Pass, Proxy, ProxyWAS, and Redirect directives in their sequence within the configuration file. When the server successfully maps a URL to a file, it does not read or process any other of these directives. (The Map directive is an exception. Refer to the WebSphere Application Server Caching Proxy Administration Guide for complete information about proxy server mapping rules.)
These configuration file directives must appear in the ibmproxy.conf file as one line, with no spaces other than those explicitly specified here. Although line breaks appear for readability in some of the syntax examples, there must be no spaces at those points in the actual directive.
The variables in these directives have the following meanings:
The Service directive requires an asterisk (*) after the function name if you want to have access to path information.
For additional information, including syntax, for these directives, see the WebSphere Application Server Caching Proxy Administration Guide.
The Caching Proxy API is backward-compatible with ICAPI and GWAPI, through version 4.6.1.
Use the following guidelines for porting CGI applications written in C to use the Caching Proxy API:
When writing API programs, you can use Caching Proxy variables that provide information about the remote client and server system.
Notes:
ACCEPT_RANGES BYTES CLIENT_ADDR 9.67.84.3
This variable can be used in the PostAuthorization, PostExit, ProxyAdvisor, or Log steps.
http://www.anynet.com/~userk/main.htm
Mon, 01 Mar 2002 19:41:17 GMT
Mon, 01 Mar 1998 19:41:17 GMT
d:\wwwhome\foo
/wwwhome/foo
application/x-www-form-urlencoded
7034
NAME=Eugene+T%2E+Fox&ADDR=etfox%7Cibm.net&INTEREST=xyz
http://www.company.com/homepage
application/x-www-form-urlencoded
First, a short review of the terminology:
Figure 3 depicts the proxy server's authentication and authorization process.
As demonstrated in Figure 3, the initiation of the authorization process is the first step in the server's authorization and authentication process.
In the Caching Proxy, authentication is part of the authorization process; it occurs only when authorization is required.
The proxy server follows these steps when processing a request that requires authorization.
If your Caching Proxy plug-in provides its own authorization process, it overrides the default server authorization and authentication. Therefore, if you have authorization directives in your configuration file, the plug-in functions associated with them must also handle any necessary authentication. The predefined HTTPD_authenticate() function is provided for you to use.
There are three ways to provide for authentication in your authorization plug-ins:
When the Authorization step is executed, it performs your authorization plug-in function, which, in turn, calls your authentication plug-in function.
When the Authorization step is executed, it performs your authorization plug-in function, which, in turn, calls the default server authentication.
When the Authorization step is executed, it performs your authorization plug-in function and any authentication it includes.
If your Caching Proxy plug-in does not provide its own authorization process, you can still provide customized authentication by using the following method:
When the Authorization step is executed, it performs the default server authorization, which, in turn, calls your authentication plug-in function.
Remember the following points:
Use variant caching to cache data that is a modified form of the original document (the URI). The Caching Proxy handles variants generated by the API. Variants are different versions of a base document.
In general, when origin servers send variants, they fail to identify them as such. The Caching Proxy supports only variants created by plug-ins (for example, code page conversion). If a plug-in creates a variant based on criteria that are not in the HTTP header, it must include a PreExit or PostAuthorization step function to create a pseudoheader so that the Caching Proxy can correctly identify the existing variant.
For example, use a Transmogrifier API program to modify the data that users request based on the value of the User-Agent header that the browser sends. In the close function, save the modified content to a file or specify a buffer length and pass the buffer as the data argument. Then use the variant caching functions httpd_variant_insert() and httpd_variant_lookup() to put the content in the cache.
To help you get started with your own Caching Proxy API functions, look at the sample programs provided in the samples directory of the Edge components installation CD-ROM. Additional information is available on the WebSphere Application Server Web site, www.ibm.com/software/webservers/appserv/.
This section discusses writing custom advisors for the Load Balancer.
Advisors are software agents that work within Load Balancer to provide information about the load on a given server. A different advisor exists for each standard protocol (HTTP, SSL, and others). Periodically, the Load Balancer base code performs an advisor cycle, during which it individually evaluates the status of all servers in its configuration.
By writing your own advisors for the Load Balancer, you can customize how your server machines' load is determined.
On Windows systems: When using a Load Balancer for IPv6 installation, if you are using IPv6 protocol on your machine and want to use advisors you must modify the protocol file that resides in C:\windows\system32\drivers\etc\ directory.
For IPv6, insert the following line in the protocol file:
ipv6-icmp 58 IPv6-ICMP # IPv6 interface control message protocol
In general, advisors work to enable load balancing in the following manner.
Standard advisors provided with the Load Balancer include advisors for the following functions. Detailed information about these advisors is available in the WebSphere Application Server Load Balancer Administration Guide
To support proprietary protocols for which standard advisors are not provided, you must write custom advisors.
A custom advisor is a small piece of Java code, provided as a class file, that is called by the Load Balancer base code to determine the load on a server. The base code provides all necessary administrative services, including starting and stopping an instance of the custom advisor, providing status and reports, recording history information in a log file, and reporting advisor results to the manager component.
When the Load Balancer base code calls a custom advisor, the following steps happen.
Custom advisors can be designed to interact with the Load Balancer in either normal mode or replace mode.
The choice for the mode of operation is specified in the custom advisor file as a parameter in the constructor method. (Each advisor operates in only one of these modes, based on its design.)
In normal mode, the custom advisor exchanges data with the server, and the base advisor code times the exchange and calculates the load value. The base code then reports this load value to the manager. The custom advisor returns the value zero to indicate success, or negative one to indicate an error.
To specify normal mode, set the replace flag in the constructor to false.
In replace mode, the base code does not perform any timing measurements. The custom advisor code performs whatever operations are specified, based on its unique requirements, and then returns an actual load number. The base code accepts the load number and reports it, unaltered, to the manager. For best results, normalize your load numbers between 10 and 1000, with 10 representing a fast server and 1000 representing a slow server.
To specify replace mode, set the replace flag in the constructor to true.
Custom advisor file names must follow the form ADV_name.java, where name is the name that you choose for your advisor. The complete name must start with the prefix ADV_ in uppercase letters, and all subsequent characters must be lowercase letters. The requirement for lowercase letters ensures that the command for running the advisor is not case sensitive.
According to Java conventions, the name of the class defined within the file must match the name of the file.
Custom advisors must be written in the Java language and compiled with a Java compiler that is installed on the development machine. The following files are referenced during compilation:
Your classpath environment variable must point to both the custom advisor file and the base classes file during the compilation. A compile command might have the following format:
javac -classpath /opt/ibm/edge/lb/servers/lib/ibmnd.jar ADV_name.java
This example uses the default Linux and UNIX installation path. The advisor file is named ADV_name.java, and the advisor file is stored in the current directory.
The output of the compilation is a class file, for example, ADV_name.class. Before starting the advisor, copy the class file to the install_path/servers/lib/CustomAdvisors/ directory.
To run the custom advisor, you must first copy the advisor's class file to the lib/CustomAdvisors/ subdirectory on the Load Balancer machine. For example, for a custom advisor named myping, the file path is install_path/servers/lib/CustomAdvisors/ADV_myping.class
Configure the Load Balancer, start its manager function, and issue the command to start your custom advisor. The custom advisor is specified by its name, excluding the ADV_ prefix and the file extension:
dscontrol advisor start myping port_number
The port number specified in the command is the port on which the advisor will open a connection with the target server.
Like all advisors, a custom advisor extends the functionality of the advisor base class, which is called ADV_Base. The advisor base performs most of the advisor's functions, such as reporting loads back to the manager for use in the manager's weight algorithm. The advisor base also performs socket connect and close operations and provides send and receive methods for use by the advisor. The advisor is used only for sending and receiving data on the specified port for the server that is being investigated. The TCP methods provided within the advisor base are timed to calculate load. A flag within the constructor of the advisor base overwrites the existing load with the new load returned from the advisor, if desired.
Advisors have the following base class methods:
Details about these required routines appear later in this section.
Custom advisors are called after native, or standard, advisors have been searched. If the Load Balancer does not find a specified advisor among the list of standard advisors, it consults the list of custom advisors. Additional information about using advisors is available in the WebSphere Application Server Load Balancer Administration Guide.
Remember the following requirements for custom advisor names and paths.
void ADV_Base Constructor ( string sName; string sVersion; int iDefaultPort; int iInterval; string sDefaultLogFileName; boolean replace )
void ADV_AdvisorInitialize()
This method is provided to perform any initialization that might be required for the custom advisor. This method is called after the advisor base module starts.
In many cases, including the standard advisors, this method is not used and its code consists of a return statement only. This method can be used to call the suppressBaseOpeningSocket method, which is valid only from within this method.
int getLoad( int iConnectTime; ADV_Thread *caller )
The methods, or functions, described in the following sections can be called from custom advisors. These methods are supported by the advisor base code.
Some of these function calls can be made directly, for example, function_name(), but others require the prefix caller. Caller represents the base advisor instance that supports the custom advisor that is being executed.
The ADVLOG function allows a custom advisor to write a text message to the advisor base log file. The format follows:
void ADVLOG (int logLevel, string message)
The getAdvisorName function returns a Java string with the suffix portion of your custom advisor's name. For example, for an advisor named ADV_cdload.java, this function returns the value cdload.
This function takes no parameters.
Note that it is not possible for this value to change during one instantiation of an advisor.
The getAdviseOnPort function returns the port number on which the calling custom advisor is running. The return value is a Java integer (int), and the function takes no parameters.
Note that it is not possible for this value to change during one instantiation of an advisor.
The getCurrentServer function returns the IP address of the current server. The return value is a Java string in IP address format, for example, 128.0.72.139
Typically, this address changes each time you call your custom advisor, because the advisor base code queries all server machines in series.
This function takes no parameters.
The getCurrentCluster function call returns the IP address of the current server cluster. The return value is a Java string in IP address format, for example, 128.0.72.139
Typically, this address changes each time you call your custom advisor, because the advisor base code queries all server clusters in series.
This function takes no parameters.
The getInterval function returns the advisor interval, that is, the number of seconds between advisor cycles. This value is equal to the default value set in the custom advisor's constructor, unless the value has been modified at run time by using the dscontrol command.
The return value is a Java integer (int). The function takes no parameters.
The getLatestLoad function allows a custom advisor to obtain the latest load value for a given server object. The load values are maintained in internal tables by the advisor base code and the manager daemon.
int caller.getLatestLoad (string cluster_IP, int port, string server_IP)
The three arguments together define one server object.
The return value is an integer.
This function call is useful if you want to make the behavior of one protocol or port dependent on the behavior of another. For example, you might use this function call in a custom advisor that disabled a particular application server if the Telnet server on that same machine was disabled.
The receive function gets information from the socket connection.
caller.receive(stringbuffer *response)
The parameter response is a string buffer into which the retrieved data is placed. Additionally, the function returns an integer value with the following significance:
The send function uses the established socket connection to send a packet of data to the server, using the specified port.
caller.send(string command)
The parameter command is a string containing the data to send to the server. The function returns an integer value with the following significance:
The suppressBaseOpeningSocket function call allows a custom advisor to specify whether the base advisor code opens a TCP socket to the server on the custom advisor's behalf. If your advisor does not use direct communication with the server to determine its status, it might not be necessary to open this socket.
This function call can be issued only once, and it must be issued from the ADV_AdvisorInitialize routine.
The function takes no parameters.
The following examples show how custom advisors can be implemented.
This sample source code is similar to the standard Load Balancer HTTP advisor. It functions as follows:
This advisor operates in normal mode, so the load measurement is based on the elapsed time in milliseconds required to perform the socket open, send, receive, and close operations.
package CustomAdvisors; import com.ibm.internet.lb.advisors.*; public class ADV_sample extends ADV_Base implements ADV_MethodInterface { static final String ADV_NAME ="Sample"; static final int ADV_DEF_ADV_ON_PORT = 80; static final int ADV_DEF_INTERVAL = 7; static final string ADV_SEND_REQUEST = "HEAD / HTTP/1.0\r\nAccept: */*\r\nUser-Agent: " + "IBM_Load_Balancer_HTTP_Advisor\r\n\r\n"; //-------- // Constructor public ADV_sample() { super(ADV_NAME, "3.0.0.0-03.31.00", ADV_DEF_ADV_ON_PORT, ADV_DEF_INTERVAL, "", false); super.setAdvisor( this ); } //-------- // ADV_AdvisorInitialize public void ADV_AdvisorInitialize() { return; // usually an empty routine } //-------- // getLoad public int getLoad(int iConnectTime, ADV_Thread caller) { int iRc; int iLoad = ADV_HOST_INACCESSIBLE; // initialize to inaccessible iRc = caller.send(ADV_SEND_REQUEST); // send the HTTP request to // the server if (0 <= iRc) { // if the send is successful StringBuffer sbReceiveData = new StringBuffer(""); // allocate a buffer // for the response iRc = caller.receive(sbReceiveData); // receive the result // parse the result here if you need to if (0 <= iRc) { // if the receive is successful iLoad = 0; // return 0 for success } // (advisor's load value is ignored by } // base in normal mode) return iLoad; } }
This sample illustrates suppressing the standard socket opened by the advisor base. Instead, this advisor opens a side stream Java socket to query a server. This procedure can be useful for servers that use a different port from normal client traffic to listen for an advisor query.
In this example, a server is listening on port 11999 and when queried returns a load value with a hexadecimal int "4". This sample runs in replace mode, that is, the last parameter of the advisor constructor is set to true and the advisor base code uses the returned load value rather than the elapsed time.
Note the call to supressBaseOpeningSocket() in the initialization routine. Suppressing the base socket when no data will be sent is not required. For example, you might want to open the socket to ensure that the advisor can contact the server. Examine the needs of your application carefully before making this choice.
package CustomAdvisors; import java.io.*; import java.net.*; import java.util.*; import java.util.Date; import com.ibm.internet.lb.advisors.*; import com.ibm.internet.lb.common.*; import com.ibm.internet.lb.server.SRV_ConfigServer; public class ADV_sidea extends ADV_Base implements ADV_MethodInterface { static final String ADV_NAME = "sidea"; static final int ADV_DEF_ADV_ON_PORT = 12345; static final int ADV_DEF_INTERVAL = 7; // create an array of bytes with the load request message static final byte[] abHealth = {(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x04}; public ADV_sidea() { super(ADV_NAME, "3.0.0.0-03.31.00", ADV_DEF_ADV_ON_PORT, ADV_DEF_INTERVAL, "", true); // replace mode parameter is true super.setAdvisor( this ); } //-------- // ADV_AdvisorInitialize public void ADV_AdvisorInitialize() { suppressBaseOpeningSocket(); // tell base code not to open the // standard socket return; } //-------- // getLoad public int getLoad(int iConnectTime, ADV_Thread caller) { int iRc; int iLoad = ADV_HOST_INACCESSIBLE; // -1 int iControlPort = 11999; // port on which to communicate with the server string sServer = caller.getCurrentServer(); // address of server to query try { socket soServer = new Socket(sServer, iControlPort); // open socket to // server DataInputStream disServer = new DataInputStream( soServer.getInputStream()); DataOutputStream dosServer = new DataOutputStream( soServer.getOutputStream()); int iRecvTimeout = 10000; // set timeout (in milliseconds) // for receiving data soServer.setSoTimeout(iRecvTimeout); dosServer.writeInt(4); // send a message to the server dosServer.flush(); iLoad = disServer.readByte(); // receive the response from the server } catch (exception e) { system.out.println("Caught exception " + e); } return iLoad; // return the load reported from the server } }
This custom advisor sample demonstrates the capability to detect failure for one port of a server based upon both its own status and on the status of a different server daemon that is running on another port on the same server machine. For example, if the HTTP daemon on port 80 stops responding, you might also want to stop routing traffic to the SSL daemon on port 443.
This advisor is more aggressive than standard advisors, because it considers any server that does not send a response to have stopped functioning, and marks it as down. Standard advisors consider unresponsive servers to be very slow. This advisor marks a server as down for both the HTTP port and the SSL port based on a lack of response from either port.
To use this custom advisor, the administrator starts two instances of the advisor: one on the HTTP port, and one on the SSL port. The advisor instantiates two static global hash tables, one for HTTP and one for SSL. Each advisor tries to communicate with its server daemon and stores the results of this event in its hash table. The value that each advisor returns to the base advisor class depends on both the ability to communicate with its own server daemon and the ability of the partner advisor to communicate with its daemon.
The following custom methods are used.
The following error conditions are detected.
This sample is written to link ports 80 for HTTP and 443 for SSL, but it can be tailored to any combination of ports.
package CustomAdvisors; import java.io.*; import java.net.*; import java.util.*; import java.util.Date; import com.ibm.internet.lb.advisors.*; import com.ibm.internet.lb.common.*; import com.ibm.internet.lb.manager.*; import com.ibm.internet.lb.server.SRV_ConfigServer; //-------- // Define the table element for the hash tables used in this custom advisor class ADV_nte implements Cloneable { private string sCluster; private int iPort; private string sServer; private int iLoad; private Date dTimestamp; //-------- // constructor public ADV_nte(string sClusterIn, int iPortIn, string sServerIn, int iLoadIn) { sCluster = sClusterIn; iPort = iPortIn; sServer = sServerIn; iLoad = iLoadIn; dTimestamp = new Date(); } //-------- // check whether this element is current or expired public boolean isCurrent(ADV_twop oThis) { boolean bCurrent; int iLifetimeMs = 3 * 1000 * oThis.getInterval(); // set lifetime as // 3 advisor cycles Date dNow = new Date(); Date dExpires = new Date(dTimestamp.getTime() + iLifetimeMs); if (dNow.after(dExpires)) { bCurrent = false; } else { bCurrent = true; } return bCurrent; } //-------- // value accessor(s) public int getLoadValue() { return iLoad; } //-------- // clone (avoids corruption between threads) public synchronized Object Clone() { try { return super.clone(); } catch (cloneNotSupportedException e) { return null; } } } //-------- // define the custom advisor public class ADV_twop extends ADV_Base implements ADV_MethodInterface, ADV_AdvisorVersionInterface { static final int ADV_TWOP_PORT_HTTP = 80; static final int ADV_TWOP_PORT_SSL = 443; //-------- // define tables to hold port-specific history information static HashTable htTwopHTTP = new Hashtable(); static HashTable htTwopSSL = new Hashtable(); static final String ADV_TWOP_NAME = "twop"; static final int ADV_TWOP_DEF_ADV_ON_PORT = 80; static final int ADV_TWOP_DEF_INTERVAL = 7; static final string ADV_HTTP_REQUEST_STRING = "HEAD / HTTP/1.0\r\nAccept: */*\r\nUser-Agent: " + "IBM_LB_Custom_Advisor\r\n\r\n"; //-------- // create byte array with SSL client hello message public static final byte[] abClientHello = { (byte)0x80, (byte)0x1c, (byte)0x01, // client hello (byte)0x03, (byte)0x00, // SSL version (byte)0x00, (byte)0x03, // cipher spec len (bytes) (byte)0x00, (byte)0x00, // session ID len (bytes) (byte)0x00, (byte)0x10, // challenge data len (bytes) (byte)0x00, (byte)0x00, (byte)0x03, // cipher spec (byte)0x1A, (byte)0xFC, (byte)0xE5, (byte)Ox20, // challenge data (byte)0xFD, (byte)0x3A, (byte)0x3C, (byte)0x18, (byte)0xAB, (byte)0x67, (byte)0xB0, (byte)0x52, (byte)0xB1, (byte)0x1D, (byte)0x55, (byte)0x44, (byte)0x0D, (byte)0x0A }; //-------- // constructor public ADV_twop() { super(ADV_TWOP_NAME, VERSION, ADV_TWOP_DEF_ADV_ON_PORT, ADV_TWOP_DEF_INTERVAL, "", false); // false = load balancer times the response setAdvisor ( this ); } //-------- // ADV_AdvisorInitialize public void ADV_AdvisorInitialize() { return; } //-------- // synchronized PUT and GET access routines for the hash tables synchronized ADV_nte getNte(Hashtable ht, String sName, String sHashKey) { ADV_nte nte = (ADV_nte)(ht.get(sHashKey)); if (null != nte) { nte = (ADV_nte)nte.clone(); } return nte; } synchronized void putNte(Hashtable ht, String sName, String sHashKey, ADV_nte nte) { ht.put(sHashKey,nte); return; } //-------- // getLoadHTTP - determine HTTP load based on server response int getLoadHTTP(int iConnectTime, ADV_Thread caller) { int iLoad = ADV_HOST_INACCESSIBLE; int iRc = caller.send(ADV_HTTP_REQUEST_STRING); // send request message // to server if (0 <= iRc) { // did the request return a failure? StringBuffer sbReceiveData = new StringBuffer("") // allocate a buffer // for the response iRc = caller.receive(sbReceiveData); // get response from server if (0 <= iRc) { // did the receive return a failure? if (0 < sbReceiveData.length()) { // is data there? iLoad = SUCCESS; // ignore retrieved data and // return success code } } } return iLoad; } //-------- // getLoadSSL() - determine SSL load based on server response int getLoadSSL(int iConnectTime, ASV_Thread caller) { int iLoad = ADV_HOST_INACCESSIBLE; int iSocket = caller.getAdvisorSocket(); // send hex request to server CMNByteArrayWrapper cbawClientHello = new CMNByteArrayWrapper( abClientHello); int iRc = SRV_ConfigServer.socketapi.sendBytes(iSocket, cbawClientHello); if (0 <= iRc) { // did the request return a failure? StringBuffer sbReceiveData = new StringBuffer(""); // allocate buffer // for the response iRc = caller.receive(sbReceiveData); // get a response from // the server if (0 <= iRc) { // did the receive return a failure? if (0 < sbReceiveData.length()) { // is data there? iLoad = SUCCESS; // ignore retrieved data and return success code } } } return iLoad; } //-------- // getLoad - merge results from the HTTP and SSL methods public int getLoad(int iConnectTime, ADV_Thread caller) { int iLoadHTTP; int iLoadSSL; int iLoad; int iRc; String sCluster = caller.getCurrentCluster(); // current cluster address int iPort = getAdviseOnPort(); String sServer = caller.getCurrentServer(); String sHashKey = sCluster = ":" + sServer; // hash table key if (ADV_TWOP_PORT_HTTP == iPort) { // handle an HTTP server iLoadHTTP = getLoadHTTP(iConnectTime, caller); // get the load for HTTP ADV_nte nteHTTP = newADV_nte(sCluster, iPort, sServer, iLoadHTTP); putNte(htTwopHTTP, "HTTP", sHashKey, nteHTTP); // save HTTP load // information ADV_nte nteSSL = getNte(htTwopSSL, "SSL", sHashKey); // get SSL // information if (null != nteSSL) { if (true == nteSSL.isCurrent(this)) { // check the time stamp if (ADV_HOST_INACCESSIBLE != nteSSL.getLoadValue()) { // is SSL // working? iLoad = iLoadHTTP; } else { // SSL is not working, so mark the HTTP server down iLoad= ADV_HOST_INACCESSIBLE; } } else { // SSL information is expired, so mark the // HTTP server down iLoad = ADV_HOST_INACCESSIBLE; } } else { // no load information about SSL, report // getLoadHTTP() results iLoad = iLoadHTTP; } } else if (ADV_TWOP_PORT_SSL == iPort) { // handle an SSL server iLoadSSL = getLoadSSL(iConnectTime, caller); // get load for SSL ADV_nte nteSSL = new ADV_nte(sCluster, iPort, sServer, iLoadSSL); putNte(htTwopSSL, "SSL", sHashKey, nteSSL); // save SSL load info. ADV_nte nteHTTP = getNte(htTwopHTTP, "SSL", sHashKey); // get HTTP // information if (null != nteHTTP) { if (true == nteHTTP.isCurrent(this)) { // check the timestamp if (ADV_HOST_INACCESSIBLE != nteHTTP.getLoadValue()) { // is HTTP // working? iLoad = iLoadSSL; } else { // HTTP server is not working, so mark SSL down iLoad = ADV_HOST_INACCESSIBLE; } } else { // expired information from HTTP, so mark SSL down iLoad = ADV_HOST_INACCESSIBLE; } } else { // no load information about HTTP, report // getLoadSSL() results iLoad = iLoadSSL; } } //-------- // error handler else { iLoad = ADV_HOST_INACCESSIBLE; } return iLoad; } }
A sample custom advisor for WebSphere Application Server is included in the install_path/servers/samples/CustomAdvisors/ directory. The full code is not duplicated in this document.
The complete advisor is only slightly more complex than the sample. It adds a specialized parsing routine that is more compact than the StringTokenizer example shown above.
The more complex part of the sample code is in the Java servlet. Among other methods, the servlet contains two methods required by the servlet specification: init() and service(), and one method, run(), that is required by the Java.lang.thread class.
The relevant fragments of the servlet code appear below.
... public void init(ServletConfig config) throws ServletException { super.init(config); ... _checker = new Thread(this); _checker.start(); } public void run() { setStatus(GOOD); while (true) { if (!getKeepRunning()) return; setStatus(figureLoad()); setLastUpdate(new java.util.Date()); try { _checker.sleep(_interval * 1000); } catch (Exception ignore) { ; } } } public void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { ServletOutputStream out = null; try { out = res.getOutputStream(); } catch (Exception e) { ... } ... res.setContentType("text/x-application-LBAdvisor"); out.println(getStatusString()); out.println(getLastUpdate().toString()); out.flush(); return; } ...
Whether you use a standard call to an existing part of the application server or add a new piece of code to be the server-side counterpart of your custom advisor, you possibly want to examine the load values returned and change server behavior. The Java StringTokenizer class, and its associated methods, make this investigation easy to do.
The content of a typical HTTP command might be GET /index.html HTTP/1.0
A typical response to this command might be the following.
HTTP/1.1 200 OK Date: Mon, 20 November 2000 14:09:57 GMT Server: Apache/1.3.12 (Linux and UNIX) Content-Location: index.html.en Vary: negotiate TCN: choice Last-Modified: Fri, 20 Oct 2000 15:58:35 GMT ETag: "14f3e5-1a8-39f06bab;39f06a02" Accept-Ranges: bytes Content-Length: 424 Connection: close Content-Type: text/html Content-Language: en <!DOCTYPE HTML PUBLIC "-//w3c//DTD HTML 3.2 Final//EN"> <HTML><HEAD><TITLE>Test Page</TITLE></HEAD> <BODY><H1>Apache server</H1> <HR> <P><P>This Web server is running Apache 1.3.12. <P><HR> <P><IMG SRC="apache_pb.gif" ALT=""> </BODY></HTML>
The items of interest are contained in the first line, specifically the HTTP return code.
The HTTP specification classifies return codes that can be summarized as follows:
If you know very precisely what codes the server can possibly return, your code might not need to be as detailed as this example. However, keep in mind that limiting the return codes you detect might limit the future flexibility of your program.
The following example is a stand-alone Java program that contains a minimal HTTP client. The example invokes a simple, general-purpose parser for examining HTTP responses.
import java.io.*; import java.util.*; import java.net.*; public class ParseTest { static final int iPort = 80; static final String sServer = "www.ibm.com"; static final String sQuery = "GET /index.html HTTP/1.0\r\n\r\n"; static final String sHTTP10 = "HTTP/1.0"; static final String sHTTP11 = "HTTP/1.1"; public static void main(String[] Arg) { String sHTTPVersion = null; String sHTTPReturnCode = null; String sResponse = null; int iRc = 0; BufferedReader brIn = null; PrintWriter psOut = null; Socket soServer= null; StringBuffer sbText = new StringBuffer(40); try { soServer = new Socket(sServer, iPort); brIn = new BufferedReader(new InputStreamReader( soServer.getInputStream())); psOut = new PrintWriter(soServer.getOutputStream()); psOut.println(sQuery); psOut.flush(); sResponse = brIn.readLine(); try { soServer.close(); } catch (Exception sc) {;} } catch (Exception swr) {;} StringTokenizer st = new StringTokenizer(sResponse, " "); if (true == st.hasMoreTokens()) { sHTTPVersion = st.nextToken(); if (sHTTPVersion.equals(sHTTP110) || sHTTPVersion.equals(sHTTP11)) { System.out.println("HTTP Version: " + sHTTPVersion); } else { System.out.println("Invalid HTTP Version: " + sHTTPVersion); } } else { System.out.println("Nothing was returned"); return; } if (true == st.hasMoreTokens()) { sHTTPReturnCode = st.nextToken(); try { iRc = Integer.parseInt(sHTTPReturnCode); } catch (NumberFormatException ne) {;} switch (iRc) { case(200): System.out.println("HTTP Response code: OK, " + iRc); break; case(400): case(401): case(402): case(403): case(404): System.out.println("HTTP Response code: Client Error, " + iRc); break; case(500): case(501): case(502): case(503): System.out.println("HTTP Response code: Server Error, " + iRc); break; default: System.out.println("HTTP Response code: Unknown, " + iRc); break; } } if (true == st.hasMoreTokens()) { while (true == st.hasMoreTokens()) { sbText.append(st.nextToken()); sbText.append(" "); } System.out.println("HTTP Response phrase: " + sbText.toString()); } } }