All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.koushikdutta.ion.IonRequestBuilder Maven / Gradle / Ivy

There is a newer version: 3.1.0
Show newest version
package com.koushikdutta.ion;

import android.app.Activity;
import android.app.ActivityManager;
import android.app.ProgressDialog;
import android.app.Service;
import android.content.Context;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.util.Base64;
import android.widget.ImageView;
import android.widget.ProgressBar;

import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
import com.koushikdutta.async.AsyncServer;
import com.koushikdutta.async.DataEmitter;
import com.koushikdutta.async.DataSink;
import com.koushikdutta.async.DataTrackingEmitter;
import com.koushikdutta.async.DataTrackingEmitter.DataTracker;
import com.koushikdutta.async.FilteredDataEmitter;
import com.koushikdutta.async.Util;
import com.koushikdutta.async.callback.CompletedCallback;
import com.koushikdutta.async.future.Future;
import com.koushikdutta.async.future.FutureCallback;
import com.koushikdutta.async.future.SimpleFuture;
import com.koushikdutta.async.future.TransformFuture;
import com.koushikdutta.async.http.AsyncHttpGet;
import com.koushikdutta.async.http.AsyncHttpPost;
import com.koushikdutta.async.http.AsyncHttpRequest;
import com.koushikdutta.async.http.Multimap;
import com.koushikdutta.async.http.body.AsyncHttpRequestBody;
import com.koushikdutta.async.http.body.DocumentBody;
import com.koushikdutta.async.http.body.FileBody;
import com.koushikdutta.async.http.body.FilePart;
import com.koushikdutta.async.http.body.MultipartFormDataBody;
import com.koushikdutta.async.http.body.StreamBody;
import com.koushikdutta.async.http.body.StringBody;
import com.koushikdutta.async.http.body.UrlEncodedFormBody;
import com.koushikdutta.async.http.libcore.RawHeaders;
import com.koushikdutta.async.http.server.AsyncHttpServer;
import com.koushikdutta.async.parser.AsyncParser;
import com.koushikdutta.async.parser.DocumentParser;
import com.koushikdutta.async.parser.StringParser;
import com.koushikdutta.async.stream.FileDataSink;
import com.koushikdutta.async.stream.OutputStreamDataSink;
import com.koushikdutta.ion.Loader.LoaderEmitter;
import com.koushikdutta.ion.builder.Builders;
import com.koushikdutta.ion.builder.FutureBuilder;
import com.koushikdutta.ion.builder.LoadBuilder;
import com.koushikdutta.ion.future.ResponseFuture;
import com.koushikdutta.ion.gson.GsonBody;
import com.koushikdutta.ion.gson.GsonParser;
import com.koushikdutta.ion.gson.GsonSerializer;
import com.koushikdutta.ion.gson.PojoBody;

import org.apache.http.NameValuePair;
import org.w3c.dom.Document;

import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.WeakReference;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * Created by koush on 5/21/13.
 */
class IonRequestBuilder implements Builders.Any.B, Builders.Any.F, Builders.Any.M, Builders.Any.U, LoadBuilder {
    Ion ion;
    WeakReference context;
    Handler handler = Ion.mainHandler;
    String method = AsyncHttpGet.METHOD;
    String uri;

    public IonRequestBuilder(Context context, Ion ion) {
        this.ion = ion;
        this.context = new WeakReference(context);
    }

    @Override
    public IonRequestBuilder load(String url) {
        return loadInternal(AsyncHttpGet.METHOD, url);
    }

    private IonRequestBuilder loadInternal(String method, String url) {
        this.method = method;
        this.uri = url;
        return this;
    }

    boolean methodWasSet;
    @Override
    public IonRequestBuilder load(String method, String url) {
        methodWasSet = true;
        return loadInternal(method, url);
    }

    RawHeaders headers;
    private RawHeaders getHeaders() {
        if (headers == null) {
            headers = new RawHeaders();
            AsyncHttpRequest.setDefaultHeaders(headers, uri == null ? null : URI.create(uri));
        }
        return headers;
    }

    @Override
    public IonRequestBuilder userAgent(String userAgent) {
        return setHeader("User-Agent", userAgent);
    }

    @Override
    public IonRequestBuilder setHeader(String name, String value) {
        getHeaders().set(name, value);
        return this;
    }

