From jeff.horner at vanderbilt.edu  Sat Oct  7 22:34:26 2006
From: jeff.horner at vanderbilt.edu (Jeffrey Horner)
Date: Sat, 07 Oct 2006 15:34:26 -0500
Subject: [Rd] Request to open up getConnection to embedded interface
Message-ID: <45280F52.8010704@vanderbilt.edu>

Hello all,

I would like to request that getConnection() and the struct Rconn 
declarations be added to the R embedded interface. Here's why.

It's common in CGI scripts for web applications to direct stdin, stdout, 
and stderr map to reading from the browser, writing to the browser, and 
writing to a web log file respectively; very nice and neat. However, 
things get a little hairy once language interpreters are embedded into 
web servers.

For modules like RApache, there are Apache api calls that govern how to 
  read/write from/to the browser so simple redirection is not feasible.

With the above request, here is how RApache can alter stdout's behavior:

     ...

     Rf_initEmbeddedR(argc, argv); /* initialize R */


     /* Redirect stdout to apache specific routines */
     con = getConnection(1);
     con->private = (void *) apr_pcalloc(p,sizeof(struct ApacheOutpcon));
     con->text = FALSE; /* allows us to do binary writes */
     con->vfprintf = mr_stdout_vfprintf;
     con->write = mr_stdout_write;
     con->fflush = mr_stdout_fflush;

     ...

And here are the definitions of the mr_* functions:

int mr_stdout_vfprintf(Rconnection con, const char *format, va_list ap){
     RApacheOutpcon this = con->private;
     apr_status_t rv;
     rv = apr_brigade_vprintf(this->brigade, ap_filter_flush, 
this->filter, format, ap);
     return (rv == APR_SUCCESS)? 0 : 1;
}
int mr_stdout_fflush(Rconnection con){
     RApacheOutpcon this = con->private;
     ap_filter_flush(this->brigade,this->filter);
     /* still need a return value */
}
size_t mr_stdout_write(const void *ptr, size_t size, size_t n, 
Rconnection con){
     RApacheOutpcon this = con->private;
     apr_status_t rv;
     rv = apr_brigade_write(this->brigade, ap_filter_flush, 
this->filter, (const char *)ptr, size*n);
     return (rv == APR_SUCCESS)? n : 1;
}

(stdin and stderr can be altered in a similar manner, but they not shown).

  And with that, it's easy to implement simple code to write data to the 
web browser in various formats:

dataprovider <- function(r){

     args <- apache.get_args(r);
     con <- dbConnect(dbDriver("MySQL"),dbname=db)
     d <- dbGetQuery(con,"select * from table")

     if (is.null(args$format)){
         apache.set_content_type(r,"text/html")
         if (length(d)>1)
             HTML(d,file=stdout()) # from R2HTML
         else
             cat("<H1>Empty</H1>")
     } else if (args$format == 'csv'){
             apache.set_content_type(r,"text/csv")
             write.csv(d)
     } else if (args$format == 'xml'){
             apache.set_content_type(r,"text/xml")
             writeSDML(d)          # from StatDataML
     } else if (args$format == 'rds'){
             apache.set_content_type(r,'application/octet-stream')
             save(d,file=stdout()) # yes, even this works
     }

     dbDisconnect(con)
     OK
}

And of course the output of the above can either be sent to the browser 
or even to an interactive R session like this:

load(url("http://example.com/dataprovider?format=rds"))

or this via StatDataML

d <- readSDML(file="http://example.com/dataprovider?format=xml")

or this via read.csv

d <- read.csv(file=url("http://example.com/dataprovider?format=csv")


Of course there are other ways to accomplish this, like allowing c code 
to place c generated connection objects onto the Connection array and 
then use sink(), but that only works for stdout. Another way is to 
enhance the default stdin, stdout, stderr reading and writing routines 
to test for the existence of user provided routines, similar to the way 
stdout_vfprintf tests R_Outputfile.

Jeff
-- 
http://biostat.mc.vanderbilt.edu/JeffreyHorner




[Rd] Request to open up getConnection to embedded interface
Jeffrey Horner