Browse files

remove trailing whitespaces

  • Loading branch information...
1 parent 58a7ee4 commit d0497517214fa3aa8540c3da147af507f65e05ff @jgritman committed Sep 14, 2012
Showing with 6,144 additions and 6,142 deletions.
  1. +8 −8 README
  2. +9 −9 pom.xml
  3. +40 −40 src/main/assembly/all.xml
  4. +57 −57 src/main/java/groovyx/net/http/AsyncHTTPBuilder.java
  5. +219 −219 src/main/java/groovyx/net/http/AuthConfig.java
  6. +127 −127 src/main/java/groovyx/net/http/ContentEncoding.java
  7. +74 −74 src/main/java/groovyx/net/http/ContentEncodingRegistry.java
  8. +98 −98 src/main/java/groovyx/net/http/ContentType.java
  9. +87 −87 src/main/java/groovyx/net/http/DeflateEncoding.java
  10. +424 −424 src/main/java/groovyx/net/http/EncoderRegistry.java
  11. +85 −85 src/main/java/groovyx/net/http/GZIPEncoding.java
  12. +374 −374 src/main/java/groovyx/net/http/HTTPBuilder.java
  13. +10 −10 src/main/java/groovyx/net/http/HttpContextDecorator.java
  14. +281 −281 src/main/java/groovyx/net/http/HttpResponseDecorator.java
  15. +47 −47 src/main/java/groovyx/net/http/HttpResponseException.java
  16. +631 −631 src/main/java/groovyx/net/http/HttpURLClient.java
  17. +55 −55 src/main/java/groovyx/net/http/Method.java
  18. +423 −423 src/main/java/groovyx/net/http/ParserRegistry.java
  19. +242 −242 src/main/java/groovyx/net/http/RESTClient.java
  20. +49 −49 src/main/java/groovyx/net/http/ResponseParseException.java
  21. +71 −71 src/main/java/groovyx/net/http/Status.java
  22. +74 −74 src/main/java/groovyx/net/http/StringHashMap.java
  23. +526 −526 src/main/java/groovyx/net/http/URIBuilder.java
  24. +56 −56 src/main/java/groovyx/net/http/package.html
  25. +5 −5 src/main/java/groovyx/net/http/thirdparty/GAEConnectionManager.java
  26. +1 −1 src/main/resources/catalog/HTMLlat1.ent
  27. +2 −2 src/main/resources/catalog/HTMLspecial.ent
  28. +7 −7 src/main/resources/catalog/HTMLsymbol.ent
  29. +3 −3 src/main/resources/catalog/frameset.dtd
  30. +4 −4 src/main/resources/catalog/html.xml
  31. +9 −9 src/main/resources/catalog/loose.dtd
  32. +10 −10 src/main/resources/catalog/strict.dtd
  33. +1 −1 src/main/resources/catalog/xhtml-special.ent
  34. +4 −4 src/main/resources/catalog/xhtml-symbol.ent
  35. +8 −8 src/main/resources/catalog/xhtml1-frameset.dtd
  36. +4 −4 src/main/resources/catalog/xhtml1-strict.dtd
  37. +8 −8 src/main/resources/catalog/xhtml1-transitional.dtd
  38. +15 −15 src/main/resources/catalog/xhtml11-flat.dtd
  39. +4 −4 src/main/resources/catalog/xhtml11.dtd
  40. +1 −1 src/main/script/release_tweet.groovy
  41. +58 −58 src/main/script/twitter_restbuilder.groovy
  42. +126 −126 src/site/apt/changes.apt
  43. +18 −18 src/site/apt/contrib.apt
  44. +23 −23 src/site/apt/doc/async.apt
  45. +26 −26 src/site/apt/doc/auth.apt
  46. +60 −60 src/site/apt/doc/contentTypes.apt
  47. +61 −61 src/site/apt/doc/get.apt
  48. +65 −65 src/site/apt/doc/handlers.apt
  49. +29 −29 src/site/apt/doc/httpurlclient.apt
  50. +103 −103 src/site/apt/doc/index.apt
  51. +36 −36 src/site/apt/doc/json.apt
  52. +56 −56 src/site/apt/doc/post.apt
  53. +82 −82 src/site/apt/doc/rest.apt
  54. +64 −64 src/site/apt/doc/ssl.apt
  55. +36 −36 src/site/apt/doc/uribuilder.apt
  56. +76 −76 src/site/apt/doc/xml.apt
  57. +71 −71 src/site/apt/download.apt.vm
  58. +93 −93 src/site/apt/home.apt
  59. +198 −198 src/site/apt/index.apt
  60. +678 −678 src/site/examples.txt
  61. +32 −30 src/test/groovy/groovyx/net/http/AsyncHTTPBuilderTest.groovy
