documentation for version 1.2.0

This manual covers installation, configuration, and various uses for rApache version 1.2.0. Read this manual for all prior versions. This document is intended to contain the most up-to-date information about rApache. Comments, suggestions should be forwarded to the maintainer.

Changes in 1.2.0

The major change, and hence the bump in the minor version, is that the working directory is sensibly set for those using RFileEval, RFileHandler, and handlers using r-script.

Changes in 1.1.15

  1. ROutputErrors and RApacheOutputErrors() are deprecated and will be removed in the next release.
  2. The undocumented RPreserveEnv is deprecated.
  3. There are two new apache directives REval and RFileEval, described below.
  4. receiveBin() is a new R level function for reading the HTTP request input body.

1. Introduction

rApache is a project supporting web application development using the R statistical language and environment and the Apache web server. The current software distribution runs on UNIX/Linux and Mac OS X operating systems. Apache servers with threaded Multi-Processing Modules are now supported, but the the Apache Prefork Multi-Processing Module is still recommended (refer to the Multi-Processing Modules chapter from Apache for more about this).

The rApache software distribution provides the Apache module named mod_R that embeds the R interpreter inside the web server. It also comes bundled with libapreq, an Apache module for manipulating client request data. Together, they provide the glue to transform R into a server-side scripting environment.

Another important project that's not bundled with rApache, but plays an important role in server-side scripting, is the R package brew (also available on CRAN). It implements a templating framework for report generation, and it's perfect for generating HTML on the fly. it's syntax is similar to PHP, Ruby's erb module, Java Server Pages, and Python's psp module. brew can be used stand-alone as well, so it's not part of the distribution.

1.1 Rook Support

Rook is an R package that defines a specification and set of convenience objects for running R web applications on various web servers without the need to modify your code. Thus, you can write and run Rook apps within R using the built-in Rhttpd web server and deploy them on rApache.

1.2 Security

As with any web server-side scripting environment, rApache is at the mercy of YOU THE PROGRAMMER. If you allow your R code to accept information from a 3rd party, then it is up to you to vet that information appropriately. So, the author maintains that rApache is more or less as secure as other popular web scripting environments such as PHP, Ruby on Rails, etc.

2. Installation

rApache follows the typical GNU/Linux source install procedure: run 'configure', then 'make', and 'make install' from the shell.

At the moment, rApache is a source-only downloadable package. I've made attempts in the past to learn the Debian packaging system but have not quite got it down yet. If anyone is interested in providing binary packages, for any Linux distribution, please email me.

Requirements for installing and using rApache are as follows:

  1. R built and installed as a shared library (see the Writing R Extensions manual for more info).
  2. Apache 2.2.x or later installed with module loading support.
  3. libapreq2 2.05 or higher. This comes bundled with rApache and will be installed by default if not automatically found by configure or overridden with the configure flag --with-apreq2-config.

2.1 Running configure

configure does it's best to probe your system to meet the above requirements. Failing that, you should use the following flags:

  • --with-R the full path to R
  • --with-apache2-apxs the full path to the Apache extension tool
  • --with-apreq2-config the full path to the libapreq2 utility

Here's an example with some common argument values:

./configure \
    --with-R=/usr/bin/R \
    --with-apache2-apxs=/usr/bin/apxs2 \
    --with-apreq2-config=/usr/bin/apreq2-config

2.2 Running make

Running make with no target will build rApache. Use 'make install' to install. The following make targets are also available:

  • itest will run apache within a single process. You should see a message on the output to point your browser to http://localhost:8181/index.html where you can test out your rApache build before it is installed. In order to stop apache, either type Control-C or Control-\.
  • test will run apache in multi-process mode. Run 'make stop' to stop apache.
  • stop will stop apache after it was started with 'make test'.
  • valgrind will run apache with valgrind in single-process mode.
  • debug will run apache with gdb in single-process mode. Besure to look for the lines that say, "Copy/paste the following line to gdb". You'll need the 'run -x -f ...' command to start apache within the debugger.

2.3 Quick Install for Debian/Ubuntu

Run the following as root (changing VERSION to the latest release version) or use sudo before each command:

apt-get install r-base-dev apache2-mpm-prefork apache2-prefork-dev
wget http://www.rapache.net/rapache-VERSION.tar.gz
tar xzvf rapache-VERSION.tar.gz
cd rapache-VERSION
./configure
make
make install

