-
Notifications
You must be signed in to change notification settings - Fork 35
Description
We were able to get RestRserve working with .Net RestSharp client but multipart requests with the .Net core HttpClient are failing. A minimal example follows.
restrserve.r
library(RestRserve)
app = Application$new(content_type = "text/plain")
app$logger$set_log_level("trace")
app$add_post(
path = "/echo",
FUN = function(request, response) {
part1 <- request$get_file("part1")
part2 <- request$get_file("part2")
response$set_body(part1)
}
)
backend = BackendRserve$new()
backend$start(app, http_port = 80)
This is started within a Docker container using the rexyai/restrserve:dev image.
Dockerfile
FROM rexyai/restrserve:dev
COPY / /
EXPOSE 80
ENTRYPOINT ["Rscript", "restrserve.r"]
The following C# code uses .Net core 3.1
Program.cs
using RestSharp;
using System;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
internal class Program
{
private static async Task Main(string[] args)
{
var requestUrl = "http://127.0.0.1:6027/echo";
try
{
Console.WriteLine("Submitting multipart with RestSharp");
Console.WriteLine("RestSharpResult: " + await TestRestSharp(requestUrl));
}
catch (Exception e)
{
Console.WriteLine($"Failed to process request (RestSharp): {e}");
}
try
{
Console.WriteLine("Submitting multipart with HttpClient");
Console.WriteLine("HttpClientResult: " + await TestHttpClient(requestUrl));
}
catch (Exception e)
{
Console.WriteLine($"Failed to process request (HttpClient): {e}");
}
Console.ReadLine();
}
private static async Task<string> TestHttpClient(string requestUrl)
{
using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(20) };
using var content = new MultipartFormDataContent
{
{ new StreamContent(ToMemoryStream("This is the first part.")), "part1", "part1.csv" },
{ new StreamContent(ToMemoryStream("This is the second part.")), "part2", "part2.csv" },
};
Console.WriteLine($"Submit request: {requestUrl}");
var response = await client.PostAsync(requestUrl, content);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
private static async Task<string> TestRestSharp(string requestUrl)
{
var client = new RestClient(requestUrl);
var streams = new Stream[] { ToMemoryStream("This is the first part."), ToMemoryStream("This is the second part.") };
var clientRequest = new RestRequest(Method.POST);
clientRequest.AddFile("part1", async writer => await streams[0].CopyToAsync(writer), "part1.csv", streams[0].Length);
clientRequest.AddFile("part2", async writer => await streams[1].CopyToAsync(writer), "part2.csv", streams[1].Length);
Console.WriteLine($"Submit request: {requestUrl}");
var response = await client.ExecuteAsync(clientRequest);
if (response.IsSuccessful == false)
throw new InvalidOperationException($"{response.Content} {response.ErrorException}");
return response.Content;
}
private static MemoryStream ToMemoryStream(string text) => new MemoryStream(Encoding.UTF8.GetBytes(text));
}
Requests submitted with the RestSharp client work fine and produce expected RestRserve log entries. Requests submitted with the HttpClient fail with the below error message and produce no RestRserve log entries.
Console output
Submitting multipart with RestSharp
Submit request: http://127.0.0.1:6027/echo
RestSharpResult: This is the first part.
Submitting multipart with HttpClient
Submit request: http://127.0.0.1:6027/echo
Failed to process request (HttpClient): System.Net.Http.HttpRequestException: Response status code does not indicate success: 500 (Evaluation error).
at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()
at Program.TestHttpClient(String requestUrl) in D:\temp\HttpClientMultipartBug\HttpClientMultipartBug\Program.cs:line 48
at Program.Main(String[] args) in D:\temp\HttpClientMultipartBug\HttpClientMultipartBug\Program.cs:line 27
Something about the way the .Net core HttpClient forms the multipart request causes problems for RestRserve. The HttpClient request should be ok as it works with the corresponding API implemented with plumber.
We would prefer to use the .Net core HttpClient over RestSharp because RestSharp uses unreasonable amounts of memory when the multipart body is large.