You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ant.apache.org by st...@apache.org on 2006/06/27 15:26:42 UTC
svn commit: r417451 [2/2] - in /ant/sandbox/antlibs/http/trunk: ./ docs/
src/ src/etc/ src/etc/testcases/ src/etc/testcases/http/ src/main/
src/main/org/ src/main/org/apache/ src/main/org/apache/ant/
src/main/org/apache/ant/http/ src/war/ src/war/WEB-I...
Added: ant/sandbox/antlibs/http/trunk/src/main/org/apache/ant/http/HttpTask.java
URL: http://svn.apache.org/viewvc/ant/sandbox/antlibs/http/trunk/src/main/org/apache/ant/http/HttpTask.java?rev=417451&view=auto
==============================================================================
--- ant/sandbox/antlibs/http/trunk/src/main/org/apache/ant/http/HttpTask.java (added)
+++ ant/sandbox/antlibs/http/trunk/src/main/org/apache/ant/http/HttpTask.java Tue Jun 27 06:26:40 2006
@@ -0,0 +1,1083 @@
+/*
+ * Copyright 2001-2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+
+package org.apache.ant.http;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Date;
+import java.util.Vector;
+
+/**
+ * This class is a foundational class for all the tasks which implement http
+ * methods. To implement a subclass you *must* provide an implementation of
+ * getRequestMethod(). Consider also stating the parameter policy
+ * (areParamsAddedToUrl()) and then, if needed, overriding doConnect, and the
+ * onConnected(), OnDownloadFinished() methods.
+ *
+ * @created March 17, 2001
+ */
+public abstract class HttpTask extends Task {
+
+ /**
+ * flag to control action on execution trouble.
+ */
+ protected boolean failOnError = true;
+
+ /**
+ * this sets the size of the buffer and the hash for download (Kilobytes).
+ */
+
+ protected int blockSize = 64;
+
+ /**
+ * property to set on success.
+ */
+
+ protected String status;
+
+ /**
+ * source URL- required.
+ */
+ private String source;
+
+ /**
+ * destination for download.
+ */
+ private File destFile;
+ /**
+ * verbose flag gives extra information.
+ */
+ private boolean verbose = false;
+
+ /**
+ * timestamp based download flag. off by default.
+ */
+ private boolean useTimestamp = false;
+
+ /**
+ * authorization mechanism in use.
+ */
+ private int authType = AUTH_NONE;
+
+ /**
+ * username for authentication.
+ */
+ private String username;
+
+ /**
+ * password for authentication.
+ */
+ private String password;
+
+ /**
+ * parameters to send on a request.
+ */
+ private Vector params = new Vector();
+
+ /**
+ * headers to send on a request
+ */
+ private Vector headers = new Vector();
+
+ /**
+ * cache policy.
+ */
+ private boolean usecaches = false;
+
+ /**
+ * the name of a destination property.
+ */
+
+ private String destProperty = null;
+
+ /**
+ * a flag to control whether or not response codes are acted on.
+ */
+ private boolean useResponseCode = true;
+
+ /**
+ * No authentication specified.
+ */
+ public final static int AUTH_NONE = 0;
+
+ /**
+ * basic 'cleartext' authentication.
+ */
+ public final static int AUTH_BASIC = 1;
+
+ /**
+ * digest auth. not actually supported but present for completeness.
+ */
+ public final static int AUTH_DIGEST = 2;
+
+
+ /**
+ * turn caching on or off. only relevant for protocols and methods which are
+ * cacheable (HEAD, GET) on http.
+ *
+ * @param usecaches The new UseCaches value
+ */
+ public void setUseCaches(boolean usecaches) {
+ this.usecaches = usecaches;
+ }
+
+ /**
+ * control whether response codes are used to determine success/failure.
+ *
+ * @param useResponseCode the new value
+ */
+ public void setUseResponseCode(boolean useResponseCode) {
+ this.useResponseCode = useResponseCode;
+ }
+
+
+ /**
+ * Set the URL.
+ *
+ * @param u URL for the operation.
+ */
+ public void setURL(String u) {
+ this.source = u;
+ }
+
+
+ /**
+ * the local destination for any response. this can be null for "don't
+ * download".
+ *
+ * @param destFile Path to file.
+ */
+ public void setDestFile(File destFile) {
+ this.destFile = destFile;
+ }
+
+ /**
+ * the local destination for any response. this can be null for "don't
+ * download"
+ *
+ * @param name Path to file.
+ */
+ public void setDestinationProperty(String name) {
+ this.destProperty = name;
+ }
+
+
+ /**
+ * Be verbose, if set to " <CODE>true</CODE> ".
+ *
+ * @param verbose The new Verbose value
+ */
+ public void setVerbose(boolean verbose) {
+ this.verbose = verbose;
+ }
+
+
+ /**
+ * set the fail on error flag.
+ *
+ * @param b The new FailOnError value
+ */
+ public void setFailOnError(boolean b) {
+ failOnError = b;
+ }
+
+
+ /**
+ * Use timestamps, if set to " <CODE>true</CODE> ". <p>
+ * <p/>
+ * In this situation, the if-modified-since header is set so that the file
+ * is only fetched if it is newer than the local file (or there is no local
+ * file) This flag is only valid on HTTP connections, it is ignored in other
+ * cases. When the flag is set, the local copy of the downloaded file will
+ * also have its timestamp set to the remote file time. <br> Note that
+ * remote files of date 1/1/1970 (GMT) are treated as 'no timestamp', and
+ * web servers often serve files with a timestamp in the future by replacing
+ * their timestamp with that of the current time. Also, inter-computer clock
+ * differences can cause no end of grief.
+ *
+ * @param usetimestamp The new UseTimestamp value
+ */
+ public void setUseTimestamp(boolean usetimestamp) {
+ this.useTimestamp = usetimestamp;
+ }
+
+
+ /**
+ * Sets the Authtype attribute of the HttpTask object REVISIT/REFACTOR.
+ *
+ * @param type The new Authtype value
+ */
+ public void setAuthtype(AuthMethodType type) {
+ this.authType = type.getIndex();
+ }
+
+
+ /**
+ * Sets the Username used for authentication. setting the username
+ * implicitly turns authentication on.
+ *
+ * @param username The new Username value
+ */
+ public void setUsername(String username) {
+ this.username = username;
+ if (authType == AUTH_NONE) {
+ authType = AUTH_BASIC;
+ }
+ }
+
+
+ /**
+ * Sets the Password for an authenticated request.
+ *
+ * @param password The new Password value
+ */
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+
+ /**
+ * set a property to be set in the event of success.
+ *
+ * @param status The new SuccessProperty value
+ */
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ /**
+ * get the block size in kilobytes.
+ */
+
+ public int getBlockSize() {
+ return blockSize;
+ }
+
+ /**
+ * set the new block size for download (in kilobytes).
+ *
+ * @param blocksize the new value
+ */
+ public void setBlockSize(int blocksize) {
+ this.blockSize = blocksize;
+ }
+
+
+ /**
+ * query cache policy.
+ *
+ * @return The UseCaches value
+ */
+ public boolean getUseCaches() {
+ return usecaches;
+ }
+
+
+ /**
+ * query fail on error flag.
+ *
+ * @return The FailFailOnError value
+ */
+ public boolean getFailOnError() {
+ return failOnError;
+ }
+
+
+ /**
+ * get the username.
+ *
+ * @return current username or null for 'none'
+ */
+ public String getUsername() {
+ return username;
+ }
+
+
+ /**
+ * get the password.
+ *
+ * @return current password or null for 'none'
+ */
+ public String getPassword() {
+ return password;
+ }
+
+
+ /**
+ * @return The RemoteURL value.
+ */
+ public String getURL() {
+ return source;
+ }
+
+
+ /**
+ * Get the vector of access parameters.
+ *
+ * @return The RequestParameters value
+ */
+ public Vector getRequestParameters() {
+ return params;
+ }
+
+
+ /**
+ * accessor of success property name
+ *
+ * @return The SuccessProperty value
+ */
+ public String getStatus() {
+ return status;
+ }
+
+ /**
+ * accessor of destination property name
+ *
+ * @return The destination value
+ */
+ public String getDestinationProperty() {
+ return destProperty;
+ }
+
+ /**
+ * accessor of destination
+ *
+ * @return Thedestination
+ */
+ public File getDestFile() {
+ return destFile;
+ }
+
+
+ /**
+ * if the user wanted a success property, this sets it. of course, it is only
+ * relevant if failonerror=false
+ */
+
+ public void noteSuccess() {
+ if (status != null && status.length() > 0) {
+ getProject().setProperty(status, "true");
+ }
+ }
+
+
+ /**
+ * Does the work.
+ *
+ * @throws BuildException Thrown in unrecoverable error.
+ */
+ public void execute() {
+
+ //check arguments, will bail out if there
+ //was trouble
+ verifyArguments();
+
+ //set up the URL connection
+ URL url = buildURL();
+
+ try {
+
+ //now create a connection
+ URLConnection connection = url.openConnection();
+
+ //set caching option to whatever
+ connection.setUseCaches(getUseCaches());
+
+ //set the timestamp option if flag is set and
+ //the local file actually exists.
+ long localTimestamp = getTimestamp();
+ if (localTimestamp != 0) {
+ if (verbose) {
+ Date t = new Date(localTimestamp);
+ log("local file date : " + t.toString());
+ }
+ connection.setIfModifiedSince(localTimestamp);
+ }
+
+ // Set auth header, if specified
+ //NB: verifyArguments will already have checked that you can't
+ //have a null username with a non-null strategy.
+ HttpAuthenticationStrategy authStrategy = getAuthStrategy();
+ if (authStrategy != null) {
+ authStrategy.setAuthenticationHeader(connection,
+ null,
+ username,
+ password);
+ }
+
+ // Set explicitly specified request headers
+ HttpRequestParameter header;
+ for (int i = 0; i < headers.size(); i++) {
+ header = (HttpRequestParameter) headers.get(i);
+ connection.setRequestProperty(header.getName(),
+ header.getValue());
+ }
+
+ //cast to an http connection if we can,
+ //then set the request method pulled from the subclass
+ String method = getRequestMethod();
+ HttpURLConnection httpConnection = null;
+ if (connection instanceof HttpURLConnection) {
+ httpConnection = (HttpURLConnection) connection;
+ httpConnection.setRequestMethod(method);
+ }
+ log("making " + method + " to " + url);
+
+ //call self or subclass for the connect.
+ //the connection object may change identity at this point.
+ connection = doConnect(connection);
+
+ //then provide a bit of overridable post processing for the fun of it
+ if (!onConnected(connection)) {
+ return;
+ }
+
+ //repeat the cast.
+ if (connection instanceof HttpURLConnection) {
+ httpConnection = (HttpURLConnection) connection;
+ }
+ if (httpConnection != null) {
+ // check for a 304 result (HTTP only) when we set the timestamp
+ // earlier on (A fractional performance tweak)
+ if (localTimestamp != 0) {
+ if (getResponseCode(httpConnection) ==
+ HttpURLConnection
+ .HTTP_NOT_MODIFIED) {
+ //not modified so no file download. just return instead
+ //and trace out something so the user doesn't think that the
+ //download happened when it didn't
+ log("Local file is up to date - so nothing was downloaded");
+ noteSuccess();
+ return;
+ }
+ }
+
+ }
+
+ //get the input stream
+ InputStream is = getInputStream(connection);
+
+ //bail out if the input stream isn't valid at this point
+ //again, though we should have got to this point earlier.
+
+ if (is == null) {
+ log("Can't get " + url, Project.MSG_ERR);
+ if (getFailOnError()) {
+ return;
+ }
+ throw new BuildException("Can't reach URL");
+ }
+
+ //pick a file or null stream for saving content
+ OutputStream out = null;
+ if (destFile != null) {
+ log("Saving output to " + destFile, Project.MSG_DEBUG);
+ out = new FileOutputStream(destFile);
+ } else {
+ if (destProperty != null) {
+ //save contents to a property
+ log("Saving output to property " + destProperty,
+ Project.MSG_DEBUG);
+ out = new ByteArrayOutputStream(blockSize * 1024);
+ } else {
+ //discard everything
+ out = new NullOutputStream();
+ }
+ }
+
+ //get content length
+ //do it this way instead of calling getContentLength() because
+ //that way is sporadically unreliable (length is downgraded to
+ //size of small packets)
+ int contentLength = connection.getHeaderFieldInt("Content-Length",
+ -1);
+ int bytesRead = 0;
+
+ //now start download.
+ byte[] buffer = new byte[blockSize * 1024];
+ int length;
+
+ while ((length = is.read(buffer)) >= 0 &&
+ (contentLength == -1 || bytesRead < contentLength)) {
+ bytesRead += length;
+ out.write(buffer, 0, length);
+ if (verbose) {
+ showProgressChar('.');
+ }
+ }
+
+ //finished successfully - clean up.
+ if (verbose) {
+ showProgressChar('\n');
+ }
+
+ //if it we were saving to a byte array, then
+ //set the destination property with its contents
+ if (out instanceof ByteArrayOutputStream) {
+ getProject().setProperty(destProperty,
+ out.toString());
+ }
+
+ //everything is downloaded; close files
+ out.flush();
+ out.close();
+ is.close();
+ is = null;
+ out = null;
+
+ //another overridable notification method
+ if (!onDownloadFinished(connection)) {
+ return;
+ }
+
+ //REFACTOR: move this down to HttpHead? What if a post wants
+ //to set a date?
+ //if (and only if) the use file time option is set, then the
+ //saved file now has its timestamp set to that of the downloaded file
+ if (useTimestamp) {
+ long remoteTimestamp = connection.getLastModified();
+ if (verbose) {
+ Date t = new Date(remoteTimestamp);
+ log("last modified = " +
+ t.toString()
+ +
+ ((remoteTimestamp ==
+ 0) ? " - using current time instead" : ""));
+ }
+ if (remoteTimestamp != 0) {
+
+ destFile.setLastModified(remoteTimestamp);
+ }
+ }
+
+
+ String failureString = null;
+ if (contentLength > -1 && bytesRead != contentLength) {
+ failureString = "Incomplete download -Expected " + contentLength
+ + "received " + bytesRead + " bytes";
+ } else {
+
+ //finally clean anything up.
+ //http requests have their response code checked, and only
+ //those in the success range are deemed successful.
+ if (httpConnection != null && useResponseCode) {
+ int statusCode = httpConnection.getResponseCode();
+ if (statusCode < 200 || statusCode > 299) {
+ failureString = "Server error code " +
+ statusCode +
+ " received";
+ }
+ }
+ }
+
+ //check for an error message
+ if (failureString == null) {
+ noteSuccess();
+ } else {
+ if (failOnError) {
+ throw new BuildException(failureString);
+ } else {
+ log(failureString, Project.MSG_ERR);
+ }
+ }
+
+ }
+ catch (IOException ioe) {
+ log("Error performing " + getRequestMethod() + " on " + url +
+ " : " + ioe.toString(), Project.MSG_ERR);
+ if (failOnError) {
+ throw new BuildException(ioe);
+ }
+ }
+ }
+
+ /**
+ * show a progress character. Not as useful as you think, given buffering.
+ */
+
+ protected void showProgressChar(char c) {
+ System.out.write(c);
+ }
+
+
+ /**
+ * Adds a form / request parameter.
+ *
+ * @param param The feature to be added to the HttpRequestParameter
+ * attribute
+ */
+ public void addParam(HttpRequestParameter param) {
+ params.add(param);
+ }
+
+
+ /**
+ * Adds an HTTP request header.
+ *
+ * @param header The feature to be added to the Header attribute
+ */
+ public void addHeader(HttpRequestParameter header) {
+ headers.add(header);
+ }
+
+
+ /**
+ * this must be overridden by implementations to set the request method to
+ * GET, POST, whatever NB: this method only gets called for an http request
+ *
+ * @return the method string
+ */
+ protected abstract String getRequestMethod();
+
+
+ /**
+ * determine the timestamp to use if the flag is set and the local file
+ * actually exists.
+ *
+ * @return 0 for 'no timestamp', a number otherwhise
+ */
+
+ protected long getTimestamp() {
+ long timestamp = 0;
+ if (useTimestamp && destFile != null && destFile.exists()) {
+ timestamp = destFile.lastModified();
+ } else {
+ timestamp = 0;
+ }
+ return timestamp;
+ }
+
+
+ /**
+ * ask for authentication details. An empty string means 'no auth'
+ *
+ * @return an RFC2617 auth string
+ */
+
+ protected String getAuthenticationString() {
+ // Set authorization eader, if specified
+ if (authType == AUTH_BASIC && username != null) {
+ password = password == null ? "" : password;
+ String encodeStr = username + ":" + password;
+ Base64Encode encoder = new Base64Encode();
+ char[] encodedPass = encoder.encodeBase64(encodeStr.getBytes());
+ String authStr = "BASIC " + new String(encodedPass);
+ return authStr;
+ } else {
+ return null;
+ }
+ }
+
+
+ /**
+ * this overridable method verifies that all the params are valid the base
+ * implementation checks for remote url validity and if the destination is
+ * not null, write access to what mustnt be a directory. sublcasses can call
+ * the base class as well as check their own data
+ *
+ * @throws BuildException only throw this when the failonerror flag is true
+ */
+
+ protected void verifyArguments()
+ throws BuildException {
+ //check remote params -but only create an exception, not throw it
+ if (getURL() == null) {
+ throw new BuildException("target URL missing");
+ }
+ //check destination parameters -but only create an exception, not throw it
+ if (destFile != null && destFile.exists()) {
+ if (destFile.isDirectory()) {
+ throw new BuildException(
+ "The specified destination is a directory");
+ } else if (!destFile.canWrite()) {
+ throw new BuildException("Can't write to " +
+ destFile.getAbsolutePath());
+ }
+ }
+ //check auth policy
+ if (authType != AUTH_NONE && username == null) {
+ throw new BuildException(
+ "no username defined to use with authorisation");
+ }
+ }
+
+
+ /**
+ * build a URL from the source url, maybe with parameters attached
+ *
+ * @return Description of the Returned Value
+ * @throws BuildException Description of Exception
+ */
+ protected URL buildURL()
+ throws BuildException {
+ String urlbase = getURL();
+ try {
+ if (areParamsAddedToUrl()) {
+ urlbase = parameterizeURL();
+ }
+ return new URL(urlbase);
+ }
+ catch (MalformedURLException e) {
+ throw new BuildException("Invalid URL");
+ }
+ }
+
+
+ /**
+ * take a url and add parameters to it. if there are no parameters the base
+ * url string is returned
+ *
+ * @return a string to be used for URL creation
+ * @throws BuildException Description of Exception
+ */
+ protected String parameterizeURL()
+ throws BuildException {
+ //return immediately if there are no parameters
+ if (params.size() == 0) {
+ return getURL();
+ }
+
+ StringBuffer buf = new StringBuffer(getURL());
+ //this devious little line code recognises a parameter string already
+ //in the source url, and if so doesnt add a new one
+ buf.append(source.indexOf('?') == -1 ? '?' : '&');
+ HttpRequestParameter param;
+
+ //run through the parameter list, encode the name/value pairs and
+ //append them to the list
+ for (int i = 0; i < params.size(); i++) {
+ if (i > 0) {
+ buf.append('&');
+ }
+ param = (HttpRequestParameter) params.get(i);
+ buf.append(param.toString());
+ }
+ return buf.toString();
+ }
+
+
+ /**
+ * query for the request wanting parameters on the url default is true,
+ * subclasses may want to change
+ *
+ * @return true if a url should have params attached.
+ */
+
+ protected boolean areParamsAddedToUrl() {
+ return true;
+ }
+
+ /**
+ * get the auth policy a null return value means 'no policy chosen'
+ *
+ * @return current authorisation strategy or null
+ */
+
+ protected HttpAuthenticationStrategy getAuthStrategy() {
+ HttpAuthenticationStrategy strategy = null;
+ switch (authType) {
+ case AUTH_BASIC:
+ strategy = new HttpBasicAuth();
+ break;
+
+ case AUTH_NONE:
+ break;
+
+ case AUTH_DIGEST:
+ default:
+ throw new BuildException("Authentication method "
+ +authType+" not supported");
+ }
+ return strategy;
+
+ }
+
+ /**
+ * this method opens the connection. It can recognise a 401 error code and
+ * in digest auth will then open a new connection with the supplied nonce
+ * encoded. That is why it can return a new connection object.
+ *
+ * @param connection where to connect to
+ * @return a new connection. This may be different than the old one
+ * @throws BuildException build trouble
+ * @throws IOException IO trouble
+ */
+
+ protected URLConnection makeConnectionWithAuthHandling(URLConnection connection)
+ throws BuildException, IOException {
+ log("Connecting to " + connection.toString(), Project.MSG_DEBUG);
+ connection.connect();
+ URLConnection returnConnection = connection;
+ log("connected", Project.MSG_DEBUG);
+ if (connection instanceof HttpURLConnection) {
+ HttpURLConnection httpConnection = (HttpURLConnection) connection;
+ if (getResponseCode(httpConnection) ==
+ HttpURLConnection
+ .HTTP_UNAUTHORIZED
+ && authType == AUTH_DIGEST) {
+ //duplicating all the settings then reconnect
+ //and return it
+ log("Digest authentication needed but not yet supported",
+ Project.MSG_DEBUG);
+ }
+ }
+
+ return returnConnection;
+ }
+
+
+ /**
+ * by making a query for a value from the connection, we force the client
+ * code to actually do the http request and go into input mode. so next we
+ * can check for trouble.
+ */
+ void probeConnection(HttpURLConnection connection) {
+ connection.getHeaderFieldKey(0);
+ }
+
+
+ /**
+ * get a response from a connection request. This code fixes a problem found
+ * in HttpURLConnection, that any attempt to get the response code would
+ * trigger a FileNotFound
+ *
+ * @param connection the current http link
+ * @return whatever we get back
+ * @throws IOException if anything other than file not found gets thrown,
+ * and even a FileNotFound exception if that gets thrown
+ * too many times.
+ * @see <a href="http://developer.java.sun.com/developer/bugParade/bugs/4160499.html">
+ * BugParade details </a> "If the requested file does not exist, and
+ * ends in .html, .htm, .txt or /, you will get the error stream with
+ * no exception thrown. If the file does not end like any of these you
+ * can catch the exception and immediately request it again to get the
+ * error stream. The response code can be obtained with
+ * getResponseCode()." which means, to really get the response code you
+ * need to ask twice.
+ */
+ protected int getResponseCode(HttpURLConnection connection)
+ throws IOException {
+ //force the creation of the input stream
+ //(which is what HttpURLConnection.getResponseCode() does internally
+ //that way the bug handler code is only needed once.
+
+ //probeConnection(connection);
+ IOException swallowed = null;
+ boolean caught = false;
+ int response = 0;
+ for (int attempts = 0; attempts < 5; attempts++) {
+ try {
+ response = connection.getResponseCode();
+ caught = true;
+ break;
+ }
+ catch (FileNotFoundException ex) {
+ log("Swallowed FileNotFoundException in getResponseCode",
+ Project.MSG_VERBOSE);
+ log(ex.toString(), Project.MSG_DEBUG);
+ swallowed = ex;
+ }
+ }
+ if (!caught && swallowed != null) {
+ throw swallowed;
+ }
+ return response;
+ }
+
+ /**
+ * get an input stream from a connection This code tries to fix a problem
+ * found in HttpURLConnection, that any attempt to get the response code
+ * would trigger a FileNotFound BugParade ID 4160499 : <blockquote> "If the
+ * requested file does not exist, and ends in .html, .htm, .txt or /, you
+ * will get the error stream with no exception thrown. If the file does not
+ * end like any of these you can catch the exception and immediately request
+ * it again to get the error stream. The response code can be obtained with
+ * getResponseCode()." <blockquote> which means, to really get the response
+ * code you need to ask twice. More to the point this handling is not
+ * consistent across JVMs: on java 1.3 you can ask as often as you like but
+ * you are not going to get the input stream on a JSP page when it has some
+ * 500 class error.
+ *
+ * @param connection the current link
+ * @return the input stream.
+ * @throws IOException if anything other than file not found gets thrown,
+ * and even a FileNotFound exception if that gets thrown
+ * too many times.
+ */
+
+ protected InputStream getInputStream(URLConnection connection)
+ throws IOException {
+ IOException swallowed = null;
+ InputStream instream = null;
+ for (int attempts = 0; attempts < 5; attempts++) {
+ try {
+ instream = connection.getInputStream();
+ break;
+ }
+ catch (FileNotFoundException ex) {
+ log("Swallowed IO exception in getInputStream",
+ Project.MSG_VERBOSE);
+ log(ex.toString(), Project.MSG_DEBUG);
+ swallowed = ex;
+ }
+ }
+ if (instream == null && swallowed != null) {
+ throw swallowed;
+ }
+ return instream;
+ }
+
+
+ /**
+ * this method is inteded for overriding. it is called when connecting to a
+ * URL, and the base implementation just calls connect() on the parameter.
+ * any subclass that wants to pump its own datastream up (like post) must
+ * override this
+ *
+ * @param connection where to connect to
+ * @throws BuildException build trouble
+ * @throws IOException IO trouble
+ */
+
+ protected URLConnection doConnect(URLConnection connection)
+ throws BuildException, IOException {
+ return makeConnectionWithAuthHandling(connection);
+ }
+
+
+ /**
+ * this is a method for upload centric post-like requests
+ *
+ * @param connection who we talk to
+ * @param contentType Description of Parameter
+ * @param contentLength Description of Parameter
+ * @param content Description of Parameter
+ * @throws IOException something went wrong with the IO
+ */
+ protected URLConnection doConnectWithUpload(URLConnection connection,
+ String contentType,
+ int contentLength,
+ InputStream content)
+ throws IOException {
+
+ log("uploading " + contentLength + " bytes of type " + contentType,
+ Project.MSG_VERBOSE);
+ //tell the connection we are in output mode
+ connection.setDoOutput(true);
+
+ // Set content length and type headers
+ connection.setRequestProperty("Content-Length",
+ String.valueOf(contentLength));
+ connection.setRequestProperty("Content-Type", contentType);
+ connection = makeConnectionWithAuthHandling(connection);
+ OutputStream toServer = connection.getOutputStream();
+
+ //create a buffer which is the smaller of
+ //the content length and the block size (in KB)
+ int buffersize = blockSize * 1024;
+ if (contentLength < buffersize) {
+ buffersize = contentLength;
+ }
+ byte[] buffer = new byte[buffersize];
+ int remaining = contentLength;
+
+ while (remaining > 0) {
+ int read = content.read(buffer);
+ log("block of " + read, Project.MSG_DEBUG);
+ toServer.write(buffer, 0, read);
+ remaining -= read;
+ if (verbose) {
+ showProgressChar('^');
+ }
+ }
+ if (verbose) {
+ showProgressChar('\n');
+ }
+ log("upload completed", Project.MSG_DEBUG);
+ return connection;
+ }
+
+ /**
+ * internal event handler called after a connect can throw an exception or
+ * return false for an immediate exit from the process
+ *
+ * @param connection the now open connection
+ * @return true if the execution is to continue
+ * @throws BuildException Description of Exception
+ */
+ protected boolean onConnected(URLConnection connection)
+ throws BuildException {
+ return true;
+ }
+
+
+ /**
+ * internal event handler called after the download is complete the code can
+ * still bail out at this point, and the connection may contain headers of
+ * interest. can throw an exception or return false for an immediate exit
+ * from the process
+ *
+ * @param connection the now open connection
+ * @return true if the execution is to continue
+ * @throws BuildException Description of Exception
+ */
+ protected boolean onDownloadFinished(URLConnection connection)
+ throws BuildException {
+ return true;
+ }
+
+
+ /**
+ * Enumerated attribute for "authType" with the value "basic" (note,
+ * eventually we can add "digest" authentication)
+ *
+ * @created March 17, 2001
+ */
+ public static class AuthMethodType extends EnumeratedAttribute {
+ /**
+ * Gets the possible values of authorisation supported
+ *
+ * @return The Values value
+ */
+ public String[] getValues() {
+ return new String[]{
+ "none",
+ "basic",
+ //commented out until implemented
+ // "digest"
+ };
+ }
+
+ }
+}
+
Added: ant/sandbox/antlibs/http/trunk/src/main/org/apache/ant/http/NullOutputStream.java
URL: http://svn.apache.org/viewvc/ant/sandbox/antlibs/http/trunk/src/main/org/apache/ant/http/NullOutputStream.java?rev=417451&view=auto
==============================================================================
--- ant/sandbox/antlibs/http/trunk/src/main/org/apache/ant/http/NullOutputStream.java (added)
+++ ant/sandbox/antlibs/http/trunk/src/main/org/apache/ant/http/NullOutputStream.java Tue Jun 27 06:26:40 2006
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2001-2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.ant.http;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * simple output stream which discards all write requests. This should really be
+ * part of java.io, as it is sporadically invaluable
+ *
+ * @created March 17, 2001
+ */
+public final class NullOutputStream extends OutputStream {
+
+ /**
+ * discard all incoming bytes
+ *
+ * @param b byte to write
+ * @throws IOException never throwable in this subclass
+ */
+ public void write(int b)
+ throws IOException {
+ }
+
+
+ /**
+ * discard all incoming bytes
+ *
+ * @param b byte array
+ * @throws IOException never throwable in this subclass
+ */
+ public void write(byte[] b)
+ throws IOException {
+ }
+
+
+ /**
+ * discard all incoming bytes
+ *
+ * @param b byte array
+ * @param off starting offset
+ * @param len length to write
+ * @throws IOException never throwable in this subclass
+ */
+ public void write(byte[] b,
+ int off,
+ int len)
+ throws IOException {
+ }
+
+}
+
+
Added: ant/sandbox/antlibs/http/trunk/src/main/org/apache/ant/http/antlib.xml
URL: http://svn.apache.org/viewvc/ant/sandbox/antlibs/http/trunk/src/main/org/apache/ant/http/antlib.xml?rev=417451&view=auto
==============================================================================
--- ant/sandbox/antlibs/http/trunk/src/main/org/apache/ant/http/antlib.xml (added)
+++ ant/sandbox/antlibs/http/trunk/src/main/org/apache/ant/http/antlib.xml Tue Jun 27 06:26:40 2006
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+<!--
+ Copyright 2006 The Apache Software Foundation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<antlib xmlns:http="antlib:org.apache.ant.http">
+
+ <taskdef name="get"
+ classname="org.apache.ant.http.HttpGet"/>
+ <taskdef name="post"
+ classname="org.apache.ant.http.HttpPost"/>
+ <taskdef name="head"
+ classname="org.apache.ant.http.HttpHead"/>
+
+</antlib>
Added: ant/sandbox/antlibs/http/trunk/src/war/WEB-INF/web.xml
URL: http://svn.apache.org/viewvc/ant/sandbox/antlibs/http/trunk/src/war/WEB-INF/web.xml?rev=417451&view=auto
==============================================================================
--- ant/sandbox/antlibs/http/trunk/src/war/WEB-INF/web.xml (added)
+++ ant/sandbox/antlibs/http/trunk/src/war/WEB-INF/web.xml Tue Jun 27 06:26:40 2006
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
+<web-app>
+</web-app>
\ No newline at end of file
Added: ant/sandbox/antlibs/http/trunk/src/war/resources/error.jsp
URL: http://svn.apache.org/viewvc/ant/sandbox/antlibs/http/trunk/src/war/resources/error.jsp?rev=417451&view=auto
==============================================================================
--- ant/sandbox/antlibs/http/trunk/src/war/resources/error.jsp (added)
+++ ant/sandbox/antlibs/http/trunk/src/war/resources/error.jsp Tue Jun 27 06:26:40 2006
@@ -0,0 +1,44 @@
+<%--
+ * Copyright 2001-2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+--%>
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<%
+ String codeParam = request.getParameter("code");
+
+ int error_code =HttpServletResponse.SC_OK;
+ if(codeParam!=null) {
+ try {
+ error_code =Integer.valueOf(codeParam).intValue();
+ } catch (NumberFormatException e) {
+ error_code =HttpServletResponse.SC_BAD_REQUEST;
+ }
+ }
+ if(error_code !=HttpServletResponse.SC_OK) {
+ response.sendError(error_code);
+ }
+%>
+<html>
+ <head><title>Error page</title></head>
+ <body>
+ <p>
+ ?code parameter <%= codeParam!=null?codeParam:"absent" %>
+ </p>
+ <p>
+ <%-- this string is searched for in the tests; do not edit without patching
+ them -->
+ error_code=<%= error_code %>
+ </p>
+ </body>
+</html>
\ No newline at end of file
Added: ant/sandbox/antlibs/http/trunk/src/war/resources/headers.jsp
URL: http://svn.apache.org/viewvc/ant/sandbox/antlibs/http/trunk/src/war/resources/headers.jsp?rev=417451&view=auto
==============================================================================
--- ant/sandbox/antlibs/http/trunk/src/war/resources/headers.jsp (added)
+++ ant/sandbox/antlibs/http/trunk/src/war/resources/headers.jsp Tue Jun 27 06:26:40 2006
@@ -0,0 +1,38 @@
+<%@ page import="java.util.Enumeration" %>
+<%--
+ List all headers in the message.
+ If you post a header with HTML or javascript in it wont be escaped, which
+ is a potential XSS security hole. Not for use on public systems
+--%>
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<%!
+ //minimal escaping of angle braces.
+ String escape(String source) {
+ String s1 = source.replace("<", "<");
+ String s2 = source.replace(">", ">");
+ return s2;
+ }
+
+%>
+<html>
+<head><title>Headers</title></head>
+
+<body>
+<h1>Headers</h1>
+<ol>
+<%
+ Enumeration headerNames = request.getHeaderNames();
+ while (headerNames.hasMoreElements()) {
+ String name = (String) headerNames.nextElement();
+ Enumeration values = request.getHeaders(name);
+ while (values.hasMoreElements()) {
+ String value = (String) values.nextElement();
+ out.print(escape(name));
+ out.print('=');
+ out.println(escape(value));
+ }
+ }
+%>
+</ol>
+</body>
+</html>
\ No newline at end of file
Added: ant/sandbox/antlibs/http/trunk/src/war/resources/index.html
URL: http://svn.apache.org/viewvc/ant/sandbox/antlibs/http/trunk/src/war/resources/index.html?rev=417451&view=auto
==============================================================================
--- ant/sandbox/antlibs/http/trunk/src/war/resources/index.html (added)
+++ ant/sandbox/antlibs/http/trunk/src/war/resources/index.html Tue Jun 27 06:26:40 2006
@@ -0,0 +1,15 @@
+<html>
+<head><title>Ant test webapp</title></head>
+<body>
+
+
+<ul>
+
+ <li><a href="error.jsp">Error page</a></li>
+ <li><a href="headers.jsp">Header listing</a></li>
+</ul>
+
+</body>
+
+
+</html>
\ No newline at end of file
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@ant.apache.org
For additional commands, e-mail: dev-help@ant.apache.org