1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.fcgi.server;
20
21 import java.util.Locale;
22 import java.util.concurrent.Executor;
23 import java.util.concurrent.atomic.AtomicReference;
24
25 import org.eclipse.jetty.fcgi.FCGI;
26 import org.eclipse.jetty.http.HostPortHttpField;
27 import org.eclipse.jetty.http.HttpField;
28 import org.eclipse.jetty.http.HttpFields;
29 import org.eclipse.jetty.http.HttpHeader;
30 import org.eclipse.jetty.http.HttpScheme;
31 import org.eclipse.jetty.http.HttpVersion;
32 import org.eclipse.jetty.http.MetaData;
33 import org.eclipse.jetty.io.EndPoint;
34 import org.eclipse.jetty.server.Connector;
35 import org.eclipse.jetty.server.HttpChannel;
36 import org.eclipse.jetty.server.HttpConfiguration;
37 import org.eclipse.jetty.server.HttpTransport;
38 import org.eclipse.jetty.util.log.Log;
39 import org.eclipse.jetty.util.log.Logger;
40
41 public class HttpChannelOverFCGI extends HttpChannel
42 {
43 private static final Logger LOG = Log.getLogger(HttpChannelOverFCGI.class);
44
45 private final HttpFields fields = new HttpFields();
46 private final Dispatcher dispatcher;
47 private String method;
48 private String path;
49 private String query;
50 private String version;
51 private HostPortHttpField hostPort;
52
53 public HttpChannelOverFCGI(Connector connector, HttpConfiguration configuration, EndPoint endPoint, HttpTransport transport)
54 {
55 super(connector, configuration, endPoint, transport);
56 this.dispatcher = new Dispatcher(connector.getServer().getThreadPool(), this);
57 }
58
59 protected void header(HttpField field)
60 {
61 String name = field.getName();
62 String value = field.getValue();
63 getRequest().setAttribute(name, value);
64 if (FCGI.Headers.REQUEST_METHOD.equalsIgnoreCase(name))
65 method = value;
66 else if (FCGI.Headers.DOCUMENT_URI.equalsIgnoreCase(name))
67 path = value;
68 else if (FCGI.Headers.QUERY_STRING.equalsIgnoreCase(name))
69 query = value;
70 else if (FCGI.Headers.SERVER_PROTOCOL.equalsIgnoreCase(name))
71 version = value;
72 else
73 processField(field);
74 }
75
76 private void processField(HttpField field)
77 {
78 HttpField httpField = convertHeader(field);
79 if (httpField != null)
80 {
81 fields.add(httpField);
82 if (HttpHeader.HOST.is(httpField.getName()))
83 hostPort = (HostPortHttpField)httpField;
84 }
85 }
86
87 public void onRequest()
88 {
89 String uri = path;
90 if (query != null && query.length() > 0)
91 uri += "?" + query;
92
93 onRequest(new MetaData.Request(method, HttpScheme.HTTP.asString(), hostPort, uri, HttpVersion.fromString(version), fields,Long.MIN_VALUE));
94 }
95
96 private HttpField convertHeader(HttpField field)
97 {
98 String name = field.getName();
99 if (name.startsWith("HTTP_"))
100 {
101
102 String[] parts = name.split("_");
103 StringBuilder httpName = new StringBuilder();
104 for (int i = 1; i < parts.length; ++i)
105 {
106 if (i > 1)
107 httpName.append("-");
108 String part = parts[i];
109 httpName.append(Character.toUpperCase(part.charAt(0)));
110 httpName.append(part.substring(1).toLowerCase(Locale.ENGLISH));
111 }
112 String headerName = httpName.toString();
113 String value = field.getValue();
114 if (HttpHeader.HOST.is(headerName))
115 return new HostPortHttpField(value);
116 else
117 return new HttpField(headerName, value);
118 }
119 return null;
120 }
121
122 protected void dispatch()
123 {
124 dispatcher.dispatch();
125 }
126
127 private static class Dispatcher implements Runnable
128 {
129 private final AtomicReference<State> state = new AtomicReference<>(State.IDLE);
130 private final Executor executor;
131 private final Runnable runnable;
132
133 private Dispatcher(Executor executor, Runnable runnable)
134 {
135 this.executor = executor;
136 this.runnable = runnable;
137 }
138
139 public void dispatch()
140 {
141 while (true)
142 {
143 State current = state.get();
144 if (LOG.isDebugEnabled())
145 LOG.debug("Dispatching, state={}", current);
146 switch (current)
147 {
148 case IDLE:
149 {
150 if (!state.compareAndSet(current, State.DISPATCH))
151 continue;
152 executor.execute(this);
153 return;
154 }
155 case DISPATCH:
156 case EXECUTE:
157 {
158 if (state.compareAndSet(current, State.SCHEDULE))
159 return;
160 continue;
161 }
162 case SCHEDULE:
163 {
164 return;
165 }
166 default:
167 {
168 throw new IllegalStateException();
169 }
170 }
171 }
172 }
173
174 @Override
175 public void run()
176 {
177 while (true)
178 {
179 State current = state.get();
180 if (LOG.isDebugEnabled())
181 LOG.debug("Running, state={}", current);
182 switch (current)
183 {
184 case DISPATCH:
185 {
186 if (state.compareAndSet(current, State.EXECUTE))
187 runnable.run();
188 continue;
189 }
190 case EXECUTE:
191 {
192 if (state.compareAndSet(current, State.IDLE))
193 return;
194 continue;
195 }
196 case SCHEDULE:
197 {
198 if (state.compareAndSet(current, State.DISPATCH))
199 continue;
200 throw new IllegalStateException();
201 }
202 default:
203 {
204 throw new IllegalStateException();
205 }
206 }
207 }
208 }
209
210 private enum State
211 {
212 IDLE, DISPATCH, EXECUTE, SCHEDULE
213 }
214 }
215 }