CrossGeeks / FileUploaderPlugin

Simple cross platform plugin to upload files.
MIT License
68 stars 27 forks source link

[Android] Error : Java.IO.IOException: stream was reset: PROTOCOL_ERROR #14

Open danielbarrosamorim opened 6 years ago

danielbarrosamorim commented 6 years ago

Bug Information

Java.IO.IOException: stream was reset: PROTOCOL_ERROR at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in

Detailed error at: https://gist.github.com/danielbarrosamorim/b92faa78c4ae27b6b62cb6eda7a069d5

Version Number of Plugin: 1.4.0 Device Tested On: Android Moto G Play Version 7.1.1 Simulator Tested On: Not tested Version of VS: 7.4.2 (Build 12) Stable Version of Xamarin: Xamarin Forms 2.5.1.444934 Versions of other things you are using: .Net Standard 1.6

Steps to reproduce the Behavior

Create a new Xamarin.Forms + .NetStandard 1.6 Create a Method to Post an Image to a server On Android just post an Image from local path to an URL

Expected Behavior

Save the image on the server.

Actual Behavior

Java error as described above.

Code snippet

var result = await CrossFileUploader.Current.UploadFileAsync(url,
 new FilePathItem("filePayload", path),
 new Dictionary<string, string>(){
 {"Authorization" , $"Bearer {Settings.AccessToken}"}
 } ,
 new Dictionary<string, string>(){
 { uploadType , Id }
 }
 
 );

 ApiResult apiResult = new ApiResult();
 apiResult.code = result.StatusCode.ToString();
 apiResult.data = result.Message;

Screenshotst

rafaelrmou commented 6 years ago

Same here

Java.IO.IOException: stream was reset: PROTOCOL_ERROR at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in :0 at Java.Interop.JniEnvironment+InstanceMethods.CallNonvirtualVoidMethod (Java.Interop.JniObjectReference instance, Java.Interop.JniObjectReference type, Java.Interop.JniMethodInfo method, Java.Interop.JniArgumentValue args) [0x00089] in <3cb12bce834a498199b2be6cd3e00922>:0 at Android.Runtime.JNIEnv.CallNonvirtualVoidMethod (System.IntPtr jobject, System.IntPtr jclass, System.IntPtr jmethod, Android.Runtime.JValue parms) [0x00015] in :0 at OkHttp.Okio.ForwardingSink.Write (OkHttp.Okio.OkBuffer p0, System.Int64 p1) [0x000a4] in :0 at Plugin.FileUploader.CountingRequestBody+CountingSink.Write (OkHttp.Okio.OkBuffer p0, System.Int64 p1) [0x00000] in C:\Plugins\FileUploader\src\Plugin.FileUploader.Android\CountingRequestBody.cs:74 --- End of managed Java.IO.IOException stack trace --- java.io.IOException: stream was reset: PROTOCOL_ERROR at com.squareup.okhttp.internal.spdy.SpdyStream.checkOutNotClosed(SpdyStream.java:572) at com.squareup.okhttp.internal.spdy.SpdyStream.access$1200(SpdyStream.java:34) at com.squareup.okhttp.internal.spdy.SpdyStream$SpdyDataSink.emitDataFrame(SpdyStream.java:510) at com.squareup.okhttp.internal.spdy.SpdyStream$SpdyDataSink.write(SpdyStream.java:490) at okio.RealBufferedSink.emitCompleteSegments(RealBufferedSink.java:156) at okio.RealBufferedSink.write(RealBufferedSink.java:46) at okio.ForwardingSink.write(ForwardingSink.java:35) at md57d3d8062e3e9a64ad1430263be8244a6.CountingRequestBody_CountingSink.n_write(Native Method) at md57d3d8062e3e9a64ad1430263be8244a6.CountingRequestBody_CountingSink.write(CountingRequestBody_CountingSink.java:29) at okio.RealBufferedSink.emitCompleteSegments(RealBufferedSink.java:156) at okio.RealBufferedSink.writeAll(RealBufferedSink.java:84) at com.squareup.okhttp.RequestBody$2.writeTo(RequestBody.java:100) at com.squareup.okhttp.MultipartBuilder$MultipartRequestBody.writeOrCountBytes(MultipartBuilder.java:277) at com.squareup.okhttp.MultipartBuilder$MultipartRequestBody.writeTo(MultipartBuilder.java:297) at md57d3d8062e3e9a64ad1430263be8244a6.CountingRequestBody.n_writeTo(Native Method) at md57d3d8062e3e9a64ad1430263be8244a6.CountingRequestBody.writeTo(CountingRequestBody.java:47) at com.squareup.okhttp.internal.http.HttpEngine$NetworkInterceptorChain.proceed(HttpEngine.java:819) at com.squareup.okhttp.internal.http.HttpEngine.readResponse(HttpEngine.java:684) at com.squareup.okhttp.Call.getResponse(Call.java:272) at com.squareup.okhttp.Call$ApplicationInterceptorChain.proceed(Call.java:228) at com.squareup.okhttp.Call.getResponseWithInterceptorChain(Call.java:199) at com.squareup.okhttp.Call.execute(Call.java:79) Debugger Connection Lost: Debugger lost connection to the running application. Likely this means the application terminated unexpectedly.

