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.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;
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
134
135
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
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
209 continue;
210 }
211 default:
212 {
213
214 String value = header.value();
215 LOG.debug("HTTP > {}: {}", name, value);
216 parsedHeader(httpHeader, name, value);
217 break;
218 }
219 }
220 }
221 }
222 }