Added support for FormData (AddFormParameter and AddFormUpload), which automatically gets converted to multipart/form-data or application/x-www-form-urlencoded.

This commit is contained in:
Taloth Saldono 2016-02-28 16:41:28 +01:00
parent 2ffbbb0e71
commit 7c54fa70d7
1 changed files with 120 additions and 0 deletions

View File

@ -22,6 +22,7 @@ namespace NzbDrone.Common.Http
public bool AllowAutoRedirect { get; set; } public bool AllowAutoRedirect { get; set; }
public NetworkCredential NetworkCredential { get; set; } public NetworkCredential NetworkCredential { get; set; }
public Dictionary<string, string> Cookies { get; private set; } public Dictionary<string, string> Cookies { get; private set; }
public List<HttpFormData> FormData { get; private set; }
public Action<HttpRequest> PostProcess { get; set; } public Action<HttpRequest> PostProcess { get; set; }
@ -35,6 +36,7 @@ namespace NzbDrone.Common.Http
Segments = new Dictionary<string, string>(); Segments = new Dictionary<string, string>();
Headers = new HttpHeader(); Headers = new HttpHeader();
Cookies = new Dictionary<string, string>(); Cookies = new Dictionary<string, string>();
FormData = new List<HttpFormData>();
} }
public HttpRequestBuilder(bool useHttps, string host, int port, string urlBase = null) public HttpRequestBuilder(bool useHttps, string host, int port, string urlBase = null)
@ -63,6 +65,7 @@ namespace NzbDrone.Common.Http
clone.Segments = new Dictionary<string, string>(clone.Segments); clone.Segments = new Dictionary<string, string>(clone.Segments);
clone.Headers = new HttpHeader(clone.Headers); clone.Headers = new HttpHeader(clone.Headers);
clone.Cookies = new Dictionary<string, string>(clone.Cookies); clone.Cookies = new Dictionary<string, string>(clone.Cookies);
clone.FormData = new List<HttpFormData>(clone.FormData);
return clone; return clone;
} }
@ -111,6 +114,8 @@ namespace NzbDrone.Common.Http
{ {
request.Cookies[cookie.Key] = cookie.Value; request.Cookies[cookie.Key] = cookie.Value;
} }
ApplyFormData(request);
} }
public virtual HttpRequest Build() public virtual HttpRequest Build()
@ -132,6 +137,86 @@ namespace NzbDrone.Common.Http
return new HttpRequestBuilderFactory(this); return new HttpRequestBuilderFactory(this);
} }
protected virtual void ApplyFormData(HttpRequest request)
{
if (FormData.Empty()) return;
if (request.ContentData != null)
{
throw new ApplicationException("Cannot send HttpRequest Body and FormData simultaneously.");
}
var shouldSendAsMultipart = FormData.Any(v => v.ContentType != null || v.FileName != null || v.ContentData.Length > 1024);
if (shouldSendAsMultipart)
{
var boundary = "-----------------------------" + DateTime.Now.Ticks.ToString("x14");
var partBoundary = string.Format("--{0}\r\n", boundary);
var endBoundary = string.Format("--{0}--\r\n", boundary);
var bodyStream = new MemoryStream();
var summary = new StringBuilder();
using (var writer = new StreamWriter(bodyStream, new UTF8Encoding(false)))
{
foreach (var formData in FormData)
{
writer.Write(partBoundary);
writer.Write("Content-Disposition: form-data");
if (formData.Name.IsNotNullOrWhiteSpace()) writer.Write("; name=\"{0}\"", formData.Name);
if (formData.FileName.IsNotNullOrWhiteSpace()) writer.Write("; filename=\"{0}\"", formData.FileName);
writer.Write("\r\n");
if (formData.ContentType.IsNotNullOrWhiteSpace()) writer.Write("Content-Type: {0}\r\n", formData.ContentType);
writer.Write("\r\n");
writer.Flush();
bodyStream.Write(formData.ContentData, 0, formData.ContentData.Length);
writer.Write("\r\n");
if (formData.FileName.IsNotNullOrWhiteSpace())
{
summary.AppendFormat("\r\n{0}={1} ({2} bytes)", formData.Name, formData.FileName, formData.ContentData.Length);
}
else
{
summary.AppendFormat("\r\n{0}={1}", formData.Name, Encoding.UTF8.GetString(formData.ContentData));
}
}
writer.Write(endBoundary);
}
var body = bodyStream.ToArray();
// TODO: Scan through body to see if we have a boundary collision?
request.Headers.ContentType = "multipart/form-data; boundary=" + boundary;
request.SetContent(body);
if (request.ContentSummary == null)
{
request.ContentSummary = summary.ToString();
}
}
else
{
var parameters = FormData.Select(v => string.Format("{0}={1}", v.Name, Uri.EscapeDataString(Encoding.UTF8.GetString(v.ContentData))));
var urlencoded = string.Join("&", parameters);
var body = Encoding.UTF8.GetBytes(urlencoded);
request.Headers.ContentType = "application/x-www-form-urlencoded";
request.SetContent(body);
if (request.ContentSummary == null)
{
request.ContentSummary = urlencoded;
}
}
}
public virtual HttpRequestBuilder Resource(string resourceUrl) public virtual HttpRequestBuilder Resource(string resourceUrl)
{ {
if (!ResourceUrl.IsNotNullOrWhiteSpace() || resourceUrl.StartsWith("/")) if (!ResourceUrl.IsNotNullOrWhiteSpace() || resourceUrl.StartsWith("/"))
@ -223,5 +308,40 @@ namespace NzbDrone.Common.Http
return this; return this;
} }
public virtual HttpRequestBuilder AddFormParameter(string key, object value)
{
if (Method != HttpMethod.POST)
{
throw new NotSupportedException("HttpRequest Method must be POST to add FormParameter.");
} }
FormData.Add(new HttpFormData
{
Name = key,
ContentData = Encoding.UTF8.GetBytes(value.ToString())
});
return this;
}
public virtual HttpRequestBuilder AddFormUpload(string name, string fileName, byte[] data, string contentType = "application/octet-stream")
{
if (Method != HttpMethod.POST)
{
throw new NotSupportedException("HttpRequest Method must be POST to add FormUpload.");
}
FormData.Add(new HttpFormData
{
Name = name,
FileName = fileName,
ContentData = data,
ContentType = contentType
});
return this;
}
}
} }