View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2014 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 LOG = 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              if (LOG.isDebugEnabled())
98                  LOG.debug("Received {} on {}", synInfo, stream);
99  
100             Fields headers = synInfo.getHeaders();
101             // According to SPDY/3 spec section 3.2.1 user-agents MUST support gzip compression. Firefox omits the
102             // accept-encoding header as it is redundant to negotiate gzip compression support with the server,
103             // if clients have to accept it.
104             // So we inject the accept-encoding header here, even if not set by the client. This will enforce SPDY
105             // clients to follow the spec and enable gzip compression if GzipFilter or the like is enabled.
106             if (!(headers.get("accept-encoding") != null && headers.get("accept-encoding").getValue().contains
107                     ("gzip")))
108                 headers.add("accept-encoding", "gzip");
109             HttpTransportOverSPDY transport = new HttpTransportOverSPDY(connector, httpConfiguration, endPoint,
110                     pushStrategy, stream, headers);
111             HttpInputOverSPDY input = new HttpInputOverSPDY();
112             HttpChannelOverSPDY channel = new HttpChannelOverSPDY(connector, httpConfiguration, endPoint, transport, input, stream);
113             stream.setAttribute(CHANNEL_ATTRIBUTE, channel);
114 
115             channel.requestStart(headers, synInfo.isClose());
116 
117             if (headers.isEmpty())
118             {
119                 // If the SYN has no headers, they may come later in a HEADERS frame
120                 return this;
121             }
122             else
123             {
124                 if (synInfo.isClose())
125                     return null;
126                 else
127                     return this;
128             }
129         }
130 
131         @Override
132         public void onReply(Stream stream, ReplyInfo replyInfo)
133         {
134             // Do nothing, servers cannot get replies
135         }
136 
137         @Override
138         public void onHeaders(Stream stream, HeadersInfo headersInfo)
139         {
140             if (LOG.isDebugEnabled())
141                 LOG.debug("Received {} on {}", headersInfo, stream);
142             HttpChannelOverSPDY channel = (HttpChannelOverSPDY)stream.getAttribute(CHANNEL_ATTRIBUTE);
143             channel.requestHeaders(headersInfo.getHeaders(), headersInfo.isClose());
144         }
145 
146         @Override
147         public StreamFrameListener onPush(Stream stream, PushInfo pushInfo)
148         {
149             return null;
150         }
151 
152         @Override
153         public void onData(Stream stream, final DataInfo dataInfo)
154         {
155             if (LOG.isDebugEnabled())
156                 LOG.debug("Received {} on {}", dataInfo, stream);
157             HttpChannelOverSPDY channel = (HttpChannelOverSPDY)stream.getAttribute(CHANNEL_ATTRIBUTE);
158             channel.requestContent(dataInfo, dataInfo.isClose());
159         }
160 
161         @Override
162         public void onFailure(Stream stream, Throwable x)
163         {
164             LOG.debug(x);
165         }
166     }
167 }