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.getExecutor(), this);
57 }
58
59 protected void header(HttpField field)
60 {
61 if (FCGI.Headers.REQUEST_METHOD.equalsIgnoreCase(field.getName()))
62 method = field.getValue();
63 else if (FCGI.Headers.DOCUMENT_URI.equalsIgnoreCase(field.getName()))
64 path = field.getValue();
65 else if (FCGI.Headers.QUERY_STRING.equalsIgnoreCase(field.getName()))
66 query = field.getValue();
67 else if (FCGI.Headers.SERVER_PROTOCOL.equalsIgnoreCase(field.getName()))
68 version = field.getValue();
69 else
70 processField(field);
71 }
72
73 private void processField(HttpField field)
74 {
75 HttpField httpField = convertHeader(field);
76 if (httpField != null)
77 {
78 fields.add(httpField);
79 if (HttpHeader.HOST.is(httpField.getName()))
80 hostPort = (HostPortHttpField)httpField;
81 }
82 }
83
84 public void onRequest()
85 {
86 String uri = path;
87 if (query != null && query.length() > 0)
88 uri += "?" + query;
89
90 onRequest(new MetaData.Request(method, HttpScheme.HTTP.asString(), hostPort, uri, HttpVersion.fromString(version), fields,Long.MIN_VALUE));
91 }
92
93 private HttpField convertHeader(HttpField field)
94 {
95 String name = field.getName();
96 if (name.startsWith("HTTP_"))
97 {
98
99 String[] parts = name.split("_");
100 StringBuilder httpName = new StringBuilder();
101 for (int i = 1; i < parts.length; ++i)
102 {
103 if (i > 1)
104 httpName.append("-");
105 String part = parts[i];
106 httpName.append(Character.toUpperCase(part.charAt(0)));
107 httpName.append(part.substring(1).toLowerCase(Locale.ENGLISH));
108 }
109 String headerName = httpName.toString();
110 if (HttpHeader.HOST.is(headerName))
111 return new HostPortHttpField(field.getValue());
112 else
113 return new HttpField(httpName.toString(), field.getValue());
114 }
115 return null;
116 }
117
118 protected void dispatch()
119 {
120 dispatcher.dispatch();
121 }
122
123 private static class Dispatcher implements Runnable
124 {
125 private final AtomicReference<State> state = new AtomicReference<>(State.IDLE);
126 private final Executor executor;
127 private final Runnable runnable;
128
129 private Dispatcher(Executor executor, Runnable runnable)
130 {
131 this.executor = executor;
132 this.runnable = runnable;
133 }
134
135 public void dispatch()
136 {
137 while (true)
138 {
139 State current = state.get();
140 if (LOG.isDebugEnabled())
141 LOG.debug("Dispatching, state={}", current);
142 switch (current)
143 {
144 case IDLE:
145 {
146 if (!state.compareAndSet(current, State.DISPATCH))
147 continue;
148 executor.execute(this);
149 return;
150 }
151 case DISPATCH:
152 case EXECUTE:
153 {
154 if (state.compareAndSet(current, State.SCHEDULE))
155 return;
156 continue;
157 }
158 case SCHEDULE:
159 {
160 return;
161 }
162 default:
163 {
164 throw new IllegalStateException();
165 }
166 }
167 }
168 }
169
170 @Override
171 public void run()
172 {
173 while (true)
174 {
175 State current = state.get();
176 if (LOG.isDebugEnabled())
177 LOG.debug("Running, state={}", current);
178 switch (current)
179 {
180 case DISPATCH:
181 {
182 if (state.compareAndSet(current, State.EXECUTE))
183 runnable.run();
184 continue;
185 }
186 case EXECUTE:
187 {
188 if (state.compareAndSet(current, State.IDLE))
189 return;
190 continue;
191 }
192 case SCHEDULE:
193 {
194 if (state.compareAndSet(current, State.DISPATCH))
195 continue;
196 throw new IllegalStateException();
197 }
198 default:
199 {
200 throw new IllegalStateException();
201 }
202 }
203 }
204 }
205
206 private enum State
207 {
208 IDLE, DISPATCH, EXECUTE, SCHEDULE
209 }
210 }
211 }