HttpClient - stuff compiles, but the puzzle pieces don't fit yet

git-svn-id: https://svn.apache.org/repos/asf/jakarta/httpcomponents/httpclient/trunk@500635 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Roland Weber 2007-01-27 22:01:23 +00:00
parent d8a3a0b5f4
commit 7bb7a457d4
6 changed files with 715 additions and 15 deletions

View File

@ -0,0 +1,100 @@
/*
* $HeadURL$
* $Revision$
* $Date$
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.http.client;
import java.io.IOException;
import org.apache.http.HttpResponse;
import org.apache.http.HttpException;
import org.apache.http.protocol.HttpContext;
import org.apache.http.conn.ManagedClientConnection;
/**
* A client-side request director.
* The director decides which steps are necessary to execute a request.
* It establishes connections and optionally processes redirects and
* authentication challenges. The director may therefore generate and
* send a sequence of requests in order to execute one initial request.
* <br/>
* This interface and it's implementations replace the
* <code>HttpMethodDirector</code> in HttpClient 3.
*
* @author <a href="mailto:rolandw@apache.org">Roland Weber</a>
*
*
* <!-- empty lines to avoid svn diff problems -->
* @version $Revision$
*
* @since 4.0
*/
public interface ClientRequestDirector {
/**
* Executes a request.
* <br/><b>Note:</b>
* For the time being, a new director is instantiated for each request.
* This is the same behavior as for <code>HttpMethodDirector</code>
* in HttpClient 3.
*
* @param roureq the route and request to execute
* @param context the context for executing the request
*
* @return the final response to the request.
* This is never an intermediate response with status code 1xx.
*
* @throws HttpException in case of a problem
* @throws IOException in case of an IO problem
*/
HttpResponse execute(RoutedRequest roureq, HttpContext context)
throws HttpException, IOException
;
/**
* Obtains the connection to which the response is tied.
* Responses with a non-buffered response entity are tied to
* their connection until the entity is consumed or discarded.
* Until then, the connection must not be released. Afterwards,
* it must be released.
*
* @return the connection to which the response from
* {@link #execute execute} is tied, or
* <code>null</code> if there is none
*/
ManagedClientConnection getConnection()
;
} // class ClientRequestDirector

View File

@ -33,6 +33,7 @@ package org.apache.http.client;
import java.io.IOException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpException;
@ -44,9 +45,11 @@ import org.apache.http.conn.ClientConnectionManager;
/**
* Interface for an HTTP client.
* HTTP clients encapsulate a collection of objects required to
* HTTP clients encapsulate a smorgasbord of objects required to
* execute HTTP requests while handling cookies, authentication,
* connection management, and other features.
* Thread safety of HTTP clients depends on the implementation
* and configuration of the specific client.
*
* @author <a href="mailto:rolandw@apache.org">Roland Weber</a>
*
@ -60,7 +63,7 @@ public interface HttpClient {
/**
* Obtains the default context for this client.
* Obtains the default context used by this client.
* This context will be used by default when executing requests
* with this client.
*
@ -73,7 +76,7 @@ public interface HttpClient {
/**
* Obtains the parameters for this client.
* These parameters will become defaults for all requests being
* executed with this client, and for the parameters of all
* executed with this client, and for the parameters of
* dependent objects in this client.
*
* @return the default parameters
@ -91,27 +94,33 @@ public interface HttpClient {
;
/**
* Executes a request using the {@link #getContext default context}.
* Same as {@link #execute(HttpRequest,HttpContext)
* client.execute(request, client.getContext())},
* Executes a request for the given target using the
* {@link #getContext default context}.
* Same as {@link #execute(HttpHost,HttpRequest,HttpContext)
* client.execute(target, request, client.getContext())},
* see there for details.
*
* @param target the target host for the request.
* Some implementations may accept <code>null</code>.
* @param request the request to execute
*
* @return the response to the request
*
* @throws HttpException in case of a protocol problem
* @throws HttpException in case of a problem
* @throws IOException in case of an IO problem
* <br/><i @@@>timeout exceptions?</i>
*/
HttpResponse execute(HttpRequest request)
HttpResponse execute(HttpHost target, HttpRequest request)
throws HttpException, IOException
;
/**
* Executes a request using the given context.
* Executes a request for the given target using the given context.
* The route to the target will be determined by the HTTP client.
*
* @param target the target host for the request.
* Some implementations may accept <code>null</code>.
* @param request the request to execute
* @param context the context to use for the execution, or
* <code>null</code> to use the
@ -119,15 +128,38 @@ public interface HttpClient {
*
* @return the response to the request. This is always a final response,
* never an intermediate response with an 1xx status code.
* Whether redirects or authentication requests will be returned
* Whether redirects or authentication challenges will be returned
* or handled automatically depends on the implementation and
* configuration of this client.
*
* @throws HttpException in case of a protocol problem
* @throws HttpException in case of a problem
* @throws IOException in case of an IO problem
* <br/><i @@@>timeout exceptions?</i>
*/
HttpResponse execute(HttpRequest request, HttpContext context)
HttpResponse execute(HttpHost target, HttpRequest request,
HttpContext context)
throws HttpException, IOException
;
/**
* Executes a request along the given route.
*
* @param roureq the request to execute along with the route
* @param context the context to use for the execution, or
* <code>null</code> to use the
* {@link #getContext default context}
*
* @return the response to the request. See
* {@link #execute(HttpHost,HttpRequest,HttpContext)
* execute(target,request,context)}
* for details.
*
* @throws HttpException in case of a problem
* @throws IOException in case of an IO problem
* <br/><i @@@>timeout exceptions?</i>
*/
HttpResponse execute(RoutedRequest roureq, HttpContext context)
throws HttpException, IOException
;

