-
Notifications
You must be signed in to change notification settings - Fork 82
Http file streaming #218
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
Http file streaming #218
Changes from all commits
3d4f98f
c169843
5714cba
206cf24
77231e1
807f804
60fb69b
568fb12
ad319d1
8378168
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,132 @@ | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.IO; | ||
| using System.Linq; | ||
| using System.Text; | ||
| using System.Threading.Tasks; | ||
| using Windows.Foundation; | ||
| using Windows.Storage.Streams; | ||
| using Windows.Web.Http; | ||
| using Windows.Web.Http.Headers; | ||
|
|
||
| namespace Microsoft.Tools.WindowsDevicePortal | ||
| { | ||
| /// <summary> | ||
| /// This class mimicks <see cref="HttpMultipartContent"/>, with two main differences | ||
| /// 1. Simplifies posting files by taking file names instead of managing streams. | ||
| /// 2. Does not quote the boundaries, due to a bug in the device portal: | ||
| /// https://insider.windows.com/FeedbackHub/fb?contextid=519&feedbackid=19a5af49-38f4-409a-b464-e66f80679545&form=1 | ||
| /// </summary> | ||
| internal sealed class HttpMultipartFileContent : IHttpContent | ||
| { | ||
| private List<string> items = new List<string>(); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Throughout, please add comment blocks similar to the other classes in the code :) |
||
| private string boundaryString; | ||
|
|
||
| public HttpMultipartFileContent() : this(Guid.NewGuid().ToString()) { } | ||
|
|
||
| public HttpMultipartFileContent(string boundary) | ||
| { | ||
| boundaryString = boundary; | ||
| Headers.ContentType = new HttpMediaTypeHeaderValue(string.Format("multipart/form-data; boundary={0}", boundaryString)); | ||
| } | ||
|
|
||
| public void Add(string filename) | ||
| { | ||
| if (filename != null) | ||
| items.Add(filename); | ||
| } | ||
|
|
||
| public void AddRange(IEnumerable<string> filenames) | ||
| { | ||
| if (filenames != null) | ||
| items.AddRange(filenames); | ||
| } | ||
|
|
||
| public HttpContentHeaderCollection Headers { get; } = new HttpContentHeaderCollection(); | ||
|
|
||
| IAsyncOperationWithProgress<ulong, ulong> IHttpContent.BufferAllAsync() | ||
| { | ||
| throw new NotImplementedException(); | ||
| } | ||
|
|
||
| void IDisposable.Dispose() | ||
| { | ||
| items.Clear(); | ||
| } | ||
|
|
||
| IAsyncOperationWithProgress<IBuffer, ulong> IHttpContent.ReadAsBufferAsync() | ||
| { | ||
| throw new NotImplementedException(); | ||
| } | ||
|
|
||
| IAsyncOperationWithProgress<IInputStream, ulong> IHttpContent.ReadAsInputStreamAsync() | ||
| { | ||
| throw new NotImplementedException(); | ||
| } | ||
|
|
||
| IAsyncOperationWithProgress<string, ulong> IHttpContent.ReadAsStringAsync() | ||
| { | ||
| throw new NotImplementedException(); | ||
| } | ||
|
|
||
| bool IHttpContent.TryComputeLength(out ulong length) | ||
| { | ||
| length = 0; | ||
| var boundaryLength = Encoding.ASCII.GetBytes(string.Format("--{0}\r\n", boundaryString)).Length; | ||
| foreach (var item in items) | ||
| { | ||
| var headerdata = GetFileHeader(new FileInfo(item)); | ||
| length += (ulong)(boundaryLength + headerdata.Length + new FileInfo(item).Length + 2); | ||
| } | ||
| length += (ulong)(boundaryLength + 2); | ||
| return true; | ||
| } | ||
|
|
||
| IAsyncOperationWithProgress<ulong, ulong> IHttpContent.WriteToStreamAsync(IOutputStream outputStream) | ||
| { | ||
| return System.Runtime.InteropServices.WindowsRuntime.AsyncInfo.Run<ulong, ulong>((token, progress) => | ||
| { | ||
| return WriteToStreamAsyncTask(outputStream, (ulong p) => progress.Report(p)); | ||
| }); | ||
| } | ||
|
|
||
| private async Task<ulong> WriteToStreamAsyncTask(IOutputStream outputStream, Action<ulong> progress) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Issue #228 will necessitate reworking this for the UWP version of WDPW. |
||
| { | ||
| ulong bytesWritten = 0; | ||
| var outStream = outputStream.AsStreamForWrite(); | ||
| var boundary = Encoding.ASCII.GetBytes($"--{boundaryString}\r\n"); | ||
| var newline = Encoding.ASCII.GetBytes("\r\n"); | ||
| foreach (var item in items) | ||
| { | ||
| outStream.Write(boundary, 0, boundary.Length); | ||
| bytesWritten += (ulong)boundary.Length; | ||
| var headerdata = GetFileHeader(new FileInfo(item)); | ||
| outStream.Write(headerdata, 0, headerdata.Length); | ||
| bytesWritten += (ulong)headerdata.Length; | ||
| using (var file = File.OpenRead(item)) | ||
| { | ||
| await file.CopyToAsync(outStream); | ||
| bytesWritten += (ulong)file.Position; | ||
| } | ||
| outStream.Write(newline, 0, newline.Length); | ||
| bytesWritten += (ulong)newline.Length; | ||
| await outStream.FlushAsync(); | ||
| progress(bytesWritten); | ||
| } | ||
| // Close the installation request data. | ||
| boundary = Encoding.ASCII.GetBytes($"--{boundaryString}--\r\n"); | ||
| outStream.Write(boundary, 0, boundary.Length); | ||
| await outStream.FlushAsync(); | ||
| bytesWritten += (ulong)boundary.Length; | ||
| return bytesWritten; | ||
| } | ||
| private static byte[] GetFileHeader(FileInfo info) | ||
| { | ||
| string contentType = "application/octet-stream"; | ||
| if (info.Extension.ToLower() == ".cer") | ||
| contentType = "application/x-x509-ca-cert"; | ||
|
|
||
| return Encoding.ASCII.GetBytes(string.Format("Content-Disposition: form-data; name=\"{0}\"; filename=\"{0}\"\r\nContent-Type: {1}\r\n\r\n", info.Name, contentType)); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| using System; | ||
| using System.Globalization; | ||
| using System.Diagnostics; | ||
| using System.Collections.Generic; | ||
| using System.IO; | ||
| using System.Net; | ||
| using System.Net.Http; | ||
| using System.Text; | ||
| using System.Threading.Tasks; | ||
|
|
||
| namespace Microsoft.Tools.WindowsDevicePortal | ||
| { | ||
| /// <summary> | ||
| /// This class mimicks <see cref="HttpMultipartContent"/>, with two main differences | ||
| /// 1. Simplifies posting files by taking file names instead of managing streams. | ||
| /// 2. Does not quote the boundaries, due to a bug in the device portal: | ||
| /// https://insider.windows.com/FeedbackHub/fb?contextid=519&feedbackid=19a5af49-38f4-409a-b464-e66f80679545&form=1 | ||
| /// </summary> | ||
| internal sealed class HttpMultipartFileContent : HttpContent | ||
| { | ||
| private List<string> items = new List<string>(); | ||
| private string boundaryString; | ||
|
|
||
| public HttpMultipartFileContent() : this(Guid.NewGuid().ToString()) { } | ||
|
|
||
| public HttpMultipartFileContent(string boundary) | ||
| { | ||
| boundaryString = boundary; | ||
| Headers.TryAddWithoutValidation("Content-Type", string.Format("multipart/form-data; boundary={0}", boundaryString)); | ||
| } | ||
|
|
||
| public void Add(string filename) | ||
| { | ||
| if (filename != null) | ||
| items.Add(filename); | ||
| } | ||
|
|
||
| public void AddRange(IEnumerable<string> filenames) | ||
| { | ||
| if (filenames != null) | ||
| items.AddRange(filenames); | ||
| } | ||
|
|
||
| protected override async Task SerializeToStreamAsync(Stream outStream, TransportContext context) | ||
| { | ||
| var boundary = Encoding.ASCII.GetBytes($"--{boundaryString}\r\n"); | ||
| var newline = Encoding.ASCII.GetBytes("\r\n"); | ||
| foreach (var item in items) | ||
| { | ||
| outStream.Write(boundary, 0, boundary.Length); | ||
| var headerdata = GetFileHeader(new FileInfo(item)); | ||
| outStream.Write(headerdata, 0, headerdata.Length); | ||
|
|
||
| using (var file = File.OpenRead(item)) | ||
| { | ||
| await file.CopyToAsync(outStream); | ||
| } | ||
| outStream.Write(newline, 0, newline.Length); | ||
| await outStream.FlushAsync(); | ||
| } | ||
| // Close the installation request data. | ||
| boundary = Encoding.ASCII.GetBytes($"--{boundaryString}--\r\n"); | ||
| outStream.Write(boundary, 0, boundary.Length); | ||
| await outStream.FlushAsync(); | ||
| } | ||
|
|
||
| protected override bool TryComputeLength(out long length) | ||
| { | ||
| length = 0; | ||
| var boundaryLength = Encoding.ASCII.GetBytes(string.Format("--{0}\r\n", boundaryString)).Length; | ||
| foreach (var item in items) | ||
| { | ||
| var headerdata = GetFileHeader(new FileInfo(item)); | ||
| length += boundaryLength + headerdata.Length + new FileInfo(item).Length + 2; | ||
| } | ||
| length += (boundaryLength + 2); | ||
| return true; | ||
| } | ||
| private static byte[] GetFileHeader(FileInfo info) | ||
| { | ||
| string contentType = "application/octet-stream"; | ||
| if (info.Extension.ToLower() == ".cer") | ||
| contentType = "application/x-x509-ca-cert"; | ||
|
|
||
| return Encoding.ASCII.GetBytes(string.Format("Content-Disposition: form-data; name=\"{0}\"; filename=\"{0}\"\r\nContent-Type: {1}\r\n\r\n", info.Name, contentType)); | ||
| } | ||
|
|
||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does removing async here create any issues with calling code?