rexyai / RestRserve

R web API framework for building high-performance microservices and app backends
https://restrserve.org
276 stars 32 forks source link

Static Route fails to return csv, rds, jpg, pdf (png and txt works) for folder #180

Closed DavZim closed 2 years ago

DavZim commented 2 years ago

Bug

Serving files via a static endpoint results in an encoding error depending on content-type. csv, rds, jpg, and pdf fail, with: can't encode body with content_type = 'image/{TYPE}' . png images and .txt files work however.

Reproduction

################################################################################
# Setup the static data directory and write some example files

if (!dir.exists("static")) dir.create("static")
set.seed(123)
png("static/testplot.png")
plot(1:100, cumsum(rnorm(100)), type = "l")
dev.off()
jpeg("static/testplot.jpg")
plot(1:100, cumsum(rnorm(100)), type = "l")
dev.off()
pdf("static/testplot.pdf")
plot(1:100, cumsum(rnorm(100)), type = "l")
dev.off()

writeLines("Hello World", "static/test.txt")
writeLines("print('Hello World')", "static/test.R")
write.csv(data.frame(x = 1:100, y = cumsum(rnorm(100))), "static/testdata.csv")
saveRDS(data.frame(x = 1:100, y = cumsum(rnorm(100))), "static/testdata.rds")

################################################################################
# Define the webserver
library(RestRserve)
app = Application$new()
app$logger$set_log_level("debug")

app$add_get(path = "/ping", FUN = function(.req, .res) .res$set_body("OK"))
app$add_get(path = "/list_files", FUN = function(.req, .res) .res$set_body(list.files("static")))
app$add_static("/static", "static")

# define a quick helper function to make testing the endpoints easier
test_endpoint = function(end) {
  req = Request$new(path = end, method = "GET")
  cat(app$process_request(req)$body, "\n")
}

################################################################################
## First check if the basic methodology works: ALL GOOD

# ping works
test_endpoint("/ping")
#> {"timestamp":"2022-03-16 10:09:54.865609","level":"DEBUG","name":"Application","pid":22568,"msg":"","context":{"request_id":"d8494ad4-a508-11ec-8000-f398b9230932","request":{"method":"GET","path":"/ping","parameters_query":[],"parameters_path":[],"headers":{}}}}
#> {"timestamp":"2022-03-16 10:09:54.865609","level":"DEBUG","name":"Application","pid":22568,"msg":"","context":{"request_id":"d8494ad4-a508-11ec-8000-f398b9230932","response":{"status_code":200,"headers":{"Server":"RestRserve/0.4.1"}}}}
#> OK 

# all files are found
test_endpoint("/list_files")
#> {"timestamp":"2022-03-16 09:43:42.430613","level":"DEBUG","name":"Application","pid":22568,"msg":"","context":{"request_id":"2ef14aca-a505-11ec-8000-f398b9230932","request":{"method":"GET","path":"/list_files","parameters_query":[],"parameters_path":[],"headers":{}}}}
#> {"timestamp":"2022-03-16 09:43:42.431622","level":"DEBUG","name":"Application","pid":22568,"msg":"","context":{"request_id":"2ef14aca-a505-11ec-8000-f398b9230932","response":{"status_code":200,"headers":{"Server":"RestRserve/0.4.1"}}}}
#> test.R
#> test.txt
#> testdata.csv
#> testdata.rds
#> testplot.jpg
#> testplot.pdf
#> testplot.png 

################################################################################
## Second, check if all files can be downloaded. Works for png, R, and txt but not the others...

# R works
test_endpoint("/static/test.R")
#> {"timestamp":"2022-03-16 09:58:32.781083","level":"DEBUG","name":"Application","pid":22568,"msg":"","context":{"request_id":"3f5f3ca8-a507-11ec-8000-f398b9230932","request":{"method":"GET","path":"/static/test.txt","parameters_query":[],"parameters_path":[],"headers":{}}}}
#> {"timestamp":"2022-03-16 09:58:32.782011","level":"DEBUG","name":"Application","pid":22568,"msg":"","context":{"request_id":"3f5f3ca8-a507-11ec-8000-f398b9230932","response":{"status_code":200,"headers":{"Server":"RestRserve/0.4.1"}}}}
#> C:\Users\REDACTED\test-restrserve\static//test.R

# TXT works
test_endpoint("/static/test.txt")
#> {"timestamp":"2022-03-16 09:58:32.781083","level":"DEBUG","name":"Application","pid":22568,"msg":"","context":{"request_id":"3f5f3ca8-a507-11ec-8000-f398b9230932","request":{"method":"GET","path":"/static/test.txt","parameters_query":[],"parameters_path":[],"headers":{}}}}
#> {"timestamp":"2022-03-16 09:58:32.782011","level":"DEBUG","name":"Application","pid":22568,"msg":"","context":{"request_id":"3f5f3ca8-a507-11ec-8000-f398b9230932","response":{"status_code":200,"headers":{"Server":"RestRserve/0.4.1"}}}}
#> C:\Users\REDACTED\test-restrserve\static//test.txt