View File

@ -0,0 +1,100 @@
/*
* $HeadURL$
* $Revision$
* $Date$
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.http.client;
import org.apache.http.HttpRequest;
import org.apache.http.conn.HostConfiguration;
/**
* A request with the route along which it should be sent.
*
* @author <a href="mailto:rolandw@apache.org">Roland Weber</a>
*
*
* <!-- empty lines to avoid svn diff problems -->
* @version $Revision$
*
* @since 4.0
*/
public interface RoutedRequest {
/**
* Obtains the request.
*
* @return the request
*/
HttpRequest getRequest()
;
/**
* Obtains the route.
*
* @return the route
*/
HostConfiguration getRoute()
;
/**
* Trivial default implementation of a routed request.
*/
public static class Impl implements RoutedRequest {
protected final HttpRequest request;
protected final HostConfiguration route;
/**
* Creates a new routed request.
*
* @param req the request
* @param rou the route
*/
public Impl(HttpRequest req, HostConfiguration rou) {
this.request = req;
this.route = rou;
}
// non-javadoc, see interface
public final HttpRequest getRequest() {
return request;
}
// non-javadoc, see interface
public final HostConfiguration getRoute() {
return route;
}
} // class Impl
} // interface RoutedRequest

View File

@ -33,6 +33,7 @@ package org.apache.http.client.impl;
import java.io.IOException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpException;
@ -41,7 +42,9 @@ import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.SyncHttpExecutionContext;
import org.apache.http.protocol.BasicHttpProcessor;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.client.HttpClient;
import org.apache.http.client.RoutedRequest;
@ -170,12 +173,78 @@ public abstract class AbstractHttpClient
}
// non-javadoc, see interface HttpClient
public final HttpResponse execute(HttpRequest request)
/**
* Maps to {@link #execute(HttpHost,HttpRequest,HttpContext)
* execute(target, request, context)}.
*
* @param target the target host for the request.
* Some implementations may accept <code>null</code>.
* @param request the request to execute
*
* @return the response to the request
*
* @throws HttpException in case of a problem
* @throws IOException in case of an IO problem
*/
public final HttpResponse execute(HttpHost target, HttpRequest request)
throws HttpException, IOException {
return execute(request, defaultContext);
return execute(target, request, defaultContext);
}
/**
* Maps to {@link HttpClient#execute(RoutedRequest,HttpContext)
* execute(roureq, context)}.
* The route is computed by {@link #determineRoute determineRoute}.
*
* @param target the target host for the request.
* Some implementations may accept <code>null</code>.
* @param request the request to execute
*/
public final HttpResponse execute(HttpHost target, HttpRequest request,
HttpContext context)
throws HttpException, IOException {
if (request == null) {
throw new IllegalArgumentException
("Request must not be null.");
}
// A null target may be acceptable if there is a default target.
// Otherwise, the null target is detected in determineRoute().
if (context == null)
context = defaultContext;
RoutedRequest roureq = determineRoute(target, request, context);
return execute(roureq, context);
}
/**
* Determines the route for a request.
* Called by {@link #execute(HttpHost,HttpRequest,HttpContext)
* execute(target, request, context)}
* to map to {@link HttpClient#execute(RoutedRequest,HttpContext)
* execute(roureq, context)}.
*
* @param target the target host for the request.
* Implementations may accept <code>null</code>
* if they can still determine a route, for example
* to a default target or by inspecting the request.
* @param request the request to execute
* @param context the context to use for the execution,
* never <code>null</code>
*
* @return the request along with the route it should take
*
* @throws HttpException in case of a problem
*/
protected abstract RoutedRequest determineRoute(HttpHost target,
HttpRequest request,
HttpContext context)
throws HttpException
;
} // class AbstractHttpClient