Any way to fix @danielbarrosamorim ?

rafaelrmou commented 6 years ago

With a fast search i found that:

https://forums.xamarin.com/discussion/125005/java-io-ioexception-stream-was-reset-protocol-error-occurred-xamarin-forms

Maybe the bug occurs because ModernHttpClient dependency?

rafaelrmou commented 6 years ago

After disable HTTP/2 from my server all work.

I will wait a fix to enable again.

sharperDev2112 commented 5 years ago

Any news on this? We are experiencing the same error and need the security etc from okhttp 2+

rr-rv commented 5 years ago

Are this plug-in maintained? I have held off on upgrading my services to http2 but now the decision was out of my hands. Anyone know a workaround?

uptondev commented 5 years ago

On Rendy Del Rosario's twitter he mentions these two alternatives:

   https://github.com/Redth/HttpTwo
   https://github.com/Matthias247/http2dotnet

I haven't used them though :)

rr-rv commented 5 years ago

thanks mate, i will look into these

rr-rv commented 5 years ago

For anyone who might get this error in the future. Ok, big a hack this is. But one "can" download the code from here and change the ModernHttpClient dependency to an updated one. then change the .net target to 2.0 for the android, abstractions projects and build a .dll. then remove the nuget from your xamarin project and use the new dlls insted.

i do not recommend this to anyone.

i will do not have the time to create a PR on this change as there is some problem with the mac and UWP implementations in this hack.

The new android FileUploadManager


using Android.Webkit;
using Java.Util.Concurrent;
using Plugin.FileUploader.Abstractions;
using Square.OkHttp3;
using Square.OkIO;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading.Tasks;

namespace Plugin.FileUploader
{
    /// <summary>
    /// Implementation for Feature
    /// </summary>
    public class FileUploadManager : IFileUploader, ICountProgressListener
    {
        public static TimeUnit UploadTimeoutUnit { get; set; } = TimeUnit.Minutes;
        public static long SocketUploadTimeout { get; set; } = 5;
        public static long ConnectUploadTimeout { get; set; } = 5;

        TaskCompletionSource<FileUploadResponse> uploadCompletionSource;
        public event EventHandler<FileUploadResponse> FileUploadCompleted = delegate { };
        public event EventHandler<FileUploadResponse> FileUploadError = delegate { };
        public event EventHandler<FileUploadProgress> FileUploadProgress = delegate { };

        public async Task<FileUploadResponse> UploadFileAsync(string url, FileBytesItem fileItem, IDictionary<string, string> headers = null, IDictionary<string, string> parameters = null, string boundary = null)
        {
            return await UploadFileAsync(url, new FileBytesItem[] { fileItem }, fileItem.Name, headers, parameters, boundary);
        }
        public async Task<FileUploadResponse> UploadFileAsync(string url, FileBytesItem[] fileItems, string tag, IDictionary<string, string> headers = null, IDictionary<string, string> parameters = null, string boundary = null)
        {

            uploadCompletionSource = new TaskCompletionSource<FileUploadResponse>();

            if (fileItems == null || fileItems.Length == 0)
            {
                var fileUploadResponse = new FileUploadResponse("There are no items to upload", -1, tag, null);
                FileUploadError(this, fileUploadResponse);

                uploadCompletionSource.TrySetResult(fileUploadResponse);
            }
            else
            {
                Task.Run(() =>
                {
                    try
                    {
                        var requestBodyBuilder = PrepareRequest(parameters, boundary);

                        foreach (var fileItem in fileItems)
                        {
                            var mediaType = MediaType.Parse(GetMimeType(fileItem.Name));

                            if (mediaType == null)
                                mediaType = MediaType.Parse("*/*");

                            RequestBody fileBody = RequestBody.Create(mediaType, fileItem.Bytes);
                            requestBodyBuilder.AddFormDataPart(fileItem.FieldName, fileItem.Name, fileBody);
                        }

                        var resp = MakeRequest(url, tag, requestBodyBuilder, headers);

                        if (!uploadCompletionSource.Task.IsCompleted)
                        {
                            uploadCompletionSource.TrySetResult(resp);
                        }

                    }
                    catch (Java.Net.UnknownHostException ex)
                    {
                        var fileUploadResponse = new FileUploadResponse("Host not reachable", -1, tag, null);
                        FileUploadError(this, fileUploadResponse);
                        System.Diagnostics.Debug.WriteLine(ex.ToString());
                        uploadCompletionSource.TrySetResult(fileUploadResponse);
                    }
                    catch (Java.IO.IOException ex)
                    {
                        var fileUploadResponse = new FileUploadResponse(ex.ToString(), -1, tag, null);
                        FileUploadError(this, fileUploadResponse);
                        System.Diagnostics.Debug.WriteLine(ex.ToString());
                        uploadCompletionSource.TrySetResult(fileUploadResponse);
                    }
                    catch (Exception ex)
                    {
                        var fileUploadResponse = new FileUploadResponse(ex.ToString(), -1, tag, null);
                        FileUploadError(this, fileUploadResponse);
                        System.Diagnostics.Debug.WriteLine(ex.ToString());
                        uploadCompletionSource.TrySetResult(fileUploadResponse);
                    }

                });
            }

            return await uploadCompletionSource.Task;
        }
        string GetMimeType(string url)
        {
            string type = "*/*";
            try
            {
                string extension = MimeTypeMap.GetFileExtensionFromUrl(url);
                if (!string.IsNullOrEmpty(extension))
                {
                    type = MimeTypeMap.Singleton.GetMimeTypeFromExtension(extension.ToLower());
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.ToString());
            }

            return type;
        }