View
16 README
@@ -1,4 +1,4 @@
-HTTPBuilder = Easy HTTP client for Groovy.
+HTTPBuilder = Easy HTTP client for Groovy.
http://groovy.codehaus.org/modules/http-builder
@@ -18,21 +18,21 @@ Contributions:
* Signpost: http://code.google.com/p/oauth-signpost/
This project also includes source code written by Martin Blom (martin@blom.org)
- in the 'thirdparty' package. It is licensed under the LGPL v3 and
- re-distributed with permission from the author.
+ in the 'thirdparty' package. It is licensed under the LGPL v3 and
+ re-distributed with permission from the author.
License:
HTTPBuilder is copyright 2009-2011 Thomas Nichols except where otherwise noted.
- This project is licensed under the Apache License Version 2.0 except where
+ This project is licensed under the Apache License Version 2.0 except where
otherwise noted in the source files.
You are receiving this code free of charge, which represents many hours of
- effort from other individuals and corporations. As a responsible member
- of the community, you are encouraged (but not required) to donate any
- enhancements or improvements back to the community under a similar open
+ effort from other individuals and corporations. As a responsible member
+ of the community, you are encouraged (but not required) to donate any
+ enhancements or improvements back to the community under a similar open
source license. Thank you. -TMN
-
+
View
18 pom.xml
@@ -8,13 +8,13 @@
<name>HTTP client framework for Groovy</name>
<url>http://groovy.codehaus.org/modules/http-builder/</url>
<inceptionYear>2008</inceptionYear>
-
+
<description>
- A builder-style HTTP client API, including authentication, and extensible
+ A builder-style HTTP client API, including authentication, and extensible
handling of common content-types such as JSON and XML. It is built on top of
Apache's HttpClient.
</description>
-
+
<properties>
<!-- Needed because this value is used in Site filtering and Velocity doesn't like the dot. -->
<project-version>${project.version}</project-version>
@@ -25,7 +25,7 @@
<m2st-gatc-id>UA-11299971-1</m2st-gatc-id>
<m2st-gatc-config>_setDetectTitle(true)</m2st-gatc-config>
</properties>
-
+
<dependencies>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
@@ -83,7 +83,7 @@
<groupId>xml-resolver</groupId>
<artifactId>xml-resolver</artifactId>
<version>1.2</version>
- </dependency>
+ </dependency>
<dependency>
<!-- Add this dependency if you are using OAuth in HTTPBuilder or RESTClient -->
<groupId>oauth.signpost</groupId>
@@ -109,10 +109,10 @@
<artifactId>xercesImpl</artifactId>
<version>[2.6.2,)</version>
<optional>true</optional>
- <!-- Should be 'provided' but this is broken due to
+ <!-- Should be 'provided' but this is broken due to
http://jira.codehaus.org/browse/MASSEMBLY-248 -->
<!-- scope>provided</scope -->
- </dependency>
+ </dependency>
<!-- Not yet used for testing:
<dependency>
<groupId>org.simpleframework</groupId>
@@ -335,7 +335,7 @@
<extensions>
<extension>
<groupId>org.apache.maven.wagon</groupId>
- <!--
+ <!--
<artifactId>wagon-http</artifactId>
<artifactId>wagon-webdav</artifactId>
-->
@@ -401,7 +401,7 @@
</plugin>
</plugins>
</reporting>
-
+
<pluginRepositories>
<pluginRepository>
<id>m2st-releases</id>
View
80 src/main/assembly/all.xml
@@ -1,40 +1,40 @@
-<assembly>
- <!-- Assembles source, compiled JARs, and site documentation into a single
- distributable package.
- -->
- <id>all</id>
- <formats>
- <format>tar.gz</format>
- <format>zip</format>
- </formats>
-
- <fileSets>
- <fileSet>
- <directory>.</directory>
- <outputDirectory></outputDirectory>
- <includes>
- <include>pom.xml</include>
- <include>src/**</include>
- </includes>
- </fileSet>
- <fileSet>
- <directory>target</directory>
- <outputDirectory></outputDirectory>
- <includes>
- <include>*.jar</include>
- <include>site/**</include>
- </includes>
- </fileSet>
- </fileSets>
-
- <dependencySets>
- <dependencySet>
- <unpack>false</unpack>
- <scope>runtime</scope>
- <useProjectArtifact>false</useProjectArtifact>
- <useProjectAttachments>false</useProjectAttachments>
- <outputDirectory>dependencies</outputDirectory>
- </dependencySet>
- </dependencySets>
-
-</assembly>
+<assembly>
+ <!-- Assembles source, compiled JARs, and site documentation into a single
+ distributable package.
+ -->
+ <id>all</id>
+ <formats>
+ <format>tar.gz</format>
+ <format>zip</format>
+ </formats>
+
+ <fileSets>
+ <fileSet>
+ <directory>.</directory>
+ <outputDirectory></outputDirectory>
+ <includes>
+ <include>pom.xml</include>
+ <include>src/**</include>
+ </includes>
+ </fileSet>
+ <fileSet>
+ <directory>target</directory>
+ <outputDirectory></outputDirectory>
+ <includes>
+ <include>*.jar</include>
+ <include>site/**</include>
+ </includes>
+ </fileSet>
+ </fileSets>
+
+ <dependencySets>
+ <dependencySet>
+ <unpack>false</unpack>
+ <scope>runtime</scope>
+ <useProjectArtifact>false</useProjectArtifact>
+ <useProjectAttachments>false</useProjectAttachments>
+ <outputDirectory>dependencies</outputDirectory>
+ </dependencySet>
+ </dependencySets>
+
+</assembly>
View
114 src/main/java/groovyx/net/http/AsyncHTTPBuilder.java
@@ -14,9 +14,9 @@
* limitations under the License.
*
* You are receiving this code free of charge, which represents many hours of
- * effort from other individuals and corporations. As a responsible member
- * of the community, you are encouraged (but not required) to donate any
- * enhancements or improvements back to the community under a similar open
+ * effort from other individuals and corporations. As a responsible member
+ * of the community, you are encouraged (but not required) to donate any
+ * enhancements or improvements back to the community under a similar open
* source license. Thank you. -TMN
*/
package groovyx.net.http;
@@ -48,12 +48,12 @@
import org.apache.http.params.HttpProtocolParams;
/**
- * This implementation makes all requests asynchronous by submitting jobs to a
- * {@link ThreadPoolExecutor}. All request methods (including <code>get</code>
- * and <code>post</code>) return a {@link Future} instance, whose
- * {@link Future#get() get} method will provide access to whatever value was
+ * This implementation makes all requests asynchronous by submitting jobs to a
+ * {@link ThreadPoolExecutor}. All request methods (including <code>get</code>
+ * and <code>post</code>) return a {@link Future} instance, whose
+ * {@link Future#get() get} method will provide access to whatever value was
* returned from the response handler closure.
- *
+ *
* @author <a href='mailto:tomstrummer+httpbuilder@gmail.com'>Tom Nichols</a>
*/
public class AsyncHTTPBuilder extends HTTPBuilder {
@@ -62,47 +62,47 @@
* Default pool size is one is not supplied in the constructor.
*/
public static final int DEFAULT_POOL_SIZE = 4;
-
+
protected ExecutorService threadPool;
// = (ThreadPoolExecutor)Executors.newCachedThreadPool();
-
+
/**
* Accepts the following named parameters:
* <dl>
- * <dt>threadPool</dt><dd>Custom {@link ExecutorService} instance for
+ * <dt>threadPool</dt><dd>Custom {@link ExecutorService} instance for
* running submitted requests. If this is an instance of {@link ThreadPoolExecutor},
* the poolSize will be determined by {@link ThreadPoolExecutor#getMaximumPoolSize()}.
- * The default threadPool uses an unbounded queue to accept an unlimited
+ * The default threadPool uses an unbounded queue to accept an unlimited
* number of requests.</dd>
* <dt>poolSize</dt><dd>Max number of concurrent requests</dd>
* <dt>uri</dt><dd>Default request URI</dd>
* <dt>contentType</dt><dd>Default content type for requests and responses</dd>
- * <dt>timeout</dt><dd>Timeout in milliseconds to wait for a connection to
+ * <dt>timeout</dt><dd>Timeout in milliseconds to wait for a connection to
* be established and request to complete.</dd>
* </dl>
*/
public AsyncHTTPBuilder( Map<String, ?> args ) throws URISyntaxException {
super();
int poolSize = DEFAULT_POOL_SIZE;
ExecutorService threadPool = null;
- if ( args != null ) {
+ if ( args != null ) {
threadPool = (ExecutorService)args.remove( "threadPool" );
if ( threadPool instanceof ThreadPoolExecutor )
poolSize = ((ThreadPoolExecutor)threadPool).getMaximumPoolSize();
-
+
Object poolSzArg = args.remove("poolSize");
if ( poolSzArg != null ) poolSize = Integer.parseInt( poolSzArg.toString() );
-
+
if ( args.containsKey( "url" ) ) throw new IllegalArgumentException(
"The 'url' parameter is deprecated; use 'uri' instead" );
Object defaultURI = args.remove("uri");
if ( defaultURI != null ) super.setUri(defaultURI);
-
+
Object defaultContentType = args.remove("contentType");
- if ( defaultContentType != null )
+ if ( defaultContentType != null )
super.setContentType(defaultContentType);
-
+
Object timeout = args.remove( "timeout" );
if ( timeout != null ) setTimeout( (Integer) timeout );
@@ -114,15 +114,15 @@ public AsyncHTTPBuilder( Map<String, ?> args ) throws URISyntaxException {
}
this.initThreadPools( poolSize, threadPool );
}
-
+
/**
- * Submits a {@link Callable} instance to the job pool, which in turn will
- * call {@link HTTPBuilder#doRequest(RequestConfigDelegate)} in an asynchronous
- * thread. The {@link Future} instance returned by this value (which in
+ * Submits a {@link Callable} instance to the job pool, which in turn will
+ * call {@link HTTPBuilder#doRequest(RequestConfigDelegate)} in an asynchronous
+ * thread. The {@link Future} instance returned by this value (which in
* turn should be returned by any of the public <code>request</code> methods
- * (including <code>get</code> and <code>post</code>) may be used to
- * retrieve whatever value may be returned from the executed response
- * handler closure.
+ * (including <code>get</code> and <code>post</code>) may be used to
+ * retrieve whatever value may be returned from the executed response
+ * handler closure.
*/
@Override
protected Future<?> doRequest( final RequestConfigDelegate delegate ) {
@@ -138,18 +138,18 @@ public AsyncHTTPBuilder( Map<String, ?> args ) throws URISyntaxException {
}
});
}
-
+
/*
- * Because we can't call "super.doRequest" from within the anonymous
+ * Because we can't call "super.doRequest" from within the anonymous
* Callable subclass.
*/
private Object doRequestSuper( RequestConfigDelegate delegate ) throws IOException {
return super.doRequest(delegate);
}
-
+
/**
- * Initializes threading parameters for the HTTPClient's
- * {@link ThreadSafeClientConnManager}, and this class' ThreadPoolExecutor.
+ * Initializes threading parameters for the HTTPClient's
+ * {@link ThreadSafeClientConnManager}, and this class' ThreadPoolExecutor.
*/
protected void initThreadPools( final int poolSize, final ExecutorService threadPool ) {
if (poolSize < 1) throw new IllegalArgumentException("poolSize may not be < 1");
@@ -164,20 +164,20 @@ protected void initThreadPools( final int poolSize, final ExecutorService thread
// Create and initialize scheme registry
SchemeRegistry schemeRegistry = new SchemeRegistry();
- schemeRegistry.register( new Scheme( "http",
+ schemeRegistry.register( new Scheme( "http",
PlainSocketFactory.getSocketFactory(), 80 ) );
- schemeRegistry.register( new Scheme( "https",
+ schemeRegistry.register( new Scheme( "https",
SSLSocketFactory.getSocketFactory(), 443));
ClientConnectionManager cm = new ThreadSafeClientConnManager(
params, schemeRegistry );
super.client = new DefaultHttpClient( cm, params );
this.threadPool = threadPool != null ? threadPool :
- new ThreadPoolExecutor( poolSize, poolSize, 120, TimeUnit.SECONDS,
+ new ThreadPoolExecutor( poolSize, poolSize, 120, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>() );
}
-
+
/**
* {@inheritDoc}
*/
@@ -186,23 +186,23 @@ protected Object defaultSuccessHandler( HttpResponseDecorator resp, Object parse
throws ResponseParseException {
return super.defaultSuccessHandler( resp, parsedData );
}
-
+
/**
- * For 'failure' responses (e.g. a 404), the exception will be wrapped in
- * a {@link ExecutionException} and held by the {@link Future} instance.
- * The exception is then re-thrown when calling {@link Future#get()
- * future.get()}. You can access the original exception (e.g. an
- * {@link HttpResponseException}) by calling <code>ex.getCause()</code>.
- *
+ * For 'failure' responses (e.g. a 404), the exception will be wrapped in
+ * a {@link ExecutionException} and held by the {@link Future} instance.
+ * The exception is then re-thrown when calling {@link Future#get()
+ * future.get()}. You can access the original exception (e.g. an
+ * {@link HttpResponseException}) by calling <code>ex.getCause()</code>.
+ *
*/
@Override
protected void defaultFailureHandler( HttpResponseDecorator resp )
throws HttpResponseException {
super.defaultFailureHandler( resp );
}
-
+
/**
- * This timeout is used for both the time to wait for an established
+ * This timeout is used for both the time to wait for an established
* connection, and the time to wait for data.
* @see HttpConnectionParams#setSoTimeout(HttpParams, int)
* @see HttpConnectionParams#setConnectionTimeout(HttpParams, int)
@@ -213,42 +213,42 @@ public void setTimeout( int timeout ) {
HttpConnectionParams.setSoTimeout( super.getClient().getParams(), timeout );
/* this will cause a thread waiting for an available connection instance
* to time-out */
-// ConnManagerParams.setTimeout( super.getClient().getParams(), timeout );
+// ConnManagerParams.setTimeout( super.getClient().getParams(), timeout );
}
-
+
/**
* Get the timeout in for establishing an HTTP connection.
* @return timeout in milliseconds.
*/
public int getTimeout() {
return HttpConnectionParams.getConnectionTimeout( super.getClient().getParams() );
}
-
+
/**
- * <p>Access the underlying threadpool to adjust things like job timeouts.</p>
- *
- * <p>Note that this is not the same pool used by the HttpClient's
- * {@link ThreadSafeClientConnManager}. Therefore, increasing the
+ * <p>Access the underlying threadpool to adjust things like job timeouts.</p>
+ *
+ * <p>Note that this is not the same pool used by the HttpClient's
+ * {@link ThreadSafeClientConnManager}. Therefore, increasing the
* {@link ThreadPoolExecutor#setMaximumPoolSize(int) maximum pool size} will
* not in turn increase the number of possible concurrent requests. It will
* simply cause more requests to be <i>attempted</i> which will then simply
* block while waiting for a free connection.</p>
- *
- * @return the service used to execute requests. By default this is a
+ *
+ * @return the service used to execute requests. By default this is a
* {@link ThreadPoolExecutor}.
*/
public ExecutorService getThreadExecutor() {
return this.threadPool;
}
-
+
/**
* {@inheritDoc}
*/
@Override public void shutdown() {
- super.shutdown();
+ super.shutdown();
this.threadPool.shutdown();
}
-
+
/**
* {@inheritDoc}
* @see #shutdown()
@@ -257,4 +257,4 @@ public ExecutorService getThreadExecutor() {
this.shutdown();
super.finalize();
}
-}
+}
View
438 src/main/java/groovyx/net/http/AuthConfig.java
@@ -1,219 +1,219 @@
-/*
- * Copyright 2008-2011 Thomas Nichols. http://blog.thomnichols.org
- *
- * 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.
- *
- * You are receiving this code free of charge, which represents many hours of
- * effort from other individuals and corporations. As a responsible member
- * of the community, you are encouraged (but not required) to donate any
- * enhancements or improvements back to the community under a similar open
- * source license. Thank you. -TMN
- */
-package groovyx.net.http;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.security.GeneralSecurityException;
-import java.security.KeyStore;
-import java.util.HashMap;
-import java.util.Map;
-
-import oauth.signpost.OAuthConsumer;
-import oauth.signpost.commonshttp.CommonsHttpOAuthConsumer;
-import oauth.signpost.exception.OAuthException;
-
-import org.apache.http.Header;
-import org.apache.http.HttpEntityEnclosingRequest;
-import org.apache.http.HttpException;
-import org.apache.http.HttpHost;
-import org.apache.http.HttpRequest;
-import org.apache.http.HttpRequestInterceptor;
-import org.apache.http.auth.AuthScope;
-import org.apache.http.auth.UsernamePasswordCredentials;
-import org.apache.http.conn.scheme.Scheme;
-import org.apache.http.conn.ssl.SSLSocketFactory;
-import org.apache.http.protocol.ExecutionContext;
-import org.apache.http.protocol.HttpContext;
-
-/**
- * Encapsulates all configuration related to HTTP authentication methods.
- * @see HTTPBuilder#getAuth()
- *
- * @author <a href='mailto:tomstrummer+httpbuilder@gmail.com'>Tom Nichols</a>
- */
-public class AuthConfig {
- protected HTTPBuilder builder;
- public AuthConfig( HTTPBuilder builder ) {
- this.builder = builder;
- }
-
- /**
- * Set authentication credentials to be used for the current
- * {@link HTTPBuilder#getUri() default host}. This method name is a bit of
- * a misnomer, since these credentials will actually work for "digest"
- * authentication as well.
- * @param user
- * @param pass
- */
- public void basic( String user, String pass ) {
- URI uri = ((URIBuilder)builder.getUri()).toURI();
- if ( uri == null ) throw new IllegalStateException( "a default URI must be set" );
- this.basic( uri.getHost(), uri.getPort(), user, pass );
- }
-
- /**
- * Set authentication credentials to be used for the given host and port.
- * @param host
- * @param port
- * @param user
- * @param pass
- */
- public void basic( String host, int port, String user, String pass ) {
- builder.getClient().getCredentialsProvider().setCredentials(
- new AuthScope( host, port ),
- new UsernamePasswordCredentials( user, pass )
- );
- }
-
- /**
- * Sets a certificate to be used for SSL authentication. See
- * {@link Class#getResource(String)} for how to get a URL from a resource
- * on the classpath.
- * @param certURL URL to a JKS keystore where the certificate is stored.
- * @param password password to decrypt the keystore
- */
- public void certificate( String certURL, String password )
- throws GeneralSecurityException, IOException {
-
- KeyStore keyStore = KeyStore.getInstance( KeyStore.getDefaultType() );
- InputStream jksStream = new URL(certURL).openStream();
- try {
- keyStore.load( jksStream, password.toCharArray() );
- } finally { jksStream.close(); }
-
- SSLSocketFactory ssl = new SSLSocketFactory(keyStore, password);
- ssl.setHostnameVerifier( SSLSocketFactory.STRICT_HOSTNAME_VERIFIER );
-
- builder.getClient().getConnectionManager().getSchemeRegistry()
- .register( new Scheme("https", ssl, 443) );
- }
-
- /**
- * </p>OAuth sign all requests. Note that this currently does <strong>not</strong>
- * wait for a <code>WWW-Authenticate</code> challenge before sending the
- * the OAuth header. All requests to all domains will be signed for this
- * instance.</p>
- *
- * <p>This assumes you've already generated an <code>accessToken</code> and
- * <code>secretToken</code> for the site you're targeting. For More information
- * on how to achieve this, see the
- * <a href='http://code.google.com/p/oauth-signpost/wiki/GettingStarted#Using_Signpost'>Signpost documentation</a>.</p>
- * @since 0.5.1
- * @param consumerKey <code>null</code> if you want to <strong>unset</strong>
- * OAuth handling and stop signing requests.
- * @param consumerSecret
- * @param accessToken
- * @param secretToken
- */
- public void oauth( String consumerKey, String consumerSecret,
- String accessToken, String secretToken ) {
- this.builder.client.removeRequestInterceptorByClass( OAuthSigner.class );
- if ( consumerKey != null )
- this.builder.client.addRequestInterceptor( new OAuthSigner(
- consumerKey, consumerSecret, accessToken, secretToken ) );
- }
-
- /**
- * This class is used to sign all requests via an {@link HttpRequestInterceptor}
- * until the context-aware AuthScheme is released in HttpClient 4.1.
- * @since 0.5.1
- */
- static class OAuthSigner implements HttpRequestInterceptor {
- protected OAuthConsumer oauth;
- public OAuthSigner( String consumerKey, String consumerSecret,
- String accessToken, String secretToken ) {
- this.oauth = new CommonsHttpOAuthConsumer( consumerKey, consumerSecret );
- oauth.setTokenWithSecret( accessToken, secretToken );
- }
-
- public void process(HttpRequest request, HttpContext ctx) throws HttpException, IOException {
- /* The full request URI must be reconstructed between the context and the request URI.
- * Best we can do until AuthScheme supports HttpContext. See:
- * https://issues.apache.org/jira/browse/HTTPCLIENT-901 */
- try {
- HttpHost host = (HttpHost) ctx.getAttribute( ExecutionContext.HTTP_TARGET_HOST );
- final URI requestURI = new URI( host.toURI() ).resolve( request.getRequestLine().getUri() );
-
- oauth.signpost.http.HttpRequest oAuthRequest =
- new OAuthRequestAdapter(request, requestURI);
- this.oauth.sign( oAuthRequest );
- }
- catch ( URISyntaxException ex ) {
- throw new HttpException( "Error rebuilding request URI", ex );
- }
- catch (OAuthException e) {
- throw new HttpException( "OAuth signing error", e);
- }
- }
-
- static class OAuthRequestAdapter implements oauth.signpost.http.HttpRequest {
-
- final HttpRequest request;
- final URI requestURI;
- OAuthRequestAdapter( HttpRequest request, URI requestURI ) {
- this.request = request;
- this.requestURI = requestURI;
- }
-
- public String getRequestUrl() { return requestURI.toString(); }
- public void setRequestUrl(String url) {/*ignore*/}
- public Map<String, String> getAllHeaders() {
- Map<String,String> headers = new HashMap<String,String>();
- // FIXME this doesn't account for repeated headers,
- // which are allowed by the HTTP spec!!
- for ( Header h : request.getAllHeaders() )
- headers.put(h.getName(), h.getValue());
- return headers;
- }
- public String getContentType() {
- try {
- return request.getFirstHeader("content-type").getValue();
- }
- catch ( Exception ex ) { // NPE or ArrayOOBEx
- return null;
- }
- }
- public String getHeader(String name) {
- Header h = request.getFirstHeader(name);
- return h != null ? h.getValue() : null;
- }
- public InputStream getMessagePayload() throws IOException {
- if ( request instanceof HttpEntityEnclosingRequest )
- return ((HttpEntityEnclosingRequest)request).getEntity().getContent();
- return null;
- }
- public String getMethod() {
- return request.getRequestLine().getMethod();
- }
- public void setHeader(String key, String val) {
- request.setHeader(key, val);
- }
- public Object unwrap() {
- return request;
- }
- };
- }
-}
+/*
+ * Copyright 2008-2011 Thomas Nichols. http://blog.thomnichols.org
+ *
+ * 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.
+ *
+ * You are receiving this code free of charge, which represents many hours of
+ * effort from other individuals and corporations. As a responsible member
+ * of the community, you are encouraged (but not required) to donate any
+ * enhancements or improvements back to the community under a similar open
+ * source license. Thank you. -TMN
+ */
+package groovyx.net.http;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.util.HashMap;
+import java.util.Map;
+
+import oauth.signpost.OAuthConsumer;
+import oauth.signpost.commonshttp.CommonsHttpOAuthConsumer;
+import oauth.signpost.exception.OAuthException;
+
+import org.apache.http.Header;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.protocol.ExecutionContext;
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * Encapsulates all configuration related to HTTP authentication methods.
+ * @see HTTPBuilder#getAuth()
+ *
+ * @author <a href='mailto:tomstrummer+httpbuilder@gmail.com'>Tom Nichols</a>
+ */
+public class AuthConfig {
+ protected HTTPBuilder builder;
+ public AuthConfig( HTTPBuilder builder ) {
+ this.builder = builder;
+ }
+
+ /**
+ * Set authentication credentials to be used for the current
+ * {@link HTTPBuilder#getUri() default host}. This method name is a bit of
+ * a misnomer, since these credentials will actually work for "digest"
+ * authentication as well.
+ * @param user
+ * @param pass
+ */
+ public void basic( String user, String pass ) {
+ URI uri = ((URIBuilder)builder.getUri()).toURI();
+ if ( uri == null ) throw new IllegalStateException( "a default URI must be set" );
+ this.basic( uri.getHost(), uri.getPort(), user, pass );
+ }
+
+ /**
+ * Set authentication credentials to be used for the given host and port.
+ * @param host
+ * @param port
+ * @param user
+ * @param pass
+ */
+ public void basic( String host, int port, String user, String pass ) {
+ builder.getClient().getCredentialsProvider().setCredentials(
+ new AuthScope( host, port ),
+ new UsernamePasswordCredentials( user, pass )
+ );
+ }
+
+ /**
+ * Sets a certificate to be used for SSL authentication. See
+ * {@link Class#getResource(String)} for how to get a URL from a resource
+ * on the classpath.
+ * @param certURL URL to a JKS keystore where the certificate is stored.
+ * @param password password to decrypt the keystore
+ */
+ public void certificate( String certURL, String password )
+ throws GeneralSecurityException, IOException {
+
+ KeyStore keyStore = KeyStore.getInstance( KeyStore.getDefaultType() );
+ InputStream jksStream = new URL(certURL).openStream();
+ try {
+ keyStore.load( jksStream, password.toCharArray() );
+ } finally { jksStream.close(); }
+
+ SSLSocketFactory ssl = new SSLSocketFactory(keyStore, password);
+ ssl.setHostnameVerifier( SSLSocketFactory.STRICT_HOSTNAME_VERIFIER );
+
+ builder.getClient().getConnectionManager().getSchemeRegistry()
+ .register( new Scheme("https", ssl, 443) );
+ }
+
+ /**
+ * </p>OAuth sign all requests. Note that this currently does <strong>not</strong>
+ * wait for a <code>WWW-Authenticate</code> challenge before sending the
+ * the OAuth header. All requests to all domains will be signed for this
+ * instance.</p>
+ *
+ * <p>This assumes you've already generated an <code>accessToken</code> and
+ * <code>secretToken</code> for the site you're targeting. For More information
+ * on how to achieve this, see the
+ * <a href='http://code.google.com/p/oauth-signpost/wiki/GettingStarted#Using_Signpost'>Signpost documentation</a>.</p>
+ * @since 0.5.1
+ * @param consumerKey <code>null</code> if you want to <strong>unset</strong>
+ * OAuth handling and stop signing requests.
+ * @param consumerSecret
+ * @param accessToken
+ * @param secretToken
+ */
+ public void oauth( String consumerKey, String consumerSecret,
+ String accessToken, String secretToken ) {
+ this.builder.client.removeRequestInterceptorByClass( OAuthSigner.class );
+ if ( consumerKey != null )
+ this.builder.client.addRequestInterceptor( new OAuthSigner(
+ consumerKey, consumerSecret, accessToken, secretToken ) );
+ }
+
+ /**
+ * This class is used to sign all requests via an {@link HttpRequestInterceptor}
+ * until the context-aware AuthScheme is released in HttpClient 4.1.
+ * @since 0.5.1
+ */
+ static class OAuthSigner implements HttpRequestInterceptor {
+ protected OAuthConsumer oauth;
+ public OAuthSigner( String consumerKey, String consumerSecret,
+ String accessToken, String secretToken ) {
+ this.oauth = new CommonsHttpOAuthConsumer( consumerKey, consumerSecret );
+ oauth.setTokenWithSecret( accessToken, secretToken );
+ }
+
+ public void process(HttpRequest request, HttpContext ctx) throws HttpException, IOException {
+ /* The full request URI must be reconstructed between the context and the request URI.
+ * Best we can do until AuthScheme supports HttpContext. See:
+ * https://issues.apache.org/jira/browse/HTTPCLIENT-901 */
+ try {
+ HttpHost host = (HttpHost) ctx.getAttribute( ExecutionContext.HTTP_TARGET_HOST );
+ final URI requestURI = new URI( host.toURI() ).resolve( request.getRequestLine().getUri() );
+
+ oauth.signpost.http.HttpRequest oAuthRequest =
+ new OAuthRequestAdapter(request, requestURI);
+ this.oauth.sign( oAuthRequest );
+ }
+ catch ( URISyntaxException ex ) {
+ throw new HttpException( "Error rebuilding request URI", ex );
+ }
+ catch (OAuthException e) {
+ throw new HttpException( "OAuth signing error", e);
+ }
+ }
+
+ static class OAuthRequestAdapter implements oauth.signpost.http.HttpRequest {
+
+ final HttpRequest request;
+ final URI requestURI;
+ OAuthRequestAdapter( HttpRequest request, URI requestURI ) {
+ this.request = request;
+ this.requestURI = requestURI;
+ }
+
+ public String getRequestUrl() { return requestURI.toString(); }
+ public void setRequestUrl(String url) {/*ignore*/}
+ public Map<String, String> getAllHeaders() {
+ Map<String,String> headers = new HashMap<String,String>();
+ // FIXME this doesn't account for repeated headers,
+ // which are allowed by the HTTP spec!!
+ for ( Header h : request.getAllHeaders() )
+ headers.put(h.getName(), h.getValue());
+ return headers;
+ }
+ public String getContentType() {
+ try {
+ return request.getFirstHeader("content-type").getValue();
+ }
+ catch ( Exception ex ) { // NPE or ArrayOOBEx
+ return null;
+ }
+ }
+ public String getHeader(String name) {
+ Header h = request.getFirstHeader(name);
+ return h != null ? h.getValue() : null;
+ }
+ public InputStream getMessagePayload() throws IOException {
+ if ( request instanceof HttpEntityEnclosingRequest )
+ return ((HttpEntityEnclosingRequest)request).getEntity().getContent();
+ return null;
+ }
+ public String getMethod() {
+ return request.getRequestLine().getMethod();
+ }
+ public void setHeader(String key, String val) {
+ request.setHeader(key, val);
+ }
+ public Object unwrap() {
+ return request;
+ }
+ };
+ }
+}
View
254 src/main/java/groovyx/net/http/ContentEncoding.java
@@ -1,127 +1,127 @@
-/*
- * Copyright 2008-2011 Thomas Nichols. http://blog.thomnichols.org
- *
- * 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.
- *
- * You are receiving this code free of charge, which represents many hours of
- * effort from other individuals and corporations. As a responsible member
- * of the community, you are encouraged (but not required) to donate any
- * enhancements or improvements back to the community under a similar open
- * source license. Thank you. -TMN
- */
-package groovyx.net.http;
-
-import java.io.IOException;
-
-import org.apache.http.Header;
-import org.apache.http.HeaderElement;
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpException;
-import org.apache.http.HttpRequest;
-import org.apache.http.HttpRequestInterceptor;
-import org.apache.http.HttpResponse;
-import org.apache.http.HttpResponseInterceptor;
-import org.apache.http.protocol.HttpContext;
-
-/**
- * Base class for handing content-encoding.
- * @author <a href='mailto:tomstrummer+httpbuilder@gmail.com'>Tom Nichols</a>
- */
-public abstract class ContentEncoding {
-
- public static final String ACCEPT_ENC_HDR = "Accept-Encoding";
- public static final String CONTENT_ENC_HDR = "Content-Encoding";
-
- protected abstract String getContentEncoding();
- protected abstract HttpEntity wrapResponseEntity( HttpEntity raw );
-
- public HttpRequestInterceptor getRequestInterceptor() {
- return new RequestInterceptor();
- }
-
- public HttpResponseInterceptor getResponseInterceptor() {
- return new ResponseInterceptor();
- }
-
- /**
- * Enumeration of common content-encodings.
- */
- public static enum Type {
- GZIP,
- COMPRESS,
- DEFLATE;
-
- /** Prints the value as it should appear in an HTTP header */
- @Override public String toString() {
- return this.name().toLowerCase();
- }
- }
-
- /**
- * Request interceptor that adds the correct <code>Accept</code> header
- * to the outgoing request.
- * @author <a href='mailto:tomstrummer+httpbuilder@gmail.com'>Tom Nichols</a>
- */
- protected class RequestInterceptor implements HttpRequestInterceptor {
- public void process( final HttpRequest req,
- final HttpContext context ) throws HttpException, IOException {
-
- // set the Accept-Encoding header:
- String encoding = getContentEncoding();
- if ( !req.containsHeader( ACCEPT_ENC_HDR ) )
- req.addHeader( ACCEPT_ENC_HDR, encoding );
-
- else {
- StringBuilder values = new StringBuilder();
- for ( Header h : req.getHeaders( ACCEPT_ENC_HDR ) )
- values.append( h.getValue() ).append( "," );
-
- String encList = (!values.toString().contains( encoding )) ? values
- .append( encoding ).toString()
- : values.toString().substring( 0, values.lastIndexOf( "," ) );
-
- req.setHeader( ACCEPT_ENC_HDR, encList );
- }
-
- //TODO compress request and add content-encoding header.
- }
- }
-
- /**
- * Response interceptor that filters the response stream to decode the
- * compressed content before it is passed on to the parser.
- * @author <a href='mailto:tomstrummer+httpbuilder@gmail.com'>Tom Nichols</a>
- */
- protected class ResponseInterceptor implements HttpResponseInterceptor {
- public void process( final HttpResponse response, final HttpContext context )
- throws HttpException, IOException {
-
- if ( hasEncoding( response, getContentEncoding() ) )
- response.setEntity( wrapResponseEntity( response.getEntity() ) );
- }
-
- protected boolean hasEncoding( final HttpResponse response, final String encoding ) {
- HttpEntity entity = response.getEntity();
- if ( entity == null ) return false;
- Header ceHeader = entity.getContentEncoding();
- if ( ceHeader == null ) return false;
-
- HeaderElement[] codecs = ceHeader.getElements();
- for ( int i = 0; i < codecs.length; i++ )
- if ( encoding.equalsIgnoreCase( codecs[i].getName() ) )
- return true;
-
- return false;
- }
- }
-}
+/*
+ * Copyright 2008-2011 Thomas Nichols. http://blog.thomnichols.org
+ *
+ * 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.
+ *
+ * You are receiving this code free of charge, which represents many hours of
+ * effort from other individuals and corporations. As a responsible member
+ * of the community, you are encouraged (but not required) to donate any
+ * enhancements or improvements back to the community under a similar open
+ * source license. Thank you. -TMN
+ */
+package groovyx.net.http;
+
+import java.io.IOException;
+
+import org.apache.http.Header;
+import org.apache.http.HeaderElement;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpResponseInterceptor;
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * Base class for handing content-encoding.
+ * @author <a href='mailto:tomstrummer+httpbuilder@gmail.com'>Tom Nichols</a>
+ */
+public abstract class ContentEncoding {
+
+ public static final String ACCEPT_ENC_HDR = "Accept-Encoding";
+ public static final String CONTENT_ENC_HDR = "Content-Encoding";
+
+ protected abstract String getContentEncoding();
+ protected abstract HttpEntity wrapResponseEntity( HttpEntity raw );
+
+ public HttpRequestInterceptor getRequestInterceptor() {
+ return new RequestInterceptor();
+ }
+
+ public HttpResponseInterceptor getResponseInterceptor() {
+ return new ResponseInterceptor();
+ }
+
+ /**
+ * Enumeration of common content-encodings.
+ */
+ public static enum Type {
+ GZIP,
+ COMPRESS,
+ DEFLATE;
+
+ /** Prints the value as it should appear in an HTTP header */
+ @Override public String toString() {
+ return this.name().toLowerCase();
+ }
+ }
+
+ /**
+ * Request interceptor that adds the correct <code>Accept</code> header
+ * to the outgoing request.
+ * @author <a href='mailto:tomstrummer+httpbuilder@gmail.com'>Tom Nichols</a>
+ */
+ protected class RequestInterceptor implements HttpRequestInterceptor {
+ public void process( final HttpRequest req,
+ final HttpContext context ) throws HttpException, IOException {
+
+ // set the Accept-Encoding header:
+ String encoding = getContentEncoding();
+ if ( !req.containsHeader( ACCEPT_ENC_HDR ) )
+ req.addHeader( ACCEPT_ENC_HDR, encoding );
+
+ else {
+ StringBuilder values = new StringBuilder();
+ for ( Header h : req.getHeaders( ACCEPT_ENC_HDR ) )
+ values.append( h.getValue() ).append( "," );
+
+ String encList = (!values.toString().contains( encoding )) ? values
+ .append( encoding ).toString()
+ : values.toString().substring( 0, values.lastIndexOf( "," ) );
+
+ req.setHeader( ACCEPT_ENC_HDR, encList );
+ }
+
+ //TODO compress request and add content-encoding header.
+ }
+ }
+
+ /**
+ * Response interceptor that filters the response stream to decode the
+ * compressed content before it is passed on to the parser.
+ * @author <a href='mailto:tomstrummer+httpbuilder@gmail.com'>Tom Nichols</a>
+ */
+ protected class ResponseInterceptor implements HttpResponseInterceptor {
+ public void process( final HttpResponse response, final HttpContext context )
+ throws HttpException, IOException {
+
+ if ( hasEncoding( response, getContentEncoding() ) )
+ response.setEntity( wrapResponseEntity( response.getEntity() ) );
+ }
+
+ protected boolean hasEncoding( final HttpResponse response, final String encoding ) {
+ HttpEntity entity = response.getEntity();
+ if ( entity == null ) return false;
+ Header ceHeader = entity.getContentEncoding();
+ if ( ceHeader == null ) return false;
+
+ HeaderElement[] codecs = ceHeader.getElements();
+ for ( int i = 0; i < codecs.length; i++ )
+ if ( encoding.equalsIgnoreCase( codecs[i].getName() ) )
+ return true;
+
+ return false;
+ }
+ }
+}
View
148 src/main/java/groovyx/net/http/ContentEncodingRegistry.java
@@ -1,74 +1,74 @@
-/*
- * Copyright 2008-2011 Thomas Nichols. http://blog.thomnichols.org
- *
- * 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.
- *
- * You are receiving this code free of charge, which represents many hours of
- * effort from other individuals and corporations. As a responsible member
- * of the community, you are encouraged (but not required) to donate any
- * enhancements or improvements back to the community under a similar open
- * source license. Thank you. -TMN
- */
-package groovyx.net.http;
-
-import groovyx.net.http.ContentEncoding.Type;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.http.client.HttpClient;
-import org.apache.http.impl.client.AbstractHttpClient;
-
-/**
- * Keeps track of available content-encoding handlers.
- * @author <a href='mailto:tomstrummer+httpbuilder@gmail.com'>Tom Nichols</a>
- */
-public class ContentEncodingRegistry {
-
- protected Map<String,ContentEncoding> availableEncoders = getDefaultEncoders();
-
- /**
- * This implementation adds a {@link GZIPEncoding} and {@link DeflateEncoding}
- * handler to the registry. Override this method to provide a different set
- * of defaults.
- * @return a map to content-encoding strings to {@link ContentEncoding} handlers.
- */
- protected Map<String,ContentEncoding> getDefaultEncoders() {
- Map<String, ContentEncoding> map = new HashMap<String, ContentEncoding>();
- map.put( Type.GZIP.toString(), new GZIPEncoding() );
- map.put( Type.DEFLATE.toString(), new DeflateEncoding() );
- return map;
- }
-
- /**
- * Add the request and response interceptors to the {@link HttpClient},
- * which will provide transparent decoding of the given content-encoding
- * types. This method is called by HTTPBuilder and probably should not need
- * be modified by sub-classes.
- * @param client client on which to set the request and response interceptors
- * @param encodings encoding name (either a {@link ContentEncoding.Type} or
- * a <code>content-encoding</code> string.
- */
- void setInterceptors( final AbstractHttpClient client, Object... encodings ) {
- // remove any encoding interceptors that are already set
- client.removeRequestInterceptorByClass( ContentEncoding.RequestInterceptor.class );
- client.removeResponseInterceptorByClass( ContentEncoding.ResponseInterceptor.class );
-
- for ( Object encName : encodings ) {
- ContentEncoding enc = availableEncoders.get( encName.toString() );
- if ( enc == null ) continue;
- client.addRequestInterceptor( enc.getRequestInterceptor() );
- client.addResponseInterceptor( enc.getResponseInterceptor() );
- }
- }
-}
+/*
+ * Copyright 2008-2011 Thomas Nichols. http://blog.thomnichols.org
+ *
+ * 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.
+ *
+ * You are receiving this code free of charge, which represents many hours of
+ * effort from other individuals and corporations. As a responsible member
+ * of the community, you are encouraged (but not required) to donate any
+ * enhancements or improvements back to the community under a similar open
+ * source license. Thank you. -TMN
+ */
+package groovyx.net.http;
+
+import groovyx.net.http.ContentEncoding.Type;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.http.client.HttpClient;
+import org.apache.http.impl.client.AbstractHttpClient;
+
+/**
+ * Keeps track of available content-encoding handlers.
+ * @author <a href='mailto:tomstrummer+httpbuilder@gmail.com'>Tom Nichols</a>
+ */
+public class ContentEncodingRegistry {
+
+ protected Map<String,ContentEncoding> availableEncoders = getDefaultEncoders();
+
+ /**
+ * This implementation adds a {@link GZIPEncoding} and {@link DeflateEncoding}
+ * handler to the registry. Override this method to provide a different set
+ * of defaults.
+ * @return a map to content-encoding strings to {@link ContentEncoding} handlers.
+ */
+ protected Map<String,ContentEncoding> getDefaultEncoders() {
+ Map<String, ContentEncoding> map = new HashMap<String, ContentEncoding>();
+ map.put( Type.GZIP.toString(), new GZIPEncoding() );
+ map.put( Type.DEFLATE.toString(), new DeflateEncoding() );
+ return map;
+ }
+
+ /**
+ * Add the request and response interceptors to the {@link HttpClient},
+ * which will provide transparent decoding of the given content-encoding
+ * types. This method is called by HTTPBuilder and probably should not need
+ * be modified by sub-classes.
+ * @param client client on which to set the request and response interceptors
+ * @param encodings encoding name (either a {@link ContentEncoding.Type} or
+ * a <code>content-encoding</code> string.
+ */
+ void setInterceptors( final AbstractHttpClient client, Object... encodings ) {
+ // remove any encoding interceptors that are already set
+ client.removeRequestInterceptorByClass( ContentEncoding.RequestInterceptor.class );
+ client.removeResponseInterceptorByClass( ContentEncoding.ResponseInterceptor.class );
+
+ for ( Object encName : encodings ) {
+ ContentEncoding enc = availableEncoders.get( encName.toString() );
+ if ( enc == null ) continue;
+ client.addRequestInterceptor( enc.getRequestInterceptor() );
+ client.addResponseInterceptor( enc.getResponseInterceptor() );
+ }
+ }
+}
View
196 src/main/java/groovyx/net/http/ContentType.java
@@ -1,98 +1,98 @@
-/*
- * Copyright 2008-2011 Thomas Nichols. http://blog.thomnichols.org
- *
- * 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.
- *
- * You are receiving this code free of charge, which represents many hours of
- * effort from other individuals and corporations. As a responsible member
- * of the community, you are encouraged (but not required) to donate any
- * enhancements or improvements back to the community under a similar open
- * source license. Thank you. -TMN
- */
-package groovyx.net.http;
-
-import java.util.Iterator;
-
-import org.apache.commons.collections.iterators.ArrayIterator;
-
-/**
- * Enumeration of common <a href="http://www.iana.org/assignments/media-types/">IANA</a>
- * content-types. This may be used to specify a request or response
- * content-type more easily than specifying the full string each time. i.e.
- * <pre>
- * http.request( GET, JSON ) {...}</pre>
- *
- * Is roughly equivalent to:
- * <pre>
- * http.request( GET, 'application/json' )</pre>
- *
- * The only difference being, equivalent content-types (i.e.
- * <code>application/xml</code> and <code>text/xml</code> are all added to the
- * request's <code>Accept</code> header. By default, all equivalent content-types
- * are handled the same by the {@link EncoderRegistry} and {@link ParserRegistry}
- * as well.
- * @author <a href='mailto:tomstrummer+httpbuilder@gmail.com'>Tom Nichols</a>
- */
-public enum ContentType {
- /** <code>&#42;/*</code> */
- ANY("*/*"),
- /** <code>text/plain</code> */
- TEXT("text/plain"),
- /**
- * <ul>
- * <li><code>application/json</code></li>
- * <li><code>application/javascript</code></li>
- * <li><code>text/javascript</code></li>
- * </ul>
- */
- JSON("application/json","application/javascript","text/javascript"),
- /**
- * <ul>
- * <li><code>application/xml</code></li>
- * <li><code>text/xml</code></li>
- * <li><code>application/xhtml+xml</code></li>
- * <li><code>application/atom+xml</code></li>
- * </ul>
- */
- XML("application/xml","text/xml","application/xhtml+xml","application/atom+xml"),
- /** <code>text/html</code> */
- HTML("text/html"),
- /** <code>application/x-www-form-urlencoded</code> */
- URLENC("application/x-www-form-urlencoded"),
- /** <code>application/octet-stream</code> */
- BINARY("application/octet-stream");
-
- private final String[] ctStrings;
- public String[] getContentTypeStrings() { return ctStrings; }
- @Override public String toString() { return ctStrings[0]; }
-
- /**
- * Builds a string to be used as an HTTP <code>Accept</code> header
- * value, i.e. "application/xml, text/xml"
- * @return
- */
- @SuppressWarnings("unchecked")
- public String getAcceptHeader() {
- Iterator<String> iter = new ArrayIterator(ctStrings);
- StringBuilder sb = new StringBuilder();
- while ( iter.hasNext() ) {
- sb.append( iter.next() );
- if ( iter.hasNext() ) sb.append( ", " );
- }
- return sb.toString();
- }
-
- private ContentType( String... contentTypes ) {
- this.ctStrings = contentTypes;
- }
-}
+/*
+ * Copyright 2008-2011 Thomas Nichols. http://blog.thomnichols.org
+ *
+ * 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.
+ *
+ * You are receiving this code free of charge, which represents many hours of
+ * effort from other individuals and corporations. As a responsible member
+ * of the community, you are encouraged (but not required) to donate any
+ * enhancements or improvements back to the community under a similar open
+ * source license. Thank you. -TMN
+ */
+package groovyx.net.http;
+
+import java.util.Iterator;
+
+import org.apache.commons.collections.iterators.ArrayIterator;
+
+/**
+ * Enumeration of common <a href="http://www.iana.org/assignments/media-types/">IANA</a>
+ * content-types. This may be used to specify a request or response
+ * content-type more easily than specifying the full string each time. i.e.
+ * <pre>
+ * http.request( GET, JSON ) {...}</pre>
+ *
+ * Is roughly equivalent to:
+ * <pre>
+ * http.request( GET, 'application/json' )</pre>
+ *
+ * The only difference being, equivalent content-types (i.e.
+ * <code>application/xml</code> and <code>text/xml</code> are all added to the
+ * request's <code>Accept</code> header. By default, all equivalent content-types
+ * are handled the same by the {@link EncoderRegistry} and {@link ParserRegistry}
+ * as well.
+ * @author <a href='mailto:tomstrummer+httpbuilder@gmail.com'>Tom Nichols</a>
+ */
+public enum ContentType {
+ /** <code>&#42;/*</code> */
+ ANY("*/*"),
+ /** <code>text/plain</code> */
+ TEXT("text/plain"),
+ /**
+ * <ul>
+ * <li><code>application/json</code></li>
+ * <li><code>application/javascript</code></li>
+ * <li><code>text/javascript</code></li>
+ * </ul>
+ */
+ JSON("application/json","application/javascript","text/javascript"),
+ /**
+ * <ul>
+ * <li><code>application/xml</code></li>
+ * <li><code>text/xml</code></li>
+ * <li><code>application/xhtml+xml</code></li>
+ * <li><code>application/atom+xml</code></li>
+ * </ul>
+ */
+ XML("application/xml","text/xml","application/xhtml+xml","application/atom+xml"),
+ /** <code>text/html</code> */
+ HTML("text/html"),
+ /** <code>application/x-www-form-urlencoded</code> */
+ URLENC("application/x-www-form-urlencoded"),
+ /** <code>application/octet-stream</code> */
+ BINARY("application/octet-stream");
+
+ private final String[] ctStrings;
+ public String[] getContentTypeStrings() { return ctStrings; }
+ @Override public String toString() { return ctStrings[0]; }
+
+ /**
+ * Builds a string to be used as an HTTP <code>Accept</code> header
+ * value, i.e. "application/xml, text/xml"
+ * @return
+ */
+ @SuppressWarnings("unchecked")
+ public String getAcceptHeader() {
+ Iterator<String> iter = new ArrayIterator(ctStrings);
+ StringBuilder sb = new StringBuilder();
+ while ( iter.hasNext() ) {
+ sb.append( iter.next() );
+ if ( iter.hasNext() ) sb.append( ", " );
+ }
+ return sb.toString();
+ }
+
+ private ContentType( String... contentTypes ) {
+ this.ctStrings = contentTypes;
+ }
+}
View
174 src/main/java/groovyx/net/http/DeflateEncoding.java
@@ -1,87 +1,87 @@
-/*
- * Copyright 2008-2011 Thomas Nichols. http://blog.thomnichols.org
- *
- * 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.
- *
- * You are receiving this code free of charge, which represents many hours of
- * effort from other individuals and corporations. As a responsible member
- * of the community, you are encouraged (but not required) to donate any
- * enhancements or improvements back to the community under a similar open
- * source license. Thank you. -TMN
- */
-package groovyx.net.http;
-
-import static groovyx.net.http.ContentEncoding.Type.DEFLATE;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.zip.InflaterInputStream;
-
-import org.apache.http.HttpEntity;
-import org.apache.http.entity.HttpEntityWrapper;
-
-/**
- * Content encoding used to handle Deflate responses.
- * @author <a href='mailto:tomstrummer+httpbuilder@gmail.com'>Tom Nichols</a>
- */
-public class DeflateEncoding extends ContentEncoding {
-
- /**
- * Returns the {@link ContentEncoding.Type#DEFLATE} encoding string which is
- * added to the <code>Accept-Encoding</code> header by the base class.
- */
- @Override
- public String getContentEncoding() {
- return DEFLATE.toString();
- }
-
-
- /**
- * Wraps the raw entity in a {@link InflaterEntity}.
- */
- @Override
- public HttpEntity wrapResponseEntity( HttpEntity raw ) {
- return new InflaterEntity( raw );
- }
-
- /**
- * Entity used to interpret a Deflate-encoded response
- * @author <a href='mailto:tomstrummer+httpbuilder@gmail.com'>Tom Nichols</a>
- */
- public static class InflaterEntity extends HttpEntityWrapper {
-
- public InflaterEntity(final HttpEntity entity) {
- super(entity);
- }
-
- /**
- * returns a {@link InflaterInputStream} which wraps the original entity's
- * content stream
- * @see HttpEntity#getContent()
- */
- @Override
- public InputStream getContent() throws IOException, IllegalStateException {
- return new InflaterInputStream( wrappedEntity.getContent() );
- }
-
- /**
- * @return -1
- */
- @Override
- public long getContentLength() {
- // length of ungzipped content is not known
- return -1;
- }
- }
-
-}
+/*
+ * Copyright 2008-2011 Thomas Nichols. http://blog.thomnichols.org
+ *
+ * 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.
+ *
+ * You are receiving this code free of charge, which represents many hours of
+ * effort from other individuals and corporations. As a responsible member
+ * of the community, you are encouraged (but not required) to donate any
+ * enhancements or improvements back to the community under a similar open
+ * source license. Thank you. -TMN
+ */
+package groovyx.net.http;
+
+import static groovyx.net.http.ContentEncoding.Type.DEFLATE;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.InflaterInputStream;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.entity.HttpEntityWrapper;
+
+/**
+ * Content encoding used to handle Deflate responses.
+ * @author <a href='mailto:tomstrummer+httpbuilder@gmail.com'>Tom Nichols</a>
+ */
+public class DeflateEncoding extends ContentEncoding {
+
+ /**
+ * Returns the {@link ContentEncoding.Type#DEFLATE} encoding string which is
+ * added to the <code>Accept-Encoding</code> header by the base class.
+ */
+ @Override
+ public String getContentEncoding() {
+ return DEFLATE.toString();
+ }
+
+
+ /**
+ * Wraps the raw entity in a {@link InflaterEntity}.
+ */
+ @Override
+ public HttpEntity wrapResponseEntity( HttpEntity raw ) {
+ return new InflaterEntity( raw );
+ }
+
+ /**
+ * Entity used to interpret a Deflate-encoded response
+ * @author <a href='mailto:tomstrummer+httpbuilder@gmail.com'>Tom Nichols</a>
+ */
+ public static class InflaterEntity extends HttpEntityWrapper {
+
+ public InflaterEntity(final HttpEntity entity) {
+ super(entity);
+ }
+
+ /**
+ * returns a {@link InflaterInputStream} which wraps the original entity's
+ * content stream
+ * @see HttpEntity#getContent()
+ */
+ @Override
+ public InputStream getContent() throws IOException, IllegalStateException {
+ return new InflaterInputStream( wrappedEntity.getContent() );
+ }
+
+ /**
+ * @return -1
+ */
+ @Override
+ public long getContentLength() {
+ // length of ungzipped content is not known
+ return -1;
+ }
+ }
+
+}
View
848 src/main/java/groovyx/net/http/EncoderRegistry.java
@@ -1,424 +1,424 @@
-/*
- * Copyright 2008-2011 Thomas Nichols. http://blog.thomnichols.org
- *
- * 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.
- *
- * You are receiving this code free of charge, which represents many hours of
- * effort from other individuals and corporations. As a responsible member
- * of the community, you are encouraged (but not required) to donate any
- * enhancements or improvements back to the community under a similar open
- * source license. Thank you. -TMN
- */
-package groovyx.net.http;
-
-import groovy.lang.Closure;
-import groovy.lang.GString;
-import groovy.lang.Writable;
-import groovy.xml.StreamingMarkupBuilder;
-import groovyx.net.http.HTTPBuilder.RequestConfigDelegate;
-
-import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.PrintWriter;
-import java.io.Reader;
-import java.io.StringWriter;
-import java.io.UnsupportedEncodingException;
-import java.nio.charset.Charset;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-import net.sf.json.JSON;
-import net.sf.json.JSONArray;
-import net.sf.json.JSONObject;
-import net.sf.json.groovy.JsonGroovyBuilder;
-
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpEntityEnclosingRequest;
-import org.apache.http.NameValuePair;
-import org.apache.http.client.entity.UrlEncodedFormEntity;
-import org.apache.http.entity.InputStreamEntity;
-import org.apache.http.entity.StringEntity;
-import org.apache.http.message.BasicNameValuePair;
-import org.codehaus.groovy.runtime.DefaultGroovyMethods;
-import org.codehaus.groovy.runtime.MethodClosure;
-
-
-/**
- * <p>This class handles creation of the request body (i.e. for a
- * PUT or POST operation) based on content-type. When a
- * {@link RequestConfigDelegate#setBody(Object) body} is set from the builder, it is
- * processed based on the {@link RequestConfigDelegate#getRequestContentType()
- * request content-type}. For instance, the {@link #encodeForm(Map)} method
- * will be invoked if the request content-type is form-urlencoded, which will
- * cause the following:<code>body=[a:1, b:'two']</code> to be encoded as
- * the equivalent <code>a=1&b=two</code> in the request body.</p>
- *
- * <p>Most default encoders can handle a closure as a request body. In this
- * case, the closure is executed and a suitable 'builder' passed to the
- * closure that is used for constructing the content. In the case of
- * binary encoding this would be an OutputStream; for TEXT encoding it would
- * be a PrintWriter, and for XML it would be an already-bound
- * {@link StreamingMarkupBuilder}. See each <code>encode...</code> method
- * for details for each particular content-type.</p>
- *
- * <p>Contrary to its name, this class does not have anything to do with the
- * <code>content-encoding</code> HTTP header. </p>
- *
- * @see RequestConfigDelegate#setBody(Object)
- * @see RequestConfigDelegate#send(Object, Object)
- * @author <a href='mailto:tomstrummer+httpbuilder@gmail.com'>Tom Nichols</a>
- */
-public class EncoderRegistry {
-
- Charset charset = Charset.defaultCharset(); // 1.5
- private Map<String,Closure> registeredEncoders = buildDefaultEncoderMap();
-
- /**
- * Set the charset used in the content-type header of all requests that send
- * textual data. This must be a chaset supported by the Java platform
- * @see Charset#forName(String)
- * @param charset
- */
- public void setCharset( String charset ) {
- this.charset = Charset.forName(charset);
- }
-
- /**
- * Default request encoder for a binary stream. Acceptable argument
- * types are:
- * <ul>
- * <li>InputStream</li>
- * <li>byte[] / ByteArrayOutputStream</li>
- * <li>Closure</li>
- * </ul>
- * If a closure is given, it is executed with an OutputStream passed
- * as the single closure argument. Any data sent to the stream from the
- * body of the closure is used as the request content body.
- * @param data
- * @return an {@link HttpEntity} encapsulating this request data
- * @throws UnsupportedEncodingException
- */
- public InputStreamEntity encodeStream( Object data, Object contentType )
- throws UnsupportedEncodingException {
- InputStreamEntity entity = null;
-
- if ( data instanceof ByteArrayInputStream ) {
- // special case for ByteArrayIS so that we can set the content length.
- ByteArrayInputStream in = ((ByteArrayInputStream)data);
- entity = new InputStreamEntity( in, in.available() );
- }
- else if ( data instanceof InputStream ) {
- entity = new InputStreamEntity( (InputStream)data, -1 );
- }
- else if ( data instanceof byte[] ) {
- byte[] out = ((byte[])data);
- entity = new InputStreamEntity( new ByteArrayInputStream(
- out), out.length );
- }
- else if ( data instanceof ByteArrayOutputStream ) {
- ByteArrayOutputStream out = ((ByteArrayOutputStream)data);
- entity = new InputStreamEntity( new ByteArrayInputStream(
- out.toByteArray()), out.size() );
- }
- else if ( data instanceof Closure ) {
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- ((Closure)data).call( out ); // data is written to out
- entity = new InputStreamEntity( new ByteArrayInputStream(
- out.toByteArray()), out.size() );
- }
-
- if ( entity == null ) throw new IllegalArgumentException(
- "Don't know how to encode " + data + " as a byte stream" );
-
- if ( contentType == null ) contentType = ContentType.BINARY;
- entity.setContentType( contentType.toString() );
- return entity;
- }
-
- /**
- * Default handler used for a plain text content-type. Acceptable argument
- * types are:
- * <ul>
- * <li>Closure</li>
- * <li>Writable</li>
- * <li>Reader</li>
- * </ul>
- * For Closure argument, a {@link PrintWriter} is passed as the single
- * argument to the closure. Any data sent to the writer from the
- * closure will be sent to the request content body.
- * @param data
- * @return an {@link HttpEntity} encapsulating this request data
- * @throws IOException
- */
- public HttpEntity encodeText( Object data, Object contentType ) throws IOException {
- if ( data instanceof Closure ) {
- StringWriter out = new StringWriter();
- PrintWriter writer = new PrintWriter( out );
- ((Closure)data).call( writer );
- writer.close();
- out.flush();
- data = out;
- }
- else if ( data instanceof Writable ) {
- StringWriter out = new StringWriter();
- ((Writable)data).writeTo(out);
- out.flush();
- data = out;
- }
- else if ( data instanceof Reader && ! (data instanceof BufferedReader) )
- data = new BufferedReader( (Reader)data );
- if ( data instanceof BufferedReader ) {
- StringWriter out = new StringWriter();
- DefaultGroovyMethods.leftShift( out, (BufferedReader)data );
-
- data = out;
- }
- // if data is a String, we are already covered.
- if ( contentType == null ) contentType = ContentType.TEXT;
- return createEntity( contentType, data.toString() );
- }
-
- /**
- * Set the request body as a url-encoded list of parameters. This is
- * typically used to simulate a HTTP form POST.
- * For multi-valued parameters, enclose the values in a list, e.g.
- * <pre>[ key1 : ['val1', 'val2'], key2 : 'etc.' ]</pre>
- * @param params
- * @return an {@link HttpEntity} encapsulating this request data
- * @throws UnsupportedEncodingException
- */
- public UrlEncodedFormEntity encodeForm( Map<?,?> params )
- throws UnsupportedEncodingException {
- return encodeForm( params, null );
- }
-
- public UrlEncodedFormEntity encodeForm( Map<?,?> params, Object contentType )
- throws UnsupportedEncodingException {
- List<NameValuePair> paramList = new ArrayList<NameValuePair>();
-
- for ( Object key : params.keySet() ) {
- Object val = params.get( key );
- if ( val instanceof List<?> )
- for ( Object subVal : (List<?>)val )
- paramList.add( new BasicNameValuePair( key.toString(),
- ( subVal == null ) ? "" : subVal.toString() ) );
-
- else paramList.add( new BasicNameValuePair( key.toString(),
- ( val == null ) ? "" : val.toString() ) );
- }
-
- UrlEncodedFormEntity e = new UrlEncodedFormEntity( paramList, charset.name() );
- if ( contentType != null ) e.setContentType( contentType.toString() );
- return e;
-
- }
-
- /**
- * Accepts a String as a url-encoded form post. This method assumes the
- * String is an already-encoded POST string.
- * @param formData a url-encoded form POST string. See
- * <a href='http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1'>
- * The W3C spec</a> for more info.
- * @return an {@link HttpEntity} encapsulating this request data
- * @throws UnsupportedEncodingException
- */
- public HttpEntity encodeForm( String formData, Object contentType ) throws UnsupportedEncodingException {
- if ( contentType == null ) contentType = ContentType.URLENC;
- return this.createEntity( contentType, formData );
- }
-
- /**
- * Encode the content as XML. The argument may be either an object whose
- * <code>toString</code> produces valid markup, or a Closure which will be
- * interpreted as a builder definition. A closure argument is
- * passed to {@link StreamingMarkupBuilder#bind(groovy.lang.Closure)}.
- * @param xml data that defines the XML structure
- * @return an {@link HttpEntity} encapsulating this request data
- * @throws UnsupportedEncodingException
- */
- public HttpEntity encodeXML( Object xml, Object contentType )
- throws UnsupportedEncodingException {
- if ( xml instanceof Closure ) {
- StreamingMarkupBuilder smb = new StreamingMarkupBuilder();
- xml = smb.bind( xml );
- }
- if ( contentType == null ) contentType = ContentType.XML;
- return createEntity( contentType, xml.toString() );
- }
-
- /**
- * <p>Accepts a Collection or a JavaBean object which is converted to JSON.
- * A Map or POJO/POGO will be converted to a {@link JSONObject}, and any
- * other collection type will be converted to a {@link JSONArray}. A
- * String or GString will be interpreted as valid JSON and passed directly
- * as the request body (with charset conversion if necessary.)</p>
- *
- * <p>If a Closure is passed as the model, it will be executed as if it were
- * a JSON object definition passed to a {@link JsonGroovyBuilder}. In order
- * for the closure to be interpreted correctly, there must be a 'root'
- * element immediately inside the closure. For example:</p>
- *
- * <pre>builder.post( JSON ) {
- * body = {
- * root {
- * first {
- * one = 1
- * two = '2'
- * }
- * second = 'some string'
- * }
- * }
- * }</pre>
- * <p> will return the following JSON string:<pre>
- * {"root":{"first":{"one":1,"two":"2"},"second":"some string"}}</pre></p>
- *
- * @param model data to be converted to JSON, as specified above.
- * @return an {@link HttpEntity} encapsulating this request data
- * @throws UnsupportedEncodingException
- */
- @SuppressWarnings("unchecked")
- public HttpEntity encodeJSON( Object model, Object contentType ) throws UnsupportedEncodingException {
-
- Object json;
- if ( model instanceof Map ) {
- json = new JSONObject();
- ((JSONObject)json).putAll( (Map)model );
- }
- else if ( model instanceof Collection ) {
- json = new JSONArray();
- ((JSONArray)json).addAll( (Collection)model );
- }
- else if ( model instanceof Closure ) {
- Closure closure = (Closure)model;
- closure.setDelegate( new JsonGroovyBuilder() );
- json = (JSON)closure.call();
- }
- else if ( model instanceof String || model instanceof GString )
- json = model; // assume string is valid JSON already.
- else json = JSONObject.fromObject( model ); // Assume object is a JavaBean
-
- if ( contentType == null ) contentType = ContentType.JSON;
- return this.createEntity( contentType, json.toString() );
- }
-
- /**
- * Helper method used by encoder methods to create an {@link HttpEntity}
- * instance that encapsulates the request data. This may be used by any
- * non-streaming encoder that needs to send textual data. It also sets the
- * {@link #setCharset(String) charset} portion of the content-type header.
- *
- * @param ct content-type of the data
- * @param data textual request data to be encoded
- * @return an instance to be used for the
- * {@link HttpEntityEnclosingRequest#setEntity(HttpEntity) request content}
- * @throws UnsupportedEncodingException
- */
- protected StringEntity createEntity( Object ct, String data )
- throws UnsupportedEncodingException {
- StringEntity entity = new StringEntity( data, charset.toString() );
- entity.setContentType( ct.toString() );
- return entity;
- }
-
- /**
- * Returns a map of default encoders. Override this method to change
- * what encoders are registered by default. You can of course call
- * <code>super.buildDefaultEncoderMap()</code> and then add or remove
- * from that result as well.
- */
- protected Map<String,Closure> buildDefaultEncoderMap() {
- Map<String,Closure> encoders = new HashMap<String,Closure>();
-
- encoders.put( ContentType.BINARY.toString(), new MethodClosure(this,"encodeStream") );
- encoders.put( ContentType.TEXT.toString(), new MethodClosure( this, "encodeText" ) );
- encoders.put( ContentType.URLENC.toString(), new MethodClosure( this, "encodeForm" ) );
-
- Closure encClosure = new MethodClosure(this,"encodeXML");
- for ( String ct : ContentType.XML.getContentTypeStrings() )
- encoders.put( ct, encClosure );
- encoders.put( ContentType.HTML.toString(), encClosure );
-
- encClosure = new MethodClosure(this,"encodeJSON");
- for ( String ct : ContentType.JSON.getContentTypeStrings() )
- encoders.put( ct, encClosure );
-
- return encoders;
- }
-
- /**
- * Retrieve a encoder for the given content-type. This
- * is called by HTTPBuilder to retrieve the correct encoder for a given
- * content-type. The encoder is then used to serialize the request data
- * in the request body.
- * @param contentType
- * @return encoder that can interpret the given content type,
- * or null.
- */
- public Closure getAt( Object contentType ) {
- String ct = contentType.toString();
- int idx = ct.indexOf( ';' );
- if ( idx > 0 ) ct = ct.substring( 0, idx );
-
- return registeredEncoders.get(ct);
- }
-
- /**
- * Register a new encoder for the given content type. If any encoder
- * previously existed for that content type it will be replaced. The
- * closure must return an {@link HttpEntity}. It will also usually
- * accept a single argument, which will be whatever is set in the request
- * configuration closure via {@link RequestConfigDelegate#setBody(Object)}.
- * @param contentType
- * @param closure
- */
- public void putAt( Object contentType, Closure value ) {
- if ( contentType instanceof ContentType ) {
- for ( String ct : ((ContentType)contentType).getContentTypeStrings() )
- this.registeredEncoders.put( ct, value );
- }
- else this.registeredEncoders.put( contentType.toString(), value );
- }
-
- /**
- * Alias for {@link #getAt(Object)} to allow property-style access.
- * @param key
- * @return
- */
- public Closure propertyMissing( Object key ) {
- return this.getAt( key );
- }
-
- /**
- * Alias for {@link #putAt(Object, Closure)} to allow property-style access.
- * @param key
- * @param value
- */
- public void propertyMissing( Object key, Closure value ) {
- this.putAt( key, value );
- }
-
- /**
- * Iterate over the entire parser map
- * @return
- */
- public Iterator<Map.Entry<String,Closure>> iterator() {
- return this.registeredEncoders.entrySet().iterator();
- }
-}
+/*
+ * Copyright 2008-2011 Thomas Nichols. http://blog.thomnichols.org
+ *
+ * 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.
+ *
+ * You are receiving this code free of charge, which represents many hours of
+ * effort from other individuals and corporations. As a responsible member
+ * of the community, you are encouraged (but not required) to donate any
+ * enhancements or improvements back to the community under a similar open
+ * source license. Thank you. -TMN
+ */
+package groovyx.net.http;
+
+import groovy.lang.Closure;
+import groovy.lang.GString;
+import groovy.lang.Writable;
+import groovy.xml.StreamingMarkupBuilder;
+import groovyx.net.http.HTTPBuilder.RequestConfigDelegate;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import net.sf.json.JSON;
+import net.sf.json.JSONArray;
+import net.sf.json.JSONObject;
+import net.sf.json.groovy.JsonGroovyBuilder;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.entity.InputStreamEntity;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.message.BasicNameValuePair;
+import org.codehaus.groovy.runtime.DefaultGroovyMethods;
+import org.codehaus.groovy.runtime.MethodClosure;
+
+
+/**
+ * <p>This class handles creation of the request body (i.e. for a
+ * PUT or POST operation) based on content-type. When a
+ * {@link RequestConfigDelegate#setBody(Object) body} is set from the builder, it is
+ * processed based on the {@link RequestConfigDelegate#getRequestContentType()
+ * request content-type}. For instance, the {@link #encodeForm(Map)} method
+ * will be invoked if the request content-type is form-urlencoded, which will
+ * cause the following:<code>body=[a:1, b:'two']</code> to be encoded as
+ * the equivalent <code>a=1&b=two</code> in the request body.</p>
+ *
+ * <p>Most default encoders can handle a closure as a request body. In this
+ * case, the closure is executed and a suitable 'builder' passed to the
+ * closure that is used for constructing the content. In the case of
+ * binary encoding this would be an OutputStream; for TEXT encoding it would
+ * be a PrintWriter, and for XML it would be an already-bound
+ * {@link StreamingMarkupBuilder}. See each <code>encode...</code> method
+ * for details for each particular content-type.</p>
+ *
+ * <p>Contrary to its name, this class does not have anything to do with the
+ * <code>content-encoding</code> HTTP header. </p>
+ *
+ * @see RequestConfigDelegate#setBody(Object)
+ * @see RequestConfigDelegate#send(Object, Object)
+ * @author <a href='mailto:tomstrummer+httpbuilder@gmail.com'>Tom Nichols</a>
+ */
+public class EncoderRegistry {
+
+ Charset charset = Charset.defaultCharset(); // 1.5
+ private Map<String,Closure> registeredEncoders = buildDefaultEncoderMap();
+
+ /**
+ * Set the charset used in the content-type header of all requests that send
+ * textual data. This must be a chaset supported by the Java platform
+ * @see Charset#forName(String)
+ * @param charset
+ */
+ public void setCharset( String charset ) {
+ this.charset = Charset.forName(charset);
+ }
+
+ /**
+ * Default request encoder for a binary stream. Acceptable argument
+ * types are:
+ * <ul>
+ * <li>InputStream</li>
+ * <li>byte[] / ByteArrayOutputStream</li>
+ * <li>Closure</li>
+ * </ul>
+ * If a closure is given, it is executed with an OutputStream passed
+ * as the single closure argument. Any data sent to the stream from the
+ * body of the closure is used as the request content body.
+ * @param data
+ * @return an {@link HttpEntity} encapsulating this request data
+ * @throws UnsupportedEncodingException
+ */
+ public InputStreamEntity encodeStream( Object data, Object contentType )
+ throws UnsupportedEncodingException {
+ InputStreamEntity entity = null;
+
+ if ( data instanceof ByteArrayInputStream ) {
+ // special case for ByteArrayIS so that we can set the content length.
+ ByteArrayInputStream in = ((ByteArrayInputStream)data);
+ entity = new InputStreamEntity( in, in.available() );
+ }
+ else if ( data instanceof InputStream ) {
+ entity = new InputStreamEntity( (InputStream)data, -1 );
+ }
+ else if ( data instanceof byte[] ) {
+ byte[] out = ((byte[])data);
+ entity = new InputStreamEntity( new ByteArrayInputStream(
+ out), out.length );
+ }
+ else if ( data instanceof ByteArrayOutputStream ) {
+ ByteArrayOutputStream out = ((ByteArrayOutputStream)data);
+ entity = new InputStreamEntity( new ByteArrayInputStream(
+ out.toByteArray()), out.size() );
+ }
+ else if ( data instanceof Closure ) {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ((Closure)data).call( out ); // data is written to out