View File

@ -0,0 +1,227 @@
/*
* $HeadURL$
* $Revision$
* $Date$
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.http.client.impl;
import java.io.IOException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpException;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpRequestExecutor;
import org.apache.http.conn.HostConfiguration;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.ManagedClientConnection;
import org.apache.http.client.RoutedRequest;
import org.apache.http.client.ClientRequestDirector;
/**
* Default implementation of a client-side request director.
* <br/>
* This class replaces the <code>HttpMethodDirector</code> in HttpClient 3.
*
* @author <a href="mailto:rolandw@apache.org">Roland Weber</a>
*
*
* <!-- empty lines to avoid svn diff problems -->
* @version $Revision$
*
* @since 4.0
*/
public class DefaultClientRequestDirector
implements ClientRequestDirector {
/** The connection manager. */
protected final ClientConnectionManager connManager;
/** The request executor. */
protected final HttpRequestExecutor requestExec;
/** The parameters. */
protected final HttpParams defaultParams;
/** The currently allocated connection. */
protected ManagedClientConnection managedConn;
public DefaultClientRequestDirector(ClientConnectionManager conman,
HttpRequestExecutor reqexec,
HttpParams params) {
this.connManager = conman;
this.requestExec = reqexec;
this.defaultParams = params;
this.managedConn = null;
//@@@ authentication?
} // constructor
// non-javadoc, see interface ClientRequestDirector
public ManagedClientConnection getConnection() {
return managedConn;
}
// non-javadoc, see interface ClientRequestDirector
public HttpResponse execute(RoutedRequest roureq, HttpContext context)
throws HttpException, IOException {
//@@@ link parameters? Let's rely on the request executor for now.
HttpResponse response = null;
boolean done = false;
while (!done) {
allocateConnection(roureq.getRoute());
establishRoute(roureq.getRoute(), context);
//@@@ prepare request (authentication)
//@@@ will this be done here or via interceptor?
response = requestExec.execute
(roureq.getRequest(), managedConn, context);
RoutedRequest followup = handleResponse(roureq, response, context);
if (followup == null) {
done = true;
} else {
if ((managedConn != null) &&
!followup.getRoute().equals(roureq.getRoute())) {
// the followup has a different route, release connection
//@@@ need to consume response body first?
//@@@ or let that be done in handleResponse(...)?
connManager.releaseConnection(managedConn);
}
roureq = followup;
}
} // while not done
//@@@ check response for entity, release connection if possible
return response;
} // execute
/**
* Obtains a connection for the target route.
*
* @param route the route for which to allocate a connection
*
* @throws HttpException in case of a problem
*/
protected void allocateConnection(HostConfiguration route)
throws HttpException {
// we assume that the connection would have been released
// if it was not appropriate for the route of the followup
if (managedConn != null)
return;
//@@@ use connection manager timeout
managedConn = connManager.getConnection(route);
} // allocateConnection
/**
* Establishes the target route.
*
* @param route the route to establish
* @param context the context for the request execution
*
* @throws HttpException in case of a problem
* @throws IOException in case of an IO problem
*/
protected void establishRoute(HostConfiguration route,
HttpContext context)
throws HttpException, IOException {
//@@@ where do we get the currently established route?
//@@@ how to handle CONNECT requests for tunnelling?
//@@@ for now, let's just deal with connected and not connected
if ((route.getProxyHost() != null) &&
!"http".equals(route.getHost().getSchemeName())) {
//@@@ the actual check should be whether the socket factory
//@@@ for the target host scheme is a SecureSocketFactory
throw new UnsupportedOperationException
("Currently only plain http via proxy is supported.");
}
if (managedConn.isOpen())
return; // already established
//@@@ should the request parameters already be used here?
//@@@ probably yes, but they're not linked yet
//@@@ will linking above cause problems with linking in reqExec?
//@@@ probably not, because the parent is replaced
//@@@ just make sure we don't link parameters to themselves
managedConn.open(route, context, defaultParams);
} // establishConnection
/**
* Analyzes a response to check need for a followup.
*
* @param roureq the request that was sent
* @param response the response to analayze
* @param context the context used for the current request execution
*
* @return the followup request and route if there is a followup, or
* <code>null</code> if the response should be returned as is
*
* @throws HttpException in case of a problem
* @throws IOException in case of an IO problem
*/
protected RoutedRequest handleResponse(RoutedRequest roureq,
HttpResponse response,
HttpContext context)
throws HttpException, IOException {
//@@@ if there is a followup, check connection keep-alive and
//@@@ consume response body if necessary or close otherwise
//@@@ if the request needs to be re-sent with authentication,
//@@@ how to revert the modifications applied by the interceptors?
//@@@ use a wrapper when sending?
return null;
} // handleResponse
} // class DefaultClientRequestDirector

