Skip to content

Commit bcabce2

Browse files
authored
deps: replace Jetty with HttpServer (#433)
* feat: Replace Jetty with HttpServer. Remove Jetty v8.2 and replace with com.sun.net.httpserver.HttpServer. Jetty has had breaking changes since 8.2 (specifically with version 9.4). This causes with conflicts with newer code using later versions of Jetty. Removing dependency on Jetty resolves this issue. Fixes #397. * feat: Rewrite findOpenSocket using try with resources * fix: Remove commented out Jetty 9.4 code since we are using HttpServer now. * fix: Enforce Google coding style.
1 parent 303e1a1 commit bcabce2

File tree

3 files changed

+144
-73
lines changed

3 files changed

+144
-73
lines changed

google-oauth-client-jetty/pom.xml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,6 @@
8686
<groupId>com.google.oauth-client</groupId>
8787
<artifactId>google-oauth-client-java6</artifactId>
8888
</dependency>
89-
<dependency>
90-
<groupId>org.eclipse.jetty</groupId>
91-
<artifactId>jetty-server</artifactId>
92-
</dependency>
9389
<dependency>
9490
<groupId>junit</groupId>
9591
<artifactId>junit</artifactId>

google-oauth-client-jetty/src/main/java/com/google/api/client/extensions/jetty/auth/oauth2/LocalServerReceiver.java

Lines changed: 141 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,24 @@
1414

1515
package com.google.api.client.extensions.jetty.auth.oauth2;
1616

17+
import static java.net.HttpURLConnection.HTTP_MOVED_TEMP;
18+
import static java.net.HttpURLConnection.HTTP_OK;
19+
1720
import com.google.api.client.extensions.java6.auth.oauth2.VerificationCodeReceiver;
1821
import com.google.api.client.util.Throwables;
22+
import com.sun.net.httpserver.Headers;
23+
import com.sun.net.httpserver.HttpContext;
24+
import com.sun.net.httpserver.HttpExchange;
25+
import com.sun.net.httpserver.HttpHandler;
26+
import com.sun.net.httpserver.HttpServer;
1927
import java.io.IOException;
28+
import java.io.OutputStream;
2029
import java.io.PrintWriter;
30+
import java.net.InetSocketAddress;
31+
import java.net.ServerSocket;
32+
import java.util.HashMap;
33+
import java.util.Map;
2134
import java.util.concurrent.Semaphore;
22-
import javax.servlet.http.HttpServletRequest;
23-
import javax.servlet.http.HttpServletResponse;
24-
import org.eclipse.jetty.server.Connector;
25-
import org.eclipse.jetty.server.Request;
26-
import org.eclipse.jetty.server.Server;
27-
import org.eclipse.jetty.server.handler.AbstractHandler;
2835

2936
/**
3037
* OAuth 2.0 verification code receiver that runs a Jetty server on a free port, waiting for a
@@ -34,34 +41,48 @@
3441
* Implementation is thread-safe.
3542
* </p>
3643
*
37-
* @since 1.11
3844
* @author Yaniv Inbar
45+
* @since 1.11
3946
*/
4047
public final class LocalServerReceiver implements VerificationCodeReceiver {
4148

4249
private static final String LOCALHOST = "localhost";
4350

4451
private static final String CALLBACK_PATH = "/Callback";
4552

46-
/** Server or {@code null} before {@link #getRedirectUri()}. */
47-
private Server server;
53+
/**
54+
* Server or {@code null} before {@link #getRedirectUri()}.
55+
*/
56+
private HttpServer server;
4857

49-
/** Verification code or {@code null} for none. */
58+
/**
59+
* Verification code or {@code null} for none.
60+
*/
5061
String code;
5162

52-
/** Error code or {@code null} for none. */
63+
/**
64+
* Error code or {@code null} for none.
65+
*/
5366
String error;
5467

55-
/** To block until receiving an authorization response or stop() is called. */
68+
/**
69+
* To block until receiving an authorization response or stop() is called.
70+
*/
5671
final Semaphore waitUnlessSignaled = new Semaphore(0 /* initially zero permit */);
5772

58-
/** Port to use or {@code -1} to select an unused port in {@link #getRedirectUri()}. */
73+
/**
74+
* Port to use or {@code -1} to select an unused port in {@link #getRedirectUri()}.
75+
*/
5976
private int port;
6077

61-
/** Host name to use. */
78+
/**
79+
* Host name to use.
80+
*/
6281
private final String host;
6382

64-
/** Callback path of redirect_uri */
83+
/**
84+
* Callback path of redirect_uri.
85+
*/
6586
private final String callbackPath;
6687

6788
/**
@@ -71,8 +92,8 @@ public final class LocalServerReceiver implements VerificationCodeReceiver {
7192
private String successLandingPageUrl;
7293

7394
/**
74-
* URL to an HTML page to be shown (via redirect) after failed login. If null, a canned
75-
* default landing page will be shown (via direct response).
95+
* URL to an HTML page to be shown (via redirect) after failed login. If null, a canned default
96+
* landing page will be shown (via direct response).
7697
*/
7798
private String failureLandingPageUrl;
7899

@@ -94,7 +115,7 @@ public LocalServerReceiver() {
94115
* @param port Port to use or {@code -1} to select an unused port
95116
*/
96117
LocalServerReceiver(String host, int port,
97-
String successLandingPageUrl, String failureLandingPageUrl) {
118+
String successLandingPageUrl, String failureLandingPageUrl) {
98119
this(host, port, CALLBACK_PATH, successLandingPageUrl, failureLandingPageUrl);
99120
}
100121

@@ -105,7 +126,7 @@ public LocalServerReceiver() {
105126
* @param port Port to use or {@code -1} to select an unused port
106127
*/
107128
LocalServerReceiver(String host, int port, String callbackPath,
108-
String successLandingPageUrl, String failureLandingPageUrl) {
129+
String successLandingPageUrl, String failureLandingPageUrl) {
109130
this.host = host;
110131
this.port = port;
111132
this.callbackPath = callbackPath;
@@ -115,28 +136,42 @@ public LocalServerReceiver() {
115136

116137
@Override
117138
public String getRedirectUri() throws IOException {
118-
server = new Server(port != -1 ? port : 0);
119-
Connector connector = server.getConnectors()[0];
120-
connector.setHost(host);
121-
server.setHandler(new CallbackHandler());
139+
140+
server = HttpServer.create(new InetSocketAddress(port != -1 ? port : findOpenPort()), 0);
141+
HttpContext context = server.createContext(callbackPath, new CallbackHandler());
142+
server.setExecutor(null);
143+
122144
try {
123145
server.start();
124-
port = connector.getLocalPort();
146+
port = server.getAddress().getPort();
125147
} catch (Exception e) {
126148
Throwables.propagateIfPossible(e);
127149
throw new IOException(e);
128150
}
129-
return "http://" + connector.getHost() + ":" + port + callbackPath;
151+
return "http://" + this.getHost() + ":" + port + callbackPath;
152+
}
153+
154+
/*
155+
*Copied from Jetty findFreePort() as referenced by: https://gist.github.com/vorburger/3429822
156+
*/
157+
158+
private int findOpenPort() {
159+
try (ServerSocket socket = new ServerSocket(0)) {
160+
socket.setReuseAddress(true);
161+
return socket.getLocalPort();
162+
} catch (IOException e) {
163+
throw new IllegalStateException("No free TCP/IP port to start embedded HTTP Server on");
164+
}
130165
}
131166

132167
/**
133-
* Blocks until the server receives a login result, or the server is stopped
134-
* by {@link #stop()}, to return an authorization code.
168+
* Blocks until the server receives a login result, or the server is stopped by {@link #stop()},
169+
* to return an authorization code.
135170
*
136-
* @return authorization code if login succeeds; may return {@code null} if the server
137-
* is stopped by {@link #stop()}
138-
* @throws IOException if the server receives an error code (through an HTTP request
139-
* parameter {@code error})
171+
* @return authorization code if login succeeds; may return {@code null} if the server is stopped
172+
* by {@link #stop()}
173+
* @throws IOException if the server receives an error code (through an HTTP request parameter
174+
* {@code error})
140175
*/
141176
@Override
142177
public String waitForCode() throws IOException {
@@ -152,7 +187,7 @@ public void stop() throws IOException {
152187
waitUnlessSignaled.release();
153188
if (server != null) {
154189
try {
155-
server.stop();
190+
server.stop(0);
156191
} catch (Exception e) {
157192
Throwables.propagateIfPossible(e);
158193
throw new IOException(e);
@@ -161,7 +196,9 @@ public void stop() throws IOException {
161196
}
162197
}
163198

164-
/** Returns the host name to use. */
199+
/**
200+
* Returns the host name to use.
201+
*/
165202
public String getHost() {
166203
return host;
167204
}
@@ -189,51 +226,69 @@ public String getCallbackPath() {
189226
*/
190227
public static final class Builder {
191228

192-
/** Host name to use. */
229+
/**
230+
* Host name to use.
231+
*/
193232
private String host = LOCALHOST;
194233

195-
/** Port to use or {@code -1} to select an unused port. */
234+
/**
235+
* Port to use or {@code -1} to select an unused port.
236+
*/
196237
private int port = -1;
197238

198239
private String successLandingPageUrl;
199240
private String failureLandingPageUrl;
200241

201242
private String callbackPath = CALLBACK_PATH;
202243

203-
/** Builds the {@link LocalServerReceiver}. */
244+
/**
245+
* Builds the {@link LocalServerReceiver}.
246+
*/
204247
public LocalServerReceiver build() {
205248
return new LocalServerReceiver(host, port, callbackPath,
206-
successLandingPageUrl, failureLandingPageUrl);
249+
successLandingPageUrl, failureLandingPageUrl);
207250
}
208251

209-
/** Returns the host name to use. */
252+
/**
253+
* Returns the host name to use.
254+
*/
210255
public String getHost() {
211256
return host;
212257
}
213258

214-
/** Sets the host name to use. */
259+
/**
260+
* Sets the host name to use.
261+
*/
215262
public Builder setHost(String host) {
216263
this.host = host;
217264
return this;
218265
}
219266

220-
/** Returns the port to use or {@code -1} to select an unused port. */
267+
/**
268+
* Returns the port to use or {@code -1} to select an unused port.
269+
*/
221270
public int getPort() {
222271
return port;
223272
}
224273

225-
/** Sets the port to use or {@code -1} to select an unused port. */
274+
/**
275+
* Sets the port to use or {@code -1} to select an unused port.
276+
*/
226277
public Builder setPort(int port) {
227278
this.port = port;
228279
return this;
229280
}
230281

231-
/** Returns the callback path of redirect_uri */
282+
/**
283+
* Returns the callback path of redirect_uri.
284+
*/
232285
public String getCallbackPath() {
233286
return callbackPath;
234287
}
235288

236-
/** Set the callback path of redirect_uri */
289+
/**
290+
* Set the callback path of redirect_uri.
291+
*/
237292
public Builder setCallbackPath(String callbackPath) {
238293
this.callbackPath = callbackPath;
239294
return this;
@@ -247,51 +302,73 @@ public Builder setLandingPages(String successLandingPageUrl, String failureLandi
247302
}
248303

249304
/**
250-
* Jetty handler that takes the verifier token passed over from the OAuth provider and stashes it
251-
* where {@link #waitForCode} will find it.
305+
* HttpServer handler that takes the verifier token passed over from the OAuth provider and
306+
* stashes it where {@link #waitForCode} will find it.
252307
*/
253-
class CallbackHandler extends AbstractHandler {
308+
class CallbackHandler implements HttpHandler {
254309

255310
@Override
256-
public void handle(
257-
String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response
258-
)
259-
throws IOException {
260-
if (!callbackPath.equals(target)) {
311+
public void handle(HttpExchange httpExchange) throws IOException {
312+
313+
if (!callbackPath.equals(httpExchange.getRequestURI().getPath())) {
261314
return;
262315
}
263316

317+
StringBuilder body = new StringBuilder();
318+
264319
try {
265-
((Request) request).setHandled(true);
266-
error = request.getParameter("error");
267-
code = request.getParameter("code");
320+
Map<String, String> parms =
321+
this.queryToMap(httpExchange.getRequestURI().getQuery());
322+
error = parms.get("error");
323+
code = parms.get("code");
268324

325+
Headers respHeaders = httpExchange.getResponseHeaders();
269326
if (error == null && successLandingPageUrl != null) {
270-
response.sendRedirect(successLandingPageUrl);
327+
respHeaders.add("Location", successLandingPageUrl);
328+
httpExchange.sendResponseHeaders(HTTP_MOVED_TEMP, -1);
271329
} else if (error != null && failureLandingPageUrl != null) {
272-
response.sendRedirect(failureLandingPageUrl);
330+
respHeaders.add("Location", failureLandingPageUrl);
331+
httpExchange.sendResponseHeaders(HTTP_MOVED_TEMP, -1);
273332
} else {
274-
writeLandingHtml(response);
333+
writeLandingHtml(httpExchange, respHeaders);
275334
}
276-
response.flushBuffer();
277-
}
278-
finally {
335+
httpExchange.close();
336+
} finally {
279337
waitUnlessSignaled.release();
280338
}
281339
}
282340

283-
private void writeLandingHtml(HttpServletResponse response) throws IOException {
284-
response.setStatus(HttpServletResponse.SC_OK);
285-
response.setContentType("text/html");
341+
private Map<String, String> queryToMap(String query) {
342+
Map<String, String> result = new HashMap<String, String>();
343+
if (query != null) {
344+
for (String param : query.split("&")) {
345+
String pair[] = param.split("=");
346+
if (pair.length > 1) {
347+
result.put(pair[0], pair[1]);
348+
} else {
349+
result.put(pair[0], "");
350+
}
351+
}
352+
}
353+
return result;
354+
}
355+
356+
private void writeLandingHtml(HttpExchange exchange, Headers headers) throws IOException {
357+
OutputStream os = exchange.getResponseBody();
358+
exchange.sendResponseHeaders(HTTP_OK, 0);
359+
headers.add("ContentType", "text/html");
286360

287-
PrintWriter doc = response.getWriter();
361+
PrintWriter doc = new PrintWriter(os);
288362
doc.println("<html>");
289363
doc.println("<head><title>OAuth 2.0 Authentication Token Received</title></head>");
290364
doc.println("<body>");
291365
doc.println("Received verification code. You may now close this window.");
292366
doc.println("</body>");
293367
doc.println("</html>");
294368
doc.flush();
369+
os.close();
295370
}
371+
296372
}
373+
297374
}

0 commit comments

Comments
 (0)