    @Override
    public IonRequestBuilder addHeader(String name, String value) {
        getHeaders().add(name, value);
        return this;
    }

    boolean noCache;
    @Override
    public Builders.Any.B noCache() {
        noCache = true;
        return setHeader("Cache-Control", "no-cache");
    }

    Multimap query;
    @Override
    public IonRequestBuilder addQuery(String name, String value) {
        if (query == null)
            query = new Multimap();
        query.add(name, value);
        return this;
    }

    @Override
    public IonRequestBuilder addQueries(Map> params) {
       if (query == null)
          query = new Multimap();
       query.putAll(params);
       return this;
    }

    int timeoutMilliseconds = AsyncHttpRequest.DEFAULT_TIMEOUT;
    @Override
    public IonRequestBuilder setTimeout(int timeoutMilliseconds) {
        this.timeoutMilliseconds = timeoutMilliseconds;
        return this;
    }

    @Override
    public IonRequestBuilder setHandler(Handler handler) {
        this.handler = handler;
        return this;
    }

    AsyncHttpRequestBody body;
    private  IonRequestBuilder setBody(AsyncHttpRequestBody body) {
        if (!methodWasSet)
            method = AsyncHttpPost.METHOD;
        this.body = body;
        return this;
    }

    @Override
    public IonRequestBuilder setJsonObjectBody(JsonObject jsonObject) {
        return setBody(new GsonBody(ion.configure().getGson(), jsonObject));
    }

    @Override
    public IonRequestBuilder setJsonArrayBody(JsonArray jsonArray) {
        return setBody(new GsonBody(ion.configure().getGson(), jsonArray));
    }

    @Override
    public IonRequestBuilder setStringBody(String string) {
        return setBody(new StringBody(string));
    }

    boolean followRedirect = true;
    @Override
    public IonRequestBuilder followRedirect(boolean follow) {
        followRedirect = follow;
        return this;
    }

    private static boolean isServiceRunning(Service candidate) {
        ActivityManager manager = (ActivityManager)candidate.getSystemService(Context.ACTIVITY_SERVICE);
        List services = manager.getRunningServices(Integer.MAX_VALUE);
        if (services == null)
            return false;
        for (ActivityManager.RunningServiceInfo service: services) {
            if (candidate.getClass().getName().equals(service.service.getClassName())) {
                return true;
            }
        }
        return false;
    }

    static boolean checkContext(WeakReference contextWeakReference) {
        Context context = contextWeakReference.get();
        if (context == null)
            return false;
        return checkContext(context);
    }

    static boolean checkContext(Context context) {
        if (context instanceof Activity) {
            Activity activity = (Activity)context;
            if (activity.isFinishing())
                return false;
        }
        else if (context instanceof Service) {
            Service service = (Service)context;
            if (!isServiceRunning(service))
                return false;
        }

        return true;
    }

    private boolean checkContext() {
        return checkContext(context);
    }

    private  void postExecute(final EmitterTransform future, final Exception ex, final T value) {
        final Runnable runner = new Runnable() {
            @Override
            public void run() {
                // check if the context is still alive...
                if (!checkContext()) {
                    future.initialRequest.logd("context has died");
                    return;
                }

                // unless we're invoked onto the handler/main/service thread, there's no frakking way to avoid a
                // race condition where the service or activity dies before this callback is invoked.
                if (ex != null)
                    future.setComplete(ex);
                else
                    future.setComplete(value);
            }
        };

        if (handler == null)
            ion.httpClient.getServer().post(runner);
        else
            AsyncServer.post(handler, runner);
    }

    private URI prepareURI() {
        URI uri;
        try {
            if (query != null) {
                Uri.Builder builder = Uri.parse(this.uri).buildUpon();
                for (String key: query.keySet()) {
                    for (String value: query.get(key)) {
                        builder = builder.appendQueryParameter(key, value);
                    }
                }
                uri = URI.create(builder.toString());
            }
            else {
                uri = URI.create(this.uri);
            }
        }
        catch (Exception e) {
            uri = null;
        }
        if (uri == null || uri.getScheme() == null)
            return null;

        return uri;
    }

