blob: cc147c166e184a4d55b99070cac90d53232700ca [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.servicecomb.foundation.vertx.stream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.CompletableFuture;
import javax.servlet.http.Part;
import org.apache.commons.io.IOUtils;
import org.apache.servicecomb.foundation.vertx.http.DownloadUtils;
import org.apache.servicecomb.foundation.vertx.http.ReadStreamPart;
import io.vertx.core.Context;
import io.vertx.core.Handler;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.streams.ReadStream;
import io.vertx.core.streams.WriteStream;
public class PumpFromPart {
private Context context;
private Part part;
public PumpFromPart(Context context, Part part) {
this.context = context;
this.part = part;
}
private CompletableFuture<ReadStream<Buffer>> prepareReadStream() {
CompletableFuture<ReadStream<Buffer>> future = new CompletableFuture<>();
if (ReadStreamPart.class.isInstance(part)) {
future.complete(((ReadStreamPart) part).getReadStream());
return future;
}
try {
InputStream inputStream = part.getInputStream();
InputStreamToReadStream inputStreamToReadStream = new InputStreamToReadStream(context, inputStream, true);
inputStreamToReadStream.pause();
future.complete(inputStreamToReadStream);
} catch (IOException e) {
future.completeExceptionally(e);
}
return future;
}
public CompletableFuture<Void> toWriteStream(WriteStream<Buffer> writeStream, Handler<Throwable> throwableHandler) {
return prepareReadStream()
.thenCompose(readStream -> new PumpCommon().pump(context, readStream, writeStream, throwableHandler))
.whenComplete((v, e) -> {
DownloadUtils.clearPartResource(part);
// PumpImpl will add drainHandler to writeStream,
// in order to support write multiple files to same writeStream,
// need reset after one stream is successful.
writeStream.drainHandler(null);
});
}
public CompletableFuture<Void> toOutputStream(OutputStream outputStream, boolean autoCloseOutputStream) {
if (context == null) {
return toOutputStreamSync(outputStream, autoCloseOutputStream);
}
return toOutputStreamAsync(outputStream, autoCloseOutputStream);
}
private CompletableFuture<Void> toOutputStreamAsync(OutputStream outputStream, boolean autoCloseOutputStream) {
OutputStreamToWriteStream outputStreamToWriteStream = new OutputStreamToWriteStream(context, outputStream,
autoCloseOutputStream);
return toWriteStream(outputStreamToWriteStream, null);
}
// DO NOT use a mocked sync context to unify the pump logic
// otherwise when pump big stream, will cause stack overflow
private CompletableFuture<Void> toOutputStreamSync(OutputStream outputStream, boolean autoCloseOutputStream) {
CompletableFuture<Void> future = new CompletableFuture<>();
try (InputStream inputStream = part.getInputStream()) {
IOUtils.copyLarge(inputStream, outputStream);
} catch (Throwable e) {
future.completeExceptionally(e);
}
if (autoCloseOutputStream) {
try {
outputStream.close();
} catch (Throwable e) {
future.completeExceptionally(e);
}
}
future.complete(null);
return future;
}
}