[Rd] Request to open up getConnection to embedded interface
Jeffrey Horner
jeff.horner at vanderbilt.edu
Sat Oct 7 22:34:26 CEST 2006
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
More information about the R-devel
mailing list