    private AsyncHttpRequest prepareRequest(URI uri, AsyncHttpRequestBody wrappedBody) {
        AsyncHttpRequest request = ion.configure().getAsyncHttpRequestFactory().createAsyncHttpRequest(uri, method, headers);
        request.setFollowRedirect(followRedirect);
        request.setBody(wrappedBody);
        request.setLogging(ion.logtag, ion.logLevel);
        if (logTag != null)
            request.setLogging(logTag, logLevel);
        request.enableProxy(proxyHost, proxyPort);
        request.setTimeout(timeoutMilliseconds);
        request.logd("preparing request");
        return request;
    }

    static interface LoadRequestCallback {
        boolean loadRequest(AsyncHttpRequest request);
    }

    LoadRequestCallback loadRequestCallback;

    private  void getLoaderEmitter(final EmitterTransform ret) {
        URI uri = prepareURI();
        if (uri == null) {
            ret.setComplete(new Exception("Invalid URI"));
            return;
        }

        AsyncHttpRequestBody wrappedBody = body;
        if (uploadProgressHandler != null || uploadProgressBar != null || uploadProgress != null || uploadProgressDialog != null) {
            wrappedBody = new RequestBodyUploadObserver(body, new ProgressCallback() {
                @Override
                public void onProgress(final int downloaded, final int total) {
                    assert Thread.currentThread() != Looper.getMainLooper().getThread();

                    final int percent = (int)((float)downloaded / total * 100f);

                    if (uploadProgressBar != null)
                        uploadProgressBar.setProgress(percent);

                    if (uploadProgressDialog != null)
                        uploadProgressDialog.setProgress(percent);

                    if (uploadProgress != null)
                        uploadProgress.onProgress(downloaded, total);

                    if (uploadProgressHandler != null) {
                        AsyncServer.post(Ion.mainHandler, new Runnable() {
                            @Override
                            public void run() {
                                if (ret.isCancelled() || ret.isDone())
                                    return;
                                uploadProgressHandler.onProgress(downloaded, total);
                            }
                        });
                    }
                }
            });
        }

        AsyncHttpRequest request = prepareRequest(uri, wrappedBody);
        ret.initialRequest = request;
        resolveAndLoadRequest(request, ret);
    }

     void resolveAndLoadRequest(final AsyncHttpRequest request, final EmitterTransform ret) {
        Future resolved = resolveRequest(request, ret);
        if (resolved != null) {
            resolved.setCallback(new FutureCallback() {
                @Override
                public void onCompleted(Exception e, final AsyncHttpRequest result) {
                    if (e != null) {
                        ret.setComplete(e);
                        return;
                    }
                    ret.finalRequest = result;
                    resolveAndLoadRequest(result, ret);
                }
            });
            return;
        }
        if (Looper.getMainLooper().getThread() != Thread.currentThread()) {
            AsyncServer.post(Ion.mainHandler, new Runnable() {
                @Override
                public void run() {
                    invokeLoadRequest(request, ret);
                }
            });
            return;
        }
        invokeLoadRequest(request, ret);
    }

     void invokeLoadRequest(final AsyncHttpRequest request, final EmitterTransform ret) {
        if (loadRequestCallback == null || loadRequestCallback.loadRequest(request))
            loadRequest(request, ret);
    }

     void loadRequest(AsyncHttpRequest request, final EmitterTransform ret) {
        // now attempt to fetch it directly
        for (Loader loader: ion.loaders) {
            Future emitter = loader.load(ion, request, ret);
            if (emitter != null) {
                request.logi("Using loader: " + loader);
                ret.setParent(emitter);
                return;
            }
        }
        ret.setComplete(new Exception("Unknown uri scheme"));
    }

     Future resolveRequest(AsyncHttpRequest request, final EmitterTransform ret) {
        // first attempt to resolve the url
        for (Loader loader: ion.loaders) {
            Future resolved = loader.resolve(ion, request);
            if (resolved != null)
                return resolved;

        }
        return null;
    }

    // transforms a LoaderEmitter, which is a DataEmitter and all associated properties about the data source
    // into the final result.
    class EmitterTransform extends TransformFuture implements ResponseFuture {
        AsyncHttpRequest initialRequest;
        AsyncHttpRequest finalRequest;
        int loadedFrom;
        Runnable cancelCallback;
        RawHeaders headers;
        DataEmitter emitter;

