Merge lp:~tjoneslo/akiban-server/add-http-session-table into lp:~akiban-technologies/akiban-server/trunk
- add-http-session-table
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Mike McMahon |
Approved revision: | 2646 |
Merged at revision: | 2653 |
Proposed branch: | lp:~tjoneslo/akiban-server/add-http-session-table |
Merge into: | lp:~akiban-technologies/akiban-server/trunk |
Diff against target: |
536 lines (+400/-8) 5 files modified
src/main/java/com/akiban/http/HttpConductorImpl.java (+81/-6) src/main/java/com/akiban/http/SimpleHandlerList.java (+14/-0) src/test/java/com/akiban/http/HttpMonitorVerifyIT.java (+106/-0) src/test/java/com/akiban/http/HttpMonitorVerifySSLIT.java (+199/-0) src/test/java/com/akiban/http/HttpThreadedLoginIT.java (+0/-2) |
To merge this branch: | bzr merge lp:~tjoneslo/akiban-server/add-http-session-table |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Mike McMahon | Approve | ||
Review via email: mp+161259@code.launchpad.net |
Commit message
Description of the change
Per the demo today, add the ability for the HttpConnections to be seen in the IS.SERVER_SESSIONS table. This allows both the REST and Postgres server connections to be tracked and some accounting performed.
This is still mostly infrastructure. There are some basic statistics capture, but a further review of the business requirements should drive design of more statistics capture.
Also add test to verify this.
Thomas Jones-Low (tjoneslo) wrote : | # |
It is possible to allow the REST processing to use the session created for the HTTP connection.
It requires a chunk of refactoring that I'd prefer to do in a separate commit.
Thomas Jones-Low (tjoneslo) wrote : | # |
It's possible, but is it ok to use the same Session across multiple threads?
The interaction of the HTTP connection (jetty) and the Servlet processing (Jersey) is via a (theoretical) thread pool. As each request comes across the wire into the connection, it gets packaged into the Request/Response and given to the servlet to process on its own thread. When the Servlet finishes, the the Response it picked up by the connection thread and sent back across the wire.
So the safe thing to do is to create a new session for each Servlet request (i.e. as we do now). Nathan and I had a discussion on the wiki page regarding the REST processing not mapping directly to the Postgres processing, and this is one of those points.
Mike McMahon (mmcm) wrote : | # |
Okay, seems that at the least more throught is required to do anything there.
Preview Diff
1 | === modified file 'src/main/java/com/akiban/http/HttpConductorImpl.java' | |||
2 | --- src/main/java/com/akiban/http/HttpConductorImpl.java 2013-04-22 22:58:39 +0000 | |||
3 | +++ src/main/java/com/akiban/http/HttpConductorImpl.java 2013-04-26 23:40:34 +0000 | |||
4 | @@ -22,8 +22,15 @@ | |||
5 | 22 | import com.akiban.server.service.monitor.MonitorService; | 22 | import com.akiban.server.service.monitor.MonitorService; |
6 | 23 | import com.akiban.server.service.monitor.ServerMonitor; | 23 | import com.akiban.server.service.monitor.ServerMonitor; |
7 | 24 | import com.akiban.server.service.security.SecurityService; | 24 | import com.akiban.server.service.security.SecurityService; |
9 | 25 | import com.akiban.sql.embedded.EmbeddedJDBCService; | 25 | import com.akiban.server.service.session.Session; |
10 | 26 | import com.akiban.server.service.session.SessionService; | ||
11 | 27 | import com.akiban.sql.server.ServerSessionMonitor; | ||
12 | 26 | import com.google.inject.Inject; | 28 | import com.google.inject.Inject; |
13 | 29 | |||
14 | 30 | import org.eclipse.jetty.io.AsyncEndPoint; | ||
15 | 31 | import org.eclipse.jetty.io.Connection; | ||
16 | 32 | import org.eclipse.jetty.io.nio.AsyncConnection; | ||
17 | 33 | import org.eclipse.jetty.io.nio.SslConnection; | ||
18 | 27 | import org.eclipse.jetty.servlets.CrossOriginFilter; | 34 | import org.eclipse.jetty.servlets.CrossOriginFilter; |
19 | 28 | import org.eclipse.jetty.util.security.Constraint; | 35 | import org.eclipse.jetty.util.security.Constraint; |
20 | 29 | import org.eclipse.jetty.security.Authenticator; | 36 | import org.eclipse.jetty.security.Authenticator; |
21 | @@ -32,6 +39,7 @@ | |||
22 | 32 | import org.eclipse.jetty.security.LoginService; | 39 | import org.eclipse.jetty.security.LoginService; |
23 | 33 | import org.eclipse.jetty.security.authentication.BasicAuthenticator; | 40 | import org.eclipse.jetty.security.authentication.BasicAuthenticator; |
24 | 34 | import org.eclipse.jetty.security.authentication.DigestAuthenticator; | 41 | import org.eclipse.jetty.security.authentication.DigestAuthenticator; |
25 | 42 | import org.eclipse.jetty.server.AsyncHttpConnection; | ||
26 | 35 | import org.eclipse.jetty.server.Connector; | 43 | import org.eclipse.jetty.server.Connector; |
27 | 36 | import org.eclipse.jetty.server.Server; | 44 | import org.eclipse.jetty.server.Server; |
28 | 37 | import org.eclipse.jetty.server.handler.ContextHandler; | 45 | import org.eclipse.jetty.server.handler.ContextHandler; |
29 | @@ -44,6 +52,8 @@ | |||
30 | 44 | 52 | ||
31 | 45 | import javax.servlet.FilterRegistration; | 53 | import javax.servlet.FilterRegistration; |
32 | 46 | import javax.servlet.ServletException; | 54 | import javax.servlet.ServletException; |
33 | 55 | |||
34 | 56 | import java.nio.channels.SocketChannel; | ||
35 | 47 | import java.util.Collections; | 57 | import java.util.Collections; |
36 | 48 | import java.util.HashSet; | 58 | import java.util.HashSet; |
37 | 49 | import java.util.Set; | 59 | import java.util.Set; |
38 | @@ -66,10 +76,12 @@ | |||
39 | 66 | private static final String CONFIG_XORIGIN_CREDENTIALS = CONFIG_XORIGIN_PREFIX + "allow_credentials"; | 76 | private static final String CONFIG_XORIGIN_CREDENTIALS = CONFIG_XORIGIN_PREFIX + "allow_credentials"; |
40 | 67 | 77 | ||
41 | 68 | private static final String REST_ROLE = "rest-user"; | 78 | private static final String REST_ROLE = "rest-user"; |
42 | 79 | public static final String SERVER_TYPE = "REST"; | ||
43 | 69 | 80 | ||
44 | 70 | private final ConfigurationService configurationService; | 81 | private final ConfigurationService configurationService; |
45 | 71 | private final SecurityService securityService; | 82 | private final SecurityService securityService; |
46 | 72 | private final MonitorService monitorService; | 83 | private final MonitorService monitorService; |
47 | 84 | private final SessionService sessionService; | ||
48 | 73 | 85 | ||
49 | 74 | private final Object lock = new Object(); | 86 | private final Object lock = new Object(); |
50 | 75 | private SimpleHandlerList handlerList; | 87 | private SimpleHandlerList handlerList; |
51 | @@ -84,10 +96,12 @@ | |||
52 | 84 | @Inject | 96 | @Inject |
53 | 85 | public HttpConductorImpl(ConfigurationService configurationService, | 97 | public HttpConductorImpl(ConfigurationService configurationService, |
54 | 86 | SecurityService securityService, | 98 | SecurityService securityService, |
56 | 87 | MonitorService monitor) { | 99 | MonitorService monitor, |
57 | 100 | SessionService session) { | ||
58 | 88 | this.configurationService = configurationService; | 101 | this.configurationService = configurationService; |
59 | 89 | this.securityService = securityService; | 102 | this.securityService = securityService; |
60 | 90 | this.monitorService = monitor; | 103 | this.monitorService = monitor; |
61 | 104 | this.sessionService = session; | ||
62 | 91 | 105 | ||
63 | 92 | jerseyLogging = java.util.logging.Logger.getLogger("com.sun.jersey"); | 106 | jerseyLogging = java.util.logging.Logger.getLogger("com.sun.jersey"); |
64 | 93 | jerseyLogging.setLevel(java.util.logging.Level.OFF); | 107 | jerseyLogging.setLevel(java.util.logging.Level.OFF); |
65 | @@ -182,14 +196,14 @@ | |||
66 | 182 | Server localServer = new Server(); | 196 | Server localServer = new Server(); |
67 | 183 | SelectChannelConnector connector; | 197 | SelectChannelConnector connector; |
68 | 184 | if (!ssl) { | 198 | if (!ssl) { |
70 | 185 | connector = new SelectChannelConnector(); | 199 | connector = new SelectChannelConnectorExtended(); |
71 | 186 | } | 200 | } |
72 | 187 | else { | 201 | else { |
73 | 188 | // Share keystore configuration with PSQL. | 202 | // Share keystore configuration with PSQL. |
74 | 189 | SslContextFactory sslFactory = new SslContextFactory(); | 203 | SslContextFactory sslFactory = new SslContextFactory(); |
75 | 190 | sslFactory.setKeyStorePath(System.getProperty("javax.net.ssl.keyStore")); | 204 | sslFactory.setKeyStorePath(System.getProperty("javax.net.ssl.keyStore")); |
76 | 191 | sslFactory.setKeyStorePassword(System.getProperty("javax.net.ssl.keyStorePassword")); | 205 | sslFactory.setKeyStorePassword(System.getProperty("javax.net.ssl.keyStorePassword")); |
78 | 192 | connector = new SslSelectChannelConnector(sslFactory); | 206 | connector = new SslSelectChannelConnectorExtended(sslFactory); |
79 | 193 | } | 207 | } |
80 | 194 | connector.setPort(portLocal); | 208 | connector.setPort(portLocal); |
81 | 195 | connector.setThreadPool(new QueuedThreadPool(200)); | 209 | connector.setThreadPool(new QueuedThreadPool(200)); |
82 | @@ -262,7 +276,7 @@ | |||
83 | 262 | @Override | 276 | @Override |
84 | 263 | public void stop() { | 277 | public void stop() { |
85 | 264 | Server localServer; | 278 | Server localServer; |
87 | 265 | monitorService.deregisterServerMonitor(monitorService.getServerMonitors().get(ConnectionMonitor.SERVER_TYPE)); | 279 | monitorService.deregisterServerMonitor(monitorService.getServerMonitors().get(SERVER_TYPE)); |
88 | 266 | synchronized (lock) { | 280 | synchronized (lock) { |
89 | 267 | xOriginFilterEnabled = false; | 281 | xOriginFilterEnabled = false; |
90 | 268 | localServer = server; | 282 | localServer = server; |
91 | @@ -313,7 +327,6 @@ | |||
92 | 313 | } | 327 | } |
93 | 314 | 328 | ||
94 | 315 | private class ConnectionMonitor implements ServerMonitor { | 329 | private class ConnectionMonitor implements ServerMonitor { |
95 | 316 | public static final String SERVER_TYPE = "REST"; | ||
96 | 317 | private final SelectChannelConnector connector; | 330 | private final SelectChannelConnector connector; |
97 | 318 | private final AtomicLong _statsStartedAt = new AtomicLong(System.currentTimeMillis()); | 331 | private final AtomicLong _statsStartedAt = new AtomicLong(System.currentTimeMillis()); |
98 | 319 | 332 | ||
99 | @@ -341,4 +354,66 @@ | |||
100 | 341 | return connector.getConnections(); | 354 | return connector.getConnections(); |
101 | 342 | } | 355 | } |
102 | 343 | } | 356 | } |
103 | 357 | |||
104 | 358 | private class SelectChannelConnectorExtended extends SelectChannelConnector { | ||
105 | 359 | private Session session; | ||
106 | 360 | @Override | ||
107 | 361 | protected AsyncConnection newConnection(SocketChannel channel,final AsyncEndPoint endpoint) | ||
108 | 362 | { | ||
109 | 363 | AsyncHttpConnection conn = (AsyncHttpConnection)super.newConnection(channel, endpoint); | ||
110 | 364 | ServerSessionMonitor sessionMonitor = new ServerSessionMonitor(SERVER_TYPE, | ||
111 | 365 | monitorService.allocateSessionId()); | ||
112 | 366 | |||
113 | 367 | conn.setAssociatedObject(sessionMonitor); | ||
114 | 368 | this.session = sessionService.createSession(); | ||
115 | 369 | monitorService.registerSessionMonitor(sessionMonitor, session); | ||
116 | 370 | return conn; | ||
117 | 371 | } | ||
118 | 372 | |||
119 | 373 | @Override | ||
120 | 374 | protected void connectionClosed(Connection connection) { | ||
121 | 375 | if (connection instanceof AsyncHttpConnection) { | ||
122 | 376 | AsyncHttpConnection conn = (AsyncHttpConnection)connection; | ||
123 | 377 | ServerSessionMonitor monitor = (ServerSessionMonitor)conn.getAssociatedObject(); | ||
124 | 378 | if (monitor != null) { | ||
125 | 379 | monitorService.deregisterSessionMonitor(monitor, session); | ||
126 | 380 | conn.setAssociatedObject(null); | ||
127 | 381 | } | ||
128 | 382 | } | ||
129 | 383 | super.connectionClosed(connection); | ||
130 | 384 | } | ||
131 | 385 | } | ||
132 | 386 | |||
133 | 387 | private class SslSelectChannelConnectorExtended extends SslSelectChannelConnector { | ||
134 | 388 | private Session session; | ||
135 | 389 | public SslSelectChannelConnectorExtended(SslContextFactory sslFactory) { | ||
136 | 390 | super(sslFactory); | ||
137 | 391 | } | ||
138 | 392 | |||
139 | 393 | @Override | ||
140 | 394 | protected AsyncConnection newConnection(SocketChannel channel,final AsyncEndPoint endpoint) | ||
141 | 395 | { | ||
142 | 396 | AsyncHttpConnection conn = (AsyncHttpConnection)((SslConnection)super.newConnection(channel, endpoint)).getSslEndPoint().getConnection(); | ||
143 | 397 | ServerSessionMonitor sessionMonitor = new ServerSessionMonitor(SERVER_TYPE, | ||
144 | 398 | monitorService.allocateSessionId()); | ||
145 | 399 | |||
146 | 400 | conn.setAssociatedObject(sessionMonitor); | ||
147 | 401 | this.session = sessionService.createSession(); | ||
148 | 402 | monitorService.registerSessionMonitor(sessionMonitor, session); | ||
149 | 403 | return conn; | ||
150 | 404 | } | ||
151 | 405 | |||
152 | 406 | @Override | ||
153 | 407 | protected void connectionClosed (Connection connection) { | ||
154 | 408 | if (connection instanceof SslConnection) { | ||
155 | 409 | AsyncHttpConnection conn = (AsyncHttpConnection)((SslConnection) connection).getSslEndPoint().getConnection(); | ||
156 | 410 | ServerSessionMonitor monitor = (ServerSessionMonitor)conn.getAssociatedObject(); | ||
157 | 411 | if (monitor != null) { | ||
158 | 412 | monitorService.deregisterSessionMonitor(monitor, session); | ||
159 | 413 | conn.setAssociatedObject(null); | ||
160 | 414 | } | ||
161 | 415 | } | ||
162 | 416 | super.connectionClosed(connection); | ||
163 | 417 | } | ||
164 | 418 | } | ||
165 | 344 | } | 419 | } |
166 | 345 | 420 | ||
167 | === modified file 'src/main/java/com/akiban/http/SimpleHandlerList.java' | |||
168 | --- src/main/java/com/akiban/http/SimpleHandlerList.java 2013-04-22 22:37:46 +0000 | |||
169 | +++ src/main/java/com/akiban/http/SimpleHandlerList.java 2013-04-26 23:40:34 +0000 | |||
170 | @@ -22,6 +22,8 @@ | |||
171 | 22 | import org.eclipse.jetty.server.handler.AbstractHandler; | 22 | import org.eclipse.jetty.server.handler.AbstractHandler; |
172 | 23 | import org.eclipse.jetty.util.MultiException; | 23 | import org.eclipse.jetty.util.MultiException; |
173 | 24 | 24 | ||
174 | 25 | import com.akiban.server.service.monitor.MonitorStage; | ||
175 | 26 | import com.akiban.sql.server.ServerSessionMonitor; | ||
176 | 25 | import com.akiban.util.tap.InOutTap; | 27 | import com.akiban.util.tap.InOutTap; |
177 | 26 | import com.akiban.util.tap.Tap; | 28 | import com.akiban.util.tap.Tap; |
178 | 27 | 29 | ||
179 | @@ -53,10 +55,18 @@ | |||
180 | 53 | Request baseRequest, | 55 | Request baseRequest, |
181 | 54 | HttpServletRequest request, | 56 | HttpServletRequest request, |
182 | 55 | HttpServletResponse response) throws IOException, ServletException { | 57 | HttpServletResponse response) throws IOException, ServletException { |
183 | 58 | ServerSessionMonitor monitor = null; | ||
184 | 56 | if(!isStarted()) { | 59 | if(!isStarted()) { |
185 | 57 | return; | 60 | return; |
186 | 58 | } | 61 | } |
187 | 59 | REST_TAP.in(); | 62 | REST_TAP.in(); |
188 | 63 | Object obj = baseRequest.getConnection().getAssociatedObject(); | ||
189 | 64 | if (obj instanceof ServerSessionMonitor) { | ||
190 | 65 | monitor = (ServerSessionMonitor)obj; | ||
191 | 66 | monitor.setRemoteAddress(request.getRemoteAddr()); | ||
192 | 67 | monitor.startStatement(request.getMethod(), request.getRequestURI()); | ||
193 | 68 | monitor.enterStage(MonitorStage.EXECUTE); | ||
194 | 69 | } | ||
195 | 60 | try { | 70 | try { |
196 | 61 | for(Handler h : handlers) { | 71 | for(Handler h : handlers) { |
197 | 62 | h.handle(target,baseRequest, request, response); | 72 | h.handle(target,baseRequest, request, response); |
198 | @@ -70,6 +80,10 @@ | |||
199 | 70 | } | 80 | } |
200 | 71 | }finally { | 81 | }finally { |
201 | 72 | REST_TAP.out(); | 82 | REST_TAP.out(); |
202 | 83 | if (monitor != null) { | ||
203 | 84 | monitor.leaveStage(); | ||
204 | 85 | monitor.endStatement(1); | ||
205 | 86 | } | ||
206 | 73 | } | 87 | } |
207 | 74 | } | 88 | } |
208 | 75 | 89 | ||
209 | 76 | 90 | ||
210 | === added file 'src/test/java/com/akiban/http/HttpMonitorVerifyIT.java' | |||
211 | --- src/test/java/com/akiban/http/HttpMonitorVerifyIT.java 1970-01-01 00:00:00 +0000 | |||
212 | +++ src/test/java/com/akiban/http/HttpMonitorVerifyIT.java 2013-04-26 23:40:34 +0000 | |||
213 | @@ -0,0 +1,106 @@ | |||
214 | 1 | /** | ||
215 | 2 | * Copyright (C) 2009-2013 Akiban Technologies, Inc. | ||
216 | 3 | * | ||
217 | 4 | * This program is free software: you can redistribute it and/or modify | ||
218 | 5 | * it under the terms of the GNU Affero General Public License as published by | ||
219 | 6 | * the Free Software Foundation, either version 3 of the License, or | ||
220 | 7 | * (at your option) any later version. | ||
221 | 8 | * | ||
222 | 9 | * This program is distributed in the hope that it will be useful, | ||
223 | 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
224 | 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
225 | 12 | * GNU Affero General Public License for more details. | ||
226 | 13 | * | ||
227 | 14 | * You should have received a copy of the GNU Affero General Public License | ||
228 | 15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
229 | 16 | */ | ||
230 | 17 | package com.akiban.http; | ||
231 | 18 | |||
232 | 19 | import static org.junit.Assert.assertEquals; | ||
233 | 20 | |||
234 | 21 | import java.net.URI; | ||
235 | 22 | import java.util.Arrays; | ||
236 | 23 | import java.util.HashMap; | ||
237 | 24 | import java.util.Map; | ||
238 | 25 | |||
239 | 26 | import org.apache.http.HttpResponse; | ||
240 | 27 | import org.apache.http.client.HttpClient; | ||
241 | 28 | import org.apache.http.client.methods.HttpGet; | ||
242 | 29 | import org.apache.http.impl.client.DefaultHttpClient; | ||
243 | 30 | import org.apache.http.util.EntityUtils; | ||
244 | 31 | import org.junit.Before; | ||
245 | 32 | import org.junit.Test; | ||
246 | 33 | import org.slf4j.Logger; | ||
247 | 34 | import org.slf4j.LoggerFactory; | ||
248 | 35 | |||
249 | 36 | import com.akiban.rest.RestService; | ||
250 | 37 | import com.akiban.rest.RestServiceImpl; | ||
251 | 38 | import com.akiban.server.service.monitor.MonitorService; | ||
252 | 39 | import com.akiban.server.service.security.SecurityService; | ||
253 | 40 | import com.akiban.server.service.security.SecurityServiceImpl; | ||
254 | 41 | import com.akiban.server.service.servicemanager.GuicedServiceManager; | ||
255 | 42 | import com.akiban.server.test.it.ITBase; | ||
256 | 43 | import com.akiban.sql.embedded.EmbeddedJDBCService; | ||
257 | 44 | import com.akiban.sql.embedded.EmbeddedJDBCServiceImpl; | ||
258 | 45 | |||
259 | 46 | public class HttpMonitorVerifyIT extends ITBase { | ||
260 | 47 | |||
261 | 48 | private static final Logger LOG = LoggerFactory.getLogger(HttpMonitorVerifyIT.class); | ||
262 | 49 | |||
263 | 50 | @Override | ||
264 | 51 | protected GuicedServiceManager.BindingsConfigurationProvider serviceBindingsProvider() { | ||
265 | 52 | return super.serviceBindingsProvider() | ||
266 | 53 | .bindAndRequire(SecurityService.class, SecurityServiceImpl.class) | ||
267 | 54 | .bindAndRequire(EmbeddedJDBCService.class, EmbeddedJDBCServiceImpl.class) | ||
268 | 55 | .bindAndRequire(RestService.class, RestServiceImpl.class); | ||
269 | 56 | } | ||
270 | 57 | |||
271 | 58 | @Before | ||
272 | 59 | public void setUp() { | ||
273 | 60 | SecurityService securityService = securityService(); | ||
274 | 61 | securityService.addRole("rest-user"); | ||
275 | 62 | securityService.addUser("user1", "password", Arrays.asList("rest-user")); | ||
276 | 63 | } | ||
277 | 64 | |||
278 | 65 | protected SecurityService securityService() { | ||
279 | 66 | return serviceManager().getServiceByClass(SecurityService.class); | ||
280 | 67 | } | ||
281 | 68 | |||
282 | 69 | protected HttpConductor httpConductor() { | ||
283 | 70 | return serviceManager().getServiceByClass(HttpConductor.class); | ||
284 | 71 | } | ||
285 | 72 | |||
286 | 73 | protected MonitorService monitorService () { | ||
287 | 74 | return serviceManager().getServiceByClass(MonitorService.class); | ||
288 | 75 | } | ||
289 | 76 | |||
290 | 77 | @Override | ||
291 | 78 | protected Map<String, String> startupConfigProperties() { | ||
292 | 79 | Map<String, String> properties = new HashMap<>(); | ||
293 | 80 | properties.put("akserver.http.login", "basic"); // "digest" | ||
294 | 81 | properties.put("akserver.restrict_user_schema", "true"); | ||
295 | 82 | return properties; | ||
296 | 83 | } | ||
297 | 84 | |||
298 | 85 | private static int openRestURL(HttpClient client, String userInfo, int port, String path) throws Exception { | ||
299 | 86 | URI uri = new URI("http", userInfo, "localhost", port, path, null, null); | ||
300 | 87 | HttpGet get = new HttpGet(uri); | ||
301 | 88 | HttpResponse response = client.execute(get); | ||
302 | 89 | int code = response.getStatusLine().getStatusCode(); | ||
303 | 90 | EntityUtils.consume(response.getEntity()); | ||
304 | 91 | return code; | ||
305 | 92 | } | ||
306 | 93 | |||
307 | 94 | @Test | ||
308 | 95 | public void runTest () throws Exception { | ||
309 | 96 | MonitorService monitor = monitorService(); | ||
310 | 97 | |||
311 | 98 | HttpClient client = new DefaultHttpClient(); | ||
312 | 99 | openRestURL(client, "user1:password", httpConductor().getPort(), "/version"); | ||
313 | 100 | |||
314 | 101 | assertEquals(monitor.getSessionMonitors().size(), 1); | ||
315 | 102 | |||
316 | 103 | client.getConnectionManager().shutdown(); | ||
317 | 104 | } | ||
318 | 105 | |||
319 | 106 | } | ||
320 | 0 | 107 | ||
321 | === added file 'src/test/java/com/akiban/http/HttpMonitorVerifySSLIT.java' | |||
322 | --- src/test/java/com/akiban/http/HttpMonitorVerifySSLIT.java 1970-01-01 00:00:00 +0000 | |||
323 | +++ src/test/java/com/akiban/http/HttpMonitorVerifySSLIT.java 2013-04-26 23:40:34 +0000 | |||
324 | @@ -0,0 +1,199 @@ | |||
325 | 1 | /** | ||
326 | 2 | * Copyright (C) 2009-2013 Akiban Technologies, Inc. | ||
327 | 3 | * | ||
328 | 4 | * This program is free software: you can redistribute it and/or modify | ||
329 | 5 | * it under the terms of the GNU Affero General Public License as published by | ||
330 | 6 | * the Free Software Foundation, either version 3 of the License, or | ||
331 | 7 | * (at your option) any later version. | ||
332 | 8 | * | ||
333 | 9 | * This program is distributed in the hope that it will be useful, | ||
334 | 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
335 | 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
336 | 12 | * GNU Affero General Public License for more details. | ||
337 | 13 | * | ||
338 | 14 | * You should have received a copy of the GNU Affero General Public License | ||
339 | 15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
340 | 16 | */ | ||
341 | 17 | package com.akiban.http; | ||
342 | 18 | |||
343 | 19 | import static org.junit.Assert.assertEquals; | ||
344 | 20 | |||
345 | 21 | import java.net.URI; | ||
346 | 22 | import java.security.cert.CertificateException; | ||
347 | 23 | import java.security.cert.X509Certificate; | ||
348 | 24 | import java.util.Arrays; | ||
349 | 25 | import java.util.HashMap; | ||
350 | 26 | import java.util.Map; | ||
351 | 27 | import java.util.Properties; | ||
352 | 28 | |||
353 | 29 | import javax.net.ssl.SSLContext; | ||
354 | 30 | import javax.net.ssl.TrustManager; | ||
355 | 31 | import javax.net.ssl.X509TrustManager; | ||
356 | 32 | |||
357 | 33 | import org.apache.http.HttpResponse; | ||
358 | 34 | import org.apache.http.client.HttpClient; | ||
359 | 35 | import org.apache.http.client.methods.HttpGet; | ||
360 | 36 | import org.apache.http.conn.ClientConnectionManager; | ||
361 | 37 | import org.apache.http.conn.scheme.Scheme; | ||
362 | 38 | import org.apache.http.conn.scheme.SchemeRegistry; | ||
363 | 39 | import org.apache.http.conn.ssl.SSLSocketFactory; | ||
364 | 40 | import org.apache.http.impl.client.DefaultHttpClient; | ||
365 | 41 | import org.apache.http.util.EntityUtils; | ||
366 | 42 | import org.junit.Before; | ||
367 | 43 | import org.junit.Ignore; | ||
368 | 44 | import org.junit.Test; | ||
369 | 45 | import org.slf4j.Logger; | ||
370 | 46 | import org.slf4j.LoggerFactory; | ||
371 | 47 | |||
372 | 48 | import com.akiban.rest.RestService; | ||
373 | 49 | import com.akiban.rest.RestServiceImpl; | ||
374 | 50 | import com.akiban.server.service.monitor.MonitorService; | ||
375 | 51 | import com.akiban.server.service.security.SecurityService; | ||
376 | 52 | import com.akiban.server.service.security.SecurityServiceImpl; | ||
377 | 53 | import com.akiban.server.service.servicemanager.GuicedServiceManager; | ||
378 | 54 | import com.akiban.server.test.it.ITBase; | ||
379 | 55 | import com.akiban.sql.embedded.EmbeddedJDBCService; | ||
380 | 56 | import com.akiban.sql.embedded.EmbeddedJDBCServiceImpl; | ||
381 | 57 | |||
382 | 58 | /** | ||
383 | 59 | * In order to run this test, you need to generate a key store, then update the | ||
384 | 60 | * javax.net.ssl.keyStore system property (set below), to the path where you put the | ||
385 | 61 | * keystore file. | ||
386 | 62 | * <pre> | ||
387 | 63 | * $ keytool -keystore keystore -alias akiban -genkey -keyalg RSA | ||
388 | 64 | * Enter keystore password: password | ||
389 | 65 | * Re-enter new password: password | ||
390 | 66 | * What is your first and last name? | ||
391 | 67 | * [Unknown]: akiban.com | ||
392 | 68 | * What is the name of your organizational unit? | ||
393 | 69 | * [Unknown]: akiban.com | ||
394 | 70 | * What is the name of your organization? | ||
395 | 71 | * [Unknown]: akiban | ||
396 | 72 | * What is the name of your City or Locality? | ||
397 | 73 | * [Unknown]: | ||
398 | 74 | * What is the name of your State or Province? | ||
399 | 75 | * [Unknown]: | ||
400 | 76 | * What is the two-letter country code for this unit? | ||
401 | 77 | * [Unknown]: | ||
402 | 78 | * Is CN=akiban.com, OU=akiban.com, O=akiban, L=Unknown, ST=Unknown, C=Unknown correct? | ||
403 | 79 | * [no]: yes | ||
404 | 80 | * </pre> | ||
405 | 81 | * Because we don't want to be shipping a half completed SSL certificate with the source | ||
406 | 82 | * code, This is a manual step required for this (otherwise) disabled test. | ||
407 | 83 | * @author tjoneslo | ||
408 | 84 | * | ||
409 | 85 | */ | ||
410 | 86 | public class HttpMonitorVerifySSLIT extends ITBase { | ||
411 | 87 | private static final Logger LOG = LoggerFactory.getLogger(HttpMonitorVerifySSLIT.class); | ||
412 | 88 | |||
413 | 89 | @Override | ||
414 | 90 | protected GuicedServiceManager.BindingsConfigurationProvider serviceBindingsProvider() { | ||
415 | 91 | return super.serviceBindingsProvider() | ||
416 | 92 | .bindAndRequire(SecurityService.class, SecurityServiceImpl.class) | ||
417 | 93 | .bindAndRequire(EmbeddedJDBCService.class, EmbeddedJDBCServiceImpl.class) | ||
418 | 94 | .bindAndRequire(RestService.class, RestServiceImpl.class); | ||
419 | 95 | } | ||
420 | 96 | |||
421 | 97 | @Before | ||
422 | 98 | public void setUp() { | ||
423 | 99 | SecurityService securityService = securityService(); | ||
424 | 100 | securityService.addRole("rest-user"); | ||
425 | 101 | securityService.addRole("admin"); | ||
426 | 102 | securityService.addUser("user1", "password", Arrays.asList("rest-user")); | ||
427 | 103 | securityService.addUser("akiban", "topsecret", Arrays.asList("rest-user", "admin")); | ||
428 | 104 | } | ||
429 | 105 | |||
430 | 106 | protected SecurityService securityService() { | ||
431 | 107 | return serviceManager().getServiceByClass(SecurityService.class); | ||
432 | 108 | } | ||
433 | 109 | |||
434 | 110 | protected HttpConductor httpConductor() { | ||
435 | 111 | return serviceManager().getServiceByClass(HttpConductor.class); | ||
436 | 112 | } | ||
437 | 113 | |||
438 | 114 | protected MonitorService monitorService () { | ||
439 | 115 | return serviceManager().getServiceByClass(MonitorService.class); | ||
440 | 116 | } | ||
441 | 117 | |||
442 | 118 | @Override | ||
443 | 119 | protected Map<String, String> startupConfigProperties() { | ||
444 | 120 | Map<String, String> properties = new HashMap<>(); | ||
445 | 121 | properties.put("akserver.http.login", "digest"); // "digest" | ||
446 | 122 | properties.put("akserver.http.ssl", "true"); | ||
447 | 123 | properties.put("akserver.restrict_user_schema", "true"); | ||
448 | 124 | |||
449 | 125 | Properties p = new Properties(System.getProperties()); | ||
450 | 126 | p.put("javax.net.ssl.keyStore", "./keystore"); | ||
451 | 127 | p.put("javax.net.ssl.keyStorePassword", "password"); | ||
452 | 128 | System.setProperties(p); | ||
453 | 129 | return properties; | ||
454 | 130 | } | ||
455 | 131 | |||
456 | 132 | private static int openRestURL(HttpClient client, String userInfo, int port, String path) throws Exception { | ||
457 | 133 | URI uri = new URI("https", userInfo, "localhost", port, path, null, null); | ||
458 | 134 | HttpGet get = new HttpGet(uri); | ||
459 | 135 | HttpResponse response = client.execute(get); | ||
460 | 136 | int code = response.getStatusLine().getStatusCode(); | ||
461 | 137 | EntityUtils.consume(response.getEntity()); | ||
462 | 138 | return code; | ||
463 | 139 | } | ||
464 | 140 | |||
465 | 141 | @Ignore ("need setup") | ||
466 | 142 | @Test | ||
467 | 143 | public void runTest () throws Exception { | ||
468 | 144 | MonitorService monitor = monitorService(); | ||
469 | 145 | |||
470 | 146 | HttpClient client = new DefaultHttpClient(); | ||
471 | 147 | client = wrapClient(client); | ||
472 | 148 | |||
473 | 149 | openRestURL(client, "user1:password", httpConductor().getPort(), "/version"); | ||
474 | 150 | |||
475 | 151 | assertEquals(monitor.getSessionMonitors().size(), 1); | ||
476 | 152 | |||
477 | 153 | client.getConnectionManager().shutdown(); | ||
478 | 154 | } | ||
479 | 155 | |||
480 | 156 | |||
481 | 157 | /** | ||
482 | 158 | * This code sets up the httpclient to accept any SSL certificate. The | ||
483 | 159 | * SSL certificate generated by the instructions above is not correctly | ||
484 | 160 | * signed, so we need ignore the problem. | ||
485 | 161 | * This code should not, under any circumstances, be allowed anywhere | ||
486 | 162 | * the production code. | ||
487 | 163 | * @param base | ||
488 | 164 | * @return | ||
489 | 165 | */ | ||
490 | 166 | private HttpClient wrapClient (HttpClient base) { | ||
491 | 167 | try { | ||
492 | 168 | SSLContext ctx = SSLContext.getInstance("TLS"); | ||
493 | 169 | |||
494 | 170 | ctx.init(null, new TrustManager[]{getTrustManager()}, null); | ||
495 | 171 | SSLSocketFactory ssf = new SSLSocketFactory(ctx); | ||
496 | 172 | ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); | ||
497 | 173 | ClientConnectionManager ccm = base.getConnectionManager(); | ||
498 | 174 | SchemeRegistry sr = ccm.getSchemeRegistry(); | ||
499 | 175 | sr.register(new Scheme("https", ssf, 8091)); | ||
500 | 176 | return new DefaultHttpClient(ccm, base.getParams()); | ||
501 | 177 | } catch (Exception ex) { | ||
502 | 178 | ex.printStackTrace(); | ||
503 | 179 | return null; | ||
504 | 180 | } | ||
505 | 181 | } | ||
506 | 182 | |||
507 | 183 | private X509TrustManager getTrustManager() { | ||
508 | 184 | return new X509TrustManager() { | ||
509 | 185 | |||
510 | 186 | public X509Certificate[] getAcceptedIssuers() { | ||
511 | 187 | return null; | ||
512 | 188 | } | ||
513 | 189 | |||
514 | 190 | @Override | ||
515 | 191 | public void checkClientTrusted(X509Certificate[] arg0, String arg1) | ||
516 | 192 | throws CertificateException { } | ||
517 | 193 | |||
518 | 194 | @Override | ||
519 | 195 | public void checkServerTrusted(X509Certificate[] arg0, String arg1) | ||
520 | 196 | throws CertificateException { } | ||
521 | 197 | }; | ||
522 | 198 | } | ||
523 | 199 | } | ||
524 | 0 | 200 | ||
525 | === modified file 'src/test/java/com/akiban/http/HttpThreadedLoginIT.java' | |||
526 | --- src/test/java/com/akiban/http/HttpThreadedLoginIT.java 2013-04-22 22:51:14 +0000 | |||
527 | +++ src/test/java/com/akiban/http/HttpThreadedLoginIT.java 2013-04-26 23:40:34 +0000 | |||
528 | @@ -21,8 +21,6 @@ | |||
529 | 21 | import com.akiban.rest.RestServiceImpl; | 21 | import com.akiban.rest.RestServiceImpl; |
530 | 22 | import com.akiban.server.service.servicemanager.GuicedServiceManager; | 22 | import com.akiban.server.service.servicemanager.GuicedServiceManager; |
531 | 23 | import com.akiban.server.test.it.ITBase; | 23 | import com.akiban.server.test.it.ITBase; |
532 | 24 | import com.akiban.sql.embedded.EmbeddedJDBCService; | ||
533 | 25 | import com.akiban.sql.embedded.EmbeddedJDBCServiceImpl; | ||
534 | 26 | 24 | ||
535 | 27 | import org.apache.http.HttpResponse; | 25 | import org.apache.http.HttpResponse; |
536 | 28 | import org.apache.http.HttpStatus; | 26 | import org.apache.http.HttpStatus; |
This gives the HTTP server connection a Session, but it isn't otherwise used. Would it be feasible for REST resources, many of which create new sessions each request today, to get access to this one?