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.parser;
20  
21  import java.nio.ByteBuffer;
22  
23  import org.eclipse.jetty.http2.ErrorCode;
24  import org.eclipse.jetty.http2.Flags;
25  import org.eclipse.jetty.http2.frames.FrameType;
26  import org.eclipse.jetty.io.ByteBufferPool;
27  import org.eclipse.jetty.util.BufferUtil;
28  import org.eclipse.jetty.util.log.Log;
29  import org.eclipse.jetty.util.log.Logger;
30  
31  public class ServerParser extends Parser
32  {
33      private static final Logger LOG = Log.getLogger(ServerParser.class);
34  
35      private final Listener listener;
36      private final PrefaceParser prefaceParser;
37      private State state = State.PREFACE;
38      private boolean notifyPreface = true;
39  
40      public ServerParser(ByteBufferPool byteBufferPool, Listener listener, int maxDynamicTableSize, int maxHeaderSize)
41      {
42          super(byteBufferPool, listener, maxDynamicTableSize, maxHeaderSize);
43          this.listener = listener;
44          this.prefaceParser = new PrefaceParser(listener);
45      }
46  
47      /**
48       * <p>A direct upgrade is an unofficial upgrade from HTTP/1.1 to HTTP/2.0.</p>
49       * <p>A direct upgrade is initiated when {@code org.eclipse.jetty.server.HttpConnection}
50       * sees a request with these bytes:</p>
51       * <pre>
52       * PRI * HTTP/2.0\r\n
53       * \r\n
54       * </pre>
55       * <p>This request is part of the HTTP/2.0 preface, indicating that a
56       * HTTP/2.0 client is attempting a h2c direct connection.</p>
57       * <p>This is not a standard HTTP/1.1 Upgrade path.</p>
58       */
59      public void directUpgrade()
60      {
61          if (state != State.PREFACE)
62              throw new IllegalStateException();
63          prefaceParser.directUpgrade();
64      }
65  
66      /**
67       * <p>The standard HTTP/1.1 upgrade path.</p>
68       */
69      public void standardUpgrade()
70      {
71          if (state != State.PREFACE)
72              throw new IllegalStateException();
73          notifyPreface = false;
74      }
75  
76      @Override
77      public void parse(ByteBuffer buffer)
78      {
79          try
80          {
81              if (LOG.isDebugEnabled())
82                  LOG.debug("Parsing {}", buffer);
83  
84              while (true)
85              {
86                  switch (state)
87                  {
88                      case PREFACE:
89                      {
90                          if (!prefaceParser.parse(buffer))
91                              return;
92                          if (notifyPreface)
93                              onPreface();
94                          state = State.SETTINGS;
95                          break;
96                      }
97                      case SETTINGS:
98                      {
99                          if (!parseHeader(buffer))
100                             return;
101                         if (getFrameType() != FrameType.SETTINGS.getType() || hasFlag(Flags.ACK))
102                         {
103                             BufferUtil.clear(buffer);
104                             notifyConnectionFailure(ErrorCode.PROTOCOL_ERROR.code, "invalid_preface");
105                             return;
106                         }
107                         if (!parseBody(buffer))
108                             return;
109                         state = State.FRAMES;
110                         break;
111                     }
112                     case FRAMES:
113                     {
114                         // Stay forever in the FRAMES state.
115                         super.parse(buffer);
116                         return;
117                     }
118                     default:
119                     {
120                         throw new IllegalStateException();
121                     }
122                 }
123             }
124         }
125         catch (Throwable x)
126         {
127             LOG.debug(x);
128             BufferUtil.clear(buffer);
129             notifyConnectionFailure(ErrorCode.PROTOCOL_ERROR.code, "parser_error");
130         }
131     }
132 
133     protected void onPreface()
134     {
135         notifyPreface();
136     }
137 
138     private void notifyPreface()
139     {
140         try
141         {
142             listener.onPreface();
143         }
144         catch (Throwable x)
145         {
146             LOG.info("Failure while notifying listener " + listener, x);
147         }
148     }
149 
150     public interface Listener extends Parser.Listener
151     {
152         public void onPreface();
153 
154         public static class Adapter extends Parser.Listener.Adapter implements Listener
155         {
156             @Override
157             public void onPreface()
158             {
159             }
160         }
161     }
162 
163     private enum State
164     {
165         PREFACE, SETTINGS, FRAMES
166     }
167 }