diff --git a/fluent-hc/pom.xml b/fluent-hc/pom.xml
new file mode 100644
index 000000000..8c2c5d577
--- /dev/null
+++ b/fluent-hc/pom.xml
@@ -0,0 +1,133 @@
+
+
+
+
+ 4.0.0
+
+ org.apache.httpcomponents
+ httpcomponents-client
+ 4.2-alpha1-SNAPSHOT
+
+ fluent-hc
+ HttpClient fluent
+
+ HttpComponents Client fluent API
+
+ http://hc.apache.org/httpcomponents-client
+ jar
+
+
+
+ org.apache.httpcomponents
+ httpclient
+ ${project.version}
+ compile
+
+
+ junit
+ junit
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+
+
+ UTF-8
+ UTF-8
+ 1.5
+ 1.5
+ true
+ true
+
+
+
+
+
+ src/main/resources
+ true
+
+ **/*.properties
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ ${maven.compile.target}
+ ${maven.compile.optimize}
+ ${maven.compile.deprecation}
+
+
+
+ maven-surefire-plugin
+
+
+
+
+
+
+
+
+ maven-javadoc-plugin
+
+
+ true
+
+
+ http://download.oracle.com/javase/1.5.0/docs/api/
+ http://hc.apache.org/httpcomponents-core-ga/httpcore/apidocs/
+ http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/
+
+
+
+
+
+ javadoc
+
+
+
+
+
+
+ maven-jxr-plugin
+
+
+
+ maven-surefire-report-plugin
+
+
+
+
+
+
diff --git a/fluent-hc/src/main/java/org/apache/http/client/fluent/FluentExecutor.java b/fluent-hc/src/main/java/org/apache/http/client/fluent/FluentExecutor.java
new file mode 100644
index 000000000..3ccd03eaa
--- /dev/null
+++ b/fluent-hc/src/main/java/org/apache/http/client/fluent/FluentExecutor.java
@@ -0,0 +1,163 @@
+/*
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ====================================================================
+ */
+
+package org.apache.http.client.fluent;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.conn.scheme.PlainSocketFactory;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.conn.scheme.SchemeSocketFactory;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.conn.SchemeRegistryFactory;
+import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
+
+public class FluentExecutor {
+
+ public static FluentExecutor newInstance() {
+ FluentExecutor fexe = new FluentExecutor();
+ return fexe;
+ }
+
+ private ThreadSafeClientConnManager localConnManager;
+ private SchemeRegistry localSchemeRegistry;
+ private SchemeSocketFactory localSocketFactory;
+
+ private FluentExecutor() {
+ localSchemeRegistry = SchemeRegistryFactory.createDefault();
+ localConnManager = new ThreadSafeClientConnManager(localSchemeRegistry);
+ localSocketFactory = PlainSocketFactory.getSocketFactory();
+
+ }
+
+ public FluentResponse exec(FluentRequest req)
+ throws ClientProtocolException, IOException {
+ DefaultHttpClient client = getClient();
+ client.setCredentialsProvider(req.credentialsProvider);
+ client.setParams(req.localParams);
+ HttpResponse resp = client.execute(req, req.localContext);
+ FluentResponse fresp = new FluentResponse(resp);
+ return fresp;
+ }
+
+ public FluentResponse[] exec(final FluentRequest[] reqs)
+ throws InterruptedException {
+ if (reqs == null)
+ throw new NullPointerException("The request array may not be null.");
+ int length = reqs.length;
+ if (length == 0)
+ return new FluentResponse[0];
+ FluentResponse[] resps = new FluentResponse[length];
+ MultiRequestThread[] threads = new MultiRequestThread[length];
+ for (int id = 0; id < length; id++) {
+ threads[id] = new MultiRequestThread(this, reqs, resps, id);
+ }
+ for (int id = 0; id < length; id++) {
+ threads[id].start();
+ }
+ for (int id = 0; id < length; id++) {
+ threads[id].join();
+ }
+ return resps;
+ }
+
+ public DefaultHttpClient getClient() {
+ DefaultHttpClient client;
+ client = new DefaultHttpClient(localConnManager);
+ return client;
+ }
+
+ public int getMaxConnectionsPerRoute() {
+ return localConnManager.getDefaultMaxPerRoute();
+ }
+
+ public int getMaxTotalConnections() {
+ return localConnManager.getMaxTotal();
+ }
+
+ public Scheme getScheme(final String name) {
+ return localSchemeRegistry.getScheme(name);
+ }
+
+ public List getSchemeNames() {
+ List schemeNames = localSchemeRegistry.getSchemeNames();
+ return schemeNames;
+ }
+
+ public FluentExecutor registerScheme(final String name, final int port) {
+ Scheme sch = new Scheme(name, port, localSocketFactory);
+ localSchemeRegistry.register(sch);
+ return this;
+ }
+
+ public FluentExecutor setMaxConnectionsPerRoute(final int maxPerRoute) {
+ localConnManager.setDefaultMaxPerRoute(maxPerRoute);
+ return this;
+ }
+
+ public FluentExecutor setMaxTotalConnections(final int maxTotal) {
+ localConnManager.setMaxTotal(maxTotal);
+ return this;
+ }
+
+ public FluentExecutor unregisterAllSchemes() {
+ for (String name : getSchemeNames())
+ localSchemeRegistry.unregister(name);
+ return this;
+ }
+
+ public FluentExecutor unregisterScheme(final String name) {
+ localSchemeRegistry.unregister(name);
+ return this;
+ }
+}
+
+class MultiRequestThread extends Thread {
+ private FluentExecutor executor;
+ private FluentRequest[] reqs;
+ private FluentResponse[] resps;
+ private int id;
+
+ MultiRequestThread(final FluentExecutor executor,
+ final FluentRequest[] reqs, final FluentResponse[] resps,
+ final int id) {
+ this.executor = executor;
+ this.reqs = reqs;
+ this.resps = resps;
+ this.id = id;
+ }
+
+ @Override
+ public void run() {
+ FluentRequest req = reqs[id];
+ try {
+ FluentResponse resp = executor.exec(req);
+ resp.loadContent();
+ resps[id] = resp;
+ } catch (Exception e) {
+ req.abort();
+ }
+ }
+}
diff --git a/fluent-hc/src/main/java/org/apache/http/client/fluent/FluentHttp.java b/fluent-hc/src/main/java/org/apache/http/client/fluent/FluentHttp.java
new file mode 100644
index 000000000..15d0e4c69
--- /dev/null
+++ b/fluent-hc/src/main/java/org/apache/http/client/fluent/FluentHttp.java
@@ -0,0 +1,33 @@
+/*
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ====================================================================
+ */
+
+package org.apache.http.client.fluent;
+
+public class FluentHttp {
+ public static final int GET_METHOD = 0;
+ public static final int POST_METHOD = 1;
+ public static final int DELETE_METHOD = 2;
+ public static final int PUT_METHOD = 3;
+ public static final int OPTION_METHOD = 4;
+ public static final int TRACE_METHOD = 5;
+ public static final String REQUEST_INTERCEPTORS = "httpclinet.request.interceptors";
+ public static final String RESPONSE_INTERCEPTORS = "httpclinet.response.interceptors";
+}
diff --git a/fluent-hc/src/main/java/org/apache/http/client/fluent/FluentHttpMethod.java b/fluent-hc/src/main/java/org/apache/http/client/fluent/FluentHttpMethod.java
new file mode 100644
index 000000000..6534a28a9
--- /dev/null
+++ b/fluent-hc/src/main/java/org/apache/http/client/fluent/FluentHttpMethod.java
@@ -0,0 +1,26 @@
+/*
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ====================================================================
+ */
+
+package org.apache.http.client.fluent;
+
+public enum FluentHttpMethod {
+ GET_METHOD, POST_METHOD, DELETE_METHOD, OPTIONS_METHOD, PUT_METHOD, HEAD_METHOD, TRACE_METHOD;
+}
diff --git a/fluent-hc/src/main/java/org/apache/http/client/fluent/FluentRequest.java b/fluent-hc/src/main/java/org/apache/http/client/fluent/FluentRequest.java
new file mode 100644
index 000000000..531bc23b5
--- /dev/null
+++ b/fluent-hc/src/main/java/org/apache/http/client/fluent/FluentRequest.java
@@ -0,0 +1,571 @@
+/*
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ====================================================================
+ */
+
+package org.apache.http.client.fluent;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.Header;
+import org.apache.http.HeaderIterator;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpVersion;
+import org.apache.http.NameValuePair;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.RequestLine;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.Credentials;
+import org.apache.http.auth.NTCredentials;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.AuthCache;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.fluent.header.DateUtils;
+import org.apache.http.client.fluent.header.HttpHeader;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpHead;
+import org.apache.http.client.methods.HttpOptions;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.client.methods.HttpTrace;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.client.protocol.ClientContext;
+import org.apache.http.conn.params.ConnRoutePNames;
+import org.apache.http.impl.auth.BasicScheme;
+import org.apache.http.impl.client.BasicAuthCache;
+import org.apache.http.impl.client.BasicCredentialsProvider;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.params.CoreProtocolPNames;
+import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.HttpContext;
+
+public class FluentRequest implements HttpUriRequest {
+ static FluentRequest build(final URI uri, final FluentHttpMethod method) {
+ FluentRequest req = new FluentRequest();
+ req.by(method, uri);
+ req.init();
+ return req;
+ }
+
+ HttpParams localParams;
+ HttpContext localContext;
+ CredentialsProvider credentialsProvider;
+ private HttpUriRequest request;
+ private FluentHttpMethod method;
+ private HttpHost localProxy;
+ protected static final Log log = LogFactory.getLog(FluentRequest.class);
+
+ private FluentRequest() {
+ // DO NOTHING
+ }
+
+ public FluentRequest(final HttpUriRequest req) {
+ this.request = req;
+ String methodName = request.getMethod().toUpperCase();
+ if (methodName.equals("GET"))
+ this.method = FluentHttpMethod.GET_METHOD;
+ else if (methodName.equals("POST"))
+ this.method = FluentHttpMethod.POST_METHOD;
+ else if (methodName.equals("OPTIONS"))
+ this.method = FluentHttpMethod.OPTIONS_METHOD;
+ else if (methodName.equals("DELETE"))
+ this.method = FluentHttpMethod.DELETE_METHOD;
+ else if (methodName.equals("HEAD"))
+ this.method = FluentHttpMethod.HEAD_METHOD;
+ else if (methodName.equals("PUT"))
+ this.method = FluentHttpMethod.PUT_METHOD;
+ else if (methodName.equals("TRACE"))
+ this.method = FluentHttpMethod.TRACE_METHOD;
+ else
+ this.method = FluentHttpMethod.GET_METHOD;
+ init();
+ }
+
+ public FluentRequest(final String uri) {
+ copyFrom(RequestBuilder.build(uri));
+ }
+
+ public FluentRequest(final String uri, final FluentHttpMethod method) {
+ copyFrom(RequestBuilder.build(uri, method));
+ }
+
+ public FluentRequest(final URI uri) {
+ copyFrom(RequestBuilder.build(uri));
+ }
+
+ public FluentRequest(final URI uri, final FluentHttpMethod method) {
+ copyFrom(RequestBuilder.build(uri, method));
+ }
+
+ public void abort() throws UnsupportedOperationException {
+ this.request.abort();
+ }
+
+
+ public void addHeader(final Header header) {
+ this.request.addHeader(header);
+ }
+
+
+ public void addHeader(final String name, final String value) {
+ this.request.addHeader(name, value);
+ }
+
+ /**
+ * Change the HTTP method used within this request.
+ *
+ * @param method
+ * which indicates the HTTP method need to use
+ * @return modified request
+ */
+ private FluentRequest by(final FluentHttpMethod method, final URI uri) {
+ switch (method) {
+ case GET_METHOD:
+ this.request = new HttpGet(uri);
+ break;
+ case POST_METHOD:
+ this.request = new HttpPost(uri);
+ break;
+ case OPTIONS_METHOD:
+ this.request = new HttpOptions(uri);
+ break;
+ case DELETE_METHOD:
+ this.request = new HttpDelete(uri);
+ break;
+ case HEAD_METHOD:
+ this.request = new HttpHead(uri);
+ break;
+ case PUT_METHOD:
+ this.request = new HttpPut(uri);
+ break;
+ case TRACE_METHOD:
+ this.request = new HttpTrace(uri);
+ break;
+ }
+ this.method = method;
+ return this;
+ }
+
+
+ public boolean containsHeader(final String name) {
+ return this.request.containsHeader(name);
+ }
+
+ private void copyFrom(FluentRequest other) {
+ this.request = other.request;
+ this.method = other.method;
+ this.localContext = other.localContext;
+ this.localParams = other.localParams;
+ this.localProxy = other.localProxy;
+ this.credentialsProvider = other.credentialsProvider;
+ }
+
+ /**
+ *
+ * @return a FluentResponse
instance referring to the response
+ * of this request
+ * @throws ClientProtocolException
+ * @throws IOException
+ */
+ public FluentResponse exec() throws ClientProtocolException, IOException {
+ DefaultHttpClient client = new DefaultHttpClient();
+ return new FluentResponse(client.execute(request));
+ }
+
+
+ public Header[] getAllHeaders() {
+ return this.request.getAllHeaders();
+ }
+
+ public String getCacheControl() {
+ return getValueOfHeader(HttpHeader.CACHE_CONTROL);
+ }
+
+ public int getConnectionTimeout() {
+ return HttpConnectionParams.getConnectionTimeout(localParams);
+ }
+
+ public String getContentCharset() {
+ return (String) localParams
+ .getParameter(CoreProtocolPNames.HTTP_CONTENT_CHARSET);
+ }
+
+ public long getContentLength() {
+ String value = getValueOfHeader(HttpHeader.CONTENT_LENGTH);
+ if (value == null)
+ return -1;
+ else {
+ long contentLength = Long.parseLong(value);
+ return contentLength;
+ }
+ }
+
+ public String getContentType() {
+ return getValueOfHeader(HttpHeader.CONTENT_TYPE);
+ }
+
+ public CredentialsProvider getCredentialProvider() {
+ return credentialsProvider;
+ }
+
+ public String getDate() {
+ return getValueOfHeader(HttpHeader.DATE);
+ }
+
+ public String getElementCharset() {
+ return (String) localParams
+ .getParameter(CoreProtocolPNames.HTTP_ELEMENT_CHARSET);
+ }
+
+
+ public Header getFirstHeader(final String name) {
+ return this.request.getFirstHeader(name);
+ }
+
+
+ public Header[] getHeaders(final String name) {
+ return this.request.getHeaders(name);
+ }
+
+ /**
+ * Returns the HTTP method as a field of FunHttpMethod
enumeration, such as
+ * GET
, PUT
, POST
, or other.
+ *
+ * @return a field of FunHttpMethod
+ * enumeration indicates the name of HTTP method
+ */
+ public FluentHttpMethod getHttpMethod() {
+ return method;
+ }
+
+ public String getIfModifiedSince() {
+ return getValueOfHeader(HttpHeader.IF_MODIFIED_SINCE);
+ }
+
+ public String getIfUnmodifiedSince() {
+ return getValueOfHeader(HttpHeader.IF_UNMODIFIED_SINCE);
+ }
+
+
+ public Header getLastHeader(final String name) {
+ return this.request.getLastHeader(name);
+ }
+
+
+ public String getMethod() {
+ return this.request.getMethod();
+ }
+
+
+ public HttpParams getParams() {
+ return this.request.getParams();
+ }
+
+
+ public ProtocolVersion getProtocolVersion() {
+ return this.request.getProtocolVersion();
+ }
+
+
+ public RequestLine getRequestLine() {
+ return this.request.getRequestLine();
+ }
+
+ public int getSocketTimeout() {
+ return HttpConnectionParams.getSoTimeout(localParams);
+ }
+
+ public boolean isStrictTransferEncoding() {
+ return (Boolean) localParams
+ .getParameter(CoreProtocolPNames.STRICT_TRANSFER_ENCODING);
+ }
+
+
+ public URI getURI() {
+ return this.request.getURI();
+ }
+
+ public boolean isUseExpectContinue() {
+ return (Boolean) localParams
+ .getParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE);
+ }
+
+ public String getUserAgent() {
+ return (String) localParams.getParameter(CoreProtocolPNames.USER_AGENT);
+ }
+
+ private String getValueOfHeader(final String headerName) {
+ Header header = request.getFirstHeader(headerName);
+ if (header != null)
+ return header.getValue();
+ else
+ return null;
+ }
+
+ public int getWaitForContinue() {
+ return (Integer) localParams
+ .getParameter(CoreProtocolPNames.WAIT_FOR_CONTINUE);
+ }
+
+
+ public HeaderIterator headerIterator() {
+ return this.request.headerIterator();
+ }
+
+
+ public HeaderIterator headerIterator(final String name) {
+ return this.request.headerIterator(name);
+ }
+
+ private void init() {
+ localParams = request.getParams();
+ localContext = new BasicHttpContext();
+ credentialsProvider = new BasicCredentialsProvider();
+ localProxy = null;
+ }
+
+
+ public boolean isAborted() {
+ return this.request.isAborted();
+ }
+
+ public FluentRequest removeAuth() {
+ return setAuth(null);
+ }
+
+
+ public void removeHeader(final Header header) {
+ this.request.removeHeader(header);
+ }
+
+
+ public void removeHeaders(final String name) {
+ this.request.removeHeaders(name);
+ }
+
+ public FluentRequest removeProxy() {
+ setProxyAuth(null);
+ localParams.removeParameter(ConnRoutePNames.DEFAULT_PROXY);
+ localProxy = null;
+ return this;
+ }
+
+ public FluentRequest setAuth(final Credentials cred) {
+ String hostAddr = request.getURI().getHost();
+ credentialsProvider.setCredentials(new AuthScope(hostAddr,
+ AuthScope.ANY_PORT), cred);
+ AuthCache authCache = new BasicAuthCache();
+ HttpHost authHost = new HttpHost(hostAddr);
+ authCache.put(authHost, new BasicScheme());
+ localContext.setAttribute(ClientContext.AUTH_CACHE, authCache);
+ return this;
+ }
+
+ public FluentRequest setAuth(final String username, final String password) {
+ return setAuth(new UsernamePasswordCredentials(username, password));
+ }
+
+ public FluentRequest setAuth(final String username, final String password,
+ final String workstation, final String domain) {
+ return setAuth(new NTCredentials(username, password, workstation,
+ domain));
+ }
+
+ public FluentRequest setCacheControl(String cacheControl) {
+ request.setHeader(HttpHeader.CACHE_CONTROL, cacheControl);
+ return this;
+ }
+
+ public FluentRequest setConnectionTimeout(final int connectionTimeoutMillis) {
+ HttpConnectionParams.setConnectionTimeout(localParams,
+ connectionTimeoutMillis);
+ return this;
+ }
+
+ public FluentRequest setContentCharset(final String charset) {
+ localParams.setParameter(CoreProtocolPNames.HTTP_CONTENT_CHARSET,
+ charset);
+ return this;
+ }
+
+ public FluentRequest setContentLength(final long contentLength) {
+ request.setHeader(HttpHeader.CONTENT_LENGTH,
+ String.valueOf(contentLength));
+ return this;
+ }
+
+ public FluentRequest setContentType(final String contentType) {
+ request.setHeader(HttpHeader.CONTENT_TYPE, contentType);
+ return this;
+ }
+
+ public FluentRequest setCredentialProvider(
+ final CredentialsProvider credProvider) {
+ credentialsProvider = credProvider;
+ return this;
+ }
+
+ public FluentRequest setDate(final Date date) {
+ String formattedDate = DateUtils.format(date);
+ request.setHeader(HttpHeader.DATE, formattedDate);
+ return this;
+ }
+
+ public FluentRequest setElementCharset(final String charset) {
+ localParams.setParameter(CoreProtocolPNames.HTTP_ELEMENT_CHARSET,
+ charset);
+ return this;
+ }
+
+ public FluentRequest setEntity(final HttpEntity entity) {
+ log.warn("");
+ this.by(FluentHttpMethod.POST_METHOD, this.request.getURI());
+ HttpPost post = (HttpPost) this.request;
+ post.setEntity(entity);
+ return this;
+ }
+
+ public FluentRequest setHTMLFormEntity(final Map form,
+ final String encoding) throws UnsupportedEncodingException {
+ List formparams = new ArrayList(
+ form.size());
+ for (String name : form.keySet()) {
+ formparams.add(new BasicNameValuePair(name, form.get("name")));
+ }
+ UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams,
+ encoding);
+ return setEntity(entity);
+ }
+
+
+ public void setHeader(final Header header) {
+ this.request.setHeader(header);
+ }
+
+
+ public void setHeader(final String name, final String value) {
+ this.request.setHeader(name, value);
+ }
+
+
+ public void setHeaders(final Header[] headers) {
+ this.request.setHeaders(headers);
+ }
+
+ public FluentRequest setIfModifiedSince(final Date date) {
+ String formattedDate = DateUtils.format(date);
+ request.setHeader(HttpHeader.IF_MODIFIED_SINCE, formattedDate);
+ return this;
+ }
+
+ public FluentRequest setIfUnmodifiedSince(final Date date) {
+ String formattedDate = DateUtils.format(date);
+ request.setHeader(HttpHeader.IF_UNMODIFIED_SINCE, formattedDate);
+ return this;
+ }
+
+
+ public void setParams(final HttpParams params) {
+ this.request.setParams(params);
+ }
+
+ public FluentRequest setProtocolVersion(HttpVersion version) {
+ localParams.setParameter(CoreProtocolPNames.PROTOCOL_VERSION, version);
+ return this;
+ }
+
+ public FluentRequest setProxy(final String proxyAddr, final int proxyPort) {
+ return setProxy(proxyAddr, proxyPort, null, null);
+ }
+
+ public FluentRequest setProxy(final String proxyAddr, final int proxyPort,
+ final String username, final String password) {
+ localProxy = new HttpHost(proxyAddr, proxyPort);
+ localParams.setParameter(ConnRoutePNames.DEFAULT_PROXY, localProxy);
+ if (username != null) {
+ setProxyAuth(username, password);
+ }
+ return this;
+ }
+
+ public FluentRequest setProxyAuth(final Credentials proxyAuth) {
+ if (localProxy == null)
+ throw new IllegalStateException("HTTP proxy is not used.");
+ credentialsProvider.setCredentials(
+ new AuthScope(localProxy.getHostName(), localProxy.getPort()),
+ proxyAuth);
+ return this;
+ }
+
+ public FluentRequest setProxyAuth(final String username,
+ final String password) {
+ return setProxyAuth(new UsernamePasswordCredentials(username, password));
+ }
+
+ public FluentRequest setProxyAuth(final String username,
+ final String password, final String workstation, final String domain) {
+ return setProxyAuth(new NTCredentials(username, password, workstation,
+ domain));
+ }
+
+ public FluentRequest setSocketTimeout(int socketTimeoutMillis) {
+ HttpConnectionParams.setSoTimeout(localParams, socketTimeoutMillis);
+ return this;
+ }
+
+ public FluentRequest setStrictTransferEncoding(final boolean bool) {
+ localParams.setBooleanParameter(
+ CoreProtocolPNames.STRICT_TRANSFER_ENCODING, bool);
+ return this;
+ }
+
+ public FluentRequest setUseExpectContinue(Boolean bool) {
+ localParams.setBooleanParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE,
+ bool);
+ return this;
+ }
+
+ public FluentRequest setUserAgent(final String agent) {
+ localParams.setParameter(CoreProtocolPNames.USER_AGENT, agent);
+ return this;
+ }
+
+ public FluentRequest setWaitForContinue(final int waitMillis) {
+ localParams.setIntParameter(CoreProtocolPNames.WAIT_FOR_CONTINUE,
+ waitMillis);
+ return this;
+ }
+
+}
diff --git a/fluent-hc/src/main/java/org/apache/http/client/fluent/FluentResponse.java b/fluent-hc/src/main/java/org/apache/http/client/fluent/FluentResponse.java
new file mode 100644
index 000000000..23fbf544d
--- /dev/null
+++ b/fluent-hc/src/main/java/org/apache/http/client/fluent/FluentResponse.java
@@ -0,0 +1,322 @@
+/*
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ====================================================================
+ */
+
+package org.apache.http.client.fluent;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Locale;
+import java.util.Scanner;
+import java.util.regex.Pattern;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.Header;
+import org.apache.http.HeaderIterator;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.StatusLine;
+import org.apache.http.client.fluent.header.HttpHeader;
+import org.apache.http.params.HttpParams;
+import org.apache.http.util.EntityUtils;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+public class FluentResponse implements HttpResponse {
+ protected static final Log log = LogFactory.getLog(FluentResponse.class);
+ private HttpResponse response;
+ private byte[] content;
+ private String contentString;
+ private boolean consumed;
+
+ FluentResponse(HttpResponse response) {
+ this.response = response;
+ consumed = false;
+ }
+
+ public int getStatusCode() {
+ return this.getStatusLine().getStatusCode();
+ }
+
+ public FluentResponse loadContent() throws IOException {
+ if (getEntity() == null)
+ content = null;
+ else {
+ content = EntityUtils.toByteArray(getEntity());
+ EntityUtils.consume(getEntity());
+ }
+ consumed = true;
+ return this;
+ }
+
+ public void addHeader(Header header) {
+ this.response.addHeader(header);
+ }
+
+ public void addHeader(String name, String value) {
+ this.response.addHeader(name, value);
+ }
+
+ public FluentResponse cacheControl(String cacheControl) {
+ response.setHeader(HttpHeader.CACHE_CONTROL, cacheControl);
+ return this;
+ }
+
+ public boolean containsHeader(String name) {
+ return this.response.containsHeader(name);
+ }
+
+ public Header[] getAllHeaders() {
+ return this.response.getAllHeaders();
+ }
+
+ public String getCacheControl() {
+ return getValueOfHeader(HttpHeader.CACHE_CONTROL);
+ }
+
+ public InputStream getContent() throws IllegalStateException, IOException {
+ return this.response.getEntity().getContent();
+ }
+
+ public byte[] getContentByteArray() throws IllegalStateException,
+ IOException {
+ if (!consumed)
+ loadContent();
+ return content;
+ }
+
+ public String getContentCharset() {
+ // if (this.getEntity() == null)
+ // throw new IllegalStateException("Response does not contain data");
+ // Header contentType = this.getEntity().getContentType();
+ // if (contentType == null)
+ // throw new IllegalStateException(
+ // "Reponse does not contain Content-Type header");
+ // NameValuePair charset = contentType.getElements()[0]
+ // .getParameterByName("charset");
+ // if (charset == null || charset.getValue().trim().equals("")) {
+ // log.warn("Charset could not be found in response");
+ // return Charset.defaultCharset().name();
+ // } else
+ // return charset.getValue();
+ return EntityUtils.getContentCharSet(getEntity());
+ }
+
+ public String getContentEncoding() {
+ if (this.getEntity() == null)
+ throw new IllegalStateException("Response does not contain data");
+ Header contentEncoding = this.getEntity().getContentEncoding();
+ if (contentEncoding == null) {
+ log.warn("Response does not contain Content-Encoding header");
+ return System.getProperty("file.encoding");
+ } else
+ return contentEncoding.getValue();
+ }
+
+ public long getContentLength() {
+ String value = getValueOfHeader(HttpHeader.CONTENT_LENGTH);
+ if (value == null)
+ return -1;
+ else {
+ long contentLength = Long.parseLong(value);
+ return contentLength;
+ }
+ }
+
+ public String getContentString() throws IOException {
+ if (contentString != null)
+ return contentString;
+ if (this.getEntity() == null)
+ return null;
+ String contentCharset = this.getContentCharset();
+ return getContentString(contentCharset);
+ }
+
+ public String getContentString(String encoding) throws IOException {
+ if (contentString != null)
+ return contentString;
+ if (getContentByteArray() == null)
+ return null;
+ if (encoding == null)
+ contentString = new String(content);
+ else
+ contentString = new String(content, encoding);
+ return contentString;
+ }
+
+ public String getContentType() {
+ if (this.getEntity() == null)
+ throw new IllegalStateException("Response does not contain data");
+ Header contentType = this.getEntity().getContentType();
+ if (contentType == null)
+ throw new IllegalStateException(
+ "Reponse does not contain Content-Type header");
+ return contentType.getElements()[0].getName();
+ }
+
+ public HttpEntity getEntity() {
+ return this.response.getEntity();
+ }
+
+ public Header getFirstHeader(String name) {
+ return this.response.getFirstHeader(name);
+ }
+
+ public Header[] getHeaders(String name) {
+ return this.response.getHeaders(name);
+ }
+
+ public Header getLastHeader(String name) {
+ return this.response.getLastHeader(name);
+ }
+
+ public Locale getLocale() {
+ return this.response.getLocale();
+ }
+
+ public HttpParams getParams() {
+ return this.response.getParams();
+ }
+
+ public ProtocolVersion getProtocolVersion() {
+ return this.response.getProtocolVersion();
+ }
+
+ public StatusLine getStatusLine() {
+ return this.response.getStatusLine();
+ }
+
+ private String getValueOfHeader(String headerName) {
+ Header header = response.getFirstHeader(headerName);
+ if (header != null)
+ return header.getValue();
+ else
+ return null;
+ }
+
+ public HeaderIterator headerIterator() {
+ return this.response.headerIterator();
+ }
+
+ public HeaderIterator headerIterator(String name) {
+ return this.response.headerIterator(name);
+ }
+
+ public void removeHeader(Header header) {
+ this.response.removeHeader(header);
+ }
+
+ public void removeHeaders(String name) {
+ this.response.removeHeaders(name);
+ }
+
+ public void setEntity(HttpEntity entity) {
+ this.response.setEntity(entity);
+ }
+
+ public void setHeader(Header header) {
+ this.response.setHeader(header);
+ }
+
+ public void setHeader(String name, String value) {
+ this.response.setHeader(name, value);
+ }
+
+ public void setHeaders(Header[] headers) {
+ this.response.setHeaders(headers);
+ }
+
+ public void setLocale(Locale loc) {
+ this.response.setLocale(loc);
+ }
+
+ public void setParams(HttpParams params) {
+ this.response.setParams(params);
+ }
+
+ public void setReasonPhrase(String reason) throws IllegalStateException {
+ this.response.setReasonPhrase(reason);
+ }
+
+ public void setStatusCode(int code) throws IllegalStateException {
+ this.response.setStatusCode(code);
+ }
+
+ public void setStatusLine(ProtocolVersion ver, int code) {
+ this.response.setStatusLine(ver, code);
+ }
+
+ public void setStatusLine(ProtocolVersion ver, int code, String reason) {
+ this.response.setStatusLine(ver, code, reason);
+ }
+
+ public void setStatusLine(StatusLine statusline) {
+ this.response.setStatusLine(statusline);
+ }
+
+ public FluentResponse assertStatus(int expected) {
+ assertNotNull(this.getStatusLine().toString(), this.getStatusLine());
+ int actual = this.getStatusCode();
+ assertEquals(this + ": expecting status " + expected, expected, actual);
+ return this;
+ }
+
+ public FluentResponse assertContentType(String expected) {
+ try {
+ String actual = this.getContentType();
+ assertEquals(this + ": expecting content type " + expected,
+ expected, actual);
+ } catch (Exception e) {
+ fail(this + ": " + e.getMessage());
+ }
+ return this;
+ }
+
+ public FluentResponse assertContentRegexp(String encoding, String... regexp) {
+ try {
+ String content = encoding == null ? getContentString()
+ : getContentString(encoding);
+ assertNotNull(this.toString(), content);
+ nextPattern: for (String expr : regexp) {
+ final Pattern p = Pattern.compile(".*" + expr + ".*");
+ final Scanner scan = new Scanner(content);
+ while (scan.hasNext()) {
+ final String line = scan.nextLine();
+ if (p.matcher(line).matches()) {
+ continue nextPattern;
+ }
+ }
+ fail(this + ": no match for regexp '" + expr + "', content=\n"
+ + content);
+ }
+ } catch (IOException e) {
+ fail(this + ": " + e.getMessage());
+ }
+ return this;
+ }
+
+ public FluentResponse assertContentRegexp(String... regexp) {
+ return assertContentRegexp(null, regexp);
+ }
+}
diff --git a/fluent-hc/src/main/java/org/apache/http/client/fluent/RequestBuilder.java b/fluent-hc/src/main/java/org/apache/http/client/fluent/RequestBuilder.java
new file mode 100644
index 000000000..fbb3185cf
--- /dev/null
+++ b/fluent-hc/src/main/java/org/apache/http/client/fluent/RequestBuilder.java
@@ -0,0 +1,153 @@
+/*
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ====================================================================
+ */
+
+package org.apache.http.client.fluent;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.apache.http.HttpEntity;
+
+public class RequestBuilder {
+
+ private FluentHttpMethod method;
+ private URI uri;
+ private HttpEntity entity;
+
+ /**
+ * Build an instance of
+ * FunRequest
+ *
+ * @param uri
+ * the URI of the request
+ * @return an instance of
+ * FunRequest
+ * @throws IllegalArgumentException
+ * if the uri is invalid.
+ */
+ @Deprecated
+ public static FluentRequest request(final String uri) {
+ URI uriObj;
+ try {
+ uriObj = new URI(uri);
+ return request(uriObj);
+ } catch (URISyntaxException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /**
+ * Build an instance of
+ * FunRequest
+ *
+ * @param uri
+ * the URI of the request
+ * @return an instance of
+ * FunRequest
+ */
+ @Deprecated
+ public static FluentRequest request(final URI uri) {
+ return FluentRequest.build(uri, FluentHttpMethod.GET_METHOD);
+ }
+
+ public RequestBuilder() {
+ method = FluentHttpMethod.GET_METHOD;
+ }
+
+ public FluentRequest build() {
+ if (uri != null) {
+ FluentRequest req = FluentRequest.build(uri, method);
+ if (entity != null)
+ req.setEntity(entity);
+ return req;
+ } else
+ throw new IllegalStateException(
+ "too less information provided to build FluentRequest");
+ }
+
+ public static FluentRequest build(final String uri) {
+ return build(uri, FluentHttpMethod.GET_METHOD);
+ }
+
+ public static FluentRequest build(final String uri,
+ final FluentHttpMethod method) {
+ try {
+ URI uriObj;
+ uriObj = new URI(uri);
+ return build(uriObj, method);
+ } catch (URISyntaxException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ public static FluentRequest build(final URI uri) {
+ return build(uri, FluentHttpMethod.GET_METHOD);
+ }
+
+ public static FluentRequest build(final URI uri,
+ final FluentHttpMethod method) {
+ return FluentRequest.build(uri, method);
+ }
+
+ public RequestBuilder by(final FluentHttpMethod method) {
+ this.method = method;
+ return this;
+ }
+
+ public RequestBuilder req(final String uri) throws URISyntaxException {
+ URI uriObj = new URI(uri);
+ req(uriObj);
+ return this;
+ }
+
+ public RequestBuilder req(final URI uri) {
+ this.uri = uri;
+ return this;
+ }
+
+ public RequestBuilder with(final HttpEntity entity) {
+ this.entity = entity;
+ return this;
+ }
+
+ public RequestBuilder removeEntity() {
+ this.entity = null;
+ return this;
+ }
+
+ public RequestBuilder set(final Object obj) throws IllegalArgumentException {
+ try {
+ if (obj instanceof String)
+ return this.req((String) obj);
+ if (obj instanceof URI)
+ return this.req((URI) obj);
+ if (obj instanceof FluentHttpMethod)
+ return this.by((FluentHttpMethod) obj);
+ if (obj instanceof HttpEntity)
+ return this.with((HttpEntity) obj);
+ } catch (URISyntaxException e) {
+ throw new IllegalArgumentException(obj.toString()
+ + " is an illegal URI value");
+ }
+ throw new IllegalArgumentException(obj.toString()
+ + " is an illegal parameter");
+ }
+}
diff --git a/fluent-hc/src/main/java/org/apache/http/client/fluent/header/CacheControl.java b/fluent-hc/src/main/java/org/apache/http/client/fluent/header/CacheControl.java
new file mode 100644
index 000000000..c1aac8e68
--- /dev/null
+++ b/fluent-hc/src/main/java/org/apache/http/client/fluent/header/CacheControl.java
@@ -0,0 +1,61 @@
+/*
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ====================================================================
+ */
+
+package org.apache.http.client.fluent.header;
+
+public class CacheControl {
+
+ public static final String NAME = "Cache-Control";
+
+ /*
+ * cache-request-directive
+ */
+ public static final String NO_CACHE = "no-cache";
+ public static final String NO_STORE = "no-store";
+ public static final String MAX_STALE = "max-stale";
+ public static final String NO_TRANSFORM = "no-transform";
+ public static final String ONLY_IF_CACHED = "only-if-cached";
+
+ public static String MAX_AGE(final long deltaSeconds) {
+ return "max-age=" + deltaSeconds;
+ }
+
+ public static String MAX_STALE(final long deltaSeconds) {
+ return MAX_STALE + "=" + deltaSeconds;
+ }
+
+ public static String MIN_FRESH(final long deltaSeconds) {
+ return "min-fresh=" + deltaSeconds;
+ }
+
+ /*
+ * cache-response-directive
+ */
+ public static final String PUBLIC = "public";
+ public static final String PRIVATE = "private";
+ public static final String MUST_REVALIDATE = "must-revalidate";
+ public static final String PROXY_REVALIDATE = "proxy-revalidate";
+
+ public static String S_MAXAGE(final long deltaSeconds) {
+ return "s-maxage=" + deltaSeconds;
+ }
+
+}
diff --git a/fluent-hc/src/main/java/org/apache/http/client/fluent/header/ContentType.java b/fluent-hc/src/main/java/org/apache/http/client/fluent/header/ContentType.java
new file mode 100644
index 000000000..208e83233
--- /dev/null
+++ b/fluent-hc/src/main/java/org/apache/http/client/fluent/header/ContentType.java
@@ -0,0 +1,33 @@
+/*
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ====================================================================
+ */
+
+package org.apache.http.client.fluent.header;
+
+public class ContentType {
+ public static final String NAME = "Content-Type";
+ public static final String HTML = "text/html";
+ public static final String JPEG = "image/jpeg";
+ public static final String PNG = "image/png";
+ public static final String GIF = "image/gif";
+ public static final String TEXT = "text/plain";
+ public static final String XML = "application/xml";
+ public static final String JSON = "application/json";
+}
diff --git a/fluent-hc/src/main/java/org/apache/http/client/fluent/header/DateUtils.java b/fluent-hc/src/main/java/org/apache/http/client/fluent/header/DateUtils.java
new file mode 100644
index 000000000..c5a3e37a4
--- /dev/null
+++ b/fluent-hc/src/main/java/org/apache/http/client/fluent/header/DateUtils.java
@@ -0,0 +1,44 @@
+/*
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ====================================================================
+ */
+
+package org.apache.http.client.fluent.header;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
+public class DateUtils {
+ public static final String DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz";
+ public static final Locale DATE_LOCALE = Locale.US;
+ public static final TimeZone TIME_ZONE = TimeZone.getTimeZone("GMT");
+
+ public static String format(Date d) {
+ SimpleDateFormat format = new SimpleDateFormat(DATE_FORMAT, DATE_LOCALE);
+ format.setTimeZone(TIME_ZONE);
+ return format.format(d);
+ }
+
+ public static String currentTime() {
+ Date d = new Date();
+ return format(d);
+ }
+}
diff --git a/fluent-hc/src/main/java/org/apache/http/client/fluent/header/HttpHeader.java b/fluent-hc/src/main/java/org/apache/http/client/fluent/header/HttpHeader.java
new file mode 100644
index 000000000..7199ea551
--- /dev/null
+++ b/fluent-hc/src/main/java/org/apache/http/client/fluent/header/HttpHeader.java
@@ -0,0 +1,31 @@
+/*
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ====================================================================
+ */
+
+package org.apache.http.client.fluent.header;
+
+public class HttpHeader {
+ public static final String CONTENT_LENGTH = "Content-Length";
+ public static final String DATE = "Date";
+ public static final String CACHE_CONTROL = CacheControl.NAME;
+ public static final String CONTENT_TYPE = ContentType.NAME;
+ public static final String IF_MODIFIED_SINCE = "If-Modified-Since";
+ public static final String IF_UNMODIFIED_SINCE = "If-Unmodified-Since";
+}
diff --git a/httpclient/src/main/java/org/apache/http/client/utils/UriBuilder.java b/httpclient/src/main/java/org/apache/http/client/utils/UriBuilder.java
new file mode 100644
index 000000000..2e3920569
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/client/utils/UriBuilder.java
@@ -0,0 +1,361 @@
+/*
+ * ====================================================================
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ====================================================================
+ */
+
+package org.apache.http.client.utils;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.nio.charset.Charset;
+import java.util.Map;
+import java.util.Set;
+
+public class UriBuilder {
+ private String scheme;
+ private String schemeSpecificPart;
+ private String authority;
+ private String userInfo;
+ private String host;
+ private int port;
+ private String path;
+ private String query;
+ private String fragment;
+ private URI uri;
+ private String enc;
+ private boolean encOn;
+ private static Set supportedEncoding;
+
+ public UriBuilder() {
+ init();
+ }
+
+ public UriBuilder(String string) throws URISyntaxException {
+ init();
+ URI uri = new URI(string);
+ from(uri);
+ }
+
+ public UriBuilder(URI uri) {
+ this.init();
+ from(uri);
+ }
+
+ public UriBuilder addParameter(String param, Object value)
+ throws URISyntaxException {
+ return this.addParameter(param, value.toString());
+ }
+
+ /**
+ * add a parameter-value pair into URI query
+ *
+ * @param param
+ * @param value
+ * @throws URISyntaxException
+ */
+ public UriBuilder addParameter(String param, String value)
+ throws URISyntaxException {
+ StringBuffer sb = this.query == null ? new StringBuffer()
+ : new StringBuffer(this.query);
+ if (sb.length() > 0 && sb.charAt(sb.length() - 1) != '&')
+ sb.append('&');
+ sb.append(encode(param)).append('=').append(encode(value));
+ return setQuery(sb.toString());
+ }
+
+ /**
+ * build a URI instance from pre-provided information
+ *
+ * @throws RuntimeException
+ */
+ public URI build() throws RuntimeException {
+ if (uri != null)
+ return uri;
+ else
+ throw new RuntimeException(
+ "too less information provided to build URI");
+ }
+
+ private void digestURI(URI uri, boolean raw) {
+ scheme = uri.getScheme();
+ host = uri.getHost();
+ port = uri.getPort();
+ if (raw) {
+ schemeSpecificPart = uri.getRawSchemeSpecificPart();
+ authority = uri.getRawAuthority();
+ userInfo = uri.getRawUserInfo();
+ path = uri.getRawPath();
+ query = uri.getRawQuery();
+ fragment = uri.getRawFragment();
+ } else {
+ schemeSpecificPart = uri.getSchemeSpecificPart();
+ authority = uri.getAuthority();
+ userInfo = uri.getUserInfo();
+ path = uri.getPath();
+ query = uri.getQuery();
+ fragment = uri.getFragment();
+ }
+ }
+
+ public UriBuilder encodingOff() {
+ this.encOn = false;
+ return this;
+ }
+
+ public UriBuilder encodingOn() {
+ try {
+ encodingOn(enc);
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ }
+ return this;
+ }
+
+ public UriBuilder encodingOn(String enc)
+ throws UnsupportedEncodingException {
+ if (enc == null)
+ throw new IllegalArgumentException(
+ "Encoding scheme cannot be null.");
+
+ // check encoding is supported
+ if (!supportedEncoding.contains(enc))
+ throw new UnsupportedEncodingException();
+ this.enc = enc;
+ this.encOn = true;
+ return this;
+ }
+
+ /**
+ * copy the uri into builder
+ *
+ * @param uri
+ * String value of a URI instance
+ * @throws URISyntaxException
+ * if uri is invalid
+ */
+ public UriBuilder from(final String uri) throws URISyntaxException {
+ URI u = new URI(uri);
+ from(u);
+ return this;
+ }
+
+ /**
+ * copy uri into builder
+ *
+ * @param uri
+ * the URI source to clone
+ */
+ public UriBuilder from(final URI uri) {
+ digestURI(uri, false);
+ this.uri = uri;
+ return this;
+ }
+
+ private void init() {
+ port = -1;
+ encOn = false;
+ enc = "UTF-8";
+ supportedEncoding = Charset.availableCharsets().keySet();
+ }
+
+ /**
+ * set URI fragment
+ *
+ * @param fragment
+ * @throws URISyntaxException
+ */
+ public UriBuilder setFragment(final String fragment)
+ throws URISyntaxException {
+ this.fragment = encode(fragment);
+ update();
+ return this;
+ }
+
+ private String encode(String string) {
+
+ try {
+ if (encOn) {
+ String encodedString = URLEncoder.encode(string, enc);
+ return encodedString;
+ } else {
+ return URLDecoder.decode(string, enc);
+ }
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ }
+ return string;
+ }
+
+ /**
+ * set URI host
+ *
+ * @param host
+ * @throws URISyntaxException
+ * if uri is invalid
+ */
+ public UriBuilder setHost(final String host) throws URISyntaxException {
+ this.host = encode(host);
+ update();
+ return this;
+ }
+
+ /**
+ * set URI path
+ *
+ * @param path
+ * a String represent the path of a URI, e.g., "/path"
+ * @throws URISyntaxException
+ */
+ public UriBuilder setPath(final String path) throws URISyntaxException {
+ this.path = encode(path);
+ update();
+ return this;
+ }
+
+ /**
+ * set URI port
+ *
+ * @param port
+ * @throws URISyntaxException
+ */
+ public UriBuilder setPort(final int port) throws URISyntaxException {
+ this.port = port < 0 ? -1 : port;
+ update();
+ return this;
+ }
+
+ /**
+ * set URI query by parameter-value pairs
+ *
+ * @param paramMap
+ * @throws URISyntaxException
+ */
+ public UriBuilder setQuery(final Map paramMap)
+ throws URISyntaxException {
+ StringBuffer sb = new StringBuffer();
+ for (String key : paramMap.keySet())
+ sb.append(encode(key)).append('=')
+ .append(encode(paramMap.get(key))).append('&');
+ if (sb.charAt(sb.length() - 1) == '&')
+ sb.deleteCharAt(sb.length() - 1);
+ return setQuery(sb.toString());
+ }
+
+ /**
+ * set URI query
+ *
+ * @param query
+ * @throws URISyntaxException
+ */
+ public UriBuilder setQuery(final String query) throws URISyntaxException {
+ this.query = query;
+ update();
+ return this;
+ }
+
+ /**
+ * set URI scheme
+ *
+ * @param scheme
+ * @throws URISyntaxException
+ * if uri is invalid
+ */
+ public UriBuilder setScheme(final String scheme) throws URISyntaxException {
+ this.scheme = encode(scheme);
+ update();
+ return this;
+ }
+
+ /**
+ * set URI user-info
+ *
+ * @param userInfo
+ * a String represents the user-info, e.g., "username:password"
+ * @throws URISyntaxException
+ */
+ public UriBuilder setUserInfo(final String userInfo)
+ throws URISyntaxException {
+ this.userInfo = userInfo;
+ update();
+ return this;
+ }
+
+ /**
+ * set URI user-info
+ *
+ * @param username
+ * @param password
+ * @throws URISyntaxException
+ */
+ public UriBuilder setUserInfo(final String username, final String password)
+ throws URISyntaxException {
+ return setUserInfo(username + ':' + password);
+ }
+
+ public UriBuilder removeQuery() {
+ this.query = null;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ URI uri = build();
+ sb.append(uri.toString()).append('\n');
+ sb.append("scheme : ").append(scheme).append('\n');
+ sb.append("sspart : ").append(schemeSpecificPart).append('\n');
+ sb.append("authority: ").append(authority).append('\n');
+ sb.append("user-info: ").append(userInfo).append('\n');
+ sb.append("host : ").append(host).append('\n');
+ sb.append("port : ").append(port).append('\n');
+ sb.append("path : ").append(path).append('\n');
+ sb.append("query : ").append(query).append('\n');
+ sb.append("fragment : ").append(fragment);
+ return sb.toString();
+ }
+
+ private void update() throws URISyntaxException {
+ if (scheme != null && host != null)
+ try {
+ uri = new URI(scheme, userInfo, host, port, path, query,
+ fragment);
+
+ // StringBuffer sb = new StringBuffer();
+ // sb.append(scheme).append("://");
+ // if(userInfo != null)
+ // sb.append(userInfo).append("@");
+ // sb.append(host);
+ // if(path != null)
+ // sb.append(path);
+ // if(query != null)
+ // sb.append('?').append(query);
+ // if(fragment != null)
+ // sb.append('#').append(fragment);
+ // uri = new URI(sb.toString());
+ digestURI(uri, false);
+ } catch (URISyntaxException e) {
+ // roll back
+ digestURI(uri, false);
+ throw e;
+ }
+ }
+}