View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 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.spdy.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.npn.NextProtoNego;
31  import org.eclipse.jetty.server.ConnectionFactory;
32  import org.eclipse.jetty.server.Connector;
33  import org.eclipse.jetty.util.BufferUtil;
34  import org.eclipse.jetty.util.log.Log;
35  import org.eclipse.jetty.util.log.Logger;
36  
37  public class NPNServerConnection extends AbstractConnection implements NextProtoNego.ServerProvider
38  {
39      private final Logger LOG = Log.getLogger(getClass());
40      private final Connector connector;
41      private final SSLEngine engine;
42      private final List<String> protocols;
43      private final String defaultProtocol;
44      private String nextProtocol; // No need to be volatile: it is modified and read by the same thread
45  
46      public NPNServerConnection(EndPoint endPoint, SSLEngine engine, Connector connector, List<String> protocols, String defaultProtocol)
47      {
48          super(endPoint, connector.getExecutor());
49          this.connector = connector;
50          this.protocols = protocols;
51          this.defaultProtocol = defaultProtocol;
52          this.engine = engine;
53          NextProtoNego.put(engine, this);
54      }
55  
56      @Override
57      public void onOpen()
58      {
59          super.onOpen();
60          fillInterested();
61      }
62  
63      @Override
64      public void onFillable()
65      {
66          int filled = fill();
67  
68          if (filled == 0)
69          {
70              if (nextProtocol == null)
71              {
72                  if (engine.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)
73                  {
74                      // Here the SSL handshake is finished, but while the client sent
75                      // the NPN extension, the server application did not select the
76                      // protocol; we need to close as the protocol cannot be negotiated.
77                      LOG.debug("{} missing next protocol. SSLEngine: {}", this, engine);
78                      close();
79                  }
80                  else
81                  {
82                      // Here the SSL handshake is not finished yet but we filled 0 bytes,
83                      // so we need to read more.
84                      fillInterested();
85                  }
86              }
87              else
88              {
89                  ConnectionFactory connectionFactory = connector.getConnectionFactory(nextProtocol);
90                  if (connectionFactory == null)
91                  {
92                      LOG.debug("{} application selected protocol '{}', but no correspondent {} has been configured",
93                              this, nextProtocol, ConnectionFactory.class.getName());
94                      close();
95                  }
96                  else
97                  {
98                      EndPoint endPoint = getEndPoint();
99                      Connection oldConnection = endPoint.getConnection();
100                     Connection newConnection = connectionFactory.newConnection(connector, endPoint);
101                     LOG.debug("{} switching from {} to {}", this, oldConnection, newConnection);
102                     oldConnection.onClose();
103                     endPoint.setConnection(newConnection);
104                     getEndPoint().getConnection().onOpen();
105                 }
106             }
107         }
108         else if (filled < 0)
109         {
110             // Something went bad, we need to close.
111             LOG.debug("{} closing on client close", this);
112             close();
113         }
114         else
115         {
116             // Must never happen, since we fill using an empty buffer
117             throw new IllegalStateException();
118         }
119     }
120 
121     private int fill()
122     {
123         try
124         {
125             return getEndPoint().fill(BufferUtil.EMPTY_BUFFER);
126         }
127         catch (IOException x)
128         {
129             LOG.debug(x);
130             close();
131             return -1;
132         }
133     }
134 
135     @Override
136     public void unsupported()
137     {
138         protocolSelected(defaultProtocol);
139     }
140 
141     @Override
142     public List<String> protocols()
143     {
144         return protocols;
145     }
146 
147     @Override
148     public void protocolSelected(String protocol)
149     {
150         LOG.debug("{} protocol selected {}", this, protocol);
151         nextProtocol = protocol != null ? protocol : defaultProtocol;
152         NextProtoNego.remove(engine);
153     }
154 
155     @Override
156     public void close()
157     {
158         NextProtoNego.remove(engine);
159         EndPoint endPoint = getEndPoint();
160         endPoint.shutdownOutput();
161         endPoint.close();
162     }
163 }