/* * Copyright (c) 2011-2013 The original author or authors * ------------------------------------------------------ * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Apache License v2.0 which accompanies this distribution. * * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html * * The Apache License v2.0 is available at * http://www.opensource.org/licenses/apache2.0.php * * You may elect to redistribute this code under either of these licenses. */ package io.vertx.core.http.impl; import io.netty.buffer.Unpooled; import io.netty.handler.codec.http2.Http2Stream; import io.netty.util.CharsetUtil; import io.vertx.codegen.annotations.Nullable; import io.vertx.core.AsyncResult; import io.vertx.core.Context; import io.vertx.core.Future; import io.vertx.core.Handler; import io.vertx.core.MultiMap; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.StreamResetException; import io.vertx.core.net.NetSocket; import io.vertx.core.net.SocketAddress; import javax.net.ssl.SSLPeerUnverifiedException; import javax.security.cert.X509Certificate; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.charset.Charset; /** * @author <a href="mailto:julien@julienviet.com">Julien Viet</a> */ class VertxHttp2NetSocket<C extends Http2ConnectionBase> extends VertxHttp2Stream<C> implements NetSocket { private Handler<Throwable> exceptionHandler; private Handler<Void> closeHandler; private Handler<Void> endHandler; private Handler<Buffer> dataHandler; private Handler<Void> drainHandler; public VertxHttp2NetSocket(C conn, Http2Stream stream, boolean writable) { super(conn, stream, writable); } // Stream impl @Override void handleEnd(MultiMap trailers) { try { if (endHandler != null) { // Give opportunity to send a last chunk endHandler.handle(null); } } finally { end(); } } @Override void handleData(Buffer buf) { if (dataHandler != null) { dataHandler.handle(buf); } } @Override void handleReset(long errorCode) { handleException(new StreamResetException(errorCode)); } @Override void handleException(Throwable cause) { if (exceptionHandler != null) { exceptionHandler.handle(cause); } } @Override void handleClose() { if (closeHandler != null) { closeHandler.handle(null); } } @Override void handleInterestedOpsChanged() { Handler<Void> handler = this.drainHandler; if (handler != null && !writeQueueFull()) { handler.handle(null); } } // NetSocket impl @Override public NetSocket exceptionHandler(Handler<Throwable> handler) { synchronized (conn) { exceptionHandler = handler; return this; } } @Override public NetSocket handler(Handler<Buffer> handler) { synchronized (conn) { dataHandler = handler; return this; } } @Override public NetSocket pause() { return this; } @Override public NetSocket resume() { return this; } @Override public NetSocket endHandler(Handler<Void> handler) { synchronized (conn) { endHandler = handler; return this; } } @Override public NetSocket write(Buffer data) { synchronized (conn) { writeData(data.getByteBuf(), false); return this; } } @Override public NetSocket setWriteQueueMaxSize(int maxSize) { return this; } @Override public NetSocket drainHandler(Handler<Void> handler) { synchronized (conn) { drainHandler = handler; return this; } } @Override public boolean writeQueueFull() { return isNotWritable(); } @Override public String writeHandlerID() { // TODO return null; } @Override public NetSocket write(String str) { return write(str, null); } @Override public NetSocket write(String str, String enc) { synchronized (conn) { Charset cs = enc != null ? Charset.forName(enc) : CharsetUtil.UTF_8; writeData(Unpooled.copiedBuffer(str, cs), false); return this; } } @Override public NetSocket sendFile(String filename, long offset, long length) { return sendFile(filename, offset, length, null); } @Override public NetSocket sendFile(String filename, long offset, long length, Handler<AsyncResult<Void>> resultHandler) { synchronized (conn) { Context resultCtx = resultHandler != null ? vertx.getOrCreateContext() : null; File file = vertx.resolveFile(filename); if (!file.exists()) { if (resultHandler != null) { resultCtx.runOnContext((v) -> resultHandler.handle(Future.failedFuture(new FileNotFoundException()))); } else { // log.error("File not found: " + filename); } return this; } RandomAccessFile raf; try { raf = new RandomAccessFile(file, "r"); } catch (IOException e) { if (resultHandler != null) { resultCtx.runOnContext((v) -> resultHandler.handle(Future.failedFuture(e))); } else { //log.error("Failed to send file", e); } return this; } long contentLength = Math.min(length, file.length() - offset); FileStreamChannel fileChannel = new FileStreamChannel(ar -> { if (resultHandler != null) { resultCtx.runOnContext(v -> { resultHandler.handle(Future.succeededFuture()); }); } }, this, offset, contentLength); drainHandler(fileChannel.drainHandler); handlerContext.channel().eventLoop().register(fileChannel); fileChannel.pipeline().fireUserEventTriggered(raf); } return this; } @Override public SocketAddress remoteAddress() { return conn.remoteAddress(); } @Override public SocketAddress localAddress() { return conn.localAddress(); } @Override public void end() { synchronized (conn) { writeData(Unpooled.EMPTY_BUFFER, true); } } @Override public void end(Buffer buffer) { synchronized (conn) { writeData(buffer.getByteBuf(), true); } } @Override public void close() { end(); } @Override public NetSocket closeHandler(@Nullable Handler<Void> handler) { synchronized (conn) { closeHandler = handler; return this; } } @Override public NetSocket upgradeToSsl(Handler<Void> handler) { throw new UnsupportedOperationException("Cannot upgrade HTTP/2 stream to SSL"); } @Override public NetSocket upgradeToSsl(String serverName, Handler<Void> handler) { throw new UnsupportedOperationException("Cannot upgrade HTTP/2 stream to SSL"); } @Override public boolean isSsl() { return conn.isSsl(); } @Override public X509Certificate[] peerCertificateChain() throws SSLPeerUnverifiedException { return conn.peerCertificateChain(); } @Override public String indicatedServerName() { return conn.indicatedServerName(); } }