
co.easimart.vertx.http.HttpServerMultipartRequest Maven / Gradle / Ivy
package co.easimart.vertx.http;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.security.cert.X509Certificate;
import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.DecoderResult;
import io.netty.handler.codec.http.DefaultHttpContent;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.multipart.Attribute;
import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory;
import io.netty.handler.codec.http.multipart.FileUpload;
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder;
import io.netty.handler.codec.http.multipart.InterfaceHttpData;
import io.netty.handler.codec.http.multipart.MemoryAttribute;
import io.vertx.core.Handler;
import io.vertx.core.MultiMap;
import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpServerFileUpload;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.http.HttpVersion;
import io.vertx.core.http.ServerWebSocket;
import io.vertx.core.net.NetSocket;
import io.vertx.core.net.SocketAddress;
/**
* HttpServerRequest wrapper which detects multipart http request and fire event
* for each part received without reading the whole http request.
*/
public abstract class HttpServerMultipartRequest implements HttpServerRequest {
private final Vertx vertx;
private HttpServerRequest request;
private HttpPostRequestDecoder decoder;
private Handler dataHandler;
private Handler uploadHandler;
private Handler multipartHandler;
private Handler exceptionHandler;
public HttpServerMultipartRequest(Vertx vertx, HttpServerRequest request) {
this.vertx = vertx;
this.request = request;
// Check if the request is really multipart request
if (isMultipartRequest(request)) {
this.decoder = new HttpPostRequestDecoder(
new DataFactory(),
new NettyHttpRequest()
);
} else {
throw new IllegalArgumentException("Input request is not multipart request");
}
this.request.handler(buffer -> {
try {
// Feed data to decoder
if (this.decoder != null) {
this.decoder.offer(new DefaultHttpContent(buffer.getByteBuf().duplicate()));
}
// Pass on the data to real data handler
if (this.dataHandler != null) {
this.dataHandler.handle(buffer);
}
} catch (Throwable t) {
notifyExceptionHandler(t);
}
});
}
public static boolean isMultipartRequest(HttpServerRequest request) {
String contentType = request.getHeader("Content-Type");
if (contentType != null) {
HttpMethod method = request.method();
String lowerCaseContentType = contentType.toLowerCase();
boolean isURLEncoded = lowerCaseContentType.startsWith("application/x-www-form-urlencoded");
return (lowerCaseContentType.startsWith("multipart/form-data") || isURLEncoded)
&& (method.equals(HttpMethod.POST) || method.equals(HttpMethod.PUT) || method.equals(HttpMethod.PATCH) || method.equals(HttpMethod.DELETE));
}
return false;
}
private void notifyExceptionHandler(Throwable t) {
if (this.exceptionHandler != null) {
this.exceptionHandler.handle(t);
}
}
public HttpServerRequest multipartHandler(Handler handler) {
this.multipartHandler = handler;
return this;
}
@Override
public HttpServerRequest exceptionHandler(Handler handler) {
this.exceptionHandler = handler;
this.request.exceptionHandler(handler);
return this;
}
@Override
public HttpServerRequest handler(Handler handler) {
this.dataHandler = handler;
return this;
}
@Override
public HttpServerRequest pause() {
this.request.pause();
return this;
}
@Override
public HttpServerRequest resume() {
this.request.resume();
return this;
}
@Override
public HttpServerRequest endHandler(Handler handler) {
this.request.endHandler(handler);
return this;
}
@Override
public HttpVersion version() {
return this.request.version();
}
@Override
public HttpMethod method() {
return this.request.method();
}
@Override
public String uri() {
return this.request.uri();
}
@Override
public String path() {
return this.request.path();
}
@Override
public String query() {
return this.request.query();
}
@Override
public HttpServerResponse response() {
return this.request.response();
}
@Override
public MultiMap headers() {
return this.request.headers();
}
@Override
public String getHeader(String s) {
return this.request.getHeader(s);
}
@Override
public String getHeader(CharSequence charSequence) {
return this.request.getHeader(charSequence);
}
@Override
public MultiMap params() {
return this.request.params();
}
@Override
public String getParam(String s) {
return this.request.getParam(s);
}
@Override
public SocketAddress remoteAddress() {
return this.request.remoteAddress();
}
@Override
public SocketAddress localAddress() {
return this.request.localAddress();
}
@Override
public X509Certificate[] peerCertificateChain() throws SSLPeerUnverifiedException {
return this.request.peerCertificateChain();
}
@Override
public String absoluteURI() {
return this.request.absoluteURI();
}
@Override
public HttpServerRequest bodyHandler(Handler handler) {
this.request.bodyHandler(handler);
return this;
}
@Override
public NetSocket netSocket() {
return this.request.netSocket();
}
@Override
public HttpServerRequest setExpectMultipart(boolean b) {
throw new UnsupportedOperationException("Method setExpectMultipart() is not supported. It always expects multipart.");
}
@Override
public boolean isExpectMultipart() {
return true;
}
@Override
public HttpServerRequest uploadHandler(Handler handler) {
this.uploadHandler = handler;
return this;
}
@Override
public MultiMap formAttributes() {
return this.request.formAttributes();
}
@Override
public String getFormAttribute(String s) {
return this.request.getFormAttribute(s);
}
@Override
public ServerWebSocket upgrade() {
return this.request.upgrade();
}
@Override
public boolean isEnded() {
return this.request.isEnded();
}
private class DataFactory extends DefaultHttpDataFactory {
DataFactory() {
super(false);
}
@Override
public Attribute createAttribute(HttpRequest req, String name) {
// Handling normal attribute in memory
return new MemoryAttribute(name) {
@Override
public void addContent(ByteBuf buffer, boolean last) throws IOException {
super.addContent(buffer, last);
if (last) {
// Add new attribute to form attributes
request.formAttributes().add(this.getName(), this.getValue());
if (multipartHandler != null) {
multipartHandler.handle(this);
}
}
}
};
}
@Override
public FileUpload createFileUpload(HttpRequest req,
String name,
String filename,
String contentType,
String contentTransferEncoding,
Charset charset,
long size) {
StreamFileUpload upload = new StreamFileUpload(
vertx, request, name, filename, contentType, contentTransferEncoding, charset, size
);
if (uploadHandler != null) {
uploadHandler.handle(upload);
}
return upload;
}
}
private class NettyHttpRequest implements HttpRequest {
public NettyHttpRequest() {
}
@Override
public io.netty.handler.codec.http.HttpMethod getMethod() {
throw new UnsupportedOperationException("Method getMethod() is not yet implemented");
}
@Override
public io.netty.handler.codec.http.HttpMethod method() {
return null;
}
@Override
public HttpRequest setMethod(io.netty.handler.codec.http.HttpMethod httpMethod) {
throw new UnsupportedOperationException("Method setMethod() is not yet implemented");
}
@Override
public String getUri() {
throw new UnsupportedOperationException("Method getUri() is not yet implemented");
}
@Override
public String uri() {
return null;
}
@Override
public HttpRequest setUri(String s) {
throw new UnsupportedOperationException("Method setUri() is not yet implemented");
}
@Override
public io.netty.handler.codec.http.HttpVersion getProtocolVersion() {
throw new UnsupportedOperationException("Method getProtocolVersion() is not yet implemented");
}
@Override
public io.netty.handler.codec.http.HttpVersion protocolVersion() {
return null;
}
@Override
public HttpRequest setProtocolVersion(io.netty.handler.codec.http.HttpVersion httpVersion) {
throw new UnsupportedOperationException("Method setProtocolVersion() is not yet implemented");
}
@Override
public HttpHeaders headers() {
return new HttpHeaders() {
@Override
public String get(String s) {
return request.getHeader(s);
}
@Override
public Integer getInt(CharSequence name) {
return null;
}
@Override
public int getInt(CharSequence name, int defaultValue) {
return 0;
}
@Override
public Short getShort(CharSequence name) {
return null;
}
@Override
public short getShort(CharSequence name, short defaultValue) {
return 0;
}
@Override
public Long getTimeMillis(CharSequence name) {
return null;
}
@Override
public long getTimeMillis(CharSequence name, long defaultValue) {
return 0;
}
@Override
public List getAll(String s) {
throw new UnsupportedOperationException("Method getAll() is not yet implemented");
}
@Override
public List> entries() {
throw new UnsupportedOperationException("Method entries() is not yet implemented");
}
@Override
public boolean contains(String s) {
return request.headers().contains(s);
}
@Override
public boolean isEmpty() {
throw new UnsupportedOperationException("Method isEmpty() is not yet implemented");
}
@Override
public int size() {
return 0;
}
@Override
public Set names() {
throw new UnsupportedOperationException("Method names() is not yet implemented");
}
@Override
public HttpHeaders add(String s, Object o) {
throw new UnsupportedOperationException("Method add() is not yet implemented");
}
@Override
public HttpHeaders add(String s, Iterable> iterable) {
throw new UnsupportedOperationException("Method add() is not yet implemented");
}
@Override
public HttpHeaders addInt(CharSequence name, int value) {
return null;
}
@Override
public HttpHeaders addShort(CharSequence name, short value) {
return null;
}
@Override
public HttpHeaders set(String s, Object o) {
throw new UnsupportedOperationException("Method set() is not yet implemented");
}
@Override
public HttpHeaders set(String s, Iterable> iterable) {
throw new UnsupportedOperationException("Method set() is not yet implemented");
}
@Override
public HttpHeaders setInt(CharSequence name, int value) {
return null;
}
@Override
public HttpHeaders setShort(CharSequence name, short value) {
return null;
}
@Override
public HttpHeaders remove(String s) {
throw new UnsupportedOperationException("Method remove() is not yet implemented");
}
@Override
public HttpHeaders clear() {
throw new UnsupportedOperationException("Method clear() is not yet implemented");
}
@Override
public Iterator> iterator() {
throw new UnsupportedOperationException("Method iterator() is not yet implemented");
}
@Override
public Iterator> iteratorCharSequence() {
return null;
}
};
}
@Override
public DecoderResult getDecoderResult() {
throw new UnsupportedOperationException("Method getDecoderResult() is not yet implemented");
}
@Override
public DecoderResult decoderResult() {
return null;
}
@Override
public void setDecoderResult(DecoderResult decoderResult) {
throw new UnsupportedOperationException("Method setDecoderResult() is not yet implemented");
}
}
private static final class StreamFileUpload implements FileUpload, HttpServerFileUpload {
private final Vertx vertx;
private final HttpServerRequest request;
private final String name;
private String contentType;
private String filename;
private String contentTransferEncoding;
private Charset charset;
private long size;
private boolean completed;
private Handler dataHandler;
private Handler endHandler;
private Handler exceptionHandler;
private boolean paused;
private Buffer pauseBuff;
private boolean lazyCalculateSize;
StreamFileUpload(Vertx vertx, HttpServerRequest request, String name, String filename, String contentType, String contentTransferEncoding, Charset charset, long size) {
this.vertx = vertx;
this.request = request;
this.name = name;
this.setFilename(filename);
this.setContentType(contentType);
this.setContentTransferEncoding(contentTransferEncoding);
this.setCharset(charset);
this.size = size;
if (size == 0L) {
this.lazyCalculateSize = true;
}
}
@Override
public String getFilename() {
return this.filename;
}
@Override
public void setFilename(String s) {
this.filename = s;
}
@Override
public void setContentType(String s) {
this.contentType = s;
}
@Override
public String getContentType() {
return this.contentType;
}
@Override
public void setContentTransferEncoding(String s) {
this.contentTransferEncoding = s;
}
@Override
public String getContentTransferEncoding() {
return this.contentTransferEncoding;
}
@Override
public long getMaxSize() {
return 0;
}
@Override
public void setMaxSize(long maxSize) {
}
@Override
public void checkSize(long newSize) throws IOException {
}
@Override
public void setContent(ByteBuf byteBuf) throws IOException {
Buffer data = Buffer.buffer(byteBuf);
this.size = data.length();
this.completed = true;
this.handleData(data);
this.handleComplete();
}
@Override
public void addContent(ByteBuf byteBuf, boolean last) throws IOException {
Buffer data = Buffer.buffer(byteBuf);
if (!this.completed) {
this.completed = last;
}
if (data.length() != 0) {
if (this.lazyCalculateSize) {
this.size += (long) data.length();
}
this.handleData(data);
}
if (this.completed) {
this.handleComplete();
}
}
@Override
public void setContent(File file) throws IOException {
throw new UnsupportedOperationException("Method setContent() is not yet implemented");
}
@Override
public void setContent(InputStream inputStream) throws IOException {
throw new UnsupportedOperationException("Method setContent() is not yet implemented");
}
@Override
public boolean isCompleted() {
return this.completed;
}
@Override
public long length() {
return this.size();
}
@Override
public long definedLength() {
return 0;
}
@Override
public void delete() {
throw new UnsupportedOperationException("Method delete() is not yet implemented");
}
@Override
public byte[] get() throws IOException {
throw new UnsupportedOperationException("Method get() is not yet implemented");
}
@Override
public ByteBuf getByteBuf() throws IOException {
throw new UnsupportedOperationException("Method getByteBuf() is not yet implemented");
}
@Override
public ByteBuf getChunk(int i) throws IOException {
throw new UnsupportedOperationException("Method getChunk() is not yet implemented");
}
@Override
public String getString() throws IOException {
throw new UnsupportedOperationException("Method getString() is not yet implemented");
}
@Override
public String getString(Charset charset) throws IOException {
throw new UnsupportedOperationException("Method getString() is not yet implemented");
}
@Override
public void setCharset(Charset charset) {
this.charset = charset;
}
@Override
public Charset getCharset() {
return this.charset;
}
@Override
public boolean renameTo(File file) throws IOException {
throw new UnsupportedOperationException("Method renameTo() is not yet implemented");
}
@Override
public boolean isInMemory() {
return true;
}
@Override
public File getFile() throws IOException {
throw new UnsupportedOperationException("Method getFile() is not yet implemented");
}
@Override
public ByteBuf content() {
throw new UnsupportedOperationException("Method content() is not yet implemented");
}
@Override
public FileUpload copy() {
throw new UnsupportedOperationException("Method copy() is not yet implemented");
}
@Override
public FileUpload duplicate() {
throw new UnsupportedOperationException("Method duplicate() is not yet implemented");
}
@Override
public FileUpload retainedDuplicate() {
return null;
}
@Override
public FileUpload replace(ByteBuf content) {
return null;
}
@Override
public int refCnt() {
return 1;
}
@Override
public FileUpload retain() {
return this;
}
@Override
public FileUpload retain(int i) {
return this;
}
@Override
public FileUpload touch() {
return null;
}
@Override
public FileUpload touch(Object hint) {
return null;
}
@Override
public boolean release() {
return false;
}
@Override
public boolean release(int i) {
return false;
}
@Override
public String getName() {
return this.name;
}
@Override
public HttpDataType getHttpDataType() {
return HttpDataType.FileUpload;
}
@Override
public int compareTo(@SuppressWarnings("NullableProblems") InterfaceHttpData o) {
return 0;
}
/// ------------------ HttpServerFileUpload
@Override
public HttpServerFileUpload exceptionHandler(Handler handler) {
this.exceptionHandler = handler;
return this;
}
@Override
public HttpServerFileUpload handler(Handler handler) {
this.dataHandler = handler;
return this;
}
@Override
public HttpServerFileUpload endHandler(Handler handler) {
this.endHandler = handler;
return this;
}
@Override
public HttpServerFileUpload pause() {
this.request.pause();
this.paused = true;
return this;
}
@Override
public HttpServerFileUpload resume() {
if (this.paused) {
this.paused = false;
Buffer pb = pauseBuff;
this.pauseBuff = null;
boolean cmp = completed;
if (pb != null || cmp) {
vertx.runOnContext(x -> {
if (pb != null) handleData(pb);
if (cmp) this.handleComplete();
});
}
this.request.resume();
}
return this;
}
@Override
public HttpServerFileUpload streamToFileSystem(String s) {
throw new UnsupportedOperationException("Method streamToFileSystem() is not yet implemented");
}
@Override
public String filename() {
return this.filename;
}
@Override
public String name() {
return this.name;
}
@Override
public String contentType() {
return this.contentType;
}
@Override
public String contentTransferEncoding() {
return this.contentTransferEncoding;
}
@Override
public String charset() {
return this.charset.toString();
}
@Override
public long size() {
return this.size;
}
@Override
public boolean isSizeAvailable() {
return !this.lazyCalculateSize;
}
void handleData(Buffer data) {
if (!this.paused) {
if (this.dataHandler != null) {
try {
this.dataHandler.handle(data);
} catch (Throwable t) {
this.notifyExceptionHandler(t);
}
}
} else {
if (this.pauseBuff == null) {
this.pauseBuff = Buffer.buffer();
}
this.pauseBuff.appendBuffer(data);
}
}
private void handleComplete() {
if (!this.paused) {
this.lazyCalculateSize = false;
this.notifyEndHandler();
}
}
private void notifyExceptionHandler(Throwable t) {
if (this.exceptionHandler != null) {
this.exceptionHandler.handle(t);
}
}
private void notifyEndHandler() {
if (this.endHandler != null) {
this.endHandler.handle(null);
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy