Jeffrey Horner
2006-Oct-07 20:34 UTC
[Rd] Request to open up getConnection to embedded interface
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
Duncan Temple Lang
2006-Oct-07 22:34 UTC
[Rd] Request to open up getConnection to embedded interface
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Hi Jeff Robert Gentleman and I were just conversing about the need to make connections part of the general API in some way, be it as they are now or slightly changed. It is not just for the embedded case, but for packages also that we need to be able to access them from C code and even extend them. For me, the RCurl and Rcompression packages on omegahat.org are two clear cases of unnecessarily constrained and complex code. We need to make much of core R system extensible at the package level rather than only allowing it to be done at the R-level. So this is on the list of things to do. D. Jeffrey Horner wrote:> 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- -- Duncan Temple Lang duncan at wald.ucdavis.edu Department of Statistics work: (530) 752-4782 4210 Mathematical Sciences Building fax: (530) 752-7099 One Shields Ave. University of California at Davis Davis, CA 95616, USA -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.3 (Darwin) iD8DBQFFKCt19p/Jzwa2QP4RAqLkAJ47ztIGE2e8ZWiLEuCEUUq4XnqP8ACfZxtN Teu5CpGmoMXfNVwMs6lw8HQ=n4LI -----END PGP SIGNATURE-----