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.http2.server;
20  
21  import java.nio.ByteBuffer;
22  import java.util.ArrayList;
23  import java.util.List;
24  import java.util.Queue;
25  import java.util.concurrent.Executor;
26  
27  import org.eclipse.jetty.http.BadMessageException;
28  import org.eclipse.jetty.http.HttpField;
29  import org.eclipse.jetty.http.HttpHeader;
30  import org.eclipse.jetty.http.HttpMethod;
31  import org.eclipse.jetty.http.MetaData;
32  import org.eclipse.jetty.http.MetaData.Request;
33  import org.eclipse.jetty.http2.HTTP2Connection;
34  import org.eclipse.jetty.http2.ISession;
35  import org.eclipse.jetty.http2.IStream;
36  import org.eclipse.jetty.http2.api.server.ServerSessionListener;
37  import org.eclipse.jetty.http2.frames.DataFrame;
38  import org.eclipse.jetty.http2.frames.Frame;
39  import org.eclipse.jetty.http2.frames.HeadersFrame;
40  import org.eclipse.jetty.http2.frames.PrefaceFrame;
41  import org.eclipse.jetty.http2.frames.SettingsFrame;
42  import org.eclipse.jetty.http2.parser.ServerParser;
43  import org.eclipse.jetty.http2.parser.SettingsBodyParser;
44  import org.eclipse.jetty.io.ByteBufferPool;
45  import org.eclipse.jetty.io.Connection;
46  import org.eclipse.jetty.io.EndPoint;
47  import org.eclipse.jetty.server.Connector;
48  import org.eclipse.jetty.server.HttpConfiguration;
49  import org.eclipse.jetty.util.B64Code;
50  import org.eclipse.jetty.util.BufferUtil;
51  import org.eclipse.jetty.util.Callback;
52  import org.eclipse.jetty.util.ConcurrentArrayQueue;
53  import org.eclipse.jetty.util.TypeUtil;
54  
55  public class HTTP2ServerConnection extends HTTP2Connection implements Connection.UpgradeTo
56  {
57      private final Queue<HttpChannelOverHTTP2> channels = new ConcurrentArrayQueue<>();
58      private final ServerSessionListener listener;
59      private final HttpConfiguration httpConfig;
60      private final List<Frame> upgradeFrames = new ArrayList<>();
61  
62      public HTTP2ServerConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, HttpConfiguration httpConfig, ServerParser parser, ISession session, int inputBufferSize, ServerSessionListener listener)
63      {
64          super(byteBufferPool, executor, endPoint, parser, session, inputBufferSize);
65          this.listener = listener;
66          this.httpConfig = httpConfig;
67      }
68  
69      @Override
70      protected ServerParser getParser()
71      {
72          return (ServerParser)super.getParser();
73      }
74  
75      @Override
76      public void onUpgradeTo(ByteBuffer buffer)
77      {
78          if (LOG.isDebugEnabled())
79              LOG.debug("HTTP2 onUpgradeTo {} {}", this, BufferUtil.toDetailString(buffer));
80          setInputBuffer(buffer);
81      }
82  
83      @Override
84      public void onOpen()
85      {
86          notifyAccept(getSession());
87          for (Frame frame : upgradeFrames)
88              getSession().onFrame(frame);
89          super.onOpen();
90      }
91  
92      private void notifyAccept(ISession session)
93      {
94          try
95          {
96              listener.onAccept(session);
97          }
98          catch (Throwable x)
99          {
100             LOG.info("Failure while notifying listener " + listener, x);
101         }
102     }
103 
104     public void onNewStream(Connector connector, IStream stream, HeadersFrame frame)
105     {
106         if (LOG.isDebugEnabled())
107             LOG.debug("Processing {} on {}", frame, stream);
108         HttpChannelOverHTTP2 channel = provideHttpChannel(connector, stream);
109         Runnable task = channel.onRequest(frame);
110         if (task != null)
111             offerTask(task, false);
112     }
113 
114     public void onData(IStream stream, DataFrame frame, Callback callback)
115     {
116         if (LOG.isDebugEnabled())
117             LOG.debug("Processing {} on {}", frame, stream);
118         HttpChannelOverHTTP2 channel = (HttpChannelOverHTTP2)stream.getAttribute(IStream.CHANNEL_ATTRIBUTE);
119         Runnable task = channel.requestContent(frame, callback);
120         if (task != null)
121             offerTask(task, false);
122     }
123 
124     public void push(Connector connector, IStream stream, MetaData.Request request)
125     {
126         if (LOG.isDebugEnabled())
127             LOG.debug("Processing push {} on {}", request, stream);
128         HttpChannelOverHTTP2 channel = provideHttpChannel(connector, stream);
129         Runnable task = channel.onPushRequest(request);
130         if (task != null)
131             offerTask(task, true);
132     }
133 
134     private HttpChannelOverHTTP2 provideHttpChannel(Connector connector, IStream stream)
135     {
136         HttpChannelOverHTTP2 channel = channels.poll();
137         if (channel != null)
138         {
139             channel.getHttpTransport().setStream(stream);
140             if (LOG.isDebugEnabled())
141                 LOG.debug("Recycling channel {} for {}", channel, this);
142         }
143         else
144         {
145             HttpTransportOverHTTP2 transport = new HttpTransportOverHTTP2(connector, this);
146             transport.setStream(stream);
147             channel = new ServerHttpChannelOverHTTP2(connector, httpConfig, getEndPoint(), transport);
148             if (LOG.isDebugEnabled())
149                 LOG.debug("Creating channel {} for {}", channel, this);
150         }
151         stream.setAttribute(IStream.CHANNEL_ATTRIBUTE, channel);
152         return channel;
153     }
154 
155     public boolean upgrade(Request request)
156     {
157         if (HttpMethod.PRI.is(request.getMethod()))
158         {
159             getParser().directUpgrade();
160         }
161         else
162         {
163             HttpField settingsField = request.getFields().getField(HttpHeader.HTTP2_SETTINGS);
164             if (settingsField == null)
165                 throw new BadMessageException("Missing " + HttpHeader.HTTP2_SETTINGS + " header");
166             String value = settingsField.getValue();
167             final byte[] settings = B64Code.decodeRFC4648URL(value == null ? "" : value);
168 
169             if (LOG.isDebugEnabled())
170                 LOG.debug("{} settings {}",this,TypeUtil.toHexString(settings));
171 
172             SettingsFrame settingsFrame = SettingsBodyParser.parseBody(BufferUtil.toBuffer(settings));
173             if (settingsFrame == null)
174             {
175                 LOG.warn("Invalid {} header value: {}", HttpHeader.HTTP2_SETTINGS, value);
176                 throw new BadMessageException();
177             }
178 
179             getParser().standardUpgrade();
180 
181             upgradeFrames.add(new PrefaceFrame());
182             upgradeFrames.add(settingsFrame);
183             // Remember the request to send a response from onOpen().
184             upgradeFrames.add(new HeadersFrame(1, request, null, true));
185         }
186         return true;
187     }
188 
189     private class ServerHttpChannelOverHTTP2 extends HttpChannelOverHTTP2
190     {
191         public ServerHttpChannelOverHTTP2(Connector connector, HttpConfiguration configuration, EndPoint endPoint, HttpTransportOverHTTP2 transport)
192         {
193             super(connector, configuration, endPoint, transport);
194         }
195 
196         @Override
197         public void onCompleted()
198         {
199             super.onCompleted();
200             recycle();
201             channels.offer(this);
202         }
203     }
204 }