# CSV fails
test_endpoint("/static/testdata.csv")
#> {"timestamp":"2022-03-16 09:51:30.174911","level":"DEBUG","name":"Application","pid":22568,"msg":"","context":{"request_id":"435fa41a-a506-11ec-8000-f398b9230932","request":{"method":"GET","path":"/static/testdata.csv","parameters_query":[],"parameters_path":[],"headers":{}}}}
#> {"timestamp":"2022-03-16 09:51:30.176912","level":"DEBUG","name":"Application","pid":22568,"msg":"","context":{"request_id":"435fa41a-a506-11ec-8000-f398b9230932","response":{"status_code":500,"headers":{"Server":"RestRserve/0.4.1"}}}}
#> 500 Internal Server Error: can't encode body with content_type = 'text/csv' 

# RDS (octet-stream) fails
test_endpoint("/static/testdata.rds")
#> {"timestamp":"2022-03-16 09:52:04.413598","level":"DEBUG","name":"Application","pid":22568,"msg":"","context":{"request_id":"5a255096-a506-11ec-8000-f398b9230932","request":{"method":"GET","path":"/static/testdata.rds","parameters_query":[],"parameters_path":[],"headers":{}}}}
#> {"timestamp":"2022-03-16 09:52:04.414597","level":"DEBUG","name":"Application","pid":22568,"msg":"","context":{"request_id":"5a255096-a506-11ec-8000-f398b9230932","response":{"status_code":500,"headers":{"Server":"RestRserve/0.4.1"}}}}
#> 500 Internal Server Error: can't encode body with content_type = 'application/octet-stream'

# PNG works as expected
test_endpoint("/static/testplot.png")
#> {"timestamp":"2022-03-16 09:44:17.175594","level":"DEBUG","name":"Application","pid":22568,"msg":"","context":{"request_id":"437f2b1a-a505-11ec-8000-f398b9230932","request":{"method":"GET","path":"/static/testplot.png","parameters_query":[],"parameters_path":[],"headers":{}}}}
#> {"timestamp":"2022-03-16 09:44:17.177634","level":"DEBUG","name":"Application","pid":22568,"msg":"","context":{"request_id":"437f2b1a-a505-11ec-8000-f398b9230932","response":{"status_code":200,"headers":{"Server":"RestRserve/0.4.1"}}}}
#> C:\Users\REDACTED\test-restrserve\static//testplot.png 

# PDF fails
test_endpoint("/static/testplot.pdf")
#> {"timestamp":"2022-03-16 09:44:46.284985","level":"DEBUG","name":"Application","pid":22568,"msg":"","context":{"request_id":"54d8f580-a505-11ec-8000-f398b9230932","request":{"method":"GET","path":"/static/testplot.pdf","parameters_query":[],"parameters_path":[],"headers":{}}}}
#> {"timestamp":"2022-03-16 09:44:46.286984","level":"DEBUG","name":"Application","pid":22568,"msg":"","context":{"request_id":"54d8f580-a505-11ec-8000-f398b9230932","response":{"status_code":500,"headers":{"Server":"RestRserve/0.4.1"}}}}
#> 500 Internal Server Error: can't encode body with content_type = 'application/pdf' 

# JPG fails
test_endpoint("/static/testplot.jpg")
#> {"timestamp":"2022-03-16 09:45:02.932342","level":"DEBUG","name":"Application","pid":22568,"msg":"","context":{"request_id":"5eced78a-a505-11ec-8000-f398b9230932","request":{"method":"GET","path":"/static/testplot.jpg","parameters_query":[],"parameters_path":[],"headers":{}}}}
#> {"timestamp":"2022-03-16 09:45:02.934370","level":"DEBUG","name":"Application","pid":22568,"msg":"","context":{"request_id":"5eced78a-a505-11ec-8000-f398b9230932","response":{"status_code":500,"headers":{"Server":"RestRserve/0.4.1"}}}}
#> 500 Internal Server Error: can't encode body with content_type = 'image/jpeg'

################################################################################
# Check if the error also happens if the endpoint only serves a single file

app$add_static("/csv", "static/testdata.csv")