3. Configuring rApache

This chapter details what to put in your Apache server configuration file(s). If you install Apache from source, then you will only have to edit the main file httpd.conf. If you install from a binary distribution, chances are the configuration is split among multiple files, so you may want to research your distribution's config layout before making any changes.

3.1 R_module

This is how Apache loads mod_R (must be placed first before any rApache directives):

# All Apache modules are loaded this way. The most important thing to
# remember is that the string "R_module" is case sensitive, so get it
# right in the config file.

LoadModule R_module           /apache/module/path/mod_R.so

LoadModule is an apache directive that links in object files that contain module structures. rApache's module structure is named "R_Module".

NOTE: When attempting to start apache2 on some UNIX systems, you may see an error message similar to this one:

apache2: Syntax error on line 186 of /etc/apache2/apache2.conf: Cannot load 
/usr/lib/apache2/modules/mod_R.so into server: libR.so: cannot open shared 
object file: No such file or directory
To fix this, you will want to instruct your run-time linker, ld.so, on where to find libR.so. The easiest way to do this is by adding the directory to the /etc/ld.so.conf file and re-running ldconfig as root. If you don't know the location you can easily find out by running:
$ R CMD config --ldflags
-L/usr/lib/R/lib -lR
In the output above, /usr/lib/R/lib is the directory you will want to place in /etc/ld.so.conf.

3.2 ROutputErrors (DEPRECATED)

In prior releases, ROutputErrors would send all warning and error messages to the browser. This has been deprecated and will be removed from the next rApache release. All warnings and errors are now always sent through the apache logging system.

3.3 SetHandler r-info

When configuring rApache for the first time, you may want to add the following directive to ensure that your system is working. It produces a report about R and Apache when you visit the url at /RApacheInfo. For production systems, you might want to leave it out.

# Prints out a nice report about R running within Apache
<Location /RApacheInfo>
SetHandler r-info
</Location>
View a sample here.

3.4 REvalOnStartup

This directive takes one argument: a string containing R expressions to evaluate upon startup. Any number of these directives can appear throughout the config files, and they are evaluated in the global environment in the order they appear. Useful for setting options and loading libraries like so:

# Load required DBI and RMySQL packages

REvalOnStartup "library(DBI); library(RMySQL)"

3.5 RSourceOnStartup

Sometimes you want to evaluate quite a bit of code on startup. Equivalent to calling source() in the global environment. Just like REvalOnStartup, these directives can appear anywhere in the config files and they are evaluated in the order that they appear.

# Configure system with startup file

RSourceOnStartup "/var/www/lib/R/startup.R"

3.6 R Handlers

It's important to read through the following section carefully as it will easily cause the most confusion. You have two Apache directives, two rApache directives, and two SetHandler options to learn.

3.6.1 Difference in Location and Directory

The Apache directives Location and Directory are used here to match up urls and files to R handlers. Certainly, Apache is very configurable and other directives exist to provide more fine-grained control over urls and files, but please refer to the Apache documentation for further info.

"Location" is used to define the behavior of url's that do not map to files on the filesytem. In rApache, they can be used to invoke an R handler. For instance, suppose we have set up rApache on the site example.com and added the following "Location" directive as in section 2.3:

<Location /Risneat>
    SetHandler r-info
</Location>

Then, the url http://example.com/Risneat will invoke the handler r-info, but that url doesn't map to any file on the filesystem.

"Directory" is used to define the behavior of files on the filesystem. In rApache, they can be used to evaluate files containing R expressions through an R handler like so:

# Any file under /var/www/brew is passed through the function brew located in
# the package brew
<Directory /var/www/brew>
    SetHandler r-script
    RHandler brew::brew
</Directory>

Suppose example.com's Apache DocumentRoot was /var/www/, then the file /var/www/brew/foo.html maps to the url http://example.com/brew/foo.html and is run throuh the R package brew.

3.6.2 Difference in r-handler and r-script

r-handler, r-script, and r-info are valid arguments to Apache's SetHandler directive ("r-info" is already described in section 2.3). Calling SetHandler forces url's to be parsed through the named handler.

r-handler is used when you want to call an R function without arguments. You can also specify a particular file as well (see below). It is generally used within Location directives.