        public async Task<FileUploadResponse> UploadFileAsync(string url, FilePathItem fileItem, IDictionary<string, string> headers = null, IDictionary<string, string> parameters = null, string boundary = null)
        {
            return await UploadFileAsync(url, new FilePathItem[] { fileItem }, fileItem.Path, headers, parameters, boundary);
        }
        public async Task<FileUploadResponse> UploadFileAsync(string url, FilePathItem[] fileItems, string tag, IDictionary<string, string> headers = null, IDictionary<string, string> parameters = null, string boundary = null)
        {

            uploadCompletionSource = new TaskCompletionSource<FileUploadResponse>();

            if (fileItems == null || fileItems.Length == 0)
            {
                var fileUploadResponse = new FileUploadResponse("There are no items to upload", -1, tag, null);
                FileUploadError(this, fileUploadResponse);
                uploadCompletionSource.TrySetResult(fileUploadResponse);
            }
            else
            {
                Task.Run(() =>
                {
                    try
                    {

                        var requestBodyBuilder = PrepareRequest(parameters, boundary);

                        foreach (var fileItem in fileItems)
                        {
                            Java.IO.File f = new Java.IO.File(fileItem.Path);
                            string fileAbsolutePath = f.AbsolutePath;

                            RequestBody file_body = RequestBody.Create(MediaType.Parse(GetMimeType(fileItem.Path)), f);
                            var fileName = fileAbsolutePath.Substring(fileAbsolutePath.LastIndexOf("/") + 1);
                            requestBodyBuilder.AddFormDataPart(fileItem.FieldName, fileName, file_body);
                        }

                        var resp = MakeRequest(url, tag, requestBodyBuilder, headers);

                        if (!uploadCompletionSource.Task.IsCompleted)
                        {
                            uploadCompletionSource.TrySetResult(resp);
                        }

                    }
                    catch (Java.Net.UnknownHostException ex)
                    {
                        var fileUploadResponse = new FileUploadResponse("Host not reachable", -1, tag, null);
                        FileUploadError(this, fileUploadResponse);
                        System.Diagnostics.Debug.WriteLine(ex.ToString());
                        uploadCompletionSource.TrySetResult(fileUploadResponse);
                    }
                    catch (Java.IO.IOException ex)
                    {
                        var fileUploadResponse = new FileUploadResponse(ex.ToString(), -1, tag, null);
                        FileUploadError(this, fileUploadResponse);
                        System.Diagnostics.Debug.WriteLine(ex.ToString());
                        uploadCompletionSource.TrySetResult(fileUploadResponse);
                    }
                    catch (Exception ex)
                    {
                        var fileUploadResponse = new FileUploadResponse(ex.ToString(), -1, tag, null);
                        FileUploadError(this, fileUploadResponse);
                        System.Diagnostics.Debug.WriteLine(ex.ToString());
                        uploadCompletionSource.TrySetResult(fileUploadResponse);
                    }
                });
            }

            return await uploadCompletionSource.Task;
        }

        MultipartBody.Builder PrepareRequest(IDictionary<string, string> parameters = null, string boundary = null)
        {
            MultipartBody.Builder requestBodyBuilder = null;

            if (string.IsNullOrEmpty(boundary))
            {
                requestBodyBuilder = new MultipartBody.Builder().SetType(MultipartBody.Form);
            }
            else
            {
                requestBodyBuilder = new MultipartBody.Builder(boundary).SetType(MultipartBody.Form);
            }

            if (parameters != null)
            {
                foreach (string key in parameters.Keys)
                {
                    if (parameters[key] != null)
                    {
                        requestBodyBuilder.AddFormDataPart(key, parameters[key]);
                    }
                }
            }
            return requestBodyBuilder;
        }
        FileUploadResponse MakeRequest(string url, string tag, MultipartBody.Builder requestBodyBuilder, IDictionary<string, string> headers = null)
        {
            //RequestBody requestBody = requestBodyBuilder.Build();
            CountingRequestBody requestBody = new CountingRequestBody(requestBodyBuilder.Build(), tag, this);
            var requestBuilder = new Request.Builder();

            if (headers != null)
            {
                foreach (string key in headers.Keys)
                {
                    if (!string.IsNullOrEmpty(headers[key]))
                    {
                        requestBuilder = requestBuilder.AddHeader(key, headers[key]);
                    }
                }
            }

            Request request = requestBuilder
                .Url(url)
                .Post(requestBody)
                .Build();

            OkHttpClient client = new OkHttpClient.Builder()
                .ConnectTimeout(ConnectUploadTimeout, UploadTimeoutUnit)
                .ReadTimeout(SocketUploadTimeout, UploadTimeoutUnit)
                .Build();
            //client.SetConnectTimeout(ConnectUploadTimeout, UploadTimeoutUnit); // connect timeout
            //client.SetReadTimeout(SocketUploadTimeout, UploadTimeoutUnit);    // socket timeout

            Response response = client.NewCall(request).Execute();
            var responseString = response.Body().String();
            var code = response.Code();

            IDictionary<string, string> responseHeaders = new Dictionary<string, string>();
            var rHeaders = response.Headers();
            if (rHeaders != null)
            {
                var names = rHeaders.Names();
                foreach (string name in names)
                {
                    if (!string.IsNullOrEmpty(rHeaders.Get(name)))
                    {
                        responseHeaders.Add(name, rHeaders.Get(name));
                    }
                }
            }

            FileUploadResponse fileUploadResponse = new FileUploadResponse(responseString, code, tag, new ReadOnlyDictionary<string, string>(responseHeaders));

            if (response.IsSuccessful)
            {

                FileUploadCompleted(this, fileUploadResponse);

            }
            else
            {
                FileUploadError(this, fileUploadResponse);

            }

            return fileUploadResponse;
        }

        public void OnProgress(string tag, long bytesWritten, long contentLength)
        {
            var fileUploadProgress = new FileUploadProgress(bytesWritten, contentLength, tag);
            FileUploadProgress(this, fileUploadProgress);
        }

        public void OnError(string tag, string error)
        {
            var fileUploadResponse = new FileUploadResponse(error, -1, tag, null);
            FileUploadError(this, fileUploadResponse);
            System.Diagnostics.Debug.WriteLine(error);

            uploadCompletionSource.TrySetResult(fileUploadResponse);
        }
    }
}

the new android CountingRequestBody


using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Square.OkHttp3;
using Square.OkIO;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Plugin.FileUploader
{
    public class CountingRequestBody : RequestBody
    {
        protected RequestBody _body;
        protected ICountProgressListener _listener;
        protected string _tag;
        protected CountingSink countingSink;

        public CountingRequestBody(RequestBody body, string tag, ICountProgressListener listener)
        {
            _body = body;
            _tag = tag;
            _listener = listener;
        }
        public override MediaType ContentType()
        {
            return _body.ContentType();
        }

        public override long ContentLength()
        {
            return _body.ContentLength();
        }

        public override void WriteTo(IBufferedSink p0)
        {

            try
            {
                IBufferedSink bufferedSink;
                countingSink = new CountingSink(this, p0);
                bufferedSink = OkIO.Buffer(countingSink);

                _body.WriteTo(bufferedSink);

                bufferedSink.Flush();
            }
            catch (Java.IO.IOException ex)
            {
                _listener?.OnError(_tag, ex.ToString());
            }
        }

        public class CountingSink : ForwardingSink
        {
            private long bytesWritten = 0;
            CountingRequestBody _parent;

            public CountingSink(CountingRequestBody parent, ISink sink) : base(sink)
            {
                _parent = parent;
            }

            public override void Write(OkBuffer p0, long p1)
            {
                try
                {
                    base.Write(p0, p1);

                    bytesWritten += p1;
                    _parent?._listener.OnProgress(_parent._tag, bytesWritten, _parent.ContentLength());
                }
                catch (Java.IO.IOException ex)
                {
                    _parent?._listener?.OnError(_parent._tag, ex.ToString());
                }

            }

        }
    }
}