View File

@ -0,0 +1,172 @@
/*
* $HeadURL$
* $Revision$
* $Date$
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.http.client.impl;
import java.io.IOException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpException;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.BasicHttpProcessor;
import org.apache.http.protocol.HttpRequestExecutor;
import org.apache.http.conn.HostConfiguration;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.client.HttpClient;
import org.apache.http.client.RoutedRequest;
import org.apache.http.client.ClientRequestDirector;
/**
* Default implementation of an HTTP client.
* <br/>
* This class replaces <code>HttpClient</code> in HttpClient 3.
*
* @author <a href="mailto:rolandw@apache.org">Roland Weber</a>
*
*
* <!-- empty lines to avoid svn diff problems -->
* @version $Revision$
*
* @since 4.0
*/
public class DefaultHttpClient extends AbstractHttpClient {
/**
* Creates a new HTTP client from parameters and a connection manager.
*
* @param params the parameters
* @param conman the connection manager
*/
public DefaultHttpClient(HttpParams params,
ClientConnectionManager conman) {
super(null, params, conman, null);
httpProcessor = createProcessor();
}
/**
* Creates and initializes an HTTP processor.
* This method is typically called by the constructor,
* after the base class has been initialized.
*
* @return a new, initialized HTTP processor
*/
protected BasicHttpProcessor createProcessor() {
BasicHttpProcessor bhp = new BasicHttpProcessor();
//@@@ evaluate defaultParams to initialize interceptors
return bhp;
}
// non-javadoc, see interface HttpClient
public HttpResponse execute(RoutedRequest roureq, HttpContext context)
throws HttpException, IOException {
if (roureq == null) {
throw new IllegalArgumentException
("Routed request must not be null.");
}
if (roureq.getRequest() == null) {
throw new IllegalArgumentException
("Request must not be null.");
}
if (roureq.getRoute() == null) {
throw new IllegalArgumentException
("Route must not be null.");
}
if (context == null)
context = defaultContext;
ClientRequestDirector director = createDirector(context);
HttpResponse response = director.execute(roureq, context);
//@@@ "finalize" response, to allow for buffering of entities?
//@@@ here or in director?
System.out.println("@@@ what about "+director.getConnection()+"?");
//@@@ return a "connected response" with a release() callback?
//@@@ use a special response entity to implement the release()?
return response;
} // execute
// non-javadoc, see base class AbstractHttpClient
protected RoutedRequest determineRoute(HttpHost target,
HttpRequest request,
HttpContext context)
throws HttpException {
//@@@ allow null target if there is a default route with a target?
if (target == null) {
throw new IllegalArgumentException
("Target host must not be null.");
}
//@@@ refer to a default HostConfiguration?
HostConfiguration route = new HostConfiguration(target, null, null);
return new RoutedRequest.Impl(request, route);
}
/**
* Creates a new director for a request execution.
*
* @param context the context to use for the execution,
* never <code>null</code>
*
* @return the new director for executing a method in the given context
*/
protected ClientRequestDirector createDirector(HttpContext context) {
//@@@ can we use a single reqexec without sacrificing thread safety?
//@@@ it seems wasteful to throw away both director and reqexec
HttpRequestExecutor reqexec = new HttpRequestExecutor(httpProcessor);
reqexec.setParams(defaultParams);
return new DefaultClientRequestDirector
(connManager, reqexec, defaultParams);
}
} // class DefaultHttpClient