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