View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2015 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  
20  package org.eclipse.jetty.server;
21  
22  import java.io.IOException;
23  import java.nio.ByteBuffer;
24  
25  import org.eclipse.jetty.http.BadMessageException;
26  import org.eclipse.jetty.http.HostPortHttpField;
27  import org.eclipse.jetty.http.HttpField;
28  import org.eclipse.jetty.http.HttpFields;
29  import org.eclipse.jetty.http.HttpGenerator;
30  import org.eclipse.jetty.http.HttpHeader;
31  import org.eclipse.jetty.http.HttpHeaderValue;
32  import org.eclipse.jetty.http.HttpMethod;
33  import org.eclipse.jetty.http.HttpParser;
34  import org.eclipse.jetty.http.HttpStatus;
35  import org.eclipse.jetty.http.HttpURI;
36  import org.eclipse.jetty.http.HttpVersion;
37  import org.eclipse.jetty.http.MetaData;
38  import org.eclipse.jetty.io.Connection;
39  import org.eclipse.jetty.io.EndPoint;
40  import org.eclipse.jetty.util.log.Log;
41  import org.eclipse.jetty.util.log.Logger;
42  
43  /**
44   * A HttpChannel customized to be transported over the HTTP/1 protocol
45   */
46  class HttpChannelOverHttp extends HttpChannel implements HttpParser.RequestHandler
47  {
48      private static final Logger LOG = Log.getLogger(HttpChannelOverHttp.class);
49      private final static HttpField PREAMBLE_UPGRADE_H2C = new HttpField(HttpHeader.UPGRADE,"h2c");
50  
51      private final HttpFields _fields = new HttpFields();
52      private final MetaData.Request _metadata = new MetaData.Request(_fields);
53      private final HttpConnection _httpConnection;
54      private HttpField _connection;
55      private HttpField _upgrade = null;
56      private boolean _delayedForContent;
57      private boolean _unknownExpectation = false;
58      private boolean _expect100Continue = false;
59      private boolean _expect102Processing = false;
60  
61  
62      public HttpChannelOverHttp(HttpConnection httpConnection, Connector connector, HttpConfiguration config, EndPoint endPoint, HttpTransport transport)
63      {
64          super(connector,config,endPoint,transport);
65          _httpConnection = httpConnection;
66          _metadata.setURI(new HttpURI());
67      }
68  
69      @Override
70      protected HttpInput newHttpInput(HttpChannelState state)
71      {
72          return new HttpInputOverHTTP(state);
73      }
74  
75      @Override
76      public void recycle()
77      {
78          super.recycle();
79          _unknownExpectation = false;
80          _expect100Continue = false;
81          _expect102Processing = false;
82          _metadata.recycle();
83          _connection=null;
84          _fields.clear();
85          _upgrade=null;
86      }
87  
88      @Override
89      public boolean isExpecting100Continue()
90      {
91          return _expect100Continue;
92      }
93  
94      @Override
95      public boolean isExpecting102Processing()
96      {
97          return _expect102Processing;
98      }
99  
100     @Override
101     public boolean startRequest(String method, String uri, HttpVersion version)
102     {
103         _metadata.setMethod(method);
104         if (HttpMethod.CONNECT.is(method))
105             _metadata.getURI().parseConnect(uri);
106         else
107             _metadata.getURI().parse(uri);
108         _metadata.setHttpVersion(version);
109         _unknownExpectation = false;
110         _expect100Continue = false;
111         _expect102Processing = false;
112         return false;
113     }
114 
115     @Override
116     public void parsedHeader(HttpField field)
117     {
118         HttpHeader header=field.getHeader();
119         String value=field.getValue();
120         if (header!=null)
121         {
122             switch(header)
123             {
124                 case CONNECTION:
125                     _connection=field;
126                     break;
127 
128                 case HOST:
129                     if (!_metadata.getURI().isAbsolute() && field instanceof HostPortHttpField)
130                     {
131                         HostPortHttpField hp = (HostPortHttpField)field;
132                         _metadata.getURI().setAuthority(hp.getHost(),hp.getPort());
133                     }
134                     break;
135 
136                 case EXPECT:
137                 {
138                     if (_metadata.getVersion()==HttpVersion.HTTP_1_1)
139                     {
140                         HttpHeaderValue expect = HttpHeaderValue.CACHE.get(value);
141                         switch (expect == null ? HttpHeaderValue.UNKNOWN : expect)
142                         {
143                             case CONTINUE:
144                                 _expect100Continue = true;
145                                 break;
146 
147                             case PROCESSING:
148                                 _expect102Processing = true;
149                                 break;
150 
151                             default:
152                                 String[] values = field.getValues();
153                                 for (int i = 0; values != null && i < values.length; i++)
154                                 {
155                                     expect = HttpHeaderValue.CACHE.get(values[i].trim());
156                                     if (expect == null)
157                                         _unknownExpectation = true;
158                                     else
159                                     {
160                                         switch (expect)
161                                         {
162                                             case CONTINUE:
163                                                 _expect100Continue = true;
164                                                 break;
165                                             case PROCESSING:
166                                                 _expect102Processing = true;
167                                                 break;
168                                             default:
169                                                 _unknownExpectation = true;
170                                         }
171                                     }
172                                 }
173                         }
174                     }
175                     break;
176                 }
177 
178                 case UPGRADE:
179                     _upgrade=field;
180                     break;
181 
182                 default:
183                     break;
184             }
185         }
186         _fields.add(field);
187     }
188 
189     /**
190      * If the associated response has the Expect header set to 100 Continue,
191      * then accessing the input stream indicates that the handler/servlet
192      * is ready for the request body and thus a 100 Continue response is sent.
193      *
194      * @throws IOException if the InputStream cannot be created
195      */
196     @Override
197     public void continue100(int available) throws IOException
198     {
199         // If the client is expecting 100 CONTINUE, then send it now.
200         // TODO: consider using an AtomicBoolean ?
201         if (isExpecting100Continue())
202         {
203             _expect100Continue = false;
204 
205             // is content missing?
206             if (available == 0)
207             {
208                 if (getResponse().isCommitted())
209                     throw new IOException("Committed before 100 Continues");
210 
211                 boolean committed = sendResponse(HttpGenerator.CONTINUE_100_INFO, null, false);
212                 if (!committed)
213                     throw new IOException("Concurrent commit while trying to send 100-Continue");
214             }
215         }
216     }
217 
218     @Override
219     public void earlyEOF()
220     {
221         // If we have no request yet, just close
222         if (_metadata.getMethod()==null)
223             _httpConnection.close();
224         else
225             onEarlyEOF();
226     }
227 
228     @Override
229     public boolean content(ByteBuffer content)
230     {
231         HttpInput.Content c = _httpConnection.newContent(content);
232         boolean handle = onContent(c) || _delayedForContent;
233         _delayedForContent=false;
234         return handle;
235     }
236 
237     public void asyncReadFillInterested()
238     {
239         _httpConnection.asyncReadFillInterested();
240     }
241 
242     @Override
243     public void badMessage(int status, String reason)
244     {
245         _httpConnection.getGenerator().setPersistent(false);
246         try
247         {
248             // Need to call onRequest, so RequestLog can reports as much as possible
249             onRequest(_metadata);
250         }
251         catch (Exception e)
252         {
253             LOG.ignore(e);
254         }
255 
256         onBadMessage(status,reason);
257     }
258 
259     @Override
260     public boolean headerComplete()
261     {
262         boolean persistent;
263 
264         switch (_metadata.getVersion())
265         {
266             case HTTP_1_0:
267             {
268                 if (getHttpConfiguration().isPersistentConnectionsEnabled())
269                 {
270                     if (_connection!=null)
271                     {
272                         if (_connection.contains(HttpHeaderValue.KEEP_ALIVE.asString()))
273                             persistent=true;
274                         else
275                             persistent=_fields.contains(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.asString());
276                     }
277                     else
278                         persistent=false;
279                 }
280                 else
281                     persistent=false;
282 
283                 if (!persistent)
284                     persistent = HttpMethod.CONNECT.is(_metadata.getMethod());
285                 if (persistent)
286                     getResponse().getHttpFields().add(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE);
287 
288                 break;
289             }
290 
291             case HTTP_1_1:
292             {
293                 if (_unknownExpectation)
294                 {
295                     badMessage(HttpStatus.EXPECTATION_FAILED_417,null);
296                     return false;
297                 }
298 
299                 if (getHttpConfiguration().isPersistentConnectionsEnabled())
300                 {
301                     if (_connection!=null)
302                     {
303                         if (_connection.contains(HttpHeaderValue.CLOSE.asString()))
304                             persistent=false;
305                         else
306                             persistent=!_fields.contains(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString()); // handle multiple connection fields
307                     }
308                     else
309                         persistent=true;
310                 }
311                 else
312                     persistent=false;
313 
314                 if (!persistent)
315                     persistent = HttpMethod.CONNECT.is(_metadata.getMethod());
316                 if (!persistent)
317                     getResponse().getHttpFields().add(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE);
318 
319                 if (_upgrade!=null && upgrade())
320                     return true;
321 
322                 break;
323             }
324 
325             case HTTP_2:
326             {
327                 // Allow direct "upgrade" to HTTP_2_0 only if the connector supports h2, but not protocol negotiation
328                 _upgrade=PREAMBLE_UPGRADE_H2C;
329 
330                 if (HttpMethod.PRI.is(_metadata.getMethod()) &&
331                     "*".equals(_metadata.getURI().toString()) &&
332                     _fields.size()==0 &&
333                     upgrade())
334                     return false;
335 
336                 badMessage(HttpStatus.UPGRADE_REQUIRED_426,null);
337                 return false;
338             }
339 
340             default:
341             {
342                 throw new IllegalStateException();
343             }
344         }
345 
346         if (!persistent)
347             _httpConnection.getGenerator().setPersistent(false);
348 
349         onRequest(_metadata);
350 
351         // Should we delay dispatch until we have some content?
352         // We should not delay if there is no content expect or client is expecting 100 or the response is already committed or the request buffer already has something in it to parse
353         _delayedForContent =  (getHttpConfiguration().isDelayDispatchUntilContent() && _httpConnection.getParser().getContentLength()>0 && !isExpecting100Continue() && !isCommitted() && _httpConnection.isRequestBufferEmpty());
354 
355         return !_delayedForContent;
356     }
357 
358 
359     /**
360      * <p>Attempts to perform a HTTP/1.1 upgrade.</p>
361      * <p>The upgrade looks up a {@link ConnectionFactory.Upgrading} from the connector
362      * matching the protocol specified in the {@code Upgrade} header.</p>
363      * <p>The upgrade may succeed, be ignored (which can allow a later handler to implement)
364      * or fail with a {@link BadMessageException}.</p>
365      * @return true if the upgrade was performed, false if it was ignored
366      * @throws BadMessageException if the upgrade failed
367      */
368     private boolean upgrade() throws BadMessageException
369     {
370         if (LOG.isDebugEnabled())
371             LOG.debug("upgrade {} {}",this,_upgrade);
372 
373         if (_upgrade!=PREAMBLE_UPGRADE_H2C && (_connection==null || !_connection.getValue().contains("Upgrade")))
374             throw new BadMessageException(HttpStatus.BAD_REQUEST_400);
375 
376         // Find the upgrade factory
377         ConnectionFactory.Upgrading factory=null;
378         for (ConnectionFactory f : getConnector().getConnectionFactories())
379         {
380             if (f instanceof ConnectionFactory.Upgrading)
381             {
382                 if (f.getProtocols().contains(_upgrade.getValue()))
383                 {
384                     factory=(ConnectionFactory.Upgrading)f;
385                     break;
386                 }
387             }
388         }
389 
390         if (factory==null)
391         {
392             if (LOG.isDebugEnabled())
393                 LOG.debug("No factory for {} in {}",_upgrade,getConnector());
394             return false;
395         }
396 
397         // Create new connection
398         HttpFields response101 = new HttpFields();
399         Connection upgrade_connection = factory.upgradeConnection(getConnector(),getEndPoint(),_metadata,response101);
400         if (upgrade_connection==null)
401         {
402             if (LOG.isDebugEnabled())
403                 LOG.debug("Upgrade ignored for {} by {}",_upgrade,factory);
404             return false;
405         }
406 
407         // Send 101 if needed
408         try
409         {
410             if (_upgrade!=PREAMBLE_UPGRADE_H2C)
411                 sendResponse(new MetaData.Response(HttpVersion.HTTP_1_1,HttpStatus.SWITCHING_PROTOCOLS_101,response101,0),null,true);
412         }
413         catch(IOException e)
414         {
415             throw new BadMessageException(HttpStatus.INTERNAL_SERVER_ERROR_500,null,e);
416         }
417 
418         if (LOG.isDebugEnabled())
419             LOG.debug("Upgrade from {} to {}", getEndPoint().getConnection(),upgrade_connection);
420         getRequest().setAttribute(HttpConnection.UPGRADE_CONNECTION_ATTRIBUTE,upgrade_connection);
421         getResponse().setStatus(101);
422         getHttpTransport().onCompleted();
423         return true;
424     }
425 
426     @Override
427     protected void handleException(Throwable x)
428     {
429         _httpConnection.getGenerator().setPersistent(false);
430         super.handleException(x);
431     }
432 
433     @Override
434     public void abort(Throwable failure)
435     {
436         super.abort(failure);
437         _httpConnection.getGenerator().setPersistent(false);
438     }
439 
440     @Override
441     public boolean messageComplete()
442     {
443         return onRequestComplete();
444     }
445 
446     @Override
447     public int getHeaderCacheSize()
448     {
449         return getHttpConfiguration().getHeaderCacheSize();
450     }
451 }