Search code examples
xmlrpostrcurlr-rook-package

Rook webserver parses content of postfields into name of list


I want to send an xml string to my Rook web server. But when I use the POST method of the Rook::Request class to parse the POST payload of my request, it puts the content into the name of the returned list. The corresponding list value is NA. I use postForm and the postfields option of the RCurl package to create my requests. A more detailed example follows below:

Put this into the file webserver.R

library(Rook)

s <- Rhttpd$new()

#set up web server app
s$add(
  name="xml_example",
  app=function(env) {
    req <- Request$new(env)

    #parse POST request
    tmp=req$POST()

    #create response
    res <- Rook::Response$new()

    #use dput and capture.output to return text of tmp
    res$write(capture.output(dput(tmp)))
    res$finish()
  }
)

#start web server on port 9000
s$start(port=9000)
#we will start the web server via Rscript and NOT via RStudio
Sys.sleep(.Machine$integer.max)

The following can be executed via RStudio (Windows user might have to change some of the commands)

library(RCurl)

#start web server outside of RStudio! Do not forget to kill it later
system("Rscript webserver.R &")

#send POST request
postForm("http://127.0.0.1:9000/custom/xml_example",
         .opts=list(postfields="<request>test</request>",
                    httpheader=c("content-type"="application/xml")))

This returns

#[1] "structure(list(`<request>test</request>` = NA),
#                    .Names = \"<request>test</request>\")"

As you can see, the xml string is put into the list name. Not exactly what I was expecting. Apart from extracting the list name to get the xml, how can this be done properly? Do I need to set options in Rook or RCurl?

BTW:

#do not forget to kill the webserver
system("ps aux|grep webserver.R")
#system("kill yourPIDhere")

Solution

  • It turned out to be a parsing error/feature within Rook. Take for example the post request

    postForm("http://127.0.0.1:9000/custom/xml_example",
             .opts=list(postfields="xml=<request>test</request>",
                        httpheader=c("content-type"="application/xml")))
    

    This gives the result

    #[1] "structure(list(xml = \"<request>test</request>\"), .Names = \"xml\")"
    

    As you can see, the Rook parser assumes some sort of key=value structure of the input. This is problematic for xmls since they can include namespace definitions that use the = sign (also when defining xml versions and probably on other occasions).

    Anyway, I turned away from Rook as it can be made accessible to remote machines only via some hack (see http://jeffreyhorner.tumblr.com/post/33814488298/deploy-rook-apps-part-ii). This hack, BTW, does not work for me. I am using the plumber package now - works like a charm! (https://github.com/trestletech/plumber)