        @Override
        public Future> withResponse() {
            final SimpleFuture> ret = new SimpleFuture>();
            setCallback(new FutureCallback() {
                @Override
                public void onCompleted(Exception e, T result) {
                    if (emitter != null) {
                        Response response = new Response();
                        response.headers = headers;
                        response.request = finalRequest;
                        response.result = result;
                        response.exception = e;
                        ret.setComplete(response);
                        return;
                    }
                    ret.setComplete(e, null);
                }
            });
            ret.setParent(this);
            return ret;
        }

        public int loadedFrom() {
            return loadedFrom;
        }

        public EmitterTransform(Runnable cancelCallback) {
            this.cancelCallback = cancelCallback;
            ion.addFutureInFlight(this, context.get());
            if (groups == null)
                return;
            for (WeakReference ref: groups) {
                Object group = ref.get();
                if (group != null)
                    ion.addFutureInFlight(this, group);
            }
        }

        @Override
        protected void cancelCleanup() {
            super.cancelCleanup();
            if (emitter != null)
                emitter.close();
            if (cancelCallback != null)
                cancelCallback.run();
        }

        @Override
        protected void error(Exception e) {
            // don't call superclass which calls setComplete... get onto handler thread.
            postExecute(this, e, null);
        }

        @Override
        protected void transform(LoaderEmitter emitter) throws Exception {
            this.emitter = emitter.getDataEmitter();
            this.loadedFrom = emitter.loadedFrom();
            this.headers = emitter.getHeaders();
            this.finalRequest = emitter.getRequest();

            if (headersCallback != null) {
                final RawHeaders headers = emitter.getHeaders();
                // what do we do on loaders that don't have headers? files, content://, etc.
                AsyncServer.post(handler, new Runnable() {
                    @Override
                    public void run() {
                        headersCallback.onHeaders(headers);
                    }
                });
            }

            // hook up data progress callbacks
            final int total = emitter.length();
            DataTrackingEmitter tracker;
            if (!(emitter instanceof DataTrackingEmitter)) {
                tracker = new FilteredDataEmitter();
                tracker.setDataEmitter(this.emitter);
            }
            else {
                tracker = (DataTrackingEmitter)this.emitter;
            }
            this.emitter = tracker;
            tracker.setDataTracker(new DataTracker() {
                int lastPercent;
                @Override
                public void onData(final int totalBytesRead) {
                    assert Thread.currentThread() != Looper.getMainLooper().getThread();
                    // if the requesting context dies during the transfer... cancel
                    if (!checkContext()) {
                        initialRequest.logd("context has died, cancelling");
                        cancel();
                        return;
                    }

                    final int percent = (int)((float)totalBytesRead / total * 100f);

                    if ((progressBar != null || progressDialog != null) && percent != lastPercent) {
                        AsyncServer.post(Ion.mainHandler, new Runnable() {
                            @Override
                            public void run() {
                                if (progressBar != null) {
                                    ProgressBar bar = progressBar.get();
                                    if (bar != null)
                                        bar.setProgress(percent);
                                }
                                if (progressDialog != null) {
                                    ProgressDialog dlg = progressDialog.get();
                                    if (dlg != null)
                                        dlg.setProgress(percent);
                                }
                            }
                        });
                    }
                    lastPercent = percent;

                    if (progress != null)
                        progress.onProgress(totalBytesRead, total);

                    if (progressHandler != null) {
                        AsyncServer.post(Ion.mainHandler, new Runnable() {
                            @Override
                            public void run() {
                                if (isCancelled() || isDone())
                                    return;
                                progressHandler.onProgress(totalBytesRead, total);
                            }
                        });
                    }
                }
            });
        }
    }

    @Override
    public IonRequestBuilder progressBar(ProgressBar progressBar) {
        this.progressBar = new WeakReference(progressBar);
        return this;
    }

    @Override
    public IonRequestBuilder progressDialog(ProgressDialog progressDialog) {
        this.progressDialog = new WeakReference(progressDialog);
        return this;
    }

    WeakReference progressBar;
    WeakReference progressDialog;

    ProgressCallback progress;
    @Override
    public IonRequestBuilder progress(ProgressCallback callback) {
        progress = callback;
        return this;
    }

    ProgressCallback progressHandler;
    @Override
    public IonRequestBuilder progressHandler(ProgressCallback callback) {
        progressHandler = callback;
        return this;
    }

