You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2022/10/12 10:12:55 UTC

[tomcat] 02/03: Add initial Loom implementation

This is an automated email from the ASF dual-hosted git repository.

markt pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit ebbe5f1d16c8ee345b3334abc3cd2c20d61171f4
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Wed Oct 12 10:49:31 2022 +0100

    Add initial Loom implementation
    
    Supports basic HTTP requests. No TLS, no async.
---
 .../coyote/http11/Http11BioLoomProtocol.java       |  33 +-
 ...oLoomProtocol.java => Http11LoomProcessor.java} |  17 +-
 .../apache/tomcat/util/net/BioLoomEndpoint.java    | 455 +++++++++++++++++++++
 3 files changed, 486 insertions(+), 19 deletions(-)

diff --git a/modules/loom/src/main/java/org/apache/coyote/http11/Http11BioLoomProtocol.java b/modules/loom/src/main/java/org/apache/coyote/http11/Http11BioLoomProtocol.java
index 9b6efddac6..e3c8acf1e1 100644
--- a/modules/loom/src/main/java/org/apache/coyote/http11/Http11BioLoomProtocol.java
+++ b/modules/loom/src/main/java/org/apache/coyote/http11/Http11BioLoomProtocol.java
@@ -16,13 +16,27 @@
  */
 package org.apache.coyote.http11;
 
