View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2012 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.spdy.server.http;
20  
21  import java.nio.ByteBuffer;
22  import java.util.LinkedList;
23  import java.util.Queue;
24  
25  import org.eclipse.jetty.http.HttpHeader;
26  import org.eclipse.jetty.http.HttpMethod;
27  import org.eclipse.jetty.http.HttpVersion;
28  import org.eclipse.jetty.io.EndPoint;
29  import org.eclipse.jetty.server.Connector;
30  import org.eclipse.jetty.server.HttpChannel;
31  import org.eclipse.jetty.server.HttpChannelConfig;
32  import org.eclipse.jetty.server.HttpTransport;
33  import org.eclipse.jetty.spdy.api.ByteBufferDataInfo;
34  import org.eclipse.jetty.spdy.api.DataInfo;
35  import org.eclipse.jetty.spdy.api.Stream;
36  import org.eclipse.jetty.util.Fields;
37  import org.eclipse.jetty.util.log.Log;
38  import org.eclipse.jetty.util.log.Logger;
39  
40  public class HttpChannelOverSPDY extends HttpChannel<DataInfo>
41  {
42      private static final Logger LOG = Log.getLogger(HttpChannelOverSPDY.class);
43  
44      private final Queue<Runnable> tasks = new LinkedList<>();
45      private final Stream stream;
46      private boolean dispatched; // Guarded by synchronization on tasks
47      private boolean headersComplete;
48  
49      public HttpChannelOverSPDY(Connector connector, HttpChannelConfig configuration, EndPoint endPoint, HttpTransport transport, HttpInputOverSPDY input, Stream stream)
50      {
51          super(connector, configuration, endPoint, transport, input);
52          this.stream = stream;
53      }
54  
55      @Override
56      public boolean headerComplete()
57      {
58          headersComplete = true;
59          return super.headerComplete();
60      }
61  
62      private void post(Runnable task)
63      {
64          synchronized (tasks)
65          {
66              LOG.debug("Posting task {}", task);
67              tasks.offer(task);
68              dispatch();
69          }
70      }
71  
72      private void dispatch()
73      {
74          synchronized (tasks)
75          {
76              if (dispatched)
77                  return;
78  
79              final Runnable task = tasks.poll();
80              if (task != null)
81              {
82                  dispatched = true;
83                  LOG.debug("Dispatching task {}", task);
84                  execute(new Runnable()
85                  {
86                      @Override
87                      public void run()
88                      {
89                          LOG.debug("Executing task {}", task);
90                          task.run();
91                          LOG.debug("Completing task {}", task);
92                          dispatched = false;
93                          dispatch();
94                      }
95                  });
96              }
97          }
98      }
99  
100     public void requestStart(final Fields headers, final boolean endRequest)
101     {
102         if (!headers.isEmpty())
103             requestHeaders(headers, endRequest);
104     }
105 
106     public void requestHeaders(Fields headers, boolean endRequest)
107     {
108         boolean proceed = performBeginRequest(headers);
109         if (!proceed)
110             return;
111 
112         performHeaders(headers);
113 
114         if (endRequest)
115         {
116             if (headerComplete())
117                 post(this);
118             if (messageComplete(-1))
119                 post(this);
120         }
121     }
122 
123     public void requestContent(final DataInfo dataInfo, boolean endRequest)
124     {
125         if (!headersComplete)
126         {
127             if (headerComplete())
128                 post(this);
129         }
130 
131         LOG.debug("HTTP > {} bytes of content", dataInfo.length());
132 
133         // We need to copy the dataInfo since we do not know when its bytes
134         // will be consumed. When the copy is consumed, we consume also the
135         // original, so the implementation can send a window update.
136         ByteBuffer copyByteBuffer = dataInfo.asByteBuffer(false);
137         ByteBufferDataInfo copyDataInfo = new ByteBufferDataInfo(copyByteBuffer, dataInfo.isClose(), dataInfo.isCompress())
138         {
139             @Override
140             public void consume(int delta)
141             {
142                 super.consume(delta);
143                 dataInfo.consume(delta);
144             }
145         };
146         LOG.debug("Queuing last={} content {}", endRequest, copyDataInfo);
147 
148         if (content(copyDataInfo))
149             post(this);
150 
151         if (endRequest)
152         {
153             if (messageComplete(-1))
154                 post(this);
155         }
156     }
157 
158     private boolean performBeginRequest(Fields headers)
159     {
160         short version = stream.getSession().getVersion();
161         Fields.Field methodHeader = headers.get(HTTPSPDYHeader.METHOD.name(version));
162         Fields.Field uriHeader = headers.get(HTTPSPDYHeader.URI.name(version));
163         Fields.Field versionHeader = headers.get(HTTPSPDYHeader.VERSION.name(version));
164 
165         if (methodHeader == null || uriHeader == null || versionHeader == null)
166         {
167             badMessage(400, "Missing required request line elements");
168             return false;
169         }
170 
171         HttpMethod httpMethod = HttpMethod.fromString(methodHeader.value());
172         HttpVersion httpVersion = HttpVersion.fromString(versionHeader.value());
173         String uriString = uriHeader.value();
174 
175         LOG.debug("HTTP > {} {} {}", httpMethod, uriString, httpVersion);
176         startRequest(httpMethod, httpMethod.asString(), uriString, httpVersion);
177 
178         Fields.Field schemeHeader = headers.get(HTTPSPDYHeader.SCHEME.name(version));
179         if (schemeHeader != null)
180             getRequest().setScheme(schemeHeader.value());
181         return true;
182     }
183 
184     private void performHeaders(Fields headers)
185     {
186         for (Fields.Field header : headers)
187         {
188             String name = header.name();
189             HttpHeader httpHeader = HttpHeader.CACHE.get(name);
190 
191             // Skip special SPDY headers, unless it's the "host" header
192             HTTPSPDYHeader specialHeader = HTTPSPDYHeader.from(stream.getSession().getVersion(), name);
193             if (specialHeader != null)
194             {
195                 if (specialHeader == HTTPSPDYHeader.HOST)
196                     name = "host";
197                 else
198                     continue;
199             }
200 
201             switch (name)
202             {
203                 case "connection":
204                 case "keep-alive":
205                 case "proxy-connection":
206                 case "transfer-encoding":
207                 {
208                     // Spec says to ignore these headers
209                     continue;
210                 }
211                 default:
212                 {
213                     // Spec says headers must be single valued
214                     String value = header.value();
215                     LOG.debug("HTTP > {}: {}", name, value);
216                     parsedHeader(httpHeader, name, value);
217                     break;
218                 }
219             }
220         }
221     }
222 }