/* * Copyright 2016 The Simple File Server Authors * * Licensed 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.sfs; import com.google.common.base.Preconditions; import io.vertx.core.AsyncResult; import io.vertx.core.Handler; import io.vertx.core.MultiMap; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpConnection; import io.vertx.core.http.HttpFrame; 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; import org.sfs.auth.UserAndRole; import org.sfs.rx.ObservableFuture; import org.sfs.rx.RxHelper; import org.sfs.util.KeepAliveHttpServerResponse; import rx.Observable; import javax.net.ssl.SSLPeerUnverifiedException; import javax.security.cert.X509Certificate; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static org.sfs.util.SfsHttpHeaders.X_SFS_KEEP_ALIVE_TIMEOUT; public class SfsRequest implements HttpServerRequest { private final HttpServerRequest httpServerRequest; private final VertxContext<Server> vertxContext; private Map<String, Object> context; private boolean wroteData = false; private KeepAliveHttpServerResponse keepAliveHttpServerResponse; private ResponseWrapper responseWrapper; private UserAndRole userAndRole; public SfsRequest(VertxContext<Server> vertxContext, HttpServerRequest httpServerRequest) { this.vertxContext = vertxContext; this.httpServerRequest = httpServerRequest; this.responseWrapper = new ResponseWrapper(this); } public SfsRequest startProxyKeepAlive() { MultiMap headers = httpServerRequest.headers(); long keepAliveTimeout = Long.parseLong(headers.contains(X_SFS_KEEP_ALIVE_TIMEOUT) ? headers.get(X_SFS_KEEP_ALIVE_TIMEOUT) : "10000"); startProxyKeepAlive(keepAliveTimeout, MILLISECONDS); return this; } public SfsRequest startProxyKeepAlive(long timeout, TimeUnit timeUnit) { Preconditions.checkState(!wroteData, "HttpServerResponse has already been used"); if (keepAliveHttpServerResponse == null) { keepAliveHttpServerResponse = new KeepAliveHttpServerResponse(vertxContext(), timeout, timeUnit, httpServerRequest.response()); } return this; } public Observable<Void> stopKeepAlive() { if (keepAliveHttpServerResponse != null) { ObservableFuture<Void> handler = RxHelper.observableFuture(); keepAliveHttpServerResponse.stopKeepAlive(handler); return handler; } else { return Observable.just(null); } } public UserAndRole getUserAndRole() { return userAndRole; } public void setUserAndRole(UserAndRole userAndRole) { this.userAndRole = userAndRole; } public boolean proxyKeepAliveStarted() { return keepAliveHttpServerResponse != null; } public VertxContext<Server> vertxContext() { return vertxContext; } public Map<String, Object> context() { if (context == null) { context = new HashMap<>(); } return context; } @Override public HttpVersion version() { return httpServerRequest.version(); } @Override public HttpMethod method() { return httpServerRequest.method(); } @Override public String uri() { return httpServerRequest.uri(); } @Override public String path() { return httpServerRequest.path(); } @Override public String query() { return httpServerRequest.query(); } @Override public HttpServerResponse response() { if (keepAliveHttpServerResponse != null) { return keepAliveHttpServerResponse; } return responseWrapper; } @Override public MultiMap headers() { return httpServerRequest.headers(); } @Override public MultiMap params() { return httpServerRequest.params(); } @Override public SocketAddress remoteAddress() { return httpServerRequest.remoteAddress(); } @Override public SocketAddress localAddress() { return httpServerRequest.localAddress(); } @Override public X509Certificate[] peerCertificateChain() throws SSLPeerUnverifiedException { return httpServerRequest.peerCertificateChain(); } @Override public String absoluteURI() { return httpServerRequest.absoluteURI(); } @Override public SfsRequest bodyHandler(Handler<Buffer> bodyHandler) { httpServerRequest.bodyHandler(bodyHandler); return this; } @Override public NetSocket netSocket() { return httpServerRequest.netSocket(); } @Override public SfsRequest setExpectMultipart(boolean expect) { httpServerRequest.setExpectMultipart(expect); return this; } @Override public SfsRequest uploadHandler(Handler<HttpServerFileUpload> uploadHandler) { httpServerRequest.uploadHandler(uploadHandler); return this; } @Override public MultiMap formAttributes() { return httpServerRequest.formAttributes(); } @Override public SfsRequest endHandler(Handler<Void> endHandler) { httpServerRequest.endHandler(endHandler); return this; } @Override public SfsRequest handler(Handler<Buffer> handler) { httpServerRequest.handler(handler); return this; } @Override public SfsRequest pause() { httpServerRequest.pause(); return this; } @Override public SfsRequest resume() { httpServerRequest.resume(); return this; } @Override public SfsRequest exceptionHandler(Handler<Throwable> handler) { httpServerRequest.exceptionHandler(handler); return this; } @Override public String rawMethod() { return httpServerRequest.rawMethod(); } @Override public boolean isSSL() { return httpServerRequest.isSSL(); } @Override public String scheme() { return httpServerRequest.scheme(); } @Override public String host() { return httpServerRequest.host(); } @Override public String getHeader(String headerName) { return httpServerRequest.getHeader(headerName); } @Override public String getHeader(CharSequence headerName) { return httpServerRequest.getHeader(headerName); } @Override public String getParam(String paramName) { return httpServerRequest.getParam(paramName); } @Override public boolean isExpectMultipart() { return httpServerRequest.isExpectMultipart(); } @Override public String getFormAttribute(String attributeName) { return httpServerRequest.getFormAttribute(attributeName); } @Override public ServerWebSocket upgrade() { return httpServerRequest.upgrade(); } @Override public boolean isEnded() { return httpServerRequest.isEnded(); } @Override public HttpServerRequest customFrameHandler(Handler<HttpFrame> handler) { return httpServerRequest.customFrameHandler(handler); } @Override public HttpConnection connection() { return httpServerRequest.connection(); } private static class ResponseWrapper implements HttpServerResponse { private final SfsRequest httpServerRequest; private final HttpServerResponse response; public ResponseWrapper(SfsRequest request) { this.httpServerRequest = request; this.response = request.httpServerRequest.response(); } @Override public int getStatusCode() { return response.getStatusCode(); } @Override public HttpServerResponse setStatusCode(int statusCode) { return response.setStatusCode(statusCode); } @Override public String getStatusMessage() { return response.getStatusMessage(); } @Override public HttpServerResponse setStatusMessage(String statusMessage) { response.setStatusMessage(statusMessage); return this; } @Override public HttpServerResponse setChunked(boolean chunked) { response.setChunked(chunked); return this; } @Override public boolean isChunked() { return response.isChunked(); } @Override public MultiMap headers() { return response.headers(); } @Override public HttpServerResponse putHeader(String name, String value) { response.putHeader(name, value); return this; } @Override public HttpServerResponse putHeader(CharSequence name, CharSequence value) { response.putHeader(name, value); return this; } @Override public HttpServerResponse putHeader(String name, Iterable<String> values) { response.putHeader(name, values); return this; } @Override public HttpServerResponse putHeader(CharSequence name, Iterable<CharSequence> values) { response.putHeader(name, values); return this; } @Override public MultiMap trailers() { return response.trailers(); } @Override public HttpServerResponse putTrailer(String name, String value) { response.putTrailer(name, value); return this; } @Override public HttpServerResponse putTrailer(CharSequence name, CharSequence value) { response.putTrailer(name, value); return this; } @Override public HttpServerResponse putTrailer(String name, Iterable<String> values) { response.putTrailer(name, values); return this; } @Override public HttpServerResponse putTrailer(CharSequence name, Iterable<CharSequence> value) { response.putTrailer(name, value); return this; } @Override public HttpServerResponse closeHandler(Handler<Void> handler) { response.closeHandler(handler); return this; } @Override public HttpServerResponse write(Buffer chunk) { httpServerRequest.wroteData = true; response.write(chunk); return this; } @Override public HttpServerResponse write(String chunk, String enc) { httpServerRequest.wroteData = true; response.write(chunk, enc); return this; } @Override public void end(String chunk) { httpServerRequest.wroteData = true; response.end(chunk); } @Override public HttpServerResponse write(String chunk) { httpServerRequest.wroteData = true; response.write(chunk); return this; } @Override public void end(String chunk, String enc) { httpServerRequest.wroteData = true; response.end(chunk, enc); } @Override public void end(Buffer chunk) { httpServerRequest.wroteData = true; response.end(chunk); } @Override public void end() { httpServerRequest.wroteData = true; response.end(); } @Override public HttpServerResponse sendFile(String filename) { httpServerRequest.wroteData = true; response.sendFile(filename); return this; } @Override public HttpServerResponse sendFile(String filename, long offset) { httpServerRequest.wroteData = true; response.sendFile(filename, offset); return this; } @Override public HttpServerResponse sendFile(String filename, long offset, long length) { httpServerRequest.wroteData = true; response.sendFile(filename, offset, length); return this; } @Override public HttpServerResponse sendFile(String filename, Handler<AsyncResult<Void>> resultHandler) { httpServerRequest.wroteData = true; response.sendFile(filename, resultHandler); return this; } @Override public HttpServerResponse sendFile(String filename, long offset, Handler<AsyncResult<Void>> resultHandler) { httpServerRequest.wroteData = true; response.sendFile(filename, offset, resultHandler); return this; } @Override public HttpServerResponse sendFile(String filename, long offset, long length, Handler<AsyncResult<Void>> resultHandler) { httpServerRequest.wroteData = true; response.sendFile(filename, offset, length, resultHandler); return this; } @Override public void close() { response.close(); } @Override public HttpServerResponse exceptionHandler(Handler<Throwable> handler) { response.exceptionHandler(handler); return this; } @Override public HttpServerResponse setWriteQueueMaxSize(int maxSize) { return response.setWriteQueueMaxSize(maxSize); } @Override public boolean writeQueueFull() { return response.writeQueueFull(); } @Override public HttpServerResponse drainHandler(Handler<Void> handler) { response.drainHandler(handler); return this; } @Override public HttpServerResponse writeContinue() { httpServerRequest.wroteData = true; response.writeContinue(); return this; } @Override public boolean ended() { return response.ended(); } @Override public boolean closed() { return response.closed(); } @Override public boolean headWritten() { return response.headWritten(); } @Override public HttpServerResponse headersEndHandler(Handler<Void> handler) { response.headersEndHandler(handler); return this; } @Override public HttpServerResponse bodyEndHandler(Handler<Void> handler) { response.bodyEndHandler(handler); return this; } @Override public long bytesWritten() { return response.bytesWritten(); } @Override public int streamId() { return response.streamId(); } @Override public HttpServerResponse push(HttpMethod method, String host, String path, Handler<AsyncResult<HttpServerResponse>> handler) { httpServerRequest.wroteData = true; response.push(method, host, path, handler); return this; } @Override public HttpServerResponse push(HttpMethod method, String path, MultiMap headers, Handler<AsyncResult<HttpServerResponse>> handler) { httpServerRequest.wroteData = true; response.push(method, path, headers, handler); return this; } @Override public HttpServerResponse push(HttpMethod method, String path, Handler<AsyncResult<HttpServerResponse>> handler) { httpServerRequest.wroteData = true; response.push(method, path, handler); return this; } @Override public HttpServerResponse push(HttpMethod method, String host, String path, MultiMap headers, Handler<AsyncResult<HttpServerResponse>> handler) { httpServerRequest.wroteData = true; response.push(method, host, path, headers, handler); return this; } @Override public void reset(long code) { response.reset(code); } @Override public HttpServerResponse writeCustomFrame(int type, int flags, Buffer payload) { httpServerRequest.wroteData = true; response.writeCustomFrame(type, flags, payload); return this; } } }