[ 
https://issues.apache.org/jira/browse/JCLOUDS-1623?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Ivan Dimitrov updated JCLOUDS-1623:
-----------------------------------
    Description: 
Hello,

We are observing an issue with JClouds while transferring a file with an input 
stream only.
We are opening an input stream to a remote file and we are passing the stream 
to the JClouds library, but after some time it fails with the following error:

{noformat}
Exception in thread "main" java.lang.RuntimeException: java.io.EOFException: 
reached end of stream after skipping 14933328 bytes; 67108864 bytes expected
        at com.google.common.base.Throwables.propagate(Throwables.java:241)
        at 
org.jclouds.io.internal.BasePayloadSlicer.doSlice(BasePayloadSlicer.java:253)
        at 
org.jclouds.io.internal.BasePayloadSlicer.slice(BasePayloadSlicer.java:228)
        at 
org.jclouds.blobstore.internal.BaseBlobStore.putMultipartBlob(BaseBlobStore.java:385)
        at 
org.jclouds.blobstore.internal.BaseBlobStore.putMultipartBlob(BaseBlobStore.java:349)
        at 
org.jclouds.aws.s3.blobstore.AWSS3BlobStore.putBlob(AWSS3BlobStore.java:79)
        at org.example.Main.main(Main.java:50)
Caused by: java.io.EOFException: reached end of stream after skipping 14933328 
bytes; 67108864 bytes expected
        at com.google.common.io.ByteStreams.skipFully(ByteStreams.java:807)
        at 
org.jclouds.io.internal.BasePayloadSlicer.doSlice(BasePayloadSlicer.java:251)
        ... 5 more
{noformat}

Note: The issue occurs only for larger files (more than 32 mb).
Java version: sapmachine-jdk-11.0.15.0.1.jdk
IAAS: AWS-s3

If we pass a BufferedInputStream the code works, so I guess that there is some 
issue with the processing of the stream. The stream returned by the Java Http 
Client is HttpResponseInputStream.

Code:

{code:title=Main.java|borderStyle=solid}
package org.example;

import com.google.common.collect.ImmutableSet;
import com.google.inject.Module;
import org.jclouds.ContextBuilder;
import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.BlobStoreContext;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.options.PutOptions;
import org.jclouds.logging.slf4j.config.SLF4JLoggingModule;

import java.io.BufferedInputStream;
import java.io.InputStream;
import java.net.Authenticator;
import java.net.PasswordAuthentication;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.UUID;

public class Main {

    public static void main(String[] args) throws Exception {
        Iterable<Module> modules = ImmutableSet.<Module>of(
                new SLF4JLoggingModule());
        ContextBuilder contextBuilder = ContextBuilder.newBuilder("aws-s3")
                .credentials("<IDENTITY>", "<CREDENTIAL>")
                .modules(modules);
        BlobStoreContext blobStoreContext = 
contextBuilder.buildView(BlobStoreContext.class);
        BlobStore blobStore = blobStoreContext.getBlobStore();
        String decodedUrl = "<URL>";
        HttpClient client = buildHttpClient(decodedUrl);
        HttpResponse<InputStream> response = 
callRemoteEndpointWithRetry(client, decodedUrl);
        long fileSize = response.headers()
                .firstValueAsLong("Content-Length")
                .orElseThrow(() -> new IllegalArgumentException("No 
Content-Length"));
        System.out.println("Bytes: " + fileSize);
        InputStream inputStream = response.body();

            String container = "<CONTAINER>";
            Blob blob = blobStore.blobBuilder("TEST.zip")
//                    .payload(new BufferedInputStream(inputStream, 8 * 1024))
                    .payload(inputStream)
                    .contentDisposition(UUID.randomUUID().toString())
                    .contentType("application/octet-stream")
                    .contentLength(fileSize)
                    .build();
            blobStore.putBlob(container, blob, PutOptions.Builder.multipart());
    }

    private static HttpClient buildHttpClient(String decodedUrl) {
        return HttpClient.newBuilder()
                .version(HttpClient.Version.HTTP_1_1)
                .connectTimeout(Duration.ofMinutes(60))
                .followRedirects(HttpClient.Redirect.NORMAL)
                .authenticator(buildPasswordAuthenticator(decodedUrl))
                .build();
    }

    private static Authenticator buildPasswordAuthenticator(String decodedUrl) {
        return new Authenticator() {
            @Override
            protected PasswordAuthentication getPasswordAuthentication() {
                var uri = URI.create(decodedUrl);
                var userInfo = uri.getUserInfo();
                if (userInfo != null) {
                    var separatorIndex = userInfo.indexOf(':');
                    var username = userInfo.substring(0, separatorIndex);
                    var password = userInfo.substring(separatorIndex + 1);
                    return new PasswordAuthentication(username, 
password.toCharArray());
                }
                return super.getPasswordAuthentication();
            }
        };
    }

    private static HttpResponse<InputStream> 
callRemoteEndpointWithRetry(HttpClient client, String decodedUrl) throws 
Exception {
        var response = client.send(buildFetchFileRequest(decodedUrl), 
HttpResponse.BodyHandlers.ofInputStream());
        if (response.statusCode() / 100 != 2) {
            throw new IllegalStateException("INVALID CODE " + 
response.statusCode());
        }
        return response;
    }

    private static HttpRequest buildFetchFileRequest(String decodedUrl) {
        var builder = HttpRequest.newBuilder()
                .GET()
                .expectContinue(true)
                .headers("Content-Type", "multipart/form-data")
                .timeout(Duration.ofMinutes(30));
        var uri = URI.create(decodedUrl);
        var userInfo = uri.getUserInfo();
        if (userInfo != null) {
            builder.uri(URI.create(decodedUrl.replace(userInfo + "@", "")));
        } else {
            builder.uri(uri);
        }
        return builder.build();
    }

}
{code}

Could you please take a look? Any advice would be helpful. The one thing I see 
differences between the HttpResponseInputStream and the BufferedInputStream is 
that HttpResponseInputStream::available returns 0. But I'm not sure if that's 
the issue because the skip of the stream is done by just reading it.

Note: Removed the binary content from jclouds-wire.log

Thanks!

  was:
Hello,

We are observing an issue with JClouds while transferring a file with an input 
stream only.
We are opening an input stream to a remote file and we are passing the stream 
to the JClouds library, but after some time it fails with the following error:

{noformat}
Exception in thread "main" java.lang.RuntimeException: java.io.EOFException: 
reached end of stream after skipping 14933328 bytes; 67108864 bytes expected
        at com.google.common.base.Throwables.propagate(Throwables.java:241)
        at 
org.jclouds.io.internal.BasePayloadSlicer.doSlice(BasePayloadSlicer.java:253)
        at 
org.jclouds.io.internal.BasePayloadSlicer.slice(BasePayloadSlicer.java:228)
        at 
org.jclouds.blobstore.internal.BaseBlobStore.putMultipartBlob(BaseBlobStore.java:385)
        at 
org.jclouds.blobstore.internal.BaseBlobStore.putMultipartBlob(BaseBlobStore.java:349)
        at 
org.jclouds.aws.s3.blobstore.AWSS3BlobStore.putBlob(AWSS3BlobStore.java:79)
        at org.example.Main.main(Main.java:50)
Caused by: java.io.EOFException: reached end of stream after skipping 14933328 
bytes; 67108864 bytes expected
        at com.google.common.io.ByteStreams.skipFully(ByteStreams.java:807)
        at 
org.jclouds.io.internal.BasePayloadSlicer.doSlice(BasePayloadSlicer.java:251)
        ... 5 more
{noformat}

Note: The issue occurs only for larger files (more than 32 mb).
Java version: sapmachine-jdk-11.0.15.0.1.jdk
IAAS: AWS-s3

If we pass a BufferedInputStream the code works, so I guess that there is some 
issue with the processing of the stream. The stream returned by the Java Http 
Client is HttpResponseInputStream.

Code:

{code:title=Main.java|borderStyle=solid}
package org.example;

import com.google.common.collect.ImmutableSet;
import com.google.inject.Module;
import org.jclouds.ContextBuilder;
import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.BlobStoreContext;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.options.PutOptions;
import org.jclouds.logging.slf4j.config.SLF4JLoggingModule;

import java.io.BufferedInputStream;
import java.io.InputStream;
import java.net.Authenticator;
import java.net.PasswordAuthentication;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.UUID;

public class Main {

    public static void main(String[] args) throws Exception {
        Iterable<Module> modules = ImmutableSet.<Module>of(
                new SLF4JLoggingModule());
        ContextBuilder contextBuilder = ContextBuilder.newBuilder("aws-s3")
                .credentials("<IDENTITY>", "<CREDENTIAL>")
                .modules(modules);
        BlobStoreContext blobStoreContext = 
contextBuilder.buildView(BlobStoreContext.class);
        BlobStore blobStore = blobStoreContext.getBlobStore();
        String decodedUrl = "<URL>";
        HttpClient client = buildHttpClient(decodedUrl);
        HttpResponse<InputStream> response = 
callRemoteEndpointWithRetry(client, decodedUrl);
        long fileSize = response.headers()
                .firstValueAsLong("Content-Length")
                .orElseThrow(() -> new IllegalArgumentException("No 
Content-Length"));
        System.out.println("Bytes: " + fileSize);
        InputStream inputStream = response.body();

            String container = "<CONTAINER>";
            Blob blob = blobStore.blobBuilder("TEST.zip")
//                    .payload(new BufferedInputStream(inputStream, 8 * 1024))
                    .payload(inputStream)
                    .contentDisposition(UUID.randomUUID().toString())
                    .contentType("application/octet-stream")
                    .contentLength(fileSize)
                    .build();
            blobStore.putBlob(container, blob, PutOptions.Builder.multipart());
    }

    private static HttpClient buildHttpClient(String decodedUrl) {
        return HttpClient.newBuilder()
                .version(HttpClient.Version.HTTP_1_1)
                .connectTimeout(Duration.ofMinutes(60))
                .followRedirects(HttpClient.Redirect.NORMAL)
                .authenticator(buildPasswordAuthenticator(decodedUrl))
                .build();
    }

    private static Authenticator buildPasswordAuthenticator(String decodedUrl) {
        return new Authenticator() {
            @Override
            protected PasswordAuthentication getPasswordAuthentication() {
                var uri = URI.create(decodedUrl);
                var userInfo = uri.getUserInfo();
                if (userInfo != null) {
                    var separatorIndex = userInfo.indexOf(':');
                    var username = userInfo.substring(0, separatorIndex);
                    var password = userInfo.substring(separatorIndex + 1);
                    return new PasswordAuthentication(username, 
password.toCharArray());
                }
                return super.getPasswordAuthentication();
            }
        };
    }

    private static HttpResponse<InputStream> 
callRemoteEndpointWithRetry(HttpClient client, String decodedUrl) throws 
Exception {
        var response = client.send(buildFetchFileRequest(decodedUrl), 
HttpResponse.BodyHandlers.ofInputStream());
        if (response.statusCode() / 100 != 2) {
            throw new IllegalStateException("INVALID CODE " + 
response.statusCode());
        }
        return response;
    }

    private static HttpRequest buildFetchFileRequest(String decodedUrl) {
        var builder = HttpRequest.newBuilder()
                .GET()
                .expectContinue(true)
                .headers("Content-Type", "multipart/form-data")
                .timeout(Duration.ofMinutes(30));
        var uri = URI.create(decodedUrl);
        var userInfo = uri.getUserInfo();
        if (userInfo != null) {
            builder.uri(URI.create(decodedUrl.replace(userInfo + "@", "")));
        } else {
            builder.uri(uri);
        }
        return builder.build();
    }

}
{code}

Could you please take a look? Any advice would be helpful. The one thing I see 
differences between the HttpResponseInputStream and the BufferedInputStream is 
that HttpResponseInputStream::isAvailable returns 0. But I'm not sure if that's 
the issue because the skip of the stream is done by just reading it.

Note: Removed the binary content from jclouds-wire.log

Thanks!


> Caused by: java.io.EOFException: reached end of stream after skipping 
> 14933328 bytes; 67108864 bytes expected
> -------------------------------------------------------------------------------------------------------------
>
>                 Key: JCLOUDS-1623
>                 URL: https://issues.apache.org/jira/browse/JCLOUDS-1623
>             Project: jclouds
>          Issue Type: Bug
>          Components: jclouds-blobstore
>    Affects Versions: 2.5.0
>            Reporter: Ivan Dimitrov
>            Assignee: Andrew Gaul
>            Priority: Major
>              Labels: aws-s3
>         Attachments: jclouds-wire.log, jclouds.log
>
>
> Hello,
> We are observing an issue with JClouds while transferring a file with an 
> input stream only.
> We are opening an input stream to a remote file and we are passing the stream 
> to the JClouds library, but after some time it fails with the following error:
> {noformat}
> Exception in thread "main" java.lang.RuntimeException: java.io.EOFException: 
> reached end of stream after skipping 14933328 bytes; 67108864 bytes expected
>       at com.google.common.base.Throwables.propagate(Throwables.java:241)
>       at 
> org.jclouds.io.internal.BasePayloadSlicer.doSlice(BasePayloadSlicer.java:253)
>       at 
> org.jclouds.io.internal.BasePayloadSlicer.slice(BasePayloadSlicer.java:228)
>       at 
> org.jclouds.blobstore.internal.BaseBlobStore.putMultipartBlob(BaseBlobStore.java:385)
>       at 
> org.jclouds.blobstore.internal.BaseBlobStore.putMultipartBlob(BaseBlobStore.java:349)
>       at 
> org.jclouds.aws.s3.blobstore.AWSS3BlobStore.putBlob(AWSS3BlobStore.java:79)
>       at org.example.Main.main(Main.java:50)
> Caused by: java.io.EOFException: reached end of stream after skipping 
> 14933328 bytes; 67108864 bytes expected
>       at com.google.common.io.ByteStreams.skipFully(ByteStreams.java:807)
>       at 
> org.jclouds.io.internal.BasePayloadSlicer.doSlice(BasePayloadSlicer.java:251)
>       ... 5 more
> {noformat}
> Note: The issue occurs only for larger files (more than 32 mb).
> Java version: sapmachine-jdk-11.0.15.0.1.jdk
> IAAS: AWS-s3
> If we pass a BufferedInputStream the code works, so I guess that there is 
> some issue with the processing of the stream. The stream returned by the Java 
> Http Client is HttpResponseInputStream.
> Code:
> {code:title=Main.java|borderStyle=solid}
> package org.example;
> import com.google.common.collect.ImmutableSet;
> import com.google.inject.Module;
> import org.jclouds.ContextBuilder;
> import org.jclouds.blobstore.BlobStore;
> import org.jclouds.blobstore.BlobStoreContext;
> import org.jclouds.blobstore.domain.Blob;
> import org.jclouds.blobstore.options.PutOptions;
> import org.jclouds.logging.slf4j.config.SLF4JLoggingModule;
> import java.io.BufferedInputStream;
> import java.io.InputStream;
> import java.net.Authenticator;
> import java.net.PasswordAuthentication;
> import java.net.URI;
> import java.net.http.HttpClient;
> import java.net.http.HttpRequest;
> import java.net.http.HttpResponse;
> import java.time.Duration;
> import java.util.UUID;
> public class Main {
>     public static void main(String[] args) throws Exception {
>         Iterable<Module> modules = ImmutableSet.<Module>of(
>                 new SLF4JLoggingModule());
>         ContextBuilder contextBuilder = ContextBuilder.newBuilder("aws-s3")
>                 .credentials("<IDENTITY>", "<CREDENTIAL>")
>                 .modules(modules);
>         BlobStoreContext blobStoreContext = 
> contextBuilder.buildView(BlobStoreContext.class);
>         BlobStore blobStore = blobStoreContext.getBlobStore();
>         String decodedUrl = "<URL>";
>         HttpClient client = buildHttpClient(decodedUrl);
>         HttpResponse<InputStream> response = 
> callRemoteEndpointWithRetry(client, decodedUrl);
>         long fileSize = response.headers()
>                 .firstValueAsLong("Content-Length")
>                 .orElseThrow(() -> new IllegalArgumentException("No 
> Content-Length"));
>         System.out.println("Bytes: " + fileSize);
>         InputStream inputStream = response.body();
>             String container = "<CONTAINER>";
>             Blob blob = blobStore.blobBuilder("TEST.zip")
> //                    .payload(new BufferedInputStream(inputStream, 8 * 1024))
>                     .payload(inputStream)
>                     .contentDisposition(UUID.randomUUID().toString())
>                     .contentType("application/octet-stream")
>                     .contentLength(fileSize)
>                     .build();
>             blobStore.putBlob(container, blob, 
> PutOptions.Builder.multipart());
>     }
>     private static HttpClient buildHttpClient(String decodedUrl) {
>         return HttpClient.newBuilder()
>                 .version(HttpClient.Version.HTTP_1_1)
>                 .connectTimeout(Duration.ofMinutes(60))
>                 .followRedirects(HttpClient.Redirect.NORMAL)
>                 .authenticator(buildPasswordAuthenticator(decodedUrl))
>                 .build();
>     }
>     private static Authenticator buildPasswordAuthenticator(String 
> decodedUrl) {
>         return new Authenticator() {
>             @Override
>             protected PasswordAuthentication getPasswordAuthentication() {
>                 var uri = URI.create(decodedUrl);
>                 var userInfo = uri.getUserInfo();
>                 if (userInfo != null) {
>                     var separatorIndex = userInfo.indexOf(':');
>                     var username = userInfo.substring(0, separatorIndex);
>                     var password = userInfo.substring(separatorIndex + 1);
>                     return new PasswordAuthentication(username, 
> password.toCharArray());
>                 }
>                 return super.getPasswordAuthentication();
>             }
>         };
>     }
>     private static HttpResponse<InputStream> 
> callRemoteEndpointWithRetry(HttpClient client, String decodedUrl) throws 
> Exception {
>         var response = client.send(buildFetchFileRequest(decodedUrl), 
> HttpResponse.BodyHandlers.ofInputStream());
>         if (response.statusCode() / 100 != 2) {
>             throw new IllegalStateException("INVALID CODE " + 
> response.statusCode());
>         }
>         return response;
>     }
>     private static HttpRequest buildFetchFileRequest(String decodedUrl) {
>         var builder = HttpRequest.newBuilder()
>                 .GET()
>                 .expectContinue(true)
>                 .headers("Content-Type", "multipart/form-data")
>                 .timeout(Duration.ofMinutes(30));
>         var uri = URI.create(decodedUrl);
>         var userInfo = uri.getUserInfo();
>         if (userInfo != null) {
>             builder.uri(URI.create(decodedUrl.replace(userInfo + "@", "")));
>         } else {
>             builder.uri(uri);
>         }
>         return builder.build();
>     }
> }
> {code}
> Could you please take a look? Any advice would be helpful. The one thing I 
> see differences between the HttpResponseInputStream and the 
> BufferedInputStream is that HttpResponseInputStream::available returns 0. But 
> I'm not sure if that's the issue because the skip of the stream is done by 
> just reading it.
> Note: Removed the binary content from jclouds-wire.log
> Thanks!



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to