View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.server;
20  
21  import java.io.IOException;
22  import java.util.List;
23  
24  import javax.net.ssl.SSLEngine;
25  import javax.net.ssl.SSLEngineResult;
26  
27  import org.eclipse.jetty.io.AbstractConnection;
28  import org.eclipse.jetty.io.Connection;
29  import org.eclipse.jetty.io.EndPoint;
30  import org.eclipse.jetty.util.BufferUtil;
31  import org.eclipse.jetty.util.log.Log;
32  import org.eclipse.jetty.util.log.Logger;
33  
34  public abstract class NegotiatingServerConnection extends AbstractConnection
35  {
36      private static final Logger LOG = Log.getLogger(NegotiatingServerConnection.class);
37  
38      public interface CipherDiscriminator
39      {
40          boolean isAcceptable(String protocol, String tlsProtocol, String tlsCipher);
41      }
42      
43      private final Connector connector;
44      private final SSLEngine engine;
45      private final List<String> protocols;
46      private final String defaultProtocol;
47      private String protocol; // No need to be volatile: it is modified and read by the same thread
48  
49      protected NegotiatingServerConnection(Connector connector, EndPoint endPoint, SSLEngine engine, List<String> protocols, String defaultProtocol)
50      {
51          super(endPoint, connector.getExecutor());
52          this.connector = connector;
53          this.protocols = protocols;
54          this.defaultProtocol = defaultProtocol;
55          this.engine = engine;
56      }
57  
58      protected List<String> getProtocols()
59      {
60          return protocols;
61      }
62  
63      protected String getDefaultProtocol()
64      {
65          return defaultProtocol;
66      }
67  
68      protected Connector getConnector()
69      {
70          return connector;
71      }
72      
73      protected SSLEngine getSSLEngine()
74      {
75          return engine;
76      }
77  
78      protected String getProtocol()
79      {
80          return protocol;
81      }
82  
83      protected void setProtocol(String protocol)
84      {
85          this.protocol = protocol;
86      }
87  
88      @Override
89      public void onOpen()
90      {
91          super.onOpen();
92          fillInterested();
93      }
94  
95      @Override
96      public void onFillable()
97      {
98          int filled = fill();
99  
100         if (filled == 0)
101         {
102             if (protocol == null)
103             {
104                 if (engine.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)
105                 {
106                     // Here the SSL handshake is finished, but the protocol has not been negotiated.
107                     if (LOG.isDebugEnabled())
108                         LOG.debug("{} could not negotiate protocol, SSLEngine: {}", this, engine);
109                     close();
110                 }
111                 else
112                 {
113                     // Here the SSL handshake is not finished yet but we filled 0 bytes,
114                     // so we need to read more.
115                     fillInterested();
116                 }
117             }
118             else
119             {
120                 ConnectionFactory connectionFactory = connector.getConnectionFactory(protocol);
121                 if (connectionFactory == null)
122                 {
123                     LOG.info("{} application selected protocol '{}', but no correspondent {} has been configured",
124                              this, protocol, ConnectionFactory.class.getName());
125                     close();
126                 }
127                 else
128                 {
129                     EndPoint endPoint = getEndPoint();
130                     Connection newConnection = connectionFactory.newConnection(connector, endPoint);
131                     endPoint.upgrade(newConnection);
132                 }
133             }
134         }
135         else if (filled < 0)
136         {
137             // Something went bad, we need to close.
138             if (LOG.isDebugEnabled())
139                 LOG.debug("{} detected close on client side", this);
140             close();
141         }
142         else
143         {
144             // Must never happen, since we fill using an empty buffer
145             throw new IllegalStateException();
146         }
147     }
148 
149     private int fill()
150     {
151         try
152         {
153             return getEndPoint().fill(BufferUtil.EMPTY_BUFFER);
154         }
155         catch (IOException x)
156         {
157             LOG.debug(x);
158             close();
159             return -1;
160         }
161     }
162 
163     @Override
164     public void close()
165     {
166         // Gentler close for SSL.
167         getEndPoint().shutdownOutput();
168         super.close();
169     }
170 }