     EmitterTransform execute(final DataSink sink, final boolean close, final T result) {
        return execute(sink, close, result, null);
    }


     EmitterTransform execute(final DataSink sink, final boolean close, final T result, final Runnable cancel) {
        EmitterTransform ret = new EmitterTransform(cancel) {
            @Override
            protected void cleanup() {
                super.cleanup();
                if (close)
                    sink.close();
            }

            EmitterTransform self = this;
            @Override
            protected void transform(LoaderEmitter emitter) throws Exception {
                super.transform(emitter);
                Util.pump(this.emitter, sink, new CompletedCallback() {
                    @Override
                    public void onCompleted(Exception ex) {
                        postExecute(self, ex, result);
                    }
                });
            }
        };
        getLoaderEmitter(ret);
        return ret;
    }

     EmitterTransform execute(final AsyncParser parser) {
        return execute(parser, null);
    }

     EmitterTransform execute(final AsyncParser parser, Runnable cancel) {
        assert parser != null;
        EmitterTransform ret = new EmitterTransform(cancel) {
            EmitterTransform self = this;
            @Override
            protected void transform(LoaderEmitter emitter) throws Exception {
                super.transform(emitter);
                parser.parse(this.emitter).setCallback(new FutureCallback() {
                    @Override
                    public void onCompleted(Exception e, T result) {
                        postExecute(self, e, result);
                    }
                });
            }
        };
        getLoaderEmitter(ret);
        return ret;
    }

    Future execute() {
        URI uri = prepareURI();
        if (uri == null)
            return null;

        AsyncHttpRequest request = prepareRequest(uri, null);

        for (Loader loader: ion.loaders) {
            Future ret = loader.load(ion, request);
            if (ret != null)
                return ret;
        }
        return null;
    }

    @Override
    public ResponseFuture asJsonObject() {
        return execute(new GsonParser());
    }

    @Override
    public ResponseFuture asJsonArray() {
        return execute(new GsonParser());
    }

    @Override
    public ResponseFuture asString() {
        return execute(new StringParser());
    }

    @Override
    public ResponseFuture asInputStream() {
        return execute(new InputStreamParser());
    }

    @Override
    public  ResponseFuture as(AsyncParser parser) {
        return execute(parser);
    }

    @Override
    public  ResponseFuture write(F outputStream, boolean close) {
        return execute(new OutputStreamDataSink(ion.getServer(), outputStream), close, outputStream);
    }

    @Override
    public  ResponseFuture write(F outputStream) {
        return execute(new OutputStreamDataSink(ion.getServer(), outputStream), true, outputStream);
    }

    @Override
    public EmitterTransform write(final File file) {
        return execute(new FileDataSink(ion.getServer(), file), true, file, new Runnable() {
            @Override
            public void run() {
                file.delete();
            }
        });
    }

    Multimap bodyParameters;
    @Override
    public IonRequestBuilder setBodyParameter(String name, String value) {
        if (bodyParameters == null) {
            bodyParameters = new Multimap();
            setBody(new UrlEncodedFormBody(bodyParameters));
        }
        bodyParameters.add(name, value);
        return this;
    }

    public IonRequestBuilder setBodyParameters(Map> params) {
       if (bodyParameters == null) {
           bodyParameters = new Multimap();
           setBody(new UrlEncodedFormBody(bodyParameters));
       }
       bodyParameters.putAll(params);
       return this;
    }

    MultipartFormDataBody multipartBody;
    @Override
    public IonRequestBuilder setMultipartFile(String name, File file) {
        return setMultipartFile(name, null, file);
    }

    @Override
    public IonRequestBuilder setMultipartFile(String name, String contentType, File file) {
        if (multipartBody == null) {
            multipartBody = new MultipartFormDataBody();
            setBody(multipartBody);
        }

        FilePart part = new FilePart(name, file);

        if (contentType == null)
            contentType = AsyncHttpServer.tryGetContentType(file.getAbsolutePath());

        if (contentType != null)
            part.setContentType(contentType);

        multipartBody.addPart(part);
        return this;
    }

    @Override
    public IonRequestBuilder setMultipartParameter(String name, String value) {
        if (multipartBody == null) {
            multipartBody = new MultipartFormDataBody();
            setBody(multipartBody);
        }
        multipartBody.addStringPart(name, value);
        return this;
    }

