2020import org .apache .http .auth .AuthenticationException ;
2121import org .apache .http .client .config .RequestConfig ;
2222import org .apache .http .client .methods .CloseableHttpResponse ;
23- import org .apache .http .client .methods .HttpPost ;
2423import org .apache .http .client .methods .HttpRequestBase ;
2524import org .apache .http .client .methods .HttpUriRequest ;
2625import org .apache .http .client .protocol .HttpClientContext ;
2726import org .apache .http .conn .HttpClientConnectionManager ;
2827import org .apache .http .conn .ssl .SSLConnectionSocketFactory ;
29- import org .apache .http .ssl .SSLContextBuilder ;
3028import org .apache .http .conn .ssl .TrustSelfSignedStrategy ;
3129import org .apache .http .cookie .Cookie ;
3230import org .apache .http .impl .client .BasicCookieStore ;
3331import org .apache .http .impl .client .CloseableHttpClient ;
3432import org .apache .http .impl .client .HttpClientBuilder ;
3533import org .apache .http .impl .conn .PoolingHttpClientConnectionManager ;
3634import org .apache .http .impl .cookie .BasicClientCookie ;
35+ import org .apache .http .ssl .SSLContextBuilder ;
3736import org .labkey .remoteapi .security .EnsureLoginCommand ;
3837import org .labkey .remoteapi .security .ImpersonateUserCommand ;
3938import org .labkey .remoteapi .security .LogoutCommand ;
10099 */
101100public class Connection
102101{
102+ public static final String X_LABKEY_CSRF = "X-LABKEY-CSRF" ;
103+ public static final String JSESSIONID = "JSESSIONID" ;
104+
103105 private static final int DEFAULT_TIMEOUT = 60000 ; // 60 seconds
104106 private static final HttpClientConnectionManager _connectionManager = new PoolingHttpClientConnectionManager ();
105107
106- private final String _baseUrl ;
108+ private final URI _baseURI ;
107109 private final CredentialsProvider _credentialsProvider ;
108110 private final HttpClientContext _httpClientContext ;
109111
@@ -113,6 +115,7 @@ public class Connection
113115 private String _proxyHost ;
114116 private Integer _proxyPort ;
115117 private String _csrf ;
118+ private String _sessionId ;
116119
117120 // The user email when impersonating a user
118121 private String _impersonateUser ;
@@ -121,65 +124,100 @@ public class Connection
121124 /**
122125 * Constructs a new Connection object given a base URL and a credentials provider.
123126 * <p>
124- * The baseUrl parameter should include the protocol, domain name, port,
127+ * The baseURI parameter should include the protocol, domain name, port,
125128 * and LabKey web application context path (if configured). For example
126129 * in a typical localhost configuration, the base URL would be:
127130 * </p>
128- * <code>http://localhost:8080/labkey</code>
131+ * <code>new URI(" http://localhost:8080/labkey") </code>
129132 * <p>
130133 * Note that https may also be used for the protocol. By default the
131134 * Connection is configured to deny self-signed SSL certificates.
132135 * If you want to accept self-signed certificates, use
133136 * <code>setAcceptSelfSignedCerts(false)</code> to enable this behavior.
134137 * </p>
135- * The email name and password should correspond to a valid user email
136- * and password on the target server.
137- * @param baseUrl The base URL
138+ * @param baseURI Base URI for this Connection
138139 * @param credentialsProvider A credentials provider
139140 */
140- public Connection (String baseUrl , CredentialsProvider credentialsProvider )
141+ public Connection (URI baseURI , CredentialsProvider credentialsProvider )
141142 {
142- _baseUrl = baseUrl ;
143+ if (baseURI .getHost () == null || baseURI .getScheme () == null )
144+ {
145+ throw new IllegalArgumentException ("Invalid server URL: " + baseURI .toString ());
146+ }
147+ _baseURI = baseURI ;
143148 _credentialsProvider = credentialsProvider ;
144149 _httpClientContext = HttpClientContext .create ();
145150 _httpClientContext .setCookieStore (new BasicCookieStore ());
146151 setAcceptSelfSignedCerts (false );
147152 }
148153
154+ /**
155+ * Constructs a new Connection object given a base URL and a credentials provider.
156+ * <p>
157+ * The baseUrl parameter should include the protocol, domain name, port,
158+ * and LabKey web application context path (if configured). For example
159+ * in a typical localhost configuration, the base URL would be:
160+ * </p>
161+ * <code>http://localhost:8080/labkey</code>
162+ * <p>
163+ * @param baseUrl The base URL
164+ * @param credentialsProvider A credentials provider
165+ * @see #Connection(URI, CredentialsProvider)
166+ */
167+ public Connection (String baseUrl , CredentialsProvider credentialsProvider )
168+ {
169+ this (toURI (baseUrl ), credentialsProvider );
170+ }
171+
149172 /**
150173 * Constructs a new Connection object with a base URL that attempts authentication via .netrc/_netrc entry, if present.
151174 * If not present, connects as guest.
152175 * @param baseUrl The base URL
153176 * @throws URISyntaxException if the given url is not a valid URI
154177 * @throws IOException if there are problems reading the credentials
155- * @see #Connection(String, CredentialsProvider)
178+ * @see NetrcCredentialsProvider
179+ * @see #Connection(URI, CredentialsProvider)
156180 */
157181 public Connection (String baseUrl ) throws URISyntaxException , IOException
158182 {
159- this (baseUrl , new NetrcCredentialsProvider (new URI (baseUrl )));
183+ this (toURI ( baseUrl ) , new NetrcCredentialsProvider (toURI (baseUrl )));
160184 }
161185
162186 /**
163187 * Constructs a new Connection object for a base URL that attempts basic authentication.
164188 * <p>
165189 * This is equivalent to calling <code>Connection(baseUrl, new BasicAuthCredentialsProvider(email, password))</code>.
190+ * The email and password should correspond to a valid user email
191+ * and password on the target server.
166192 * @param baseUrl The base URL
167193 * @param email The user email address to pass for authentication
168194 * @param password The user password to send for authentication
169- * @see #Connection(String, CredentialsProvider)
195+ * @see BasicAuthCredentialsProvider#BasicAuthCredentialsProvider(String, String)
196+ * @see #Connection(URI, CredentialsProvider)
170197 */
171198 public Connection (String baseUrl , String email , String password )
172199 {
173- this (baseUrl , new BasicAuthCredentialsProvider (email , password ));
200+ this (toURI ( baseUrl ) , new BasicAuthCredentialsProvider (email , password ));
174201 }
175202
176203 /**
177204 * Returns the base URL for this connection.
178205 * @return The base URL.
206+ * @deprecated Use {@link #getBaseURI()}
179207 */
208+ @ Deprecated
180209 public String getBaseUrl ()
181210 {
182- return _baseUrl ;
211+ return _baseURI .toString ();
212+ }
213+
214+ /**
215+ * Returns the base URI for this connection.
216+ * @return The target LabKey instance's base URI
217+ */
218+ public URI getBaseURI ()
219+ {
220+ return _baseURI ;
183221 }
184222
185223 /**
@@ -228,11 +266,15 @@ protected HttpClientBuilder clientBuilder()
228266 return builder ;
229267 }
230268
231-
232-
233- protected void beforeExecute (HttpRequest request ) throws IOException , CommandException
269+ /**
270+ * If necessary, prime the Connection for non-GET requests.
271+ * Executes a dummy command to trigger authentication and populate CSRF and session info.
272+ * @param request HttpRequest that is about to be executed
273+ */
274+ protected void beforeExecute (HttpRequest request )
234275 {
235- if (null == _csrf && request instanceof HttpPost )
276+ if (null == _csrf &&
277+ request instanceof HttpRequestBase && !"GET" .equals (((HttpRequestBase ) request ).getMethod ()))
236278 {
237279 // make a request to get a JSESSIONID
238280 try
@@ -244,7 +286,9 @@ protected void beforeExecute(HttpRequest request) throws IOException, CommandExc
244286 }
245287 }
246288 if (null != _csrf )
247- request .setHeader ("X-LABKEY-CSRF" , _csrf );
289+ request .setHeader (X_LABKEY_CSRF , _csrf );
290+ if (null != _sessionId )
291+ request .setHeader (JSESSIONID , _sessionId );
248292 }
249293
250294
@@ -253,8 +297,11 @@ protected void afterExecute()
253297 // Always update our CSRF token as the session may be new since our last request
254298 for (Cookie c : _httpClientContext .getCookieStore ().getCookies ())
255299 {
256- if ("JSESSIONID" .equals (c .getName ()))
300+ if (X_LABKEY_CSRF .equals (c .getName ()))
257301 _csrf = c .getValue ();
302+
303+ if (JSESSIONID .equals (c .getName ()))
304+ _sessionId = c .getValue ();
258305 }
259306 }
260307
@@ -354,10 +401,10 @@ public Connection stopImpersonate() throws IOException, CommandException
354401 return this ;
355402 }
356403
357- CloseableHttpResponse executeRequest (HttpUriRequest request , Integer timeout ) throws IOException , URISyntaxException , AuthenticationException , CommandException
404+ CloseableHttpResponse executeRequest (HttpUriRequest request , Integer timeout ) throws IOException , URISyntaxException , AuthenticationException
358405 {
359406 // Delegate authentication setup to CredentialsProvider
360- _credentialsProvider .configureRequest (getBaseUrl (), request , _httpClientContext );
407+ _credentialsProvider .configureRequest (getBaseURI (), request , _httpClientContext );
361408
362409 CloseableHttpClient client = getHttpClient ();
363410
@@ -462,7 +509,37 @@ public Connection addCookie(String name, String value, String domain, String pat
462509 cookie .setExpiryDate (expiry );
463510 cookie .setSecure (isSecure );
464511 _httpClientContext .getCookieStore ().addCookie (cookie );
512+
513+ // Don't use session info from different server
514+ if (combineHostPath (_baseURI .getHost (), _baseURI .getPath ()).equals (combineHostPath (domain , path )))
515+ {
516+ if (X_LABKEY_CSRF .equals (name ))
517+ _csrf = value ;
518+ if (JSESSIONID .equals (name ))
519+ _sessionId = value ;
520+ }
521+
465522 return this ;
466523 }
467524
525+ private String combineHostPath (String host , String path )
526+ {
527+ String hostPath = (host == null ? "" : host .trim ()) + (path == null ? "" : path .trim ());
528+ return hostPath .replaceAll ("//+" , "/" ).replaceFirst ("/$" , "" );
529+ }
530+
531+ /**
532+ * Utility method to construct a URI without modifying constructor signature
533+ */
534+ private static URI toURI (String baseUrl )
535+ {
536+ try
537+ {
538+ return new URI (baseUrl );
539+ }
540+ catch (URISyntaxException e )
541+ {
542+ throw new IllegalArgumentException ("Invalid target server URL: " + baseUrl );
543+ }
544+ }
468545}
0 commit comments