[
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)