0

The R httpuv startServer function should support async processing in the call portion of the app parameter but I'm not able to get it to work. Does anyone know how to do this? The example below won't work but it shows the idea of what I'm trying to do, run each request (or for a specific page) async so a page can load while another request is processing.

startServer(
        host,
        port,
        app = list(
          call = function(req) {
            req <- list(
              "REQUEST_METHOD" = req$REQUEST_METHOD,
              "SCRIPT_NAME" = req$SCRIPT_NAME,
              "PATH_INFO" = req$PATH_INFO,
              "QUERY_STRING" = req$QUERY_STRING,
              "SERVER_NAME" = req$SERVER_NAME,
              "SERVER_PORT" = req$SERVER_PORT,
              "HEADERS" = req$HEADERS,
              "rook.input" = req[["rook.input"]]$read_lines()
            )

            future_promise({
              if(req$PATH_INFO %in% valid_dynamic_paths){

                x <- eval(dynamic[[req$PATH_INFO]][req$REQUEST_METHOD])

                list(
                  status = x[["status"]],
                  headers = x[["headers"]],
                  body = x[["body"]]
                )

              }else{

                list(
                  status = 404,
                  headers = list(
                    'Content-Type' = 'text/html'
                  ),
                  body = "404. Page not found."
                )

              }
            })
          },
          staticPaths = static
        )
      )
Tim
  • 13
  • 4
  • were you able to find a solution for that problem? I saw that (assumingly) your [Github issue](https://github.com/rstudio/httpuv/issues/323) was closed as completed. – seasick Aug 18 '22 at 13:47
  • @seasick, I gave up on finding a solution, but it looks like your solution below works nicely. I have a R package called webdeveloper that would benefit from including this approach. Any interest in contributing to that package? – Tim Sep 07 '22 at 19:32
  • sry, but I don't think I will find time for that at the moment. But I can put it in my backlog - is there a Github/Gitlab repository for it? – seasick Sep 09 '22 at 19:30

1 Answers1

0

I was able to get something similar to work. The code below shows the gist of it:

# fork a process for each new request
future::plan(future::multicore)

httpuv::runServer("0.0.0.0", 8080, list(
    call = function(req) {
        # `as.promise` is necessary, because `httpuv` is using `is.promise`
        # under the hood to act differently. Unfortunately `is.promise` returns
        # `FALSE` for a `future`.
        promises::as.promise(
            future::future({
                Sys.sleep(5)

                # Respond with HTTP 200 OK
                list(
                    status = 200,
                    body = "Slept for 5 seconds",
                    headers = list(
                        # Content-Type is important, otherwise you will run
                        # into a "not compatible with STRSXP" error.
                        "content-type" = "text/plain"
                    )
                )
            })
        )
    }
))

Calling the server with to requests at (nearly) the same time, will show that you are waiting only for 5 seconds for both requests, and not 5 for one and 10 for the other.

time curl -s localhost:8080 > /dev/null &
time curl -s localhost:8080 > /dev/null 

# After 5 seconds you should see output similar to the following:

# real    0m5.089s
# user    0m0.011s
# sys     0m0.010s

# real    0m5.112s
# user    0m0.020s
# sys     0m0.024s
seasick
  • 473
  • 1
  • 4
  • 15