# also fails for serving single files
test_endpoint("/csv")
#> {"timestamp":"2022-03-16 10:03:42.505070","level":"DEBUG","name":"Application","pid":22568,"msg":"","context":{"request_id":"fa57bc56-a507-11ec-8000-f398b9230932","request":{"method":"GET","path":"/csv","parameters_query":[],"parameters_path":[],"headers":{}}}}
#> {"timestamp":"2022-03-16 10:03:42.507071","level":"DEBUG","name":"Application","pid":22568,"msg":"","context":{"request_id":"fa57bc56-a507-11ec-8000-f398b9230932","response":{"status_code":500,"headers":{"Server":"RestRserve/0.4.1"}}}}
#> 500 Internal Server Error: can't encode body with content_type = 'text/csv'

Expected Behavior

Should return CSV, RDS, JPG, and PDF files as it returns PNG and TXT files.

Environment information

Output of sessioninfo::session_info()

- Session info ------------------------------------------------------------------------
 setting  value
 version  R version 4.1.2 (2021-11-01)
 os       Windows 10 x64 (build 19043)
 system   x86_64, mingw32
 ui       RStudio
 language (EN)
 collate  German_Germany.1252
 ctype    German_Germany.1252
 tz       Europe/Berlin
 date     2022-03-16
 rstudio  2021.09.2+382 Ghost Orchid (desktop)
 pandoc   2.17.1.1 @ C:/Users/REDACTED/AppData/Local/Programs/Quarto/bin/ (via rmarkdown)

- Packages ----------------------------------------------------------------------------
 package     * version date (UTC) lib source
 backports     1.4.1   2021-12-13 [1] CRAN (R 4.1.2)
 checkmate     2.0.0   2020-02-06 [1] CRAN (R 4.1.2)
 cli           3.1.0   2021-10-27 [1] CRAN (R 4.1.2)
 digest        0.6.29  2021-12-01 [1] CRAN (R 4.1.2)
 evaluate      0.14    2019-05-28 [1] CRAN (R 4.1.2)
 fastmap       1.1.0   2021-01-25 [1] CRAN (R 4.1.2)
 htmltools     0.5.2   2021-08-25 [1] CRAN (R 4.1.2)
 jsonlite      1.8.0   2022-02-22 [1] CRAN (R 4.1.2)
 knitr         1.37    2021-12-16 [1] CRAN (R 4.1.2)
 mime          0.12    2021-09-28 [1] CRAN (R 4.1.1)
 R6            2.5.1   2021-08-19 [1] CRAN (R 4.1.2)
 Rcpp          1.0.8   2022-01-13 [1] CRAN (R 4.1.2)
 RestRserve  * 0.4.1   2021-01-04 [1] CRAN (R 4.1.2)
 rlang         0.4.12  2021-10-18 [1] CRAN (R 4.1.2)
 rmarkdown     2.11    2021-09-14 [1] CRAN (R 4.1.2)
 sessioninfo   1.2.2   2021-12-06 [1] CRAN (R 4.1.2)
 uuid          1.0-3   2021-11-01 [1] CRAN (R 4.1.2)
 xfun          0.29    2021-12-14 [1] CRAN (R 4.1.2)
 yaml          2.2.1   2020-02-01 [1] CRAN (R 4.1.1)

 [1] C:/Users/REDACTED/Documents/R/R-4.1.2/library

---------------------------------------------------------------------------------------

Outro

Having said that, thank you all so much for this wonderful library, really makes my life easier and is highly appreciated!

dselivanov commented 2 years ago

@DavZim thanks for clean report, appreciate! Please try latest commit from the dev branch and see if it works

DavZim commented 2 years ago

Works perfectly for me. Thank you for the fast response.

Is there anything I can help you with to get this to CRAN?

dselivanov commented 2 years ago

Will take some time to publish to CRAN. You can help to cover this fixed bug with tests, PR welcome!

On Wed, 16 Mar 2022, 18:36 DavZim, @.***> wrote:

Works perfectly for me. Thank you for the fast response.

Is there anything I can help you with to get this to CRAN?

— Reply to this email directly, view it on GitHub https://github.com/rexyai/RestRserve/issues/180#issuecomment-1068975429, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABHC5XLWX6DEA7QM6DAJG6LVAG2TFANCNFSM5Q3HGRYA . You are receiving this because you commented.Message ID: @.***>

DavZim commented 2 years ago

Sure, I'll look into it. Quick question regarding the added tests:

Is that OK for you or do you have a better idea?

dselivanov commented 2 years ago

sounds perfect!

On Thu, 17 Mar 2022, 14:31 DavZim, @.***> wrote:

Sure, I'll look into it. Quick question regarding the added tests:

Is that OK for you or do you have a better idea?

— Reply to this email directly, view it on GitHub https://github.com/rexyai/RestRserve/issues/180#issuecomment-1070765978, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABHC5XKSWLBBYJKDEPEYVR3VAMCWPANCNFSM5Q3HGRYA . You are receiving this because you commented.Message ID: @.***>