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  
20  package org.eclipse.jetty.spdy.server.http;
21  
22  import org.eclipse.jetty.io.EndPoint;
23  import org.eclipse.jetty.server.Connector;
24  import org.eclipse.jetty.server.HttpConfiguration;
25  import org.eclipse.jetty.spdy.api.DataInfo;
26  import org.eclipse.jetty.spdy.api.HeadersInfo;
27  import org.eclipse.jetty.spdy.api.PushInfo;
28  import org.eclipse.jetty.spdy.api.ReplyInfo;
29  import org.eclipse.jetty.spdy.api.Stream;
30  import org.eclipse.jetty.spdy.api.StreamFrameListener;
31  import org.eclipse.jetty.spdy.api.SynInfo;
32  import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
33  import org.eclipse.jetty.spdy.server.SPDYServerConnectionFactory;
34  import org.eclipse.jetty.util.Fields;
35  import org.eclipse.jetty.util.annotation.Name;
36  import org.eclipse.jetty.util.log.Log;
37  import org.eclipse.jetty.util.log.Logger;
38  
39  public class HTTPSPDYServerConnectionFactory extends SPDYServerConnectionFactory implements HttpConfiguration.ConnectionFactory
40  {
41      private static final String CHANNEL_ATTRIBUTE = "org.eclipse.jetty.spdy.server.http.HTTPChannelOverSPDY";
42      private static final Logger logger = Log.getLogger(HTTPSPDYServerConnectionFactory.class);
43  
44      private final PushStrategy pushStrategy;
45      private final HttpConfiguration httpConfiguration;
46  
47      public HTTPSPDYServerConnectionFactory(
48          @Name("version") int version,
49          @Name("config") HttpConfiguration config)
50      {
51          this(version,config,new PushStrategy.None());
52      }
53  
54      public HTTPSPDYServerConnectionFactory(
55          @Name("version") int version,
56          @Name("config") HttpConfiguration config,
57          @Name("pushStrategy") PushStrategy pushStrategy)
58      {
59          super(version);
60          this.pushStrategy = pushStrategy;
61          httpConfiguration = config;
62          addBean(httpConfiguration);
63      }
64  
65      @Override
66      public HttpConfiguration getHttpConfiguration()
67      {
68          return httpConfiguration;
69      }
70  
71      @Override
72      protected ServerSessionFrameListener provideServerSessionFrameListener(Connector connector, EndPoint endPoint)
73      {
74          return new HTTPServerFrameListener(connector,endPoint);
75      }
76  
77      private class HTTPServerFrameListener extends ServerSessionFrameListener.Adapter implements StreamFrameListener
78      {
79          private final Connector connector;
80          private final EndPoint endPoint;
81  
82          public HTTPServerFrameListener(Connector connector,EndPoint endPoint)
83          {
84              this.endPoint = endPoint;
85              this.connector=connector;
86          }
87  
88          @Override
89          public StreamFrameListener onSyn(final Stream stream, SynInfo synInfo)
90          {
91              // Every time we have a SYN, it maps to a HTTP request.
92              // We can have multiple concurrent SYNs on the same connection,
93              // and this is very different from HTTP, where only one request
94              // can arrive on the same connection, so we need to create an
95              // HttpChannel for each SYN in order to run concurrently.
96  
97              logger.debug("Received {} on {}", synInfo, stream);
98  
99              Fields headers = synInfo.getHeaders();
100             // According to SPDY/3 spec section 3.2.1 user-agents MUST support gzip compression. Firefox omits the
101             // accept-encoding header as it is redundant to negotiate gzip compression support with the server,
102             // if clients have to accept it.
103             // So we inject the accept-encoding header here, even if not set by the client. This will enforce SPDY
104             // clients to follow the spec and enable gzip compression if GzipFilter or the like is enabled.
105             if (!(headers.get("accept-encoding") != null && headers.get("accept-encoding").value().contains
106                     ("gzip")))
107                 headers.add("accept-encoding", "gzip");
108             HttpTransportOverSPDY transport = new HttpTransportOverSPDY(connector, httpConfiguration, endPoint,
109                     pushStrategy, stream, headers);
110             HttpInputOverSPDY input = new HttpInputOverSPDY();
111             HttpChannelOverSPDY channel = new HttpChannelOverSPDY(connector, httpConfiguration, endPoint, transport, input, stream);
112             stream.setAttribute(CHANNEL_ATTRIBUTE, channel);
113 
114             channel.requestStart(headers, synInfo.isClose());
115 
116             if (headers.isEmpty())
117             {
118                 // If the SYN has no headers, they may come later in a HEADERS frame
119                 return this;
120             }
121             else
122             {
123                 if (synInfo.isClose())
124                     return null;
125                 else
126                     return this;
127             }
128         }
129 
130         @Override
131         public void onReply(Stream stream, ReplyInfo replyInfo)
132         {
133             // Do nothing, servers cannot get replies
134         }
135 
136         @Override
137         public void onHeaders(Stream stream, HeadersInfo headersInfo)
138         {
139             logger.debug("Received {} on {}", headersInfo, stream);
140             HttpChannelOverSPDY channel = (HttpChannelOverSPDY)stream.getAttribute(CHANNEL_ATTRIBUTE);
141             channel.requestHeaders(headersInfo.getHeaders(), headersInfo.isClose());
142         }
143 
144         @Override
145         public StreamFrameListener onPush(Stream stream, PushInfo pushInfo)
146         {
147             return null;
148         }
149 
150         @Override
151         public void onData(Stream stream, final DataInfo dataInfo)
152         {
153             logger.debug("Received {} on {}", dataInfo, stream);
154             HttpChannelOverSPDY channel = (HttpChannelOverSPDY)stream.getAttribute(CHANNEL_ATTRIBUTE);
155             channel.requestContent(dataInfo, dataInfo.isClose());
156         }
157     }
158 }