1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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.HttpField;
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.HttpConfiguration;
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.BufferUtil;
37 import org.eclipse.jetty.util.Fields;
38 import org.eclipse.jetty.util.log.Log;
39 import org.eclipse.jetty.util.log.Logger;
40
41 public class HttpChannelOverSPDY extends HttpChannel<DataInfo>
42 {
43 private static final Logger LOG = Log.getLogger(HttpChannelOverSPDY.class);
44
45 private final Queue<Runnable> tasks = new LinkedList<>();
46 private final Stream stream;
47 private boolean dispatched;
48 private boolean headersComplete;
49
50 public HttpChannelOverSPDY(Connector connector, HttpConfiguration configuration, EndPoint endPoint, HttpTransport transport, HttpInputOverSPDY input, Stream stream)
51 {
52 super(connector, configuration, endPoint, transport, input);
53 this.stream = stream;
54 }
55
56 @Override
57 public boolean headerComplete()
58 {
59 headersComplete = true;
60 return super.headerComplete();
61 }
62
63 private void post(Runnable task)
64 {
65 synchronized (tasks)
66 {
67 LOG.debug("Posting task {}", task);
68 tasks.offer(task);
69 dispatch();
70 }
71 }
72
73 private void dispatch()
74 {
75 synchronized (tasks)
76 {
77 if (dispatched)
78 return;
79
80 final Runnable task = tasks.poll();
81 if (task != null)
82 {
83 dispatched = true;
84 LOG.debug("Dispatching task {}", task);
85 execute(new Runnable()
86 {
87 @Override
88 public void run()
89 {
90 LOG.debug("Executing task {}", task);
91 task.run();
92 LOG.debug("Completing task {}", task);
93 dispatched = false;
94 dispatch();
95 }
96 });
97 }
98 }
99 }
100
101 public void requestStart(final Fields headers, final boolean endRequest)
102 {
103 if (!headers.isEmpty())
104 requestHeaders(headers, endRequest);
105 }
106
107 public void requestHeaders(Fields headers, boolean endRequest)
108 {
109 boolean proceed = performBeginRequest(headers);
110 if (!proceed)
111 return;
112
113 performHeaders(headers);
114
115 if (endRequest)
116 {
117 if (headerComplete())
118 post(this);
119 if (messageComplete())
120 post(this);
121 }
122 }
123
124 public void requestContent(final DataInfo dataInfo, boolean endRequest)
125 {
126 if (!headersComplete)
127 {
128 if (headerComplete())
129 post(this);
130 }
131
132 LOG.debug("HTTP > {} bytes of content", dataInfo.length());
133
134
135
136
137 ByteBuffer copyByteBuffer = dataInfo.asByteBuffer(false);
138 ByteBufferDataInfo copyDataInfo = new ByteBufferDataInfo(copyByteBuffer, dataInfo.isClose())
139 {
140 @Override
141 public void consume(int delta)
142 {
143 super.consume(delta);
144 dataInfo.consume(delta);
145 }
146 };
147 LOG.debug("Queuing last={} content {}", endRequest, copyDataInfo);
148
149 if (content(copyDataInfo))
150 post(this);
151
152 if (endRequest)
153 {
154 if (messageComplete())
155 post(this);
156 }
157 }
158
159 private boolean performBeginRequest(Fields headers)
160 {
161 short version = stream.getSession().getVersion();
162 Fields.Field methodHeader = headers.get(HTTPSPDYHeader.METHOD.name(version));
163 Fields.Field uriHeader = headers.get(HTTPSPDYHeader.URI.name(version));
164 Fields.Field versionHeader = headers.get(HTTPSPDYHeader.VERSION.name(version));
165
166 if (methodHeader == null || uriHeader == null || versionHeader == null)
167 {
168 badMessage(400, "Missing required request line elements");
169 return false;
170 }
171
172 HttpMethod httpMethod = HttpMethod.fromString(methodHeader.value());
173 HttpVersion httpVersion = HttpVersion.fromString(versionHeader.value());
174
175
176
177 ByteBuffer uri = BufferUtil.toBuffer(uriHeader.value());
178
179 LOG.debug("HTTP > {} {} {}", httpMethod, uriHeader.value(), httpVersion);
180 startRequest(httpMethod, httpMethod.asString(), uri, httpVersion);
181
182 Fields.Field schemeHeader = headers.get(HTTPSPDYHeader.SCHEME.name(version));
183 if (schemeHeader != null)
184 getRequest().setScheme(schemeHeader.value());
185 return true;
186 }
187
188 private void performHeaders(Fields headers)
189 {
190 for (Fields.Field header : headers)
191 {
192 String name = header.name();
193
194
195 HTTPSPDYHeader specialHeader = HTTPSPDYHeader.from(stream.getSession().getVersion(), name);
196 if (specialHeader != null)
197 {
198 if (specialHeader == HTTPSPDYHeader.HOST)
199 name = "host";
200 else
201 continue;
202 }
203
204 switch (name)
205 {
206 case "connection":
207 case "keep-alive":
208 case "proxy-connection":
209 case "transfer-encoding":
210 {
211
212 continue;
213 }
214 default:
215 {
216
217 String value = header.value();
218 LOG.debug("HTTP > {}: {}", name, value);
219 parsedHeader(new HttpField(name,value));
220 break;
221 }
222 }
223 }
224 }
225 }