Skip to content

Commit

Permalink
Consider the boundary quotes request option value (#2020)
Browse files Browse the repository at this point in the history
* Consider the boundary quotes request option value

* Set the boundary quote marks to true as it's the current default

* Fixed the boundary quotes
  • Loading branch information
alexeyzimarev committed Mar 30, 2023
1 parent 93ca86a commit d184961
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 25 deletions.
30 changes: 17 additions & 13 deletions src/RestSharp/Request/RequestContent.cs
Expand Up @@ -49,7 +49,7 @@ class RequestContent : IDisposable {
void AddFiles() {
if (!_request.HasFiles() && !_request.AlwaysMultipartFormData) return;

var mpContent = new MultipartFormDataContent(GetOrSetFormBoundary());
var mpContent = CreateMultipartFormDataContent();

foreach (var file in _request.Files) {
var stream = file.GetFile();
Expand All @@ -60,10 +60,7 @@ class RequestContent : IDisposable {

var dispositionHeader = file.Options.DisableFilenameEncoding
? ContentDispositionHeaderValue.Parse($"form-data; name=\"{file.Name}\"; filename=\"{file.FileName}\"")
: new ContentDispositionHeaderValue("form-data") {
Name = $"\"{file.Name}\"",
FileName = $"\"{file.FileName}\""
};
: new ContentDispositionHeaderValue("form-data") { Name = $"\"{file.Name}\"", FileName = $"\"{file.FileName}\"" };
if (!file.Options.DisableFileNameStar) dispositionHeader.FileNameStar = file.FileName;
fileContent.Headers.ContentDisposition = dispositionHeader;

Expand Down Expand Up @@ -104,11 +101,7 @@ class RequestContent : IDisposable {

var contentType = body.ContentType.Or(serializer.Serializer.ContentType);

return new StringContent(
content,
_client.Options.Encoding,
contentType.Value
);
return new StringContent(content, _client.Options.Encoding, contentType.Value);
}
}

Expand All @@ -119,6 +112,15 @@ class RequestContent : IDisposable {

string GetOrSetFormBoundary() => _request.FormBoundary ?? (_request.FormBoundary = Guid.NewGuid().ToString());

MultipartFormDataContent CreateMultipartFormDataContent() {
var boundary = GetOrSetFormBoundary();
var mpContent = new MultipartFormDataContent(boundary);
var contentType = new MediaTypeHeaderValue("multipart/form-data");
contentType.Parameters.Add(new NameValueHeaderValue(nameof(boundary), GetBoundary(boundary, _request.MultipartFormQuoteParameters)));
mpContent.Headers.ContentType = contentType;
return mpContent;
}

void AddBody(bool hasPostParameters) {
if (!_request.TryGetBodyParameter(out var bodyParameter)) return;

Expand All @@ -127,7 +129,7 @@ class RequestContent : IDisposable {
// we need to send the body
if (hasPostParameters || _request.HasFiles() || BodyShouldBeMultipartForm(bodyParameter!) || _request.AlwaysMultipartFormData) {
// here we must use multipart form data
var mpContent = Content as MultipartFormDataContent ?? new MultipartFormDataContent(GetOrSetFormBoundary());
var mpContent = Content as MultipartFormDataContent ?? CreateMultipartFormDataContent();
var ct = bodyContent.Headers.ContentType?.MediaType;
var name = bodyParameter!.Name.IsEmpty() ? ct : bodyParameter.Name;

Expand Down Expand Up @@ -157,7 +159,7 @@ class RequestContent : IDisposable {

mpContent.Add(
new StringContent(postParameter.Value?.ToString() ?? "", _client.Options.Encoding, postParameter.ContentType.Value),
_request.MultipartFormQuoteParameters ? $"\"{parameterName}\"" : parameterName
parameterName
);
}
}
Expand All @@ -179,6 +181,8 @@ class RequestContent : IDisposable {
}
}

static string GetBoundary(string boundary, bool quote) => quote ? $"\"{boundary}\"" : boundary;

void AddHeaders() {
var contentHeaders = _parameters
.GetParameters<HeaderParameter>()
Expand All @@ -205,7 +209,7 @@ class RequestContent : IDisposable {

string GetContentTypeHeader(string contentType)
=> Content is MultipartFormDataContent
? $"{contentType}; boundary=\"{GetOrSetFormBoundary()}\""
? $"{contentType}; boundary={GetBoundary(GetOrSetFormBoundary(), _request.MultipartFormQuoteParameters)}"
: contentType;
}

Expand Down
2 changes: 1 addition & 1 deletion src/RestSharp/Request/RestRequest.cs
Expand Up @@ -86,7 +86,7 @@ public RestRequest(Uri resource, Method method = Method.Get)
/// quotation marks. Default is false. Enable it if the remote endpoint requires parameters
/// to be in quotes (for example, FreshDesk API).
/// </summary>
public bool MultipartFormQuoteParameters { get; set; }
public bool MultipartFormQuoteParameters { get; set; } = true;

public string? FormBoundary { get; set; }

Expand Down
36 changes: 25 additions & 11 deletions test/RestSharp.Tests.Integrated/MultipartFormDataTests.cs
Expand Up @@ -32,13 +32,13 @@ public sealed class MultipartFormDataTests : IDisposable {
$"--{{0}}--{LineBreak}";

const string ExpectedFileAndBodyRequestContent =
"--{0}" +
$"{LineBreak}{KnownHeaders.ContentType}: application/octet-stream" +
"--{0}" +
$"{LineBreak}{KnownHeaders.ContentType}: application/octet-stream" +
$"{LineBreak}{KnownHeaders.ContentDisposition}: form-data; name=\"fileName\"; filename=\"TestFile.txt\"" +
$"{LineBreak}{LineBreak}This is a test file for RestSharp.{LineBreak}" +
$"--{{0}}{LineBreak}{KnownHeaders.ContentType}: application/json; {CharsetString}" +
$"{LineBreak}{KnownHeaders.ContentDisposition}: form-data; name=controlName" +
$"{LineBreak}{LineBreak}test{LineBreak}" +
$"{LineBreak}{LineBreak}This is a test file for RestSharp.{LineBreak}" +
$"--{{0}}{LineBreak}{KnownHeaders.ContentType}: application/json; {CharsetString}" +
$"{LineBreak}{KnownHeaders.ContentDisposition}: form-data; name=controlName" +
$"{LineBreak}{LineBreak}test{LineBreak}" +
$"--{{0}}--{LineBreak}";

const string ExpectedDefaultMultipartContentType = "multipart/form-data; boundary=\"{0}\"";
Expand Down Expand Up @@ -76,11 +76,24 @@ static class RequestHandler {
Assert.Null(response.ErrorException);
}

[Fact]
public async Task MultipartFormData_NoBoundaryQuotes() {
var request = new RestRequest("/", Method.Post) { AlwaysMultipartFormData = true };

AddParameters(request);
request.MultipartFormQuoteParameters = false;

var response = await _client.ExecuteAsync(request);

var expected = string.Format(Expected, request.FormBoundary);

response.Content.Should().Be(expected);
RequestHandler.CapturedContentType.Should().Be($"multipart/form-data; boundary={request.FormBoundary}");
}

[Fact]
public async Task MultipartFormData() {
var request = new RestRequest("/", Method.Post) {
AlwaysMultipartFormData = true
};
var request = new RestRequest("/", Method.Post) { AlwaysMultipartFormData = true };

AddParameters(request);

Expand All @@ -91,7 +104,8 @@ static class RequestHandler {
_output.WriteLine($"Expected: {expected}");
_output.WriteLine($"Actual: {response.Content}");

Assert.Equal(expected, response.Content);
response.Content.Should().Be(expected);
RequestHandler.CapturedContentType.Should().Be($"multipart/form-data; boundary=\"{request.FormBoundary}\"");
}

[Fact]
Expand Down Expand Up @@ -187,4 +201,4 @@ static class RequestHandler {

var response = await _client.ExecuteAsync(request);
}
}
}

0 comments on commit d184961

Please sign in to comment.