You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by di...@apache.org on 2002/09/01 03:27:37 UTC
cvs commit: jakarta-commons/httpclient/src/java/org/apache/commons/httpclient HttpUrlMethod.java HttpMultiClient.java Authenticator.java
dion 2002/08/31 18:27:37
Modified: httpclient/src/java/org/apache/commons/httpclient/methods
PostMethod.java HeadMethod.java GetMethod.java
httpclient/src/java/org/apache/commons/httpclient
HttpUrlMethod.java HttpMultiClient.java
Authenticator.java
Log:
Reformatting with Jalopy
Revision Changes Path
1.17 +457 -333 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/PostMethod.java
Index: PostMethod.java
===================================================================
RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/PostMethod.java,v
retrieving revision 1.16
retrieving revision 1.17
diff -u -r1.16 -r1.17
--- PostMethod.java 8 Aug 2002 21:51:36 -0000 1.16
+++ PostMethod.java 1 Sep 2002 01:27:37 -0000 1.17
@@ -59,9 +59,16 @@
* [Additional notices, if required by prior licensing conditions]
*
*/
-
package org.apache.commons.httpclient.methods;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpConnection;
import org.apache.commons.httpclient.HttpException;
@@ -71,43 +78,46 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import java.util.Vector;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.InputStream;
-import java.io.IOException;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Vector;
-
/**
- * POST Method.
- * This class encapsulates the HTTP POST specification. According to
- * <a href="http://www.ietf.org/rfc/rfc2616.txt">RFC2616</a>:
- * <blockquote>The POST method is used to request that the origin server accept the
- * entity enclosed in the request as a new subordinate of the resource
- * identified by the Request-URI in the Request-Line. POST is designed
- * to allow a uniform method to cover the following functions:
- * <ul><li>Annotation of existing resources;
- * <li>Posting a message to a bulletin board, newsgroup, mailing list,
- * or similar group of articles;
- * <li>Providing a block of data, such as the result of submitting a
- * form, to a data-handling process;
- * <li>Extending a database through an append operation.
+ * POST Method. This class encapsulates the HTTP POST specification. According
+ * to <a href="http://www.ietf.org/rfc/rfc2616.txt">RFC2616</a>:
+ * <blockquote>
+ * The POST method is used to request that the origin server accept the entity
+ * enclosed in the request as a new subordinate of the resource identified by
+ * the Request-URI in the Request-Line. POST is designed to allow a uniform
+ * method to cover the following functions:
+ *
+ * <ul>
+ * <li>
+ * Annotation of existing resources;
+ * </li>
+ * <li>
+ * Posting a message to a bulletin board, newsgroup, mailing list, or similar
+ * group of articles;
+ * </li>
+ * <li>
+ * Providing a block of data, such as the result of submitting a form, to a
+ * data-handling process;
+ * </li>
+ * <li>
+ * Extending a database through an append operation.
+ * </li>
* </ul>
+ *
* </blockquote>
- *
+ *
* @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
* @author <a href="mailto:dsale@us.britannica.com">Doug Sale</a>
* @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
* @author Ortwin Gl�ck
- *
* @since 1.0
*/
public class PostMethod extends GetMethod {
+ //~ Static variables/initializers ������������������������������������������
+
/**
- * The content length will be calculated automatically. This implies buffering
- * of the content.
+ * The content length will be calculated automatically. This implies
+ * buffering of the content.
*/
public static final int CONTENT_LENGTH_AUTO = -2;
@@ -118,11 +128,37 @@
*/
public static final int CONTENT_LENGTH_CHUNKED = -1;
+ // -------------------------------------------------------------- Constants
+
+ /** Log object for this class. */
+ private static final Log log = LogFactory.getLog(PostMethod.class);
+
+ /** The Content-Type header for www-form-urlcoded. */
+ static final Header CONTENT_TYPE = new Header("Content-Type",
+ "application/x-www-form-urlencoded");
+
+ /** DOCUMENT ME! */
+ private ByteArrayOutputStream buffer = null;
+
+ /** DOCUMENT ME! */
+ private InputStream requestBody = null;
+
+ /** DOCUMENT ME! */
+ private Vector parameters = new Vector();
+
+ /** DOCUMENT ME! */
+ private int repeatCount = 0;
+
+ /** DOCUMENT ME! */
+ private int requestContentLength = CONTENT_LENGTH_AUTO;
+
+ //~ Constructors �����������������������������������������������������������
+
// ----------------------------------------------------------- Constructors
/**
* No-arg constructor.
- *
+ *
* @since 1.0
*/
public PostMethod() {
@@ -132,8 +168,9 @@
/**
* Path-setting constructor.
+ *
* @param path the path to request
- *
+ *
* @since 1.0
*/
public PostMethod(String path) {
@@ -143,9 +180,10 @@
/**
* Path and temp directory constructor.
+ *
* @param path the path to request
* @param tempDir directory to store temp files in
- *
+ *
* @since 1.0
*/
public PostMethod(String path, String tempDir) {
@@ -155,10 +193,11 @@
/**
* Path, temp directory and temp file constructor.
+ *
* @param path the path to request
* @param tempDir directory to store temp files in
* @param tempFile file to store temporary data in
- *
+ *
* @since 1.0
*/
public PostMethod(String path, String tempDir, String tempFile) {
@@ -166,13 +205,32 @@
setFollowRedirects(false);
}
+ //~ Methods ����������������������������������������������������������������
+
+ /**
+ * A POST request can only be redirected if input is buffered. Overrides
+ * method of {@link org.apache.commons.httpclient.HttpMethodBase}.
+ *
+ * @return true if request is buffered and <code>setFollowRedirects</code>
+ * was set to <code>true</code>.
+ *
+ * @since 2.0
+ */
+ public boolean getFollowRedirects() {
+ if (!super.getFollowRedirects()) {
+ return false;
+ }
+
+ return (buffer == null);
+ }
// ----------------------------------------------------- Instance Methods
/**
* Returns <tt>"POST"</tt>.
+ *
* @return <tt>"POST"</tt>
- *
+ *
* @since 2.0
*/
public String getName() {
@@ -180,509 +238,575 @@
}
/**
- * A POST request can only be redirected if input is buffered.
- * Overrides method of {@link org.apache.commons.httpclient.HttpMethodBase}.
- * @return true if request is buffered and <code>setFollowRedirects</code>
- * was set to <code>true</code>.
- *
+ * Set the value of parameter with parameterName to parameterValue. Does
+ * not preserve the initial insertion order.
+ *
+ * @param parameterName DOCUMENT ME!
+ * @param parameterValue DOCUMENT ME!
+ *
+ * @throws IllegalStateException if my request body has already been
+ * generated.
+ *
* @since 2.0
+ * @deprecated use {@link #removeParameter(String,String)} followed by
+ * {@link #addParameter(String,String)}.
*/
- public boolean getFollowRedirects() {
- if (!super.getFollowRedirects()) return false;
- return (buffer == null);
+ public void setParameter(String parameterName, String parameterValue) {
+ log.trace("enter PostMethod.setParameter(String, String)");
+
+ if (null != requestBody) {
+ throw new IllegalStateException("Request body already generated.");
+ }
+
+ removeParameter(parameterName, parameterValue);
+ addParameter(parameterName, parameterValue);
}
+ /**
+ * Gets the parameter of the specified name. If there exists more than one
+ * parameter with the name paramName, then only the first one is returned.
+ *
+ * @param paramName DOCUMENT ME!
+ *
+ * @return If a parameter exists with the name argument, the coresponding
+ * NameValuePair is returned. Otherwise null.
+ *
+ * @since 2.0
+ */
+ public NameValuePair getParameter(String paramName) {
+ log.trace("enter PostMethod.getParameter(String)");
+
+ if (paramName == null) {
+ return null;
+ }
+
+ Iterator iter = parameters.iterator();
+
+ while (iter.hasNext()) {
+ NameValuePair parameter = (NameValuePair) iter.next();
+
+ if (paramName.equals(parameter.getName())) {
+ return parameter;
+ }
+ }
+
+ return null;
+ }
/**
- * Override method of {@link org.apache.commons.httpclient.HttpMethodBase}
- * to clear my request body.
- *
- * @since 1.0
+ * Gets the parameters currently added to the PostMethod. If there are no
+ * parameters, a valid array is returned with zero elements. The returned
+ * array object contains an array of pointers to the internal data
+ * members. TODO: is it ok to return internal data?
+ *
+ * @return An array of the current parameters
+ *
+ * @since 2.0
+ * @see #getParameter(java.lang.String)
*/
- public void recycle() {
- log.trace("enter PostMethod.recycle()");
- super.recycle();
- requestBody = null;
- requestContentLength = CONTENT_LENGTH_AUTO;
- buffer = null;
- parameters.clear();
+ public NameValuePair[] getParameters() {
+ log.trace("enter PostMethod.getParameters()");
+
+ int numPairs = parameters.size();
+ Object[] objectArr = parameters.toArray();
+ NameValuePair[] nvPairArr = new NameValuePair[numPairs];
+
+ for (int i = 0; i < numPairs; i++) {
+ nvPairArr[i] = (NameValuePair) objectArr[i];
+ }
+
+ return nvPairArr;
}
/**
- * Set the value of parameter with parameterName to parameterValue.
- * Does not preserve the initial insertion order.
- *
- * @throws IllegalStateException if my request body has already been generated.
- * @deprecated use {@link #removeParameter(String,String)} followed by
- * {@link #addParameter(String,String)}.
- *
+ * Sets the request body to be the specified string.
+ *
+ * <p>
+ * Once this method has been invoked, the request parameters cannot be
+ * altered until I am {@link #recycle recycled}.
+ * </p>
+ *
+ * @param body DOCUMENT ME!
+ *
+ * @throws IllegalStateException if request params have been added
+ *
* @since 2.0
+ * @deprecated This method converts characters to bytes in a platform
+ * dependent encoding. Use {@link
+ * #setRequestBody(java.io.InputStream)} instead.
*/
- public void setParameter(String parameterName, String parameterValue) {
- log.trace("enter PostMethod.setParameter(String, String)");
+ public void setRequestBody(String body) {
+ log.trace("enter PostMethod.setRequestBody(String)");
- if(null != requestBody) {
- throw new IllegalStateException("Request body already generated.");
+ if (!parameters.isEmpty()) {
+ throw new IllegalStateException(
+ "Request parameters have already been added.");
+ }
+
+ requestBody = new ByteArrayInputStream(body.getBytes());
+ }
+
+ /**
+ * Sets the request body to be the specified inputstream.
+ *
+ * <p>
+ * Once this method has been invoked, the request parameters cannot be
+ * altered until I am {@link #recycle recycled}.
+ * </p>
+ *
+ * @param body DOCUMENT ME!
+ *
+ * @throws IllegalStateException if request params have been added
+ *
+ * @since 2.0
+ */
+ public void setRequestBody(InputStream body) {
+ log.trace("enter PostMethod.getRequestBody(InputStream)");
+
+ if (!parameters.isEmpty()) {
+ throw new IllegalStateException(
+ "Request parameters have already been added.");
+ }
+
+ requestBody = body;
+ }
+
+ /**
+ * Gets the requestBody as it would be if it was executed.
+ *
+ * @return The request body if it has been set. The generated request
+ * body from the paramters if they exist. Null otherwise.
+ *
+ * @since 2.0
+ */
+ public InputStream getRequestBody() {
+ log.trace("enter PostMethod.getRequestBody()");
+
+ if (requestBody != null) {
+ return requestBody;
+ } else if (!parameters.isEmpty()) {
+ return generateRequestBody(parameters);
+ } else {
+ return null;
}
- removeParameter(parameterName, parameterValue);
- addParameter(parameterName, parameterValue);
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @return the request body as a string
+ * @throws IOException DOCUMENT ME!
+ *
+ * @since 2.0
+ */
+ public String getRequestBodyAsString() throws IOException {
+ log.trace("enter PostMethod.getRequestBodyAsString()");
+
+ StringBuffer buffer = new StringBuffer();
+ InputStream requestBody = getRequestBody();
+ int data = requestBody.read();
+
+ while (data != -1) {
+ buffer.append((char) data);
+ data = requestBody.read();
+ }
+
+ return buffer.toString();
}
/**
* Sets length information about the request body.
- * <p>Note: If you specify a content length the request is unbuffered. This
+ *
+ * <p>
+ * Note: If you specify a content length the request is unbuffered. This
* prevents automatic retry if a request fails the first time. This means
- * that the HttpClient can not perform authorization automatically but will
- * throw an Exception. You will have to set the necessary 'Authorization' or
- * 'Proxy-Authorization' headers manually.</p>
- *
+ * that the HttpClient can not perform authorization automatically but
+ * will throw an Exception. You will have to set the necessary
+ * 'Authorization' or 'Proxy-Authorization' headers manually.
+ * </p>
+ *
* @param length size in bytes or any of CONTENT_LENGTH_AUTO,
- * CONTENT_LENGTH_CHUNKED. If number of bytes is specified the content will
- * not be buffered internally and the Content-Length header of the request
- * will be used. In this case the user is responsible to supply the correct
- * content length.
- *
+ * CONTENT_LENGTH_CHUNKED. If number of bytes is specified the
+ * content will not be buffered internally and the Content-Length
+ * header of the request will be used. In this case the user is
+ * responsible to supply the correct content length.
+ * @throws RuntimeException DOCUMENT ME!
+ *
* @since 2.0
*/
public void setRequestContentLength(int length) {
log.trace("enter PostMethod.setRequestContentLength(int)");
if ((length == CONTENT_LENGTH_CHUNKED) && !isHttp11()) {
- throw new RuntimeException("Chunked transfer encoding not allowed for HTTP/1.0");
+ throw new RuntimeException(
+ "Chunked transfer encoding not allowed for HTTP/1.0");
}
+
requestContentLength = length;
}
/**
* Add a new parameter to be used in the POST request body.
- *
+ *
* @param paramName The parameter name to add.
* @param paramValue The parameter value to add.
- * @throws IllegalStateException if my request body has already been generated.
+ *
+ * @throws IllegalStateException if my request body has already been
+ * generated.
* @throws IllegalArgumentException if either argument is null
- *
+ *
* @since 1.0
*/
public void addParameter(String paramName, String paramValue) {
log.trace("enter PostMethod.addParameter(String, String)");
- if(null != requestBody) {
+ if (null != requestBody) {
throw new IllegalStateException("Request body already generated.");
}
- if (paramName == null || paramValue == null){
- throw new IllegalArgumentException("Arguments to addParameter(String, String) cannot be null");
- }else{
+
+ if ((paramName == null) || (paramValue == null)) {
+ throw new IllegalArgumentException(
+ "Arguments to addParameter(String, String) cannot be null");
+ } else {
parameters.add(new NameValuePair(paramName, paramValue));
}
}
-
/**
* Add a new parameter to be used in the POST request body.
- *
- * @param parameter The parameter to add.
- * @throws IllegalStateException if my request body has already been generated.
- * @throws IllegalArgumentException if the argument is null or contains null values
- * @see #addParameter(String,String)
- *
+ *
+ * @param param The parameter to add.
+ *
+ * @throws IllegalStateException if my request body has already been
+ * generated.
+ * @throws IllegalArgumentException if the argument is null or contains
+ * null values
+ *
* @since 2.0
+ * @see #addParameter(String,String)
*/
public void addParameter(NameValuePair param) {
log.trace("enter PostMethod.addParameter(NameValuePair)");
- if(null != requestBody) {
+ if (null != requestBody) {
throw new IllegalStateException("Request body already generated.");
}
- if(null == param){
- throw new IllegalArgumentException("Argument to addParameter(NameValuePair) cannot be null");
- }else{
+
+ if (null == param) {
+ throw new IllegalArgumentException(
+ "Argument to addParameter(NameValuePair) cannot be null");
+ } else {
addParameter(param.getName(), param.getValue());
}
}
/**
- * Add an Array of parameters to be used in the POST request body.
- * Logs a warning if the parameters argument is null.
- *
- * @param parameter The array of parameters to add.
- * @throws IllegalStateException if my request body has already been generated.
- * @see #addParameter(org.apache.commons.httpclient.NameValuePair)
- *
+ * Add an Array of parameters to be used in the POST request body. Logs a
+ * warning if the parameters argument is null.
+ *
+ * @param parameters The array of parameters to add.
+ *
+ * @throws IllegalStateException if my request body has already been
+ * generated.
+ *
* @since 2.0
+ * @see #addParameter(org.apache.commons.httpclient.NameValuePair)
*/
public void addParameters(NameValuePair[] parameters) {
log.trace("enter PostMethod.addParameters(NameValuePair[])");
- if(null != requestBody) {
+ if (null != requestBody) {
throw new IllegalStateException("Request body already generated.");
}
- if(null == parameters){
+
+ if (null == parameters) {
log.warn("Attempt to addParameters(null) ignored");
- }else{
- for (int i=0; i<parameters.length; i++){
+ } else {
+ for (int i = 0; i < parameters.length; i++) {
addParameter(parameters[i]);
}
}
}
-
- /**
- * Gets the parameter of the specified name.
- *
- * If there exists more than one parameter with the name paramName,
- * then only the first one is returned.
- *
- * @return If a parameter exists with the name argument, the coresponding
- * NameValuePair is returned. Otherwise null.
- *
- * @since 2.0
- */
- public NameValuePair getParameter(String paramName){
- log.trace("enter PostMethod.getParameter(String)");
-
- if (paramName == null){
- return null;
- }
- Iterator iter = parameters.iterator();
- while(iter.hasNext()){
- NameValuePair parameter = (NameValuePair)iter.next();
- if (paramName.equals(parameter.getName())){
- return parameter;
- }
- }
- return null;
- }
-
/**
- * Gets the parameters currently added to the PostMethod.
- *
- * If there are no parameters, a valid array is returned with
- * zero elements.
- * The returned array object contains an array of pointers to
- * the internal data members.
- * TODO: is it ok to return internal data?
+ * Override method of {@link org.apache.commons.httpclient.HttpMethodBase}
+ * to clear my request body.
*
- * @return An array of the current parameters
- * @see #getParameter(java.lang.String)
- *
- * @since 2.0
+ * @since 1.0
*/
- public NameValuePair[] getParameters(){
- log.trace("enter PostMethod.getParameters()");
-
- int numPairs = parameters.size();
- Object[] objectArr= parameters.toArray();
- NameValuePair[] nvPairArr = new NameValuePair[numPairs];
- for(int i=0; i<numPairs; i++){
- nvPairArr[i] = (NameValuePair)objectArr[i];
- }
- return nvPairArr;
+ public void recycle() {
+ log.trace("enter PostMethod.recycle()");
+ super.recycle();
+ requestBody = null;
+ requestContentLength = CONTENT_LENGTH_AUTO;
+ buffer = null;
+ parameters.clear();
}
/**
- * Removes all parameters with the given paramName.
- * If there is more than one parameter with the given paramName, all
- * of them are removed. If there is just one, it is removed. If there
- * are none, then the request is ignored.
+ * Removes all parameters with the given paramName. If there is more than
+ * one parameter with the given paramName, all of them are removed. If
+ * there is just one, it is removed. If there are none, then the request
+ * is ignored.
*
* @param paramName The parameter name to remove.
+ *
* @return true if at least one parameter was removed
- * @throws IllegalStateException if my request body has already been generated.
- *
+ *
+ * @throws IllegalStateException if my request body has already been
+ * generated.
+ * @throws IllegalArgumentException When the parameter name passed is null
+ *
* @since 2.0
*/
public boolean removeParameter(String paramName) {
log.trace("enter PostMethod.removeParameter(String)");
- if(null != requestBody) {
+ if (null != requestBody) {
throw new IllegalStateException("Request body already generated.");
}
- if (paramName == null){
- throw new IllegalArgumentException("Argument passed to removeParameter(String) cannot be null");
+
+ if (paramName == null) {
+ throw new IllegalArgumentException(
+ "Argument passed to removeParameter(String) cannot be null");
}
+
boolean removed = true;
Iterator iter = parameters.iterator();
- while(iter.hasNext()){
- NameValuePair pair = (NameValuePair)iter.next();
- if (paramName.equals(pair.getName())){
+
+ while (iter.hasNext()) {
+ NameValuePair pair = (NameValuePair) iter.next();
+
+ if (paramName.equals(pair.getName())) {
iter.remove();
removed = true;
}
}
+
return removed;
}
/**
- * Removes all parameter with the given paramName and paramValue.
- * If there is more than one parameter with the given paramName, only
- * one is removed. If there are none, then the request is ignored.
- *
+ * Removes all parameter with the given paramName and paramValue. If there
+ * is more than one parameter with the given paramName, only one is
+ * removed. If there are none, then the request is ignored.
+ *
* @param paramName The parameter name to remove.
* @param paramValue The parameter value to remove.
+ *
* @return true if a parameter was removed.
- * @throws IllegalStateException if my request body has already been generated.
- *
+ *
+ * @throws IllegalStateException if my request body has already been
+ * generated.
+ * @throws IllegalArgumentException when param name or value are null
+ *
* @since 2.0
*/
public boolean removeParameter(String paramName, String paramValue) {
log.trace("enter PostMethod.removeParameter(String, String)");
- if(null != requestBody) {
+ if (null != requestBody) {
throw new IllegalStateException("Request body already generated.");
}
- if (paramName == null || paramValue == null){
- throw new IllegalArgumentException("Argument passed to removeParameter(String,String) cannot be null");
+
+ if ((paramName == null) || (paramValue == null)) {
+ throw new IllegalArgumentException(
+ "Argument passed to removeParameter(String,String) cannot be "
+ + "null");
}
+
Iterator iter = parameters.iterator();
- while(iter.hasNext()){
- NameValuePair pair = (NameValuePair)iter.next();
- if (paramName.equals(pair.getName()) && paramValue.equals(pair.getValue())){
+
+ while (iter.hasNext()) {
+ NameValuePair pair = (NameValuePair) iter.next();
+
+ if (paramName.equals(pair.getName())
+ && paramValue.equals(pair.getValue())) {
iter.remove();
+
return true;
}
}
+
return false;
}
- /**
- * Sets the request body to be the specified string.
+ /**
+ * Override method of {@link org.apache.commons.httpclient.HttpMethodBase}
+ * to return the length of the request body.
*
- * <p>Once this method has been invoked, the request parameters
- * cannot be altered until I am {@link #recycle recycled}.
+ * @return number of bytes in the request body
*
- * @throws IllegalStateException if request params have been added
- * @deprecated This method converts characters to bytes in a platform dependent
- * encoding. Use {@link #setRequestBody(java.io.InputStream)} instead.
- *
* @since 2.0
*/
- public void setRequestBody(String body) {
- log.trace("enter PostMethod.setRequestBody(String)");
+ protected int getRequestContentLength() {
+ log.trace("enter PostMethod.getRequestContentLength()");
- if(!parameters.isEmpty()) {
- throw new IllegalStateException("Request parameters have already been added.");
+ if (null == requestBody) {
+ requestBody = generateRequestBody(parameters);
+ bufferContent();
}
- requestBody = new ByteArrayInputStream(body.getBytes());
- }
- /**
- * Sets the request body to be the specified inputstream.
- *
- * <p>Once this method has been invoked, the request parameters
- * cannot be altered until I am {@link #recycle recycled}.
- *
- * @throws IllegalStateException if request params have been added
- *
- * @since 2.0
- */
- public void setRequestBody(InputStream body) {
- log.trace("enter PostMethod.getRequestBody(InputStream)");
-
- if(!parameters.isEmpty()) {
- throw new IllegalStateException("Request parameters have already been added.");
+ if (requestContentLength != CONTENT_LENGTH_AUTO) {
+ return requestContentLength;
}
- requestBody = body;
- }
- /**
- * Gets the requestBody as it would be if it was executed.
- *
- * @return The request body if it has been set. The generated
- * request body from the paramters if they exist. Null otherwise.
- *
- * @since 2.0
- */
- public InputStream getRequestBody() {
- log.trace("enter PostMethod.getRequestBody()");
-
- if (requestBody != null) {
- return requestBody;
- } else if (!parameters.isEmpty()) {
- return generateRequestBody(parameters);
- } else {
- return null;
- }
- }
-
- /**
- * @return the request body as a string
- *
- * @since 2.0
- */
- public String getRequestBodyAsString() throws IOException{
- log.trace("enter PostMethod.getRequestBodyAsString()");
+ bufferContent();
- StringBuffer buffer = new StringBuffer();
- InputStream requestBody = getRequestBody();
- int data = requestBody.read();
- while (data != -1) {
- buffer.append((char) data);
- data = requestBody.read();
- }
- return buffer.toString();
+ return requestContentLength;
}
-
/**
- * Override method of {@link org.apache.commons.httpclient.HttpMethodBase} to
- * also add <tt>Content-Type</tt> header
- * when appropriate.
- *
+ * Override method of {@link org.apache.commons.httpclient.HttpMethodBase}
+ * to also add <tt>Content-Type</tt> header when appropriate.
+ *
+ * @param state DOCUMENT ME!
+ * @param conn DOCUMENT ME!
+ * @throws IOException DOCUMENT ME!
+ * @throws HttpException DOCUMENT ME!
+ *
* @since 2.0
*/
protected void addRequestHeaders(HttpState state, HttpConnection conn)
throws IOException, HttpException {
- super.addRequestHeaders(state,conn);
- if(!parameters.isEmpty()) { //there are some parameters, so set the contentType header
+ super.addRequestHeaders(state, conn);
+
+ if (!parameters.isEmpty()) {
+ //there are some parameters, so set the contentType header
setRequestHeader(CONTENT_TYPE);
}
}
-
/**
* Override method of {@link org.apache.commons.httpclient.HttpMethodBase}
- * to write request parameters as the request body.
+ * to write request parameters as the request body. The input stream will
+ * be truncated after the specified content length.
+ *
+ * @param state DOCUMENT ME!
+ * @param conn DOCUMENT ME!
*
- * The input stream will be truncated after the specified content length.
- * @throws IOException if the stream ends before the specified content length.
- *
- * <p>Once this method has been invoked, the request parameters
- * cannot be altered until I am {@link #recycle recycled}.
- *
* @return always returns true
- *
+ *
+ * @throws IOException if the stream ends before the specified content
+ * length. <p>
+ * @throws HttpException DOCUMENT ME!
+ *
* @since 2.0
*/
protected boolean writeRequestBody(HttpState state, HttpConnection conn)
throws IOException, HttpException {
- log.trace("enter PostMethod.writeRequestBody(HttpState, HttpConnection)");
+ log.trace(
+ "enter PostMethod.writeRequestBody(HttpState, HttpConnection)");
- if(null == requestBody) {
+ if (null == requestBody) {
requestBody = generateRequestBody(parameters);
}
+
if ((repeatCount > 0) && (buffer == null)) {
- throw new HttpException("Sorry, unbuffered POST request can not be repeated.");
+ throw new HttpException(
+ "Sorry, unbuffered POST request can not be repeated.");
}
+
repeatCount++;
byte[] data = new byte[10000];
int l = requestBody.read(data);
int total = 0;
+
while (l > 0) {
- if ((requestContentLength > 0) && (total + l > requestContentLength)) {
+ if ((requestContentLength > 0)
+ && (total + l > requestContentLength)) {
l = requestContentLength - total;
conn.write(data, 0, l);
+
break;
}
+
conn.write(data, 0, l);
total += l;
l = requestBody.read(data);
}
+
if ((requestContentLength > 0) && (total < requestContentLength)) {
throw new IOException("unexpected end of input stream");
}
+
if (buffer != null) {
//restore buffered content for repeated requests
requestBody = new ByteArrayInputStream(buffer.toByteArray());
}
+
return true;
}
+ // ------------------------------------------------------------Class Methods
/**
- * Override method of {@link org.apache.commons.httpclient.HttpMethodBase}
- * to return the length of the request body.
- *
- * @return number of bytes in the request body
- *
- * @since 2.0
+ * Encode the list of parameters into a urlencoded string.
+ * TODO: consider moving this out into URIUtil.
+ *
+ * @param params DOCUMENT ME!
+ *
+ * @return urlencoded string
+ *
+ * @since 1.0
*/
- protected int getRequestContentLength() {
- log.trace("enter PostMethod.getRequestContentLength()");
+ static InputStream generateRequestBody(List params) {
+ log.trace("enter PostMethod.generateRequestBody(List)");
- if(null == requestBody) {
- requestBody = generateRequestBody(parameters);
- bufferContent();
- }
+ Iterator it = params.iterator();
+ StringBuffer sb = new StringBuffer();
- if (requestContentLength != CONTENT_LENGTH_AUTO) {
- return requestContentLength;
- }
+ while (it.hasNext()) {
+ NameValuePair parameter = (NameValuePair) it.next();
- bufferContent();
- return requestContentLength;
+ //TODO: make sure these encodings conform to the RFC
+ sb.append(URIUtil.encode(parameter.getName(),
+ URIUtil.queryStringValueSafe(), false));
+ sb.append("=");
+ sb.append(URIUtil.encode(parameter.getValue(),
+ URIUtil.queryStringValueSafe(), true));
+
+ if (it.hasNext()) {
+ sb.append("&");
+ }
+ }
+
+ return new ByteArrayInputStream(sb.toString().getBytes());
}
/**
- * Buffers the request body and calculates the content length.
- * If the method was called earlier it returns immediately.
- *
+ * Buffers the request body and calculates the content length. If the
+ * method was called earlier it returns immediately.
+ *
* @since 1.0
*/
private void bufferContent() {
log.trace("enter PostMethod.bufferContent()");
- if (buffer != null) return;
+ if (buffer != null) {
+ return;
+ }
+
try {
buffer = new ByteArrayOutputStream();
+
byte[] data = new byte[10000];
int l = requestBody.read(data);
int total = 0;
+
while (l > 0) {
buffer.write(data, 0, l);
total += l;
l = requestBody.read(data);
}
+
requestBody = new ByteArrayInputStream(buffer.toByteArray());
requestContentLength = total;
- } catch(IOException e) {
+ } catch (IOException e) {
requestBody = null;
requestContentLength = 0;
}
}
-
- // -------------------------------------------------------------- Class Methods
-
- /**
- * Encode the list of parameters into a urlencoded string.
- *
- * TODO: consider moving this out into URIUtil.
- * @return urlencoded string
- *
- * @since 1.0
- */
- static InputStream generateRequestBody(List params) {
- log.trace("enter PostMethod.generateRequestBody(List)");
-
- Iterator it = params.iterator();
- StringBuffer sb = new StringBuffer();
- while(it.hasNext()) {
- NameValuePair parameter = (NameValuePair)it.next();
- //TODO: make sure these encodings conform to the RFC
- sb.append(URIUtil.encode(parameter.getName(), URIUtil.queryStringValueSafe(), false));
- sb.append("=");
- sb.append(URIUtil.encode(parameter.getValue(), URIUtil.queryStringValueSafe(), true));
- if (it.hasNext()){
- sb.append("&");
- }
- }
- return new ByteArrayInputStream(sb.toString().getBytes());
- }
-
- // -------------------------------------------------------------- Instance Variables
-
- private InputStream requestBody = null;
- private Vector parameters = new Vector();
- private int requestContentLength = CONTENT_LENGTH_AUTO;
- private ByteArrayOutputStream buffer = null;
- private int repeatCount = 0;
-
- // -------------------------------------------------------------- Constants
-
- /** Log object for this class. */
- private static final Log log = LogFactory.getLog(PostMethod.class);
-
- /** The Content-Type header for www-form-urlcoded. */
- static final Header CONTENT_TYPE = new Header ("Content-Type","application/x-www-form-urlencoded");
-}
+}
\ No newline at end of file
1.11 +43 -46 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/HeadMethod.java
Index: HeadMethod.java
===================================================================
RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/HeadMethod.java,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -r1.10 -r1.11
--- HeadMethod.java 8 Aug 2002 21:51:36 -0000 1.10
+++ HeadMethod.java 1 Sep 2002 01:27:37 -0000 1.11
@@ -59,48 +59,45 @@
* [Additional notices, if required by prior licensing conditions]
*
*/
-
package org.apache.commons.httpclient.methods;
+import java.io.IOException;
+
import org.apache.commons.httpclient.HttpConnection;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpMethodBase;
import org.apache.commons.httpclient.HttpState;
-
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import java.io.IOException;
-
-
/**
* HEAD Method.
- *
+ *
* @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
- *
* @since 1.0
*/
-public class HeadMethod
- extends HttpMethodBase {
-
+public class HeadMethod extends HttpMethodBase {
+ //~ Static variables/initializers ������������������������������������������
- // ----------------------------------------------------------- Constructors
+ /** Log object for this class. */
+ private static final Log log = LogFactory.getLog(HeadMethod.class);
+ //~ Constructors �����������������������������������������������������������
/**
* No-arg constructor.
- *
+ *
* @since 1.0
*/
public HeadMethod() {
setFollowRedirects(true);
}
-
/**
* Path-setting constructor.
+ *
* @param path the path to request
- *
+ *
* @since 1.0
*/
public HeadMethod(String path) {
@@ -108,26 +105,13 @@
setFollowRedirects(true);
}
-
- // ---------------------------------------------------------------- Methods
-
-
- /** Override recycle to reset redirects default.
- *
- * @since 1.0
- */
- public void recycle() {
- super.recycle();
- setFollowRedirects(true);
- }
-
-
- // --------------------------------------------------- WebdavMethod Methods
+ //~ Methods ����������������������������������������������������������������
/**
* Returns <tt>"HEAD"</tt>.
+ *
* @return <tt>"HEAD"</tt>
- *
+ *
* @since 2.0
*/
public String getName() {
@@ -135,22 +119,35 @@
}
/**
- * Overrides {@link HttpMethodBase} method to
- * <i>not</i> read a response body, despite the
- * presence of a <tt>Content-Length</tt> or
+ * Override recycle to reset redirects default.
+ *
+ * @since 1.0
+ */
+ public void recycle() {
+ super.recycle();
+ setFollowRedirects(true);
+ }
+
+ /**
+ * Overrides {@link HttpMethodBase} method to <i>not</i> read a response
+ * body, despite the presence of a <tt>Content-Length</tt> or
* <tt>Transfer-Encoding</tt> header.
- *
+ *
+ * @param state shared http state
+ * @param conn the http connection to read from
+ *
+ * @throws IOException when there's an error reading data
+ * @throws HttpException never FIXME
+ *
* @since 2.0
*/
protected void readResponseBody(HttpState state, HttpConnection conn)
throws IOException, HttpException {
- log.trace("enter HeadMethod.readResponseBody(HttpState, HttpConnection)");
- return; // despite the possible presence of a content-length header, HEAD returns no response body
- }
-
+ log.trace(
+ "enter HeadMethod.readResponseBody(HttpState, HttpConnection)");
- // -------------------------------------------------------------- Class Variables
-
- /** Log object for this class. */
- private static final Log log = LogFactory.getLog(HeadMethod.class);
-}
+ // despite the possible presence of a content-length header,
+ // HEAD returns no response body
+ return;
+ }
+}
\ No newline at end of file
1.16 +193 -178 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/GetMethod.java
Index: GetMethod.java
===================================================================
RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/GetMethod.java,v
retrieving revision 1.15
retrieving revision 1.16
diff -u -r1.15 -r1.16
--- GetMethod.java 8 Aug 2002 21:51:36 -0000 1.15
+++ GetMethod.java 1 Sep 2002 01:27:37 -0000 1.16
@@ -59,15 +59,8 @@
* [Additional notices, if required by prior licensing conditions]
*
*/
-
package org.apache.commons.httpclient.methods;
-import org.apache.commons.httpclient.HttpConnection;
-import org.apache.commons.httpclient.HttpMethodBase;
-import org.apache.commons.httpclient.HttpState;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
@@ -78,18 +71,22 @@
import java.io.OutputStream;
import java.net.URLEncoder;
+import org.apache.commons.httpclient.HttpConnection;
+import org.apache.commons.httpclient.HttpMethodBase;
+import org.apache.commons.httpclient.HttpState;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
/**
- * GET Method.
- * Implements an HTTP GET request.
- *
+ * GET Method. Implements an HTTP GET request.
+ *
* @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
* @author Sung-Gu Park
* @author Sean C. Sullivan
- *
* @since 1.0
*/
public class GetMethod extends HttpMethodBase {
+ //~ Static variables/initializers ������������������������������������������
// -------------------------------------------------------------- Constants
@@ -99,23 +96,43 @@
/** Temporary directory. */
private static final String TEMP_DIR = "temp/";
- // ----------------------------------------------------------- Constructors
+ //~ Instance variables �����������������������������������������������������
+
+ /** File which contains the buffered data. */
+ private File fileData;
+
+ /** Temporary directory to use. */
+ private String tempDir = TEMP_DIR;
+
+ /** Temporary file to use. */
+ private String tempFile = null;
+
+ /** If we're not using the HD, we're using a memory byte buffer. */
+ private byte[] memoryData;
+
+ // ----------------------------------------------------- Instance Variables
+ /** By default, the get method will buffer read data to the memory. */
+ private boolean useDisk = false;
+
+ //~ Constructors �����������������������������������������������������������
+
+ // ----------------------------------------------------------- Constructors
/**
* No-arg constructor.
- *
+ *
* @since 1.0
*/
public GetMethod() {
setFollowRedirects(true);
}
-
/**
* Path-setting constructor.
+ *
* @param path the path to request
- *
+ *
* @since 1.0
*/
public GetMethod(String path) {
@@ -124,12 +141,12 @@
setFollowRedirects(true);
}
-
/**
* Constructor.
+ *
* @param path the path to request
* @param tempDir the directory in which to store temporary files
- *
+ *
* @since 1.0
*/
public GetMethod(String path, String tempDir) {
@@ -142,10 +159,11 @@
/**
* Constructor.
+ *
* @param path the path to request
* @param tempDir the directory in which to store temporary files
* @param tempFile the file (under tempDir) to buffer contents to
- *
+ *
* @since 1.0
*/
public GetMethod(String path, String tempDir, String tempFile) {
@@ -159,9 +177,10 @@
/**
* Constructor.
+ *
* @param path the path to request
- * @param tempFile the file to buffer contents to
- *
+ * @param fileData the file to buffer contents to
+ *
* @since 1.0
*/
public GetMethod(String path, File fileData) {
@@ -172,72 +191,140 @@
setFollowRedirects(true);
}
- // ----------------------------------------------------- Instance Variables
+ //~ Methods ����������������������������������������������������������������
/**
- * By default, the get method will buffer read data to the memory.
+ * File data setter.
+ *
+ * @param fileData the file to buffer data to
+ *
+ * @since 1.0
*/
- protected boolean useDisk = false;
-
+ public void setFileData(File fileData) {
+ checkNotUsed();
+ this.fileData = fileData;
+ }
/**
- * If we're not using the HD, we're using a memory byte buffer.
+ * File data getter.
+ *
+ * @return the file being used for buffering data
+ *
+ * @since 1.0
*/
- protected byte[] memoryData;
+ public File getFileData() {
+ return fileData;
+ }
+ // --------------------------------------------------------- Public Methods
/**
- * File which contains the buffered data.
+ * Returns <tt>"GET"</tt>.
+ *
+ * @return <tt>"GET"</tt>
+ *
+ * @since 2.0
*/
- protected File fileData;
-
+ public String getName() {
+ return "GET";
+ }
/**
- * Temporary directory to use.
+ * Return my response body, if any, as a byte array. Otherwise return
+ * <tt>null</tt>.
+ *
+ * @return the response body as a byte array
+ *
+ * @since 2.0
*/
- protected String tempDir = TEMP_DIR;
+ public byte[] getResponseBody() {
+ log.trace("enter GetMethod.getResponseBody()");
+ checkUsed();
- /**
- * Temporary file to use.
- */
- protected String tempFile = null;
+ if (useDisk) {
+ try {
+ InputStream is = new FileInputStream(fileData);
+ byte[] buffer = new byte[4096];
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ int nb = 0;
+ while (true) {
+ nb = is.read(buffer);
- // ------------------------------------------------------------- Properties
+ if (nb == -1) {
+ break;
+ }
+ os.write(buffer, 0, nb);
+ }
+
+ is.close();
+
+ return os.toByteArray();
+ } catch (IOException e) {
+ log.error("Exception in GetMethod.getResponseBody() while "
+ + "retrieving data from file \"" + fileData + "\".",
+ e);
+
+ return null;
+ }
+ } else {
+ return memoryData;
+ }
+ }
/**
- * Buffer the response in a file or not. The default is false.
- *
- * @param useDisk If true the entire response will be buffered in a
- * temporary file.
- *
- * @since 1.0
+ * Return my response body, if any, as an {@link InputStream}. Otherwise
+ * return <tt>null</tt>.
+ *
+ * @return a stream to read the response from
+ *
+ * @throws IOException when there is an error reading the response
+ *
+ * @since 2.0
*/
- public void setUseDisk(boolean useDisk) {
- checkNotUsed();
- this.useDisk = useDisk;
- }
+ public InputStream getResponseBodyAsStream() throws IOException {
+ log.trace("enter GetMethod.getResponseBodyAsStream()");
+
+ checkUsed();
+ if (useDisk) {
+ return new FileInputStream(fileData);
+ } else {
+ if (null == memoryData) {
+ return null;
+ } else {
+ return new ByteArrayInputStream(memoryData);
+ }
+ }
+ }
/**
- * Tells if the response will be buffered in a file.
- *
- * @param boolean If true the entire response will be buffered in a
- * temporary file.
- *
- * @since 1.0
+ * Return the response body as a String
+ *
+ * @return Return my response body, if any, as a {@link String}. Otherwise
+ * return <tt>null</tt>.
+ *
+ * @since 2.0
*/
- public boolean getUseDisk() {
- return useDisk;
+ public String getResponseBodyAsString() {
+ log.trace("enter GetMethod.getResponseBodyAsString()");
+
+ byte[] data = getResponseBody();
+
+ if (null == data) {
+ return null;
+ } else {
+ return new String(data);
+ }
}
/**
* Temporary directory setter.
- *
+ *
* @param tempDir New value of tempDir
- *
+ *
* @since 1.0
*/
public void setTempDir(String tempDir) {
@@ -246,22 +333,22 @@
setUseDisk(true);
}
-
/**
* Temporary directory getter.
- *
+ *
+ * @return the current temporary directory
+ *
* @since 1.0
*/
public String getTempDir() {
return tempDir;
}
-
/**
* Temporary file setter.
- *
+ *
* @param tempFile New value of tempFile
- *
+ *
* @since 1.0
*/
public void setTempFile(String tempFile) {
@@ -269,57 +356,49 @@
this.tempFile = tempFile;
}
-
/**
* Temporary file getter.
- *
+ *
+ * @return the current temporary file
+ *
* @since 1.0
*/
public String getTempFile() {
return tempFile;
}
+ // ------------------------------------------------------------- Properties
/**
- * File data getter.
- *
+ * Buffer the response in a file or not. The default is false.
+ *
+ * @param useDisk If true the entire response will be buffered in a
+ * temporary file.
+ *
* @since 1.0
*/
- public File getFileData() {
- return fileData;
+ public void setUseDisk(boolean useDisk) {
+ checkNotUsed();
+ this.useDisk = useDisk;
}
-
/**
- * File data setter.
- *
+ * Tells if the response will be buffered in a file.
+ *
+ * @return true if the response will be buffered
+ *
* @since 1.0
*/
- public void setFileData(File fileData) {
- checkNotUsed();
- this.fileData = fileData;
+ public boolean getUseDisk() {
+ return useDisk;
}
-
- // --------------------------------------------------------- Public Methods
-
/**
- * Returns <tt>"GET"</tt>.
- * @return <tt>"GET"</tt>
- *
- * @since 2.0
- */
- public String getName() {
- return "GET";
- }
-
-
- /**
* Override recycle to reset redirects default.
- *
+ *
* @since 1.0
*/
- public void recycle() {
+ public void recycle() {
log.trace("enter GetMethod.recycle()");
super.recycle();
@@ -328,118 +407,54 @@
setFollowRedirects(true);
}
- /**
- * Return my response body, if any,
- * as a byte array.
- * Otherwise return <tt>null</tt>.
- *
- * @since 2.0
- */
- public byte[] getResponseBody() {
- log.trace("enter GetMethod.getResponseBody()");
-
- checkUsed();
- if(useDisk) {
- try {
- InputStream is = new FileInputStream(fileData);
- byte[] buffer = new byte[4096];
- ByteArrayOutputStream os = new ByteArrayOutputStream();
- int nb = 0;
- while (true) {
- nb = is.read(buffer);
- if (nb == -1)
- break;
- os.write(buffer, 0, nb);
- }
- is.close();
- return os.toByteArray();
- } catch(IOException e) {
- log.error("Exception in GetMethod.getResponseBody() while retrieving data from file \"" + fileData + "\".",e);
- return null;
- }
- } else {
- return memoryData;
- }
- }
-
- /**
- * Return my response body, if any,
- * as a {@link String}.
- * Otherwise return <tt>null</tt>.
- *
- * @since 2.0
- */
- public String getResponseBodyAsString() {
- log.trace("enter GetMethod.getResponseBodyAsString()");
-
- byte[] data = getResponseBody();
- if(null == data) {
- return null;
- } else {
- return new String(data);
- }
- }
-
-
- /**
- * Return my response body, if any,
- * as an {@link InputStream}.
- * Otherwise return <tt>null</tt>.
- *
- * @since 2.0
- */
- public InputStream getResponseBodyAsStream()
- throws IOException {
- log.trace("enter GetMethod.getResponseBodyAsStream()");
-
- checkUsed();
- if (useDisk) {
- return new FileInputStream(fileData);
- } else {
- if(null == memoryData) {
- return null;
- } else {
- return new ByteArrayInputStream(memoryData);
- }
- }
- }
-
-
// ----------------------------------------------------- HttpMethod Methods
/**
- * Overrides method in {@link HttpMethodBase} to
- * write data to the appropriate buffer.
- *
+ * Overrides method in {@link HttpMethodBase} to write data to the
+ * appropriate buffer.
+ *
+ * @param state the shared http state
+ * @param conn the connection to read data from
+ *
+ * @throws IOException when there are problems reading from the connection
+ *
* @since 2.0
*/
protected void readResponseBody(HttpState state, HttpConnection conn)
- throws IOException {
- log.trace("enter GetMethod.readResponseBody(HttpState, HttpConnection)");
+ throws IOException {
+ log.trace("enter GetMethod.readResponseBody(HttpState, "
+ + "HttpConnection)");
OutputStream out = null;
+
if (useDisk) {
if (fileData == null) {
// Create a temporary file on the HD
File dir = new File(tempDir);
dir.deleteOnExit();
dir.mkdirs();
+
String tempFileName = null;
+
if (tempFile == null) {
String encodedPath = URLEncoder.encode(getPath());
int length = encodedPath.length();
+
if (length > 200) {
- encodedPath =
- encodedPath.substring(length - 190, length);
+ encodedPath = encodedPath.substring(length - 190,
+ length);
}
+
tempFileName = System.currentTimeMillis() + "-"
- + encodedPath + ".tmp";
+ + encodedPath + ".tmp";
} else {
tempFileName = tempFile;
}
+
fileData = new File(tempDir, tempFileName);
fileData.deleteOnExit();
}
+
out = new FileOutputStream(fileData);
} else {
out = new ByteArrayOutputStream();
@@ -453,4 +468,4 @@
out.close();
}
-}
+}
\ No newline at end of file
1.4 +21 -20 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpUrlMethod.java
Index: HttpUrlMethod.java
===================================================================
RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpUrlMethod.java,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- HttpUrlMethod.java 28 Jul 2002 18:08:57 -0000 1.3
+++ HttpUrlMethod.java 1 Sep 2002 01:27:37 -0000 1.4
@@ -59,36 +59,37 @@
* [Additional notices, if required by prior licensing conditions]
*
*/
-
package org.apache.commons.httpclient;
import java.net.MalformedURLException;
/**
- * HttpUrlMethod extends HttpMethod. HttpMethod only contains
- * the path portion of a URL and gets the host:port from the connection
- * maintained in HttpCleint. HttpUrlMethod is initialized with a fully
- * specified URL and is used with HttpMultiClient. HttpMultiClient
- * chooses the appropriate HttpConnectoin (via HttpConnectionManager)
- * based on the host and port in the URL.
- *
+ * HttpUrlMethod extends HttpMethod. HttpMethod only contains the path portion
+ * of a URL and gets the host:port from the connection maintained in
+ * HttpCleint. HttpUrlMethod is initialized with a fully specified URL and is
+ * used with HttpMultiClient. HttpMultiClient chooses the appropriate
+ * HttpConnectoin (via HttpConnectionManager) based on the host and port in
+ * the URL.
+ *
* @author Marc A. Saegesser
*/
-public interface HttpUrlMethod extends HttpMethod
-{
+public interface HttpUrlMethod extends HttpMethod {
+ //~ Methods ����������������������������������������������������������������
+
/**
- * Sets the URL. Calls the underlying HttpMethod.setPath()
- * with the url's path. If the url contains a query string
- * the underlying HttpMethod.setQueryString() is called.
- *
+ * Sets the URL. Calls the underlying HttpMethod.setPath() with the url's
+ * path. If the url contains a query string the underlying
+ * HttpMethod.setQueryString() is called.
+ *
* @param url - the URL for this request.
+ * @throws MalformedURLException when the {@link URL} can't be created
*/
- public void setUrl(String url) throws MalformedURLException;
+ void setUrl(String url) throws MalformedURLException;
/**
* Returns this request's URL.
*
* @return the request's URL.
*/
- public String getUrl();
-}
+ String getUrl();
+}
\ No newline at end of file
1.14 +18 -12 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpMultiClient.java
Index: HttpMultiClient.java
===================================================================
RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpMultiClient.java,v
retrieving revision 1.13
retrieving revision 1.14
diff -u -r1.13 -r1.14
--- HttpMultiClient.java 28 Jul 2002 18:08:57 -0000 1.13
+++ HttpMultiClient.java 1 Sep 2002 01:27:37 -0000 1.14
@@ -98,7 +98,7 @@
private int timeoutConnection = 0;
/** how long to wait for a request to complete */
private int timeoutRequest = 0;
-
+ /** factory for ssl connections */
private SSLSocketFactory sslSocketFactory = null;
// ----------------------------------------------------------- Constructors
@@ -176,12 +176,15 @@
}
/**
- * Sets the connection timeout. The connection timeout is how long (in milliseconds) HttpMultiClient
- * will block in executeMethod waiting for connection to the requested server to
- * become available. Setting the connection timeout to 0 will cause executeMethod() to
+ * Sets the connection timeout. The connection timeout is how long (in
+ * milliseconds) HttpMultiClient will block in executeMethod waiting for
+ * a connection to the requested server to become available.
+ * <br />
+ * Setting the connection timeout to 0 will cause executeMethod() to
* block indefinitely (this is the default behaviour).
*
- * @param timeout the time, in milliseconds, to block waiting for an available connection
+ * @param timeout the time, in milliseconds, to block waiting for an
+ * available connection
* @see #getConnectionTimeout
* @see #executeMethod
*/
@@ -200,8 +203,9 @@
}
/**
- * Sets the request timeout. The executeMethod method calls HttpConnection.setSoTimeout()
- * with this value before executing the request.
+ * Sets the request timeout. The executeMethod method calls
+ * HttpConnection.setSoTimeout() with this value before executing the
+ * request.
*
* @param timeout the SoTimeout value, in milliseconds
* @see #getRequestTimeout
@@ -256,8 +260,10 @@
/**
* Specifies an alternative factory for SSL sockets.
- * @see HttpConnection#setSSLSocketFactory(SSLSocketFactory) HttpConnection.setSSLSocketFactory
- * @param sslSocketFactory a living instance of the alternative SSLSocketFactory
+ * @see HttpConnection#setSSLSocketFactory(SSLSocketFactory) HttpConnection
+ * setSSLSocketFactory
+ * @param sslSocketFactory a living instance of the alternative
+ * SSLSocketFactory
*/
public void setSSLSocketFactory(SSLSocketFactory sslSocketFactory) {
this.sslSocketFactory = sslSocketFactory;
1.26 +509 -407 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/Authenticator.java
Index: Authenticator.java
===================================================================
RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/Authenticator.java,v
retrieving revision 1.25
retrieving revision 1.26
diff -u -r1.25 -r1.26
--- Authenticator.java 25 Aug 2002 16:28:44 -0000 1.25
+++ Authenticator.java 1 Sep 2002 01:27:37 -0000 1.26
@@ -59,44 +59,44 @@
* [Additional notices, if required by prior licensing conditions]
*
*/
-
package org.apache.commons.httpclient;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
import java.security.MessageDigest;
import java.util.Hashtable;
import java.util.StringTokenizer;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
/**
- * Utility methods for HTTP authorization and authentication.
+ * Utility methods for HTTP authorization and authentication. This class
+ * provides utility methods for generating responses to HTTP www and proxy
+ * authentication challenges.
*
- * This class provides utility methods for generating
- * responses to HTTP www and proxy authentication challenges.
* <p>
- * Preemptive authentication can be turned on by using the property value of
+ * Preemptive authentication can be turned on by using the property value of
* #PREEMPTIVE_PROPERTY. If left unspecified, it has the default value of
* #PREEMPTIVE_DEFAULT. This configurable behaviour conforms to rcf2617:
* <blockquote>
- * A client SHOULD assume that all paths at or deeper than the depth of
- * the last symbolic element in the path field of the Request-URI also
- * are within the protection space specified by the Basic realm value of
- * the current challenge. A client MAY preemptively send the
- * corresponding Authorization header with requests for resources in
- * that space without receipt of another challenge from the server.
- * Similarly, when a client sends a request to a proxy, it may reuse a
- * userid and password in the Proxy-Authorization header field without
- * receiving another challenge from the proxy server.
+ * A client SHOULD assume that all paths at or deeper than the depth of the
+ * last symbolic element in the path field of the Request-URI also are within
+ * the protection space specified by the Basic realm value of the current
+ * challenge. A client MAY preemptively send the corresponding Authorization
+ * header with requests for resources in that space without receipt of another
+ * challenge from the server. Similarly, when a client sends a request to a
+ * proxy, it may reuse a userid and password in the Proxy-Authorization header
+ * field without receiving another challenge from the proxy server.
* </blockquote>
- *
+ * </p>
+ *
+ * @version $Revision$ $Date$
* @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
* @author Rodney Waldhoff
* @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
* @author Ortwin Gl�ck
- * @version $Revision$ $Date$
*/
class Authenticator {
+ //~ Static variables/initializers ������������������������������������������
/** <tt>org.apache.commons.httpclient.Authenticator</tt> log. */
private static final Log log = LogFactory.getLog(Authenticator.class);
@@ -104,378 +104,481 @@
/** Base 64 encoder. */
private static Base64 base64 = new Base64();
- /**
- * The boolean property name to turn on preemptive authentication.
- */
+ /** The boolean property name to turn on preemptive authentication. */
static final String PREEMPTIVE_PROPERTY =
"httpclient.authentication.preemptive";
- /**
- * The default property value for #PREEMPTIVE_PROPERTY.
- */
+ /** The default property value for #PREEMPTIVE_PROPERTY. */
static final String PREEMPTIVE_DEFAULT = "false";
- /**
- * The www authenticate challange header
- */
+ /** The www authenticate challange header */
public static final String WWW_AUTH = "WWW-Authenticate";
- /**
- * The www authenticate response header
- */
+ /** The www authenticate response header */
public static final String WWW_AUTH_RESP = "Authorization";
- /**
- * The proxy authenticate challange header
- */
+ /** The proxy authenticate challange header */
public static final String PROXY_AUTH = "Proxy-Authenticate";
+ /** The proxy authenticate response header */
+ public static final String PROXY_AUTH_RESP = "Proxy-Authorization";
+
/**
- * The proxy authenticate response header
+ * Hexa values used when creating 32 character long digest in HTTP Digest
+ * in case of authentication.
+ *
+ * @see #encode(byte[])
*/
- public static final String PROXY_AUTH_RESP = "Proxy-Authorization";
+ private static final char[] HEXADECIMAL = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
+ 'e', 'f'
+ };
+ //~ Methods ����������������������������������������������������������������
/**
- * Add requisite authentication credentials to the given <i>method</i>
- * in the given <i>state</i> if possible.
- *
+ * Creates an MD5 response digest.
+ *
+ * @param uname Username
+ * @param pwd Password
+ * @param dCreds Hashtable containing necessary header parameters to
+ * construct the digest. It must/can contain: uri, realm, nonce,
+ * cnonce, qop, nc.
+ *
+ * @return The created digest as string. This will be the response tag's
+ * value in the Authentication HTTP header.
+ * @throws HttpException when MD5 is an unsupported algorithm
+ *
+ * @todo + Add createDigest() method
+ */
+ public static String createDigest(String uname, String pwd,
+ Hashtable dCreds)
+ throws HttpException {
+ log.trace("enter Authenticator.createDigest(String, String, "
+ + "Hashtable)");
+
+ final String digAlg = "MD5";
+
+ // Collecting required tokens
+ String uri = removeQuotes((String) dCreds.get("uri"));
+ String realm = removeQuotes((String) dCreds.get("realm"));
+ String nonce = removeQuotes((String) dCreds.get("nonce"));
+ String nc = removeQuotes((String) dCreds.get("nc"));
+ String cnonce = removeQuotes((String) dCreds.get("cnonce"));
+ String qop = removeQuotes((String) dCreds.get("qop"));
+ String method = (String) dCreds.get("methodname");
+
+ if (qop != null) {
+ qop = "auth";
+ }
+
+ MessageDigest md5Helper;
+
+ try {
+ md5Helper = MessageDigest.getInstance(digAlg);
+ } catch (Exception e) {
+ log.error("ERROR! Unsupported algorithm in HTTP Digest "
+ + "authentication: " + digAlg, e);
+ throw new HttpException("Unsupported algorithm in HTTP Digest "
+ + "authentication: " + digAlg);
+ }
+
+ // Calculating digest according to rfc 2617
+ String a2 = method + ":" + uri;
+ String md5a2 = encode(md5Helper.digest(a2.getBytes()));
+ String digestValue = uname + ":" + realm + ":" + pwd;
+ String md5a1 = encode(md5Helper.digest(digestValue.getBytes()));
+ String serverDigestValue;
+
+ if (qop == null) {
+ serverDigestValue = md5a1 + ":" + nonce + ":" + md5a2;
+ } else {
+ serverDigestValue = md5a1 + ":" + nonce + ":" + nc + ":" + cnonce
+ + ":" + qop + ":" + md5a2;
+ }
+
+ String serverDigest = encode(md5Helper.digest(
+ serverDigestValue.getBytes()));
+
+ return serverDigest;
+ }
+
+ /**
+ * Add requisite authentication credentials to the given <i>method</i> in
+ * the given <i>state</i> if possible.
+ *
* @param method the HttpMethod which requires authentication
* @param state the HttpState object providing Credentials
- *
- * @throws HttpException when a parsing or other error occurs
- * @throws UnsupportedOperationException when the challenge type is not supported
+ *
* @return true if the Authenticate response header was added
- *
+ *
+ * @throws HttpException when a parsing or other error occurs
+ * @throws UnsupportedOperationException when the challenge type is not
+ * supported
+ *
* @see HttpState#setCredentials(String,Credentials)
* @see #authenticate(HttpMethod,HttpState,Header,String)
*/
- static boolean authenticate(HttpMethod method, HttpState state)
- throws HttpException, UnsupportedOperationException {
+ static boolean authenticate(HttpMethod method, HttpState state)
+ throws HttpException, UnsupportedOperationException {
log.trace("enter Authenticator.authenticate(HttpMethod, HttpState)");
Header challengeHeader = method.getResponseHeader(WWW_AUTH);
+
return authenticate(method, state, challengeHeader, WWW_AUTH_RESP);
}
/**
- * Add requisite proxy authentication credentials to the given <i>method</i>
- * in the given <i>state</i> if possible.
- *
+ * Add requisite proxy authentication credentials to the given
+ * <i>method</i> in the given <i>state</i> if possible.
+ *
* @param method the HttpMethod which requires authentication
* @param state the HttpState object providing Credentials
- *
- * @throws HttpException when a parsing or other error occurs
- * @throws UnsupportedOperationException when the given challenge type is not supported
+ *
* @return true if the Authenticate response header was added
- *
+ *
+ * @throws HttpException when a parsing or other error occurs
+ * @throws UnsupportedOperationException when the given challenge type is
+ * not supported
+ *
* @see HttpState#setProxyCredentials(String,Credentials)
* @see #authenticate(HttpMethod,HttpState,Header,String)
*/
- static boolean authenticateProxy(HttpMethod method, HttpState state)
- throws HttpException, UnsupportedOperationException {
- log.trace("enter Authenticator.authenticateProxy(HttpMethod, HttpState)");
+ static boolean authenticateProxy(HttpMethod method, HttpState state)
+ throws HttpException,
+ UnsupportedOperationException {
+ log.trace("enter Authenticator.authenticateProxy(HttpMethod, "
+ + "HttpState)");
Header challengeHeader = method.getResponseHeader(PROXY_AUTH);
+
return authenticate(method, state, challengeHeader, PROXY_AUTH_RESP);
}
-
/**
- * Add requisite authentication credentials to the given
- * <i>method</i> using the given the <i>challengeHeader</i>.
- *
- * Currently <b>Basic</b> and <b>Digest</b> authentication are supported.
- * If the challengeHeader is null, the default authentication credentials
- * will be sent.
- *
- * @param method the http method to add the authentication header to
- * @param challengeHeader the header the web server created to challenge the credentials
- * @param state the http state object providing {@link Credentials}
- * @param respHeader the response header to add (e.g. proxy or standard)
- *
- * @throws HttpException when an error occurs parsing the challenge
- * @throws UnsupportedOperationException when the given challenge type is not supported
- * @return true if a response header was added
- *
- * @see #basic
- * @see #digest
- * @see HttpMethod#addRequestHeader
+ * Return a Basic <tt>Authorization</tt> header value for the given {@link
+ * UsernamePasswordCredentials}.
+ *
+ * @param cred the credentials to encode
+ *
+ * @return the credentials as a Basic Authentication string
*/
- private static boolean authenticate(HttpMethod method, HttpState state, Header authenticateHeader, String respHeader)
- throws HttpException, UnsupportedOperationException {
-
- log.trace("enter Authenticator.authenticate(HttpMethod, HttpState, Header, String)");
-
- //check the preemptive policy
- //TODO: this needs to be a service from some configuration class
- String preemptive_str = System.getProperties().getProperty(
- PREEMPTIVE_PROPERTY, PREEMPTIVE_DEFAULT);
- preemptive_str = preemptive_str.trim().toLowerCase();
-
- if (! (preemptive_str.equals("true") || preemptive_str.equals("false")) ){ //property problem
- log.warn("Configuration property " + PREEMPTIVE_PROPERTY +
- " must be either true or false. Using default: " + PREEMPTIVE_DEFAULT);
- preemptive_str = PREEMPTIVE_DEFAULT;
- }
- boolean preemptive = ("true".equals(preemptive_str));
-
- //if there is no challenge, attempt to use preemptive authorization
- if (authenticateHeader == null){
- if (preemptive){
- log.debug("Preemptively sending default basic credentials");
- try{
- Header requestHeader = Authenticator.basic(null, state, respHeader);
- method.addRequestHeader(requestHeader);
- return true;
- } catch (HttpException httpe) {
- log.debug("No default credentials to preemptively send");
- return false;
- }
- } else { //no challenge and no default creds so do nothing
- return false;
- }
- }
- log.debug("Attempting to authenticate header: " + authenticateHeader);
-
-
- // XXX: Get the challenge from the header
- String authenticateValue = authenticateHeader.getValue();
-
-
- // TODO: Use regular expression pattern matching to parse the challenge
- //FIXME: This fails if the contents of a challenge contains a ','
- StringTokenizer challengeTok = new StringTokenizer(authenticateValue, ",");
- Hashtable challengeMap = new Hashtable(7);
- while(challengeTok.hasMoreTokens()){
-
- // Parse the authentication scheme from the challenge
- String chall = challengeTok.nextToken();
- StringTokenizer authTok = new StringTokenizer(chall, " ");
- String authScheme = authTok.nextToken();
-
- // Store the challenge keyed on the lower case authenticaion scheme
- challengeMap.put(authScheme.toLowerCase(), chall);
- }
-
- Header requestHeader = null;
- if (challengeMap.containsKey("digest")) {
- String challenge = (String)challengeMap.get("digest");
- String realm = parseRealmFromChallenge(challenge);
- requestHeader = Authenticator.digest(realm, method, state, respHeader);
- } else if (challengeMap.containsKey("basic")) {
- String challenge = (String)challengeMap.get("basic");
- String realm = parseRealmFromChallenge(challenge);
- requestHeader = Authenticator.basic(realm, state, respHeader);
- } else if (challengeMap.size() == 0) {
- throw new HttpException("No authentication scheme found in '" +
- authenticateValue);
- } else {
- throw new UnsupportedOperationException("Requested authentication scheme " +
- challengeMap.keySet() + " is unsupported");
- }
-
- //Add the header and return the result
- if(requestHeader != null) { // add the header
- method.addRequestHeader(requestHeader);
- return true;
- } else { // don't add the header
- return false;
- }
-
- }
-
+ static String basic(UsernamePasswordCredentials cred) {
+ log.trace("enter Authenticator.basic(UsernamePasswordCredentials)");
- /**
- * Parse the realm from the authentication challenge
- */
- private static String parseRealmFromChallenge(String challenge)
- throws HttpException {
- // FIXME: Note that this won't work if there is more than one realm within the challenge
- try{
- StringTokenizer strtok = new StringTokenizer(challenge, "=");
- String realmName = strtok.nextToken().trim();
- String realm = strtok.nextToken().trim();
- int firstq = realm.indexOf('"');
- int lastq = realm.lastIndexOf('"');
- if (firstq+1 < lastq) {
- realm = realm.substring(firstq+1, lastq);
- }
- log.debug("Parsed realm '" + realm + "' from challenge '" + challenge + "'");
- return realm;
- } catch (Exception ex) {
- throw new HttpException("Failed to parse realm from challenge '" + challenge + "'");
- }
+ String authString = cred.getUserName() + ":" + cred.getPassword();
+ return "Basic " + new String(base64.encode(authString.getBytes()));
}
-
-
/**
- * Create a Basic <tt>Authorization</tt> header for the given
- * <i>realm</i> and <i>state</i> to the given <i>method</i>.
- *
+ * Create a Basic <tt>Authorization</tt> header for the given <i>realm</i>
+ * and <i>state</i> to the given <i>method</i>.
+ *
* @param realm the basic realm to authenticate to
* @param state a {@link HttpState} object providing {@link Credentials}
* @param respHeader the header's name to store the authentication response
- * in. PROXY_AUTH_RESP will force the proxy credentials to be used.
- *
+ * in. PROXY_AUTH_RESP will force the proxy credentials to be used.
+ *
* @return a basic <tt>Authorization</tt> header
- *
+ *
* @throws HttpException when no matching credentials are available
*/
static Header basic(String realm, HttpState state, String respHeader)
- throws HttpException {
+ throws HttpException {
log.trace("enter Authenticator.basic(String, HttpState, String)");
boolean proxy = PROXY_AUTH_RESP.equals(respHeader);
UsernamePasswordCredentials cred = null;
+
try {
- cred = (UsernamePasswordCredentials) ( proxy ?
- state.getProxyCredentials(realm) :
- state.getCredentials(realm));
- } catch(ClassCastException e) {
- throw new HttpException("UsernamePasswordCredentials required for Basic authentication.");
+ cred = (UsernamePasswordCredentials) (proxy
+ ? state.getProxyCredentials(realm)
+ : state.getCredentials(realm));
+ } catch (ClassCastException e) {
+ throw new HttpException("UsernamePasswordCredentials required for "
+ + "Basic authentication.");
}
- if(null == cred) {
- throw new HttpException("No credentials available for the Basic authentication realm \'" +
- realm + "\'");
+ if (null == cred) {
+ throw new HttpException("No credentials available for the Basic "
+ + "authentication realm \'" + realm + "\'");
} else {
return new Header(respHeader, Authenticator.basic(cred));
}
}
/**
- * Return a Basic <tt>Authorization</tt> header value for the
- * given {@link UsernamePasswordCredentials}.
- */
- static String basic(UsernamePasswordCredentials cred)
- throws HttpException {
- log.trace("enter Authenticator.basic(UsernamePasswordCredentials)");
-
- String authString = cred.getUserName() + ":" + cred.getPassword();
- return "Basic " + new String(base64.encode(authString.getBytes()));
- }
-
- /**
- * Create a Digest <tt>Authorization</tt> header for the given
- * <i>realm</i> and <i>state</i> to the given <i>method</i>.
- *
+ * Create a Digest <tt>Authorization</tt> header for the given <i>realm</i>
+ * and <i>state</i> to the given <i>method</i>.
+ *
* @param realm the basic realm to authenticate to
+ * @param method the {@link HttpMethod request} requiring the digest
* @param state a {@link HttpState} object providing {@link Credentials}
* @param respHeader the header's name to store the authentication response
- * in. PROXY_AUTH_RESP will force the proxy credentials to be used.
- *
+ * in. PROXY_AUTH_RESP will force the proxy credentials to be used.
+ *
* @return a digest <tt>Authorization</tt> header
- *
+ *
* @throws HttpException when no matching credentials are available
*/
- static Header digest(String realm, HttpMethod method, HttpState state, String respHeader)
- throws HttpException {
- log.trace("enter Authenticator.digest(String, HttpMethod, HttpState, String)");
+ static Header digest(String realm, HttpMethod method, HttpState state,
+ String respHeader) throws HttpException {
+ log.trace("enter Authenticator.digest(String, HttpMethod, HttpState, "
+ + "String)");
boolean proxy = PROXY_AUTH_RESP.equals(respHeader);
UsernamePasswordCredentials cred = null;
+
try {
- cred = (UsernamePasswordCredentials) ( proxy ?
- state.getProxyCredentials(realm) :
- state.getCredentials(realm));
- } catch(ClassCastException e) {
- throw new HttpException("UsernamePasswordCredentials required for Digest authentication.");
- }
- if(null == cred) {
- if(log.isInfoEnabled()) {
- log.info("No credentials found for realm \"" + realm + "\", attempting to use default credentials.");
+ cred = (UsernamePasswordCredentials) (proxy
+ ? state.getProxyCredentials(realm)
+ : state.getCredentials(realm));
+ } catch (ClassCastException e) {
+ throw new HttpException("UsernamePasswordCredentials required for "
+ + "Digest authentication.");
+ }
+
+ if (null == cred) {
+ if (log.isInfoEnabled()) {
+ log.info("No credentials found for realm \"" + realm + "\", "
+ + "attempting to use default credentials.");
}
+
try {
- cred = (UsernamePasswordCredentials)( proxy ?
- state.getProxyCredentials(null) :
- state.getCredentials(null));
- } catch(ClassCastException e) {
- throw new HttpException("UsernamePasswordCredentials required for Digest authentication.");
+ cred = (UsernamePasswordCredentials) (proxy
+ ? state.getProxyCredentials(null)
+ : state.getCredentials(null));
+ } catch (ClassCastException e) {
+ throw new HttpException("UsernamePasswordCredentials required "
+ + "for Digest authentication.");
}
}
- if(null == cred) {
- throw new HttpException("No credentials available for the Digest authentication realm \"" + realm + "\"/");
+
+ if (null == cred) {
+ throw new HttpException("No credentials available for the Digest "
+ + "authentication realm \"" + realm + "\"/");
} else {
Hashtable headers = getHTTPDigestCredentials(method, proxy);
- headers.put( "cnonce", "\""+createCnonce()+"\"");
- headers.put( "nc", "00000001");
- headers.put( "uri", method.getPath() );
- headers.put( "methodname", method.getName() );
+ headers.put("cnonce", "\"" + createCnonce() + "\"");
+ headers.put("nc", "00000001");
+ headers.put("uri", method.getPath());
+ headers.put("methodname", method.getName());
+
return new Header(respHeader, Authenticator.digest(cred, headers));
}
}
/**
- * Return a Digest <tt>Authorization</tt> header value for the
- * given {@link UsernamePasswordCredentials}.
+ * Return a Digest <tt>Authorization</tt> header value for the given {@link
+ * UsernamePasswordCredentials}.
+ * @param cred Credentials to create the digest with
+ * @param headers The headers for the current request
+ *
+ * @return a string containing the authorization header for digest
+ * @throws HttpException When a recoverable error occurs
+ */
+ static String digest(UsernamePasswordCredentials cred, Hashtable headers)
+ throws HttpException {
+ log.trace("enter Authenticator.digest(UsernamePasswordCredentials, "
+ + "Hashtable)");
+
+ String digest = createDigest(cred.getUserName(), cred.getPassword(),
+ headers);
+
+ return "Digest "
+ + createDigestHeader(cred.getUserName(), headers, digest);
+ }
+
+ /**
+ * Processes the authenticate HTTP header received from the server that
+ * requires Digest authentication.
+ *
+ * @param method The HTTP method.
+ * @param proxy true if authorizing for a proxy
+ *
+ * @return The parameters from the authenticate header as a Hashtable or
+ * empty Hashtable if there is no valid authorization.
+ *
+ * @since 2.0
+ * @see #processDigestToken(String,java.util.Hashtable)
+ * @see PROXY_AUTH
+ * @see WWW_AUTH
*/
- static String digest(UsernamePasswordCredentials cred, Hashtable headers)
- throws HttpException {
- log.trace("enter Authenticator.digest(UsernamePasswordCredentials, Hashtable)");
+ private static Hashtable getHTTPDigestCredentials(HttpMethod method,
+ boolean proxy) {
+ log.trace("enter Authenticator.getHTTPDigestCredentials(HttpMethod, "
+ + "boolean)");
+
+ //Determine wether to use proxy or www header
+ String authName = proxy ? PROXY_AUTH : WWW_AUTH;
+ String authHeader = null;
- String digest = createDigest(cred.getUserName(), cred.getPassword(), headers);
- return "Digest " + createDigestHeader(cred.getUserName(), headers, digest);
+ //Get the authorization header value
+ try {
+ authHeader = method.getResponseHeader(authName).getValue();
+ authHeader = authHeader.substring(7).trim();
+ } catch (NullPointerException npe) {
+ return new Hashtable(0);
+ }
+
+ //Hashtable of digest tokens
+ Hashtable ht = new Hashtable(17);
+
+ //parse the authenticate header
+ int i = 0;
+ int j = authHeader.indexOf(",");
+
+ while (j >= 0) {
+ processDigestToken(authHeader.substring(i, j), ht);
+ i = j + 1;
+ j = authHeader.indexOf(",", i);
+ }
+
+ if (i < authHeader.length()) {
+ processDigestToken(authHeader.substring(i), ht);
+ }
+
+ return ht;
}
/**
- * @todo + Add createDigest() method
- * Creates an MD5 response digest.
- *
- * @param uname Username
- * @param pwd Password
- * @param dCreds Hashtable containing necessary header parameters to
- * construct the digest. It must/can contain: uri, realm, nonce, cnonce,
- * qop, nc.
- * @return The created digest as string. This will be the response tag's
- * value in the Authentication HTTP header.
+ * Add requisite authentication credentials to the given <i>method</i>
+ * using the given the <i>challengeHeader</i>. Currently <b>Basic</b> and
+ * <b>Digest</b> authentication are supported. If the challengeHeader is
+ * null, the default authentication credentials will be sent.
+ *
+ * @param method the http method to add the authentication header to
+ * @param authenticateHeader the header the web server created to challenge
+ * the credentials
+ * @param state the http state object providing {@link Credentials}
+ * @param respHeader the response header to add (e.g. proxy or standard)
+ *
+ * @return true if a response header was added
+ *
+ * @throws HttpException when an error occurs parsing the challenge
+ * @throws UnsupportedOperationException when the given challenge type is
+ * not supported
+ *
+ * @see #basic
+ * @see #digest
+ * @see HttpMethod#addRequestHeader
*/
- public static String createDigest(String uname, String pwd, Hashtable dCreds)
- throws HttpException {
- log.trace("enter Authenticator.createDigest(String, String, Hashtable)");
+ private static boolean authenticate(HttpMethod method, HttpState state,
+ Header authenticateHeader,
+ String respHeader)
+ throws HttpException,
+ UnsupportedOperationException {
+ log.trace("enter Authenticator.authenticate(HttpMethod, HttpState, "
+ + "Header, String)");
- final String digAlg = "MD5";
+ //check the preemptive policy
+ //TODO: this needs to be a service from some configuration class
+ String preemptiveDefault = System.getProperties()
+ .getProperty(PREEMPTIVE_PROPERTY,
+ PREEMPTIVE_DEFAULT);
+ preemptiveDefault = preemptiveDefault.trim().toLowerCase();
+
+ if (!(preemptiveDefault.equals("true")
+ || preemptiveDefault.equals("false"))) { //property problem
+ log.warn("Configuration property " + PREEMPTIVE_PROPERTY
+ + " must be either true or false. Using default: "
+ + PREEMPTIVE_DEFAULT);
+ preemptiveDefault = PREEMPTIVE_DEFAULT;
+ }
- // Collecting required tokens
- String uri = removeQuotes((String)dCreds.get("uri"));
- String realm = removeQuotes((String)dCreds.get("realm"));
- String nonce = removeQuotes((String)dCreds.get("nonce"));
- String nc = removeQuotes((String)dCreds.get("nc"));
- String cnonce = removeQuotes((String)dCreds.get("cnonce"));
- String qop = removeQuotes((String)dCreds.get("qop"));
- String method = (String)dCreds.get( "methodname" );
- if (qop!=null) qop = "auth";
+ boolean preemptive = ("true".equals(preemptiveDefault));
- MessageDigest md5Helper;
- try {
- md5Helper = MessageDigest.getInstance(digAlg);
- } catch (Exception e) {
- log.error("ERROR! Unsupported algorithm in HTTP Digest authentication: "+digAlg);
- HttpException he = new HttpException("Unsupported algorithm in HTTP Digest authentication: "+digAlg);
- throw he;
+ //if there is no challenge, attempt to use preemptive authorization
+ if (authenticateHeader == null) {
+ if (preemptive) {
+ log.debug("Preemptively sending default basic credentials");
+
+ try {
+ Header requestHeader = Authenticator.basic(null, state,
+ respHeader);
+ method.addRequestHeader(requestHeader);
+
+ return true;
+ } catch (HttpException httpe) {
+ if (log.isDebugEnabled()) {
+ log.debug("No default credentials to preemptively"
+ + " send");
+ }
+
+ return false;
+ }
+ } else { //no challenge and no default creds so do nothing
+
+ return false;
+ }
}
- // Calculating digest according to rfc 2617
- String a2 = method + ":" + uri;
- String md5a2 = encode(md5Helper.digest(a2.getBytes()));
- String digestValue = uname + ":" + realm + ":" + pwd;
- String md5a1 = encode(md5Helper.digest(digestValue.getBytes()));
- String serverDigestValue;
- if (qop==null) serverDigestValue = md5a1 + ":" + nonce + ":" + md5a2;
- else serverDigestValue = md5a1 + ":" + nonce + ":" + nc + ":" +
- cnonce + ":" + qop + ":" + md5a2;
- String serverDigest = encode(md5Helper.digest(serverDigestValue.getBytes()));
- return serverDigest;
+ if (log.isDebugEnabled()) {
+ log.debug("Attempting to authenticate header: "
+ + authenticateHeader);
+ }
+
+ // XXX: Get the challenge from the header
+ String authenticateValue = authenticateHeader.getValue();
+
+ // TODO: Use regular expression pattern matching to parse the challenge
+ //FIXME: This fails if the contents of a challenge contains a ','
+ StringTokenizer challengeTok = new StringTokenizer(authenticateValue,
+ ",");
+ Hashtable challengeMap = new Hashtable(7);
+
+ while (challengeTok.hasMoreTokens()) {
+ // Parse the authentication scheme from the challenge
+ String chall = challengeTok.nextToken();
+ StringTokenizer authTok = new StringTokenizer(chall, " ");
+ String authScheme = authTok.nextToken();
+
+
+ // Store the challenge keyed on the lower case authenticaion scheme
+ challengeMap.put(authScheme.toLowerCase(), chall);
+ }
+
+ Header requestHeader = null;
+
+ if (challengeMap.containsKey("digest")) {
+ String challenge = (String) challengeMap.get("digest");
+ String realm = parseRealmFromChallenge(challenge);
+ requestHeader = Authenticator.digest(realm, method, state,
+ respHeader);
+ } else if (challengeMap.containsKey("basic")) {
+ String challenge = (String) challengeMap.get("basic");
+ String realm = parseRealmFromChallenge(challenge);
+ requestHeader = Authenticator.basic(realm, state, respHeader);
+ } else if (challengeMap.size() == 0) {
+ throw new HttpException("No authentication scheme found in '"
+ + authenticateValue);
+ } else {
+ throw new UnsupportedOperationException("Requested authentication "
+ + "scheme "
+ + challengeMap.keySet()
+ + " is unsupported");
+ }
+
+ //Add the header and return the result
+ if (requestHeader != null) { // add the header
+ method.addRequestHeader(requestHeader);
+
+ return true;
+ } else { // don't add the header
+
+ return false;
+ }
}
/**
- * @todo + Add createCnonce() method
* Creates a random cnonce value based on the current time.
- *
+ *
* @return The cnonce value as String.
- * @throws AxisFault if MD5 algorithm is not supported.
+ *
+ * @throws HttpException if MD5 algorithm is not supported.
+ *
+ * @todo + Add createCnonce() method
*/
private static String createCnonce() throws HttpException {
log.trace("enter Authenticator.createCnonce()");
@@ -483,184 +586,183 @@
String cnonce;
final String digAlg = "MD5";
MessageDigest md5Helper;
+
try {
md5Helper = MessageDigest.getInstance(digAlg);
} catch (Exception e) {
- log.error("ERROR! Unsupported algorithm in HTTP Digest authentication: "+digAlg);
- HttpException he = new HttpException("Unsupported algorithm in HTTP Digest authentication: "+digAlg);
- throw he;
+ log.error("ERROR! Unsupported algorithm in HTTP Digest "
+ + "authentication: " + digAlg);
+ throw new HttpException("Unsupported algorithm in HTTP Digest "
+ + "authentication: " + digAlg);
}
+
cnonce = Long.toString(System.currentTimeMillis());
cnonce = encode(md5Helper.digest(cnonce.getBytes()));
+
return cnonce;
}
/**
- * @todo + Add createDigestHeader() method
* Creates the header information that must be specified after the "Digest"
* string in the HTTP Authorization header (digest-response in RFC2617).
- *
+ *
* @param uname Username
* @param dCreds Hashtable containing header information (uri, realm,
- * nonce, nc, cnonce, opaque, qop).
+ * nonce, nc, cnonce, opaque, qop).
* @param digest The response tag's value as String.
+ *
* @return The digest-response as String.
+ *
+ * @todo + Add createDigestHeader() method
*/
- private static String createDigestHeader(String uname, Hashtable dCreds, String digest) {
- log.trace("enter Authenticator.createDigestHeader(String, Hashtable, String)");
+ private static String createDigestHeader(String uname, Hashtable dCreds,
+ String digest) {
+ log.trace("enter Authenticator.createDigestHeader(String, Hashtable, "
+ + "String)");
StringBuffer sb = new StringBuffer();
- String uri = removeQuotes((String)dCreds.get("uri"));
- String realm = removeQuotes((String)dCreds.get("realm"));
- String nonce = removeQuotes((String)dCreds.get("nonce"));
- String nc = removeQuotes((String)dCreds.get("nc"));
- String cnonce = removeQuotes((String)dCreds.get("cnonce"));
- String opaque = removeQuotes((String)dCreds.get("opaque"));
+ String uri = removeQuotes((String) dCreds.get("uri"));
+ String realm = removeQuotes((String) dCreds.get("realm"));
+ String nonce = removeQuotes((String) dCreds.get("nonce"));
+ String nc = removeQuotes((String) dCreds.get("nc"));
+ String cnonce = removeQuotes((String) dCreds.get("cnonce"));
+ String opaque = removeQuotes((String) dCreds.get("opaque"));
String response = digest;
- String qop= removeQuotes((String)dCreds.get("qop"));
- if (qop!=null) qop = "auth"; //we only support auth
- String algorithm = "MD5"; //we only support MD5
-
- sb.append("username=\""+uname+"\"")
- .append(", realm=\""+realm+"\"")
- .append(", nonce=\""+nonce+"\"")
- .append(", uri=\""+uri+"\"")
- .append((qop==null?"":", qop=\""+qop+"\""))
- .append(", algorithm=\""+algorithm+"\"")
- .append((qop==null?"":", nc="+nc))
- .append((qop==null?"":", cnonce=\""+cnonce+"\""))
- .append(", response=\""+response+"\"")
- .append(opaque==null?"":", opaque=\""+opaque+"\"");
- return sb.toString();
- }
+ String qop = removeQuotes((String) dCreds.get("qop"));
- /**
- * Takes a <CODE>String</CODE> and cuts its prefix until the first double
- * quotation mark and its suffix from the last double quotation mark (and cuts
- * also the quotation marks).
- *
- * @param str the <CODE>String</CODE> from which the prefix and suffix is to
- * be cut.
- *
- * @return the stumped <CODE>String</CODE> if the format of <CODE>str</CODE>
- * is <CODE>*"*"*</CODE>. Otherwise the return value is same as
- * <CODE>str</CODE>
- */
- private static String removeQuotes(String str) {
- log.trace("enter Authenticator.removeQuotes(String)");
+ if (qop != null) {
+ qop = "auth"; //we only support auth
+ }
- if (str == null)
- return null;
+ String algorithm = "MD5"; //we only support MD5
- int fqpos = str.indexOf("\"")+1;
- int lqpos = str.lastIndexOf("\"");
- if (fqpos > 0 && lqpos > fqpos)
- return str.substring(fqpos,lqpos);
- else
- return str;
+ sb.append("username=\"" + uname + "\"")
+ .append(", realm=\"" + realm + "\"")
+ .append(", nonce=\"" + nonce + "\"").append(", uri=\"" + uri + "\"")
+ .append(((qop == null) ? "" : ", qop=\"" + qop + "\""))
+ .append(", algorithm=\"" + algorithm + "\"")
+ .append(((qop == null) ? "" : ", nc=" + nc))
+ .append(((qop == null) ? "" : ", cnonce=\"" + cnonce
+ + "\"")).append(", response=\"" + response + "\"")
+ .append((opaque == null) ? "" : ", opaque=\"" + opaque
+ + "\"");
+
+ return sb.toString();
}
/**
- * @todo + Add encode() method
- * Encodes the 128 bit (16 bytes) MD5 digest into a 32 characters log
+ * Encodes the 128 bit (16 bytes) MD5 digest into a 32 characters long
* <CODE>String</CODE> according to RFC 2617.
- *
+ *
* @param binaryData array containing the digest
+ *
* @return encoded MD5, or <CODE>null</CODE> if encoding failed
+ *
+ * @todo + Add encode() method
*/
- private static String encode( byte[] binaryData ) {
+ private static String encode(byte[] binaryData) {
log.trace("enter Authenticator.encode(byte[])");
- if (binaryData.length != 16)
+ if (binaryData.length != 16) {
return null;
+ }
char[] buffer = new char[32];
- for (int i=0; i<16; i++) {
+ for (int i = 0; i < 16; i++) {
int low = (int) (binaryData[i] & 0x0f);
int high = (int) ((binaryData[i] & 0xf0) >> 4);
- buffer[i*2] = hexadecimal[high];
- buffer[i*2 + 1] = hexadecimal[low];
+ buffer[i * 2] = HEXADECIMAL[high];
+ buffer[(i * 2) + 1] = HEXADECIMAL[low];
}
return new String(buffer);
}
/**
- * Hexa values used when creating 32 character long digest in HTTP Digest in
- * case of authentication.
- *
- * @see #encode(byte[])
- */
- private static final char[] hexadecimal =
- { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
-
- /**
- * Processes the authenticate HTTP header received from the server that
- * requires Digest authentication.
- *
- * @param method The HTTP method.
- * @param proxy true if authorizing for a proxy
- * @return The parameters from the authenticate header as a Hashtable
- * or empty Hashtable if there is no valid authorization.
- *
- * @see #processDigestToken(String,java.util.Hashtable)
- * @see PROXY_AUTH
- * @see WWW_AUTH
- * @since 2.0
+ * Parse the realm from the authentication challenge
+ *
+ * @param challenge the authentication challenge
+ *
+ * @return the realm
+ *
+ * @throws HttpException when the realm can't be parsed
*/
- private static Hashtable getHTTPDigestCredentials(HttpMethod method, boolean proxy) {
- log.trace("enter Authenticator.getHTTPDigestCredentials(HttpMethod, boolean)");
-
- //Determine wether to use proxy or www header
- String authName = proxy ? PROXY_AUTH : WWW_AUTH;
- String authHeader = null;
-
- //Get the authorization header value
- try{
- authHeader = method.getResponseHeader(authName).getValue();
- authHeader = authHeader.substring(7).trim();
- } catch (NullPointerException npe){
- return new Hashtable(0);
- }
+ private static String parseRealmFromChallenge(String challenge)
+ throws HttpException {
+ // FIXME: Note that this won't work if there is more than one realm
+ // within the challenge
+ try {
+ StringTokenizer strtok = new StringTokenizer(challenge, "=");
+ String realmName = strtok.nextToken().trim();
+ String realm = strtok.nextToken().trim();
+ int firstq = realm.indexOf('"');
+ int lastq = realm.lastIndexOf('"');
- //Hashtable of digest tokens
- Hashtable ht = new Hashtable(17);
+ if ((firstq + 1) < lastq) {
+ realm = realm.substring(firstq + 1, lastq);
+ }
- //parse the authenticate header
- int i = 0;
- int j = authHeader.indexOf(",");
- while(j >= 0) {
- processDigestToken(authHeader.substring(i,j), ht);
- i = j+1;
- j = authHeader.indexOf(",",i);
- }
+ if (log.isDebugEnabled()) {
+ log.debug("Parsed realm '" + realm + "' from challenge '"
+ + challenge + "'");
+ }
- if (i < authHeader.length()) {
- processDigestToken(authHeader.substring(i), ht);
+ return realm;
+ } catch (Exception ex) {
+ throw new HttpException("Failed to parse realm from challenge '"
+ + challenge + "'");
}
- return ht;
}
/**
- * @todo + Add processDigestToken() method
- * Takes an entry of <CODE>"xxx=yyy"</CODE> format, partitions into a key and
- * a value (key will be the left side, value will be the right side of the
- * equal sign) and places that into a <CDE>Hashtable</CODE>.
- *
+ * Takes an entry of <CODE>"xxx=yyy"</CODE> format, partitions into a key
+ * and a value (key will be the left side, value will be the right side of
+ * the equal sign) and places that into a <CODE>Hashtable</CODE>.
+ *
* @param token the entry to be processed
* @param ht the <CODE>java.util.Hashtable</CODE> into which the processed
- * entry is placed (only if it has <CODE>"xxx=yyy"</CODE> format).
+ * entry is placed (only if it has <CODE>"xxx=yyy"</CODE> format).
+ *
+ * @todo + Add processDigestToken() method
*/
private static void processDigestToken(String token, Hashtable ht) {
log.trace("enter Authenticator.processDigestToken(String, Hashtable)");
int eqpos = token.indexOf("=");
- if (eqpos > 0 && eqpos < token.length()-1)
- ht.put(token.substring(0,eqpos).trim(),token.substring(eqpos+1).trim());
+ if ((eqpos > 0) && (eqpos < (token.length() - 1))) {
+ ht.put(token.substring(0, eqpos).trim(),
+ token.substring(eqpos + 1).trim());
+ }
}
+ /**
+ * Takes a <CODE>String</CODE> and cuts its prefix until the first double
+ * quotation mark and its suffix from the last double quotation mark (and
+ * cuts also the quotation marks).
+ *
+ * @param str the <CODE>String</CODE> from which the prefix and suffix is
+ * to be cut.
+ *
+ * @return the stumped <CODE>String</CODE> if the format of
+ * <CODE>str</CODE> is <CODE>""</CODE>. Otherwise the return value
+ * is same as <CODE>str</CODE>
+ */
+ private static String removeQuotes(String str) {
+ log.trace("enter Authenticator.removeQuotes(String)");
+
+ if (str == null) {
+ return null;
+ }
+ int fqpos = str.indexOf("\"") + 1;
+ int lqpos = str.lastIndexOf("\"");
-}
+ if ((fqpos > 0) && (lqpos > fqpos)) {
+ return str.substring(fqpos, lqpos);
+ } else {
+ return str;
+ }
+ }
+}
\ No newline at end of file
--
To unsubscribe, e-mail: <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>