r-script is used when you want to call an R function with 2 arguments: the first is the full path to the file, and the second is an R environment. It is generally used within Directory directives.

Using either of these requires that the function or script return a suitable HTTP response code. For most cases this will be the value OK, which sends an HTTP response code of 200 to the browser. If the function or script would like to signal an error condition, then it should return an object with an S3 class of 'try-error' (read the R documentation for try and tryCatch for further info on the 'try-error' class).

3.6.3 RHandler

RHandler is used to specify an R function to handle incoming web requests. The function must exist either in an attached package or it must be found on the R search path. You can use the "::" to preface the function with the package name, just as in R. Examples:

# Specify foo as the function to run. Probably created 
# by REvalOnStartup or RSourceOnStartup

RHandler foo

# Run the function bar located in the package foo

RHandler foo::bar
3.6.4 RFileHandler

RFileHandler is used to specify a file and/or the function to handle incoming web requests. The "::" notation is used to specifiy the file and the function together. Absolute paths to files are expected. Examples:

# Hello world example. equivalent to calling source('/var/www/R/hello.R')
# on each request.

RFileHandler /var/www/R/hello.R

# Call the function foo within the file bar.R

RFileHandler /var/www/R/bar.R::foo

Another note about RFileHandler. Each file specified is parsed only when its timestamp changes. This is useful for debugging, and once you are happy with the functionality, you may want to place it in a package use RSourceOnStartup and turn it into a function call instead for more efficiency.

3.6.5 REval and RFileEval

These two will take arbitrary R expressions and run them on each request. They are similar to RHandler and RFileHandler. For instance the above RHandler configuration example can be converted to use REval:

# Specify foo as the function to run. Probably created 
# by REvalOnStartup or RSourceOnStartup

RHandler "foo()"

# Run the function bar located in the package foo

RHandler "foo::bar()"

and similarly for RFileHandler:

# Call the function foo within the file bar.R

RFileHandler "/var/www/R/bar.R:foo()"

Note that a single colon is used to separate the filename from the expression to be evaluated. Note also that r-script is ignored in this case and r-handler is presumed.

3.6.6 Example Configurations

By far the easiest configuration will be the brew example. Each file under /var/www/brew is treated as a brew script:

# Any file under /var/www/brew is passed through the function brew located in
# the package brew.
<Directory /var/www/brew>
	SetHandler r-script
	RHandler brew::brew
</Directory>

Another option is to use sys.source:

# Any file under /var/www/R-files is passed through the function sys.source.
<Directory /var/www/R-files>
	SetHandler r-script
	RHandler sys.source
</Directory>

Hello World for the url at /test/helloworld:

# Runs the R expressions in helloworld.r for every request
# that matches /test/helloworld, including /test/helloworld/foobar

<Location /test/helloworld>
        SetHandler r-handler
        RFileHandler /path/to/R/scripts/helloworld.r
</Location>

Deploying a Rook application:

# Run the Rook application named 'app'. On each request, the expression 
# 'Rook::Server$call(app)' is evaluated in an environment populated by
# rookapp.R. 'app' is expected to be found in that environment.
<Location /test/RookApp>
        SetHandler r-handler
        RFileEval /path/to/Rook/App/rookapp.R:Rook::Server$call(app)
</Location>

4. rApache Functions

The following functions can be used inside R handlers.

4.1 setHeader

Add HTTP Response Headers (RFC2616) to the response. All headers must be added before the first output from print() or cat().

Example:

setHeader(header='X-Powered-By',value='rApache')

Arguments:

  • header Character. Name of the header: case-insensitive.
  • value Any valid R object of length 1 that can be converted to type character. Value of the header.

Returns:

  • the value passed in or NULL if name or value are not of type character.

4.2 setContentType

Allows handler to set the content type of the request. Must be called before output with print or cat().

Example:

setContentType(type='image/png')

Arguments:

  • type character vector of length 1. Valid MIME type.

Returns:

  • type or NULL on error.

4.3 setCookie

Add HTTP Cookies to the response headers. In the simplest case, calling setCookie('foo','bar') sets the cookie 'foo' to the value 'bar'. Calling setCookie('foo') will delete the cookie 'foo'. Any non-standard key value pairs can be appended by using ...

Example:

setCookie(name='sessionID',value=paste(rnorm(1)))

Arguments:

  • name Character. Name of the cookie.
  • value Any valid R object of length 1 that can be converted to type character. Value of the cookie.
  • expires POSIXct object. Determines when the cookie expires.
  • path Character. URL to which the cookie pertains.
  • domain Character. Domain to which the cookie pertains.
  • ... Any additional values appended to the cookie.

Returns:

  • Either the string representation of the cookie or NULL if name was not provided.

4.4 urlEncode and urlDecode

Percent encoding and decoding (url for short) of character vectors.

Example:

urlEncode(str='hello world@example.com')
urlDecode(str='hello+world%40example.com')

Arguments:

  • str Character vector of strings to encode or decode.

Returns:

  • Character vector the same length as str.

4.5 RApacheInfo

Print out a report (sample) about rApache. Should be the only call in your R handler. Equivalent to using "SetHandler r-info".

Example:

RApacheInfo()

Arguments:

  • None

Returns:

  • NULL.

4.6 sendBin

Sends binary data to the browser. This function is equivalent to R's writeBin() function, but the connection argument is ignored. See the documentation to writeBin() in your R distribution for more information.

sendBin(object=readBin(t,'raw',n=file.info(t)$size))

Arguments:

  • object An R object to be written to the connection.
  • con Ignored, and can be omitted from the call.
  • size integer. The number of bytes per element in the byte stream. The default, NA_integer_, uses the natural size. Size changing is not supported for raw and complex vectors.
  • endian The endian-ness ("big" or "little" of the target system for the file. Using "swap" will force swapping endian-ness.

Returns:

  • NULL

4.7 receiveBin

Reads data from the HTTP request body, if any. Returns a length zero raw vector when there's no more data to read.

receiveBin(length=-1L)

Arguments:

  • length integer. The number of bytes to read from the request. The default is -1L, which reads the entire request body.

Returns:

  • a raw vector with a length of zero or the number of bytes actually read.

4.8 RApacheOutputErrors (DEPRECATED)

This function has been deprecated and will be removed from the next rApache release. It invokes a warning if it is called.

5. rApache Variables

In previous releases of rApache, information from the web server was passed to R handlers in a single variable. This system design element was copied from other apache modules, but it has proven to be too cumbersome to support in software maintenance. Rather, because R has support for lexical scoping and in a far broader sense the ability to manipulate the language, a simpler approach was implemented.

rApache variables, named similar to PHP variables, are read-only list variables whose values are in most cases character vectors. They are injected into the environment of the R handler, and they are found by your R code via lexical scoping rules.

5.1 GET

The GET variable contains those values obtained from an HTTP GET method, i.e. the key-value pairs found after the "?" of an URL, or data passed from an HTTP form when the method attribute is "GET". For example, the following form:

<form method="GET" action="http://example.com/brew/get.html">
	<input type="text" name="p1" value="0.95">
	<input type="text" name="p2" value="0.7">
<input type="submit" name="Submit">
</form>
produces the following GET list variable:
> str(GET)
List of 3
 $ p1    : chr "0.95"
 $ p2    : chr "0.7"
 $ Submit: chr "Submit Query"

5.2 POST

The POST variable contains those values obtained from an HTTP POST method, i.e. data passed from an HTTP form when the method attribute is "POST". Switching the method to "POST" in the example form from the previous section will produce the same values, yet in the POST variable:

> str(POST)
List of 3
 $ p1    : chr "0.95"
 $ p2    : chr "0.7"
 $ Submit: chr "Submit Query"

5.3 COOKIES

The COOKIES variable contains those values obtained from the HTTP response header named "Cookie". It is a list variable whose values are character vectors. See the setCookie function and this link for more info.

5.4 FILES

The FILES variable contains information about uploaded files via HTTP forms when the enctype attribute is set to "multipart/form-data". The following form:

<form enctype="multipart/form-data" method="POST" action="URL">
	<input type="file" name="FirstFile">
	<input type="file" name="SecondFile">
<input type="submit" name="Upload">
produces this FILES variable:
> str(FILES)
List of 2
 $ FirstFile :List of 2
  ..$ name    : chr "useR2007poster.pdf"
  ..$ tmp_name: chr "/tmp/apreqc9GlXE"
 $ SecondFile:List of 2
  ..$ name    : chr "rapache-1.0.0-useR2007.tar.gz"
  ..$ tmp_name: chr "/tmp/apreqoQ2hhX"

It is a list of lists, with the nested list giving you the "name" of the file and the "tmp_name", the location of the temporary file. For instance, to reference the uploaded file from the input tag named "FirstFile" you would use FILES$FirstFile$tmp_name. Here's a code snippet to copy the file to the '/usr/local/uploaded_files' directory:

destination <- file.path('/usr/local/uploaded_files',FILES$FirstFile$name)
file.copy(FILES$FirstFile$tmp_name,destination,overwrite=TRUE)

NOTE: the temporary files are deleted after the R handler completes handling the request. Thus, it is imperative that you copy/move this file to your desired location before the R handler returns.

5.5 SERVER

As you can see from the below output, the SERVER variable contains a wealth of information about the incoming web request:

> str(SERVER)
List of 30
 $ headers_in        :List of 9
  ..$ Host           : chr "localhost:8181"
  ..$ User-Agent     : chr "Mozilla/5.0 (X11; U; Linux i686; en-US; ..."
  ..$ Accept         : chr "text/xml,application/xml,application/x..."
  ..$ Accept-Language: chr "en-us,en;q=0.5"
  ..$ Accept-Encoding: chr "gzip,deflate"
  ..$ Accept-Charset : chr "ISO-8859-1,utf-8;q=0.7,*;q=0.7"
  ..$ Keep-Alive     : chr "300"
  ..$ Connection     : chr "keep-alive"
  ..$ Cache-Control  : chr "max-age=0"
 $ proto_num         : int 1001
 $ protocol          : chr "HTTP/1.1"
 $ unparsed_uri      : chr "/brew/server.html/beetles/?foo=bar"
 $ uri               : chr "/brew/server.html/beetles/"
 $ filename          : chr "/home/hornerj/rapache/branches/rapache-1-0-br..."
 $ canonical_filename: chr "/home/hornerj/rapache/branches/rapache-1-0-br..."
 $ path_info         : chr "/beetles/"
 $ args              : chr "foo=bar"
 $ content_type      : chr "text/html"
 $ handler           : chr "r-script"
 $ content_encoding  : NULL
 $ range             : NULL
 $ hostname          : chr "localhost"
 $ user              : NULL
 $ header_only       : logi FALSE
 $ no_cache          : logi FALSE
 $ no_local_copy     : logi FALSE
 $ assbackwards      : logi FALSE
 $ status            : int 200
 $ method_number     : int 0
 $ eos_sent          : logi FALSE
 $ the_request       : chr "GET /brew/server.html/beetles/?foo=bar HTTP/1.1"
 $ method            : chr "GET"
 $ status_line       : NULL
 $ bytes_sent        : num 0
 $ clength           : num 0
 $ remaining         : num 0
 $ read_length       : num 0
 $ request_time      :'POSIXct', format: chr "2007-08-15 11:11:49"
 $ mtime             :'POSIXct', format: chr "1969-12-31 18:00:00"

Here's a description of each list element:

headers_in list containing all the HTTP headers sent by the client.

proto_num Integer. Protocol version number of protocol; 1.1 = 1001

protocol Character. Protocol string, as given to us, or HTTP/0.9.

unparsed_uri Character. The URI without any parsing performed.

uri Character. The path portion of the URI.

filename Character. The name of the file with full path information.

canonical_filename Character. The true filename. Case and aliases/symbolic links have been resolved.

path_info Character. The suffix portion of the url after it has been matched to an asset that the web server knows about. An asset is either a file or an url defined by an Apache Location directive.

cmd_path Character. The path taken from the corresponding configuration directive.

args Character. The HTTP GET data extracted from this request.

content_type Character. The content-type for the current request.

handler Character. The handler string that we use to call a handler function.

content_encoding Character. How to encode the data.

range Character. The HTTP Response header named "Range:".

hostname Character. The server hostname.

user Character. If an HTTP authentication check was made, this gets set to the user name.

header_only Logical. HEAD request, as opposed to GET.

no_cache Logical. This response can not be cached.

no_local_copy Logical. There is no local copy of this response.

assbackwards Logical. HTTP/0.9, 'simple' request (e.g. GET /foo\n w/no headers). Developers have found this a useful way to internally redirect without headers.

status Integer. Status line.

method_number Integer value of GET, POST, etc.

eos_sent Logical. A flag to determine if the eos bucket has been sent yet.

the_request Character. First line of the request.

method Character. Request method (eg. GET, HEAD, POST, etc.)

status_line Character. Status line, if set by script.

bytes_sent Numeric. Number of bytes sent.

clength Numeric. The 'real' content length.

remaining Numeric. Remaining bytes left to read from the request body.

read_length Numeric. Number of bytes that have been read from the request body.

request_time POSIXct DateTime object. Time when the request started.

mtime POSIXct DateTime object. Last modified time of the requested resource .

5.6 Handler Return Values and HTTP Response Codes

The following table describes variables that exist as integer vectors of length 1 in the R handler environment and are proper return values for R handlers. They consist of Apache module return values and HTTP Status Codes(see Status Code Definitions from RFC2616 for more info). The most reasonable response value and the one that most handlers will return is the value DONE.

namevaluedescription
DECLINDED -1Module declines to handle
DONE -2Module has served response completely.
OK -0Module has handled this Apache response stage.
HTTP_CONTINUE 100
HTTP_SWITCHING_PROTOCOLS 101
HTTP_PROCESSING 102
HTTP_OK 200
HTTP_CREATED 201
HTTP_ACCEPTED 202
HTTP_NON_AUTHORITATIVE 203
HTTP_NO_CONTENT 204
HTTP_RESET_CONTENT 205
HTTP_PARTIAL_CONTENT 206
HTTP_MULTI_STATUS 207
HTTP_MULTIPLE_CHOICES 300
HTTP_MOVED_PERMANENTLY 301
HTTP_MOVED_TEMPORARILY 302
HTTP_SEE_OTHER 303
HTTP_NOT_MODIFIED 304
HTTP_USE_PROXY 305
HTTP_TEMPORARY_REDIRECT 307
HTTP_BAD_REQUEST 400
HTTP_UNAUTHORIZED 401
HTTP_PAYMENT_REQUIRED 402
HTTP_FORBIDDEN 403
HTTP_NOT_FOUND 404
HTTP_METHOD_NOT_ALLOWED 405
HTTP_NOT_ACCEPTABLE 406
HTTP_PROXY_AUTHENTICATION_REQUIRED407
HTTP_REQUEST_TIME_OUT 408
HTTP_CONFLICT 409
HTTP_GONE 410
HTTP_LENGTH_REQUIRED 411
HTTP_PRECONDITION_FAILED 412
HTTP_REQUEST_ENTITY_TOO_LARGE 413
HTTP_REQUEST_URI_TOO_LARGE 414
HTTP_UNSUPPORTED_MEDIA_TYPE 415
HTTP_RANGE_NOT_SATISFIABLE 416
HTTP_EXPECTATION_FAILED 417
HTTP_UNPROCESSABLE_ENTITY 422
HTTP_LOCKED 423
HTTP_FAILED_DEPENDENCY 424
HTTP_UPGRADE_REQUIRED 426
HTTP_INTERNAL_SERVER_ERROR 500
HTTP_NOT_IMPLEMENTED 501
HTTP_BAD_GATEWAY 502
HTTP_SERVICE_UNAVAILABLE 503
HTTP_GATEWAY_TIME_OUT 504
HTTP_VERSION_NOT_SUPPORTED 505
HTTP_VARIANT_ALSO_VARIES 506
HTTP_INSUFFICIENT_STORAGE 507
HTTP_NOT_EXTENDED 510

Another suitable value to return from a handler is an object with S3 class of 'try-error'.

6. rApache Cookbook

For a complete look at an rApache application, download the useR2007 application which uses Hmisc and brew for power and sample size calculations.

The following code exercises all rApache functionality by just echoing what was sent from the browser. Copy and paste the following code to a file and then set up apache with the following configuration. You should then be able to point your browser at http://example.com/rapachetest (replacing example.com with your own hostname).

# 
# Place this in your Apache config file.
#
<Location /rapachetest>
	SetHandler r-handler
	RFileHandler /var/www/R/test.R
</Location>
#
# Copy and save this code to /var/www/R/test.R
#
hrefify <- function(title) gsub('[\\.()]','_',title,perl=TRUE)
scrub <- function(str){ 
	if (is.null(str)) return('NULL')
	if (length(str) == 0) return('length 0 string')
	cat("\n<!-- before as.character: (",str,")-->\n",sep='')
	str <- as.character(str)
	cat("\n<!-- after as.character: (",str,")-->\n",sep='')
	str <- gsub('&','&amp;',str); str <- gsub('@','_at_',str); 
	str <- gsub('<','&lt;',str); str <- gsub('>','&gt;',str); 
	if (length(str) == 0 || is.null(str) || str == '')
		str <- '&nbsp;' 
	str
}
cl<-'e'
zebary <- function(i){ 
	cl <<- ifelse(cl=='e','o','e')
	cat('<tr class="',cl,'"><td>',scrub(i),'</td></tr>\n',sep='')
}
zeblist <- function(i,l){ 
	cl <<- ifelse(cl=='e','o','e')
	 cat('<tr class="',cl,'"><td class="l">',names(l)[i],'</td><td>')
	if(is.list(l[[i]]))
		zebra(names(l)[i],l[[i]])
	else {
		if (length(l[[i]]) > 1)
			zebary(l[[i]])
		else
			cat(scrub(l[[i]]))
	}
		
	cat('</td></tr>\n',sep='')
}
zebra <- function(title,l){ 
	cat('<h2><a name="',hrefify(title),'"> </a>',title,'</h2>\n<table><tbody>',sep='')
	ifelse(is.list(l),lapply(1:length(l),zeblist,l), lapply(l,zebary))
	cat('</tbody></table>\n<br/><hr/>') 
}

# Output starts here
setContentType("text/html")

if(is.null(GET)){
	called <- 1
} else {
	called <- as.integer(GET$called) + 1
}

setCookie('called',called,expires=Sys.time()+100)

cat('<HTML><head><style type="text/css">\n') 
cat('table { border: 1px solid #8897be; border-spacing: 0px; font-size: 10pt; }')
cat('td { border-bottom:1px solid #d9d9d9; border-left:1px solid #d9d9d9; border-spacing: 0px; padding: 3px 8px; }')
cat('td.l { font-weight: bold; width: 10%; }\n')
cat('tr.e { background-color: #eeeeee; border-spacing: 0px; }\n')
cat('tr.o { background-color: #ffffff; border-spacing: 0px; }\n')
cat('</style></head><BODY><H1>Canonical Test for rApache</H1>\n')
cat('<form enctype=multipart/form-data method=POST action="?called=',called,'">\n',sep='')
cat('Enter a string: <input type=text name=name value=""><br>\n',sep='')
cat('Enter another string: <input type=text name=name value=""><br>\n',sep='')
cat('Upload a file: <input type=file name=fileUpload><br>\n')
cat('Upload another file: <input type=file name=anotherFile><br>\n')
cat('<input type=submit name=Submit>')

cat("<hr>\n")
zebra('CGI GET Data',GET)
zebra('CGI POST Data',POST)
zebra('Cookies',COOKIES)
if (!is.null(FILES)){
	cat('<h2>Files Uploaded in POST Data</h2>\n')
	for (n in names(FILES)){
		zebra(paste("Form Variable",n),FILES[[n]])
	}
}
zebra("SERVER Variables",SERVER)
cat("</BODY></HTML>\n")

DONE

7. License

The rApache source code is licensed under the Apache License Version 2.0.

8. Citing rApache

To cite rApache, use the following:

Jeffrey Horner (2012). rApache: Web application development with R and Apache. http://www.rapache.net/

A BibTeX entry for LaTeX users is

@Manual{,
	title = {rApache: Web application development with R and Apache.},
	author = {Jeffrey Horner},
	year = {2012},
	url = {http://www.rapache.net/},
}

9. Thanks

Thanks to the following people for their contributions, giving advice, noticing when things were broken and such. If I've forgotten to mention you, please email me.

	Gregoire Thomas
	Jan de Leeuw
	Keven E. Thorpe
	Jeremy Stephens
	Aleksander Wawer
	David Konerding
	Robert Kofler
	Jeroen Ooms
	Michael Driscoll

© 2013 Vanderbilt University | License (Apache 2.0) | Styled with skeleton.