    @Override
    public IonRequestBuilder setMultipartParameters(Map> params) {
        for (String key: params.keySet()) {
            for (String value: params.get(key)) {
                setMultipartParameter(key, value);
            }
        }
        return this;
    }

    @Override
    public IonBitmapRequestBuilder withBitmap() {
        return new IonBitmapRequestBuilder(this);
    }

    IonBitmapRequestBuilder withImageView(ImageView imageView) {
        return new IonBitmapRequestBuilder(this).withImageView(imageView);
    }

    @Override
    public Future intoImageView(ImageView imageView) {
        return new IonBitmapRequestBuilder(this).intoImageView(imageView);
    }

    @Override
    public IonRequestBuilder load(File file) {
        loadInternal(null, file.toURI().toString());
        return this;
    }

    @Override
    public Future asBitmap() {
        return new IonBitmapRequestBuilder(this).asBitmap();
    }

    String logTag;
    int logLevel;
    @Override
    public IonRequestBuilder setLogging(String tag, int level) {
        logTag = tag;
        logLevel = level;
        return this;
    }

    @Override
    public  ResponseFuture as(Class clazz) {
        return execute(new GsonSerializer(ion.configure().getGson(), clazz));
    }

    @Override
    public  ResponseFuture as(TypeToken token) {
        return execute(new GsonSerializer(ion.configure().getGson(), token));
    }

    ArrayList> groups;
    @Override
    public FutureBuilder group(Object groupKey) {
        if (groups == null)
            groups = new ArrayList>();
        groups.add(new WeakReference(groupKey));
        return this;
    }

    String proxyHost;
    int proxyPort;
    @Override
    public IonRequestBuilder proxy(String host, int port) {
        proxyHost = host;
        proxyPort = port;
        return this;
    }

    @Override
    public IonRequestBuilder setJsonObjectBody(Object object, TypeToken token) {
        setBody(new PojoBody(ion.configure().getGson(), object, token));
        return this;
    }

    @Override
    public IonRequestBuilder setJsonObjectBody(Object object) {
        setBody(new PojoBody(ion.configure().getGson(), object, null));
        return this;
    }

    @Override
    public IonRequestBuilder basicAuthentication(String username, String password) {
        return setHeader("Authorization", "Basic " + Base64.encodeToString(String.format("%s:%s", username, password).getBytes(), Base64.NO_WRAP));
    }

    ProgressCallback uploadProgress;
    @Override
    public Builders.Any.B uploadProgress(ProgressCallback callback) {
        uploadProgress = callback;
        return this;
    }

    ProgressBar uploadProgressBar;
    @Override
    public Builders.Any.B uploadProgressBar(ProgressBar progressBar) {
        uploadProgressBar = progressBar;
        return this;
    }

    ProgressDialog uploadProgressDialog;
    @Override
    public Builders.Any.B uploadProgressDialog(ProgressDialog progressDialog) {
        uploadProgressDialog = progressDialog;
        return this;
    }

    ProgressCallback uploadProgressHandler;
    @Override
    public Builders.Any.B uploadProgressHandler(ProgressCallback callback) {
        uploadProgressHandler = callback;
        return this;
    }

    HeadersCallback headersCallback;
    @Override
    public Builders.Any.B onHeaders(HeadersCallback callback) {
        headersCallback = callback;
        return this;
    }

    @Override
    public Builders.Any.F setDocumentBody(Document document) {
        setBody(new DocumentBody(document));
        return this;
    }

    @Override
    public ResponseFuture asDocument() {
        return execute(new DocumentParser());
    }

    @Override
    public Builders.Any.F setFileBody(File file) {
        setBody(new FileBody(file));
        return this;
    }

    @Override
    public Builders.Any.F setStreamBody(InputStream inputStream) {
        setBody(new StreamBody(inputStream, -1));
        return this;
    }

    @Override
    public Builders.Any.F setStreamBody(InputStream inputStream, int length) {
        setBody(new StreamBody(inputStream, length));
        return this;
    }

    @Override
    public Builders.Any.B setHeader(NameValuePair... header) {
        for (NameValuePair h: header) {
            this.headers.set(h.getName(), h.getValue());
        }
        return this;
    }
}