-/**
- * Dummy implementation for now that just extends the standard NIO
- * implementation.
- */
-public class Http11BioLoomProtocol extends Http11NioProtocol {
+import java.net.Socket;
+
+import org.apache.coyote.Processor;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.net.BioLoomEndpoint;
+
+public class Http11BioLoomProtocol extends AbstractHttp11Protocol<Socket> {
+
+    private static final Log log = LogFactory.getLog(Http11BioLoomProtocol.class);
+
+    public Http11BioLoomProtocol() {
+        super(new BioLoomEndpoint());
+    }
+
+
+    @Override
+    protected Log getLog() {
+        return log;
+    }
 
-    // ----------------------------------------------------- JMX related methods
 
     @Override
     protected String getNamePrefix() {
@@ -32,4 +46,11 @@ public class Http11BioLoomProtocol extends Http11NioProtocol {
             return "http-bio-loom";
         }
     }
+
+
+    @Override
+    protected Processor createProcessor() {
+        Http11Processor processor = new Http11LoomProcessor(this, adapter);
+        return processor;
+    }
 }
diff --git a/modules/loom/src/main/java/org/apache/coyote/http11/Http11BioLoomProtocol.java b/modules/loom/src/main/java/org/apache/coyote/http11/Http11LoomProcessor.java
similarity index 65%
copy from modules/loom/src/main/java/org/apache/coyote/http11/Http11BioLoomProtocol.java
copy to modules/loom/src/main/java/org/apache/coyote/http11/Http11LoomProcessor.java
index 9b6efddac6..cc1e5de4d0 100644
--- a/modules/loom/src/main/java/org/apache/coyote/http11/Http11BioLoomProtocol.java
+++ b/modules/loom/src/main/java/org/apache/coyote/http11/Http11LoomProcessor.java
@@ -16,20 +16,11 @@
  */
 package org.apache.coyote.http11;
 
-/**
- * Dummy implementation for now that just extends the standard NIO
- * implementation.
- */
-public class Http11BioLoomProtocol extends Http11NioProtocol {
+import org.apache.coyote.Adapter;
 
-    // ----------------------------------------------------- JMX related methods
+public class Http11LoomProcessor extends Http11Processor {
 
-    @Override
-    protected String getNamePrefix() {
-        if (isSSLEnabled()) {
-            return "https-" + getSslImplementationShortName()+ "-bio-loom";
-        } else {
-            return "http-bio-loom";
-        }
+    public Http11LoomProcessor(AbstractHttp11Protocol<?> protocol, Adapter adapter) {
+        super(protocol, adapter);
     }
 }
diff --git a/modules/loom/src/main/java/org/apache/tomcat/util/net/BioLoomEndpoint.java b/modules/loom/src/main/java/org/apache/tomcat/util/net/BioLoomEndpoint.java
new file mode 100644
index 0000000000..3047253a4c
--- /dev/null
+++ b/modules/loom/src/main/java/org/apache/tomcat/util/net/BioLoomEndpoint.java
@@ -0,0 +1,455 @@
+/*
+ * 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.tomcat.util.net;
+
+import java.io.IOException;
+import java.net.BindException;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.nio.ByteBuffer;
+import java.nio.channels.CompletionHandler;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+import javax.net.ServerSocketFactory;
+
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.ExceptionUtils;
+
+public class BioLoomEndpoint extends AbstractEndpoint<Socket, Socket> {
+
+    private static final Log log = LogFactory.getLog(BioLoomEndpoint.class);
+    //private static final Log logHandshake = LogFactory.getLog(JioLoomEndpoint.class.getName() + ".handshake");
+
+    private volatile ServerSocket serverSocket = null;
+
+    private SocketAddress previousAcceptedSocketRemoteAddress = null;
+    private long previousAcceptedSocketNanoTime = 0;
+
+    private Thread.Builder threadBuilder;
+
+
+    @Override
+    public void bind() throws Exception {
+        initServerSocket();
+
+        // Initialize SSL if needed
+        initialiseSsl();
+    }
+
+
+    protected void initServerSocket() throws Exception {
+        try {
+            ServerSocketFactory serverSocketFactory = ServerSocketFactory.getDefault();
+            if (getAddress() == null) {
+                serverSocket = serverSocketFactory.createServerSocket(getPort(), getAcceptCount());
+            } else {
+                serverSocket = serverSocketFactory.createServerSocket(getPort(), getAcceptCount(), getAddress());
+            }
+        } catch (BindException orig) {
+            String msg;
+            if (getAddress() == null) {
+              msg = orig.getMessage() + " <null>:" + getPort();
+            } else {
+              msg = orig.getMessage() + " " + getAddress().toString() + ":" + getPort();
+            }
+            BindException be = new BindException(msg);
+            be.initCause(orig);
+            throw be;
+        }
+    }
+
+
+    @Override
+    public void startInternal() throws Exception {
+        if (!running) {
+            running = true;
+            paused = false;
+
+            threadBuilder = Thread.ofVirtual().name(getName() + "-", 0);
+
+            initializeConnectionLatch();
+
+            startAcceptorThread();
+        }
+    }
+
+
+    @Override
+    public void stopInternal() throws Exception {
+        if (!paused) {
+            pause();
+        }
+        if (running) {
+            running = false;
+            acceptor.stop(10);
+        }
+    }
+
+
+    @Override
+    public void unbind() throws Exception {
+        if (log.isDebugEnabled()) {
+            log.debug("Destroy initiated for " +
+                    new InetSocketAddress(getAddress(),getPortWithOffset()));
+        }
+        if (running) {
+            stop();
+        }
+        try {
+            doCloseServerSocket();
+        } catch (IOException ioe) {
+            getLog().warn(sm.getString("endpoint.serverSocket.closeFailed", getName()), ioe);
+        }
+        destroySsl();
+        super.unbind();
+        if (getHandler() != null ) {
+            getHandler().recycle();
+        }
+        if (log.isDebugEnabled()) {
+            log.debug("Destroy completed for " +
+                    new InetSocketAddress(getAddress(), getPortWithOffset()));
+        }
+    }
+
+
+    @Override
+    protected void doCloseServerSocket() throws IOException {
+        ServerSocket serverSocket = this.serverSocket;
+
+        if (serverSocket != null) {
+            serverSocket.close();
+            serverSocket = null;
+        }
+    }
+
+
+    @Override
+    protected Socket serverSocketAccept() throws Exception {
+        Socket result = serverSocket.accept();
+
+        SocketAddress currentRemoteAddress = result.getRemoteSocketAddress();
+        long currentNanoTime = System.nanoTime();
+        if (currentRemoteAddress.equals(previousAcceptedSocketRemoteAddress) &&
+                currentNanoTime - previousAcceptedSocketNanoTime < 1000) {
+            throw new IOException(sm.getString("endpoint.err.duplicateAccept"));
+        }
+        previousAcceptedSocketRemoteAddress = currentRemoteAddress;
+        previousAcceptedSocketNanoTime = currentNanoTime;
+
+        return result;
+    }
+
+
+    @Override
+    protected boolean setSocketOptions(Socket socket) {
+        try {
+            socketProperties.setProperties(socket);
+
+        } catch (SocketException s) {
+            // Error here is common if the client has reset the connection
+            if (log.isDebugEnabled()) {
+                log.debug(sm.getString("endpoint.err.unexpected"), s);
+            }
+            // Close the socket
+            return false;
+        } catch (Throwable t) {
+            ExceptionUtils.handleThrowable(t);
+            log.error(sm.getString("endpoint.err.unexpected"), t);
+            // Close the socket
+            return false;
+        }
+
+        // Process the request from this socket
+        try {
+            BioLoomSocketWrapper wrapper = new BioLoomSocketWrapper(socket, this);
+
+            connections.put(socket, wrapper);
+            wrapper.setKeepAliveLeft(getMaxKeepAliveRequests());
+
+            SocketProcessor socketProcessor = new SocketProcessor(wrapper);
+            threadBuilder.start(socketProcessor);
+        } catch (Throwable t) {
+            ExceptionUtils.handleThrowable(t);
+            log.error(sm.getString("endpoint.process.fail"), t);
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    protected void destroySocket(Socket socket) {
+        countDownConnection();
+        try {
+            socket.close();
+        } catch (IOException ioe) {
+            if (log.isDebugEnabled()) {
+                log.debug(sm.getString("endpoint.err.close"), ioe);
+            }
+        }
+    }
+
+
+    @Override
+    protected InetSocketAddress getLocalAddress() throws IOException {
+
+        ServerSocket serverSocket = this.serverSocket;
+
+        if (serverSocket == null) {
+            return null;
+        }
+
+        SocketAddress sa = serverSocket.getLocalSocketAddress();
+        if (sa instanceof InetSocketAddress) {
+            return (InetSocketAddress) sa;
+        }
+
+        return null;
+    }
+
+
+    @Override
+    protected Log getLog() {
+        return log;
+    }
+
+
+    @Override
+    protected SocketProcessorBase<Socket> createSocketProcessor(SocketWrapperBase<Socket> socketWrapper,
+            SocketEvent event) {
+        // This method should never be called for this Endpoint.
+        // TODO i18n message
+        throw new IllegalStateException();
+    }
+
+
+    @Override
+    public boolean getUseSendfile() {
+        // Disable sendfile
+        return false;
+    }
+
+
+    public static class BioLoomSocketWrapper extends SocketWrapperBase<Socket> {
+
+        public BioLoomSocketWrapper(Socket socket, AbstractEndpoint<Socket, ?> endpoint) {
+            super(socket, endpoint);
+            socketBufferHandler = new SocketBufferHandler(
+                    endpoint.getSocketProperties().getAppReadBufSize(),
+                    endpoint.getSocketProperties().getAppWriteBufSize(),
+                    false);
+        }
+
+        @Override
+        protected void populateRemoteHost() {
+            // TODO Auto-generated method stub
+
+        }
+
+        @Override
+        protected void populateRemoteAddr() {
+            // TODO Auto-generated method stub
+
+        }
+
+        @Override
+        protected void populateRemotePort() {
+            // TODO Auto-generated method stub
+
+        }
+
+        @Override
+        protected void populateLocalName() {
+            // TODO Auto-generated method stub
+
+        }
+
+        @Override
+        protected void populateLocalAddr() {
+            // TODO Auto-generated method stub
+
+        }
+
+        @Override
+        protected void populateLocalPort() {
+            // TODO Auto-generated method stub
+
+        }
+
+        @Override
+        public int read(boolean block, byte[] b, int off, int len) throws IOException {
+            // TODO Auto-generated method stub
+            return 0;
+        }
+
+
+        @Override
+        public int read(boolean block, ByteBuffer to) throws IOException {
+            if (to.hasArray()) {
+                int read = getSocket().getInputStream().read(
+                        to.array(), to.arrayOffset() + to.position(), to.remaining());
+                if (read > 0) {
+                    to.position(to.position() + read);
+                }
+                return read;
+            } else {
+                throw new IllegalStateException();
+            }
+        }
+
+
+        @Override
+        public boolean isReadyForRead() throws IOException {
+            // TODO Auto-generated method stub
+            return false;
+        }
+
+        @Override
+        public void setAppReadBufHandler(ApplicationBufferHandler handler) {
+            // TODO Auto-generated method stub
+
+        }
+
+
+        @Override
+        protected void doClose() {
+            if (log.isDebugEnabled()) {
+                log.debug("Calling [" + getEndpoint() + "].closeSocket([" + this + "])");
+            }
+            try {
+                getEndpoint().connections.remove(getSocket());
+                getSocket().close();
+            } catch (Throwable e) {
+                ExceptionUtils.handleThrowable(e);
+                if (log.isDebugEnabled()) {
+                    // TODO i18n
+                    log.error("Socket close fail", e);
+                }
+            } finally {
+                socketBufferHandler = SocketBufferHandler.EMPTY;
+                nonBlockingWriteBuffer.clear();
+            }
+        }
+
+
+        @Override
+        protected boolean flushNonBlocking() throws IOException {
+            // TODO Auto-generated method stub
+            return false;
+        }
+
+        @Override
+        protected void doWrite(boolean block, ByteBuffer from) throws IOException {
+            if (from.hasArray()) {
+                getSocket().getOutputStream().write(from.array(), from.arrayOffset(), from.remaining());
+                from.position(from.limit());
+            } else {
+                throw new IllegalStateException();
+            }
+        }
+
+        @Override
+        public void registerReadInterest() {
+            // TODO Auto-generated method stub
+
+        }
+
+        @Override
+        public void registerWriteInterest() {
+            // TODO Auto-generated method stub
+
+        }
+
+        @Override
+        public SendfileDataBase createSendfileData(String filename, long pos, long length) {
+            // TODO Auto-generated method stub
+            return null;
+        }
+
+        @Override
+        public SendfileState processSendfile(SendfileDataBase sendfileData) {
+            // TODO Auto-generated method stub
+            return null;
+        }
+
+        @Override
+        public void doClientAuth(SSLSupport sslSupport) throws IOException {
+            // TODO Auto-generated method stub
+
+        }
+
+        @Override
+        public SSLSupport getSslSupport() {
+            // TODO Auto-generated method stub
+            return null;
+        }
+
+        @Override
+        protected <A> SocketWrapperBase<Socket>.OperationState<A> newOperationState(boolean read, ByteBuffer[] buffers,
+                int offset, int length, BlockingMode block, long timeout, TimeUnit unit, A attachment,
+                CompletionCheck check, CompletionHandler<Long, ? super A> handler, Semaphore semaphore,
+                SocketWrapperBase<Socket>.VectoredIOCompletionHandler<A> completion) {
+            // TODO Auto-generated method stub
+            return null;
+        }
+    }
+
+
+    protected class SocketProcessor implements Runnable {
+
+        private final SocketWrapperBase<Socket> socketWrapper;
+
+        public SocketProcessor(SocketWrapperBase<Socket> socketWrapper) {
+            this.socketWrapper = socketWrapper;
+        }
+
+        @Override
+        public void run() {
+            try {
+                if (isSSLEnabled()) {
+                    // Need to do handshake
+                    Socket s = socketWrapper.getSocket();
+
+                    // TODO
+                    socketWrapper.reset(s);
+                    /*
+                    try {
+                    } catch (IOException ioe) {
+                        if (logHandshake.isDebugEnabled()) {
+                            logHandshake.debug(sm.getString("endpoint.err.handshake",
+                                    socketWrapper.getRemoteAddr(), Integer.toString(socketWrapper.getRemotePort())), ioe);
+                        }
+                    }
+                    */
+                }
+
+                getHandler().process(socketWrapper, SocketEvent.OPEN_READ);
+
+            } catch (VirtualMachineError vme) {
+                ExceptionUtils.handleThrowable(vme);
+            } catch (Throwable t) {
+                log.error(sm.getString("endpoint.processing.fail"), t);
+            } finally {
+                socketWrapper.close();
+            }
+        }
+    }
+}
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org