Skip to content

HTTP Rest Server #48

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ export(request)
export(send)
export(send_aio)
export(serial_config)
export(server)
export(set_promise_context)
export(socket)
export(stat)
Expand Down
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

* Adds support for threaded dispatcher in `mirai`.
* Adds 'recvAio' method for `promises::as.promise()` and `promises::is.promising()` to enable 'recvAio' promises.
* Adds `server()`, an HTTP REST server for evaluation of R expressions (experimental).

#### Updates

Expand Down
79 changes: 79 additions & 0 deletions R/server.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Copyright (C) 2022-2024 Hibiki AI Limited <[email protected]>
#
# This file is part of nanonext.
#
# nanonext is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# nanonext is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# nanonext. If not, see <https://www.gnu.org/licenses/>.

# nanonext - server - HTTP REST Server -----------------------------------------

#' Start REST Server
#'
#' Creates an instance of an HTTP REST server which evaluates R expressions sent
#' to it [EXPERIMENTAL]. As arbitrary R expressions are evaluated, this
#' should only be deployed on the local machine (using the 127.0.0.1
#' loopback address) in a trusted environment.
#'
#' @param url full http address including hostname, port and path at which to
#' host the server.
#'
#' @details Query the API with an HTTP client using the \sQuote{POST} method,
#' with the request data being the R expression as a text string. The
#' received response body will consist of the evaluation result as a text
#' string (if of the appropriate type), or otherwise a serialized R object,
#' which should be passed to \code{\link{unserialize}}.
#'
#' If the expression could not be parsed or evaluated, the response will be
#' returned with a status code of 500 and a blank body.
#'
#' User interrupts will only be processed after the next query has been
#' completed, hence return from the function may not be immediate. Use
#' \sQuote{ctrl + \\} to forcibly quit the entire R session if required.
#'
#' @return This function never returns.
#'
#' @examples
#' if (interactive()) {
#'
#' # run server in a new session:
#' # Rscript -e 'nanonext::server("http://127.0.0.1:5555/api/rest")'
#'
#' # query using curl:
#' # curl -X POST http://127.0.0.1:5555/api/rest -d 'format(Sys.time())'
#'
#' ncurl(
#' "http://127.0.0.1:5555/api/rest",
#' method = "POST",
#' data = "format(Sys.time())"
#' )
#'
#' # error will return status of 500
#' ncurl(
#' "http://127.0.0.1:5555/api/rest",
#' method = "POST",
#' data = "not_valid()"
#' )
#'
#' res <- ncurl(
#' "http://127.0.0.1:5555/api/rest",
#' convert = FALSE,
#' method = "POST",
#' data = "data.frame(random = nanonext::random(3))"
#' )
#' if (!is_error_value(res$data)) unserialize(res$data)
#'
#' }
#'
#' @export
#'
server <- function(url = "http://127.0.0.1:5555/api/rest")
.Call(rnng_rest_server, url)
68 changes: 68 additions & 0 deletions man/server.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ static const R_CallMethodDef callMethods[] = {
{"rnng_recv", (DL_FUNC) &rnng_recv, 4},
{"rnng_recv_aio", (DL_FUNC) &rnng_recv_aio, 6},
{"rnng_request", (DL_FUNC) &rnng_request, 7},
{"rnng_rest_server", (DL_FUNC) &rnng_rest_server, 1},
{"rnng_send", (DL_FUNC) &rnng_send, 4},
{"rnng_send_aio", (DL_FUNC) &rnng_send_aio, 5},
{"rnng_serial_config", (DL_FUNC) &rnng_serial_config, 4},
Expand Down
2 changes: 2 additions & 0 deletions src/nanonext.h
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ int nano_matchargs(const SEXP);

void pipe_cb_signal(nng_pipe, nng_pipe_ev, void *);
void tls_finalizer(SEXP);
void nano_printf(const int, const char *, ...);

SEXP rnng_advance_rng_state(void);
SEXP rnng_aio_call(SEXP);
Expand Down Expand Up @@ -347,6 +348,7 @@ SEXP rnng_reap(SEXP);
SEXP rnng_recv(SEXP, SEXP, SEXP, SEXP);
SEXP rnng_recv_aio(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP);
SEXP rnng_request(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP);
SEXP rnng_rest_server(SEXP);
SEXP rnng_send(SEXP, SEXP, SEXP, SEXP);
SEXP rnng_send_aio(SEXP, SEXP, SEXP, SEXP, SEXP);
SEXP rnng_serial_config(SEXP, SEXP, SEXP, SEXP);
Expand Down
Loading
Loading