[
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: koush\npatreon: # Replace with a single Patreon username\nopen_collective: # Replace with a single Open Collective username\nko_fi: # Replace with a single Ko-fi username\ntidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\ncommunity_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\nliberapay: # Replace with a single Liberapay username\nissuehunt: # Replace with a single IssueHunt username\notechie: # Replace with a single Otechie username\ncustom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']\n"
  },
  {
    "path": ".gitignore",
    "content": "bin\n.settings\nlocal.properties\ngen\n.gradle\nbuild\n.idea/\n.DS_Store\nokhttp/\nokio/\nlibs\n*.iml\n"
  },
  {
    "path": "AndroidAsync/.classpath",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<classpath>\n\t<classpathentry kind=\"con\" path=\"org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8/\"/>\n\t<classpathentry kind=\"con\" path=\"org.eclipse.buildship.core.gradleclasspathcontainer\"/>\n\t<classpathentry kind=\"output\" path=\"bin/default\"/>\n</classpath>\n"
  },
  {
    "path": "AndroidAsync/Android.mk",
    "content": "#\n# Copyright (C) 2011 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nLOCAL_PATH := $(call my-dir)\n\ninclude $(CLEAR_VARS)\n\nLOCAL_MODULE := AndroidAsync\nLOCAL_SDK_VERSION := 8\nLOCAL_SRC_FILES := $(call all-java-files-under, src)\n\ninclude $(BUILD_STATIC_JAVA_LIBRARY)\n"
  },
  {
    "path": "AndroidAsync/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.koushikdutta.async\"\n    android:versionCode=\"310\"\n    android:versionName=\"3.1.0\">\n\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n\n    <application>\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "AndroidAsync/build.gradle",
    "content": "apply plugin: 'com.android.library'\n\nandroid {\n    sourceSets {\n        main {\n            manifest.srcFile 'AndroidManifest.xml'\n\n            jniLibs.srcDirs = ['libs/']\n\n            java.srcDirs=['src/'\n//                          , '../conscrypt/'\n//                          , '../compat/'\n            ]\n        }\n        androidTest.java.srcDirs=['test/src/']\n        androidTest.res.srcDirs=['test/res/']\n        androidTest.assets.srcDirs=['test/assets/']\n    }\n\n    lintOptions {\n        abortOnError false\n    }\n\n    defaultConfig {\n        targetSdkVersion 30\n        minSdkVersion 21\n\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n    }\n\n    compileOptions {\n        sourceCompatibility 1.8\n        targetCompatibility 1.8\n    }\n\n    compileSdkVersion 30\n    buildToolsVersion '30.0.2'\n\n    dependencies {\n        // this is only necessary to get compilation working for self signed certificates. dependency isn't added.\n        compileOnly group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.60'\n        compileOnly group: 'org.bouncycastle', name: 'bcpkix-jdk15on', version: '1.60'\n\n\n        testImplementation 'junit:junit:4.12'\n        androidTestImplementation 'com.android.support.test:runner:1.0.2'\n        androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'\n        // for when i wanna test this against gms conscrypt\n        androidTestImplementation 'com.google.android.gms:play-services-base:17.0.0'\n    }\n}\n\napply from: 'maven.gradle'\n"
  },
  {
    "path": "AndroidAsync/lint.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<lint>\n</lint>"
  },
  {
    "path": "AndroidAsync/maven.gradle",
    "content": "// Setup\n\n// 0) Setup your sonatype credentials by editing/creating ~/.gradle/gradle.properties and enter:\n// signing.keyId=<HEXADECIMCAL KEY ID RETRIVABLE VIA gpg --list-keys>\n// signing.password=<KEY PASSWORD>\n// signing.secretKeyRingFile=<PATH TO KEY RING, USUALLY ~/.gnupg/secring.gpg>\n// sonatypeUsername=<SONATYPE USERNAME OR WHATEVER YOU USE>\n// sonatypePassword=<CORRESPONDING PASSWORD>\n\n// 1) Setup your build.gradle for your android project and add this one line of code which imports this gist:\n// apply from: 'https://raw.github.com/koush/mvn-repo/master/maven.gradle'\n\n// 2) gradle clean && gradle build && gradle uploadArchives\n\n// 3) That's it!\n\n\napply plugin: 'maven'\napply plugin: 'signing'\n\n\nif (hasProperty('sonatypeUsername') && hasProperty('sonatypePassword') && hasProperty('githubToken')) {\n\n    afterEvaluate { project ->\n        String user = null\n        String repo = null\n        'git remote -v'.execute(null, project.projectDir).getText().find('.*?git@github.com/(.*?)/(.*?) .*?') {\n            match ->\n                user = match[1]\n                repo = match[2]\n        }\n\n        String githubUrl = 'https://api.github.com/repos/' + user + '/' + repo\n        if (System.getenv().GITHUB_TOKEN)\n            githubUrl += '?access_token=' + System.getenv().GITHUB_TOKEN\n        def repoInfo = new groovy.json.JsonSlurper().parseText(new URL(githubUrl).getText())\n\n        def android_manifest\n        try {\n            android_manifest = new XmlParser(false, false).parseText(new File(project.projectDir, 'AndroidManifest.xml').getText())\n        }\n        catch (e) {\n            android_manifest = new XmlParser(false, false).parseText(new File(project.projectDir, 'src/main/AndroidManifest.xml').getText())\n        }\n        def versionName = android_manifest.'@android:versionName'\n        def package_name = android_manifest.'@package'\n        def artifact_id = project.projectDir.getName().toLowerCase()\n        project.version = versionName\n        project.group = package_name\n\n        uploadArchives {\n            repositories {\n                mavenDeployer {\n\n                    beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }\n                    pom.groupId = package_name\n                    pom.artifactId = artifact_id\n                    pom.version = versionName\n\n                    repository(url: \"https://oss.sonatype.org/service/local/staging/deploy/maven2/\") {\n                        authentication(userName: sonatypeUsername, password: sonatypePassword)\n                    }\n\n                    pom.project {\n                        name repo\n                        packaging 'jar'\n                        description repoInfo.description\n                        url repoInfo.html_url\n\n                        scm {\n                            url repoInfo.git_url\n                            connection repoInfo.git_url\n                            developerConnection repoInfo.ssh_url\n                        }\n\n                        licenses {\n                            license {\n                                name 'The Apache Software License, Version 2.0'\n                                url 'http://www.apache.org/licenses/LICENSE-2.0.txt'\n                                distribution 'repo'\n                            }\n                        }\n\n                        developers {\n                            developer {\n                                id user\n                                name user\n                            }\n                        }\n                    }\n                }\n            }\n        }\n\n        signing {\n            sign configurations.archives\n        }\n\n        task androidJavadocs(type: Javadoc) {\n            source = android.sourceSets.main.java.srcDirs\n        }\n\n        task androidJavadocsJar(type: Jar) {\n            classifier = 'javadoc'\n            baseName = artifact_id\n            from androidJavadocs.destinationDir\n        }\n\n        task androidSourcesJar(type: Jar) {\n            classifier = 'sources'\n            baseName = artifact_id\n            from android.sourceSets.main.java.srcDirs\n        }\n\n        artifacts {\n            // archives packageReleaseJar\n            archives androidSourcesJar\n            archives androidJavadocsJar\n        }\n    }\n}"
  },
  {
    "path": "AndroidAsync/proguard-project.txt",
    "content": "# To enable ProGuard in your project, edit project.properties\n# to define the proguard.config property as described in that file.\n#\n# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in ${sdk.dir}/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the ProGuard\n# include property in project.properties.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n-keep class * extends com.koushikdutta.async.TapCallback {\n  public protected private *;\n}\n"
  },
  {
    "path": "AndroidAsync/project.properties",
    "content": "# This file is automatically generated by Android Tools.\n# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\n#\n# This file must be checked in Version Control Systems.\n#\n# To customize properties used by the Ant build system edit\n# \"ant.properties\", and override values to adapt the script to your\n# project structure.\n#\n# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):\n#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt\n\n# Project target.\ntarget=android-19\nandroid.library=true\n\n\n"
  },
  {
    "path": "AndroidAsync/res/.gitignore",
    "content": ""
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/AsyncDatagramSocket.java",
    "content": "package com.koushikdutta.async;\n\nimport java.io.IOException;\nimport java.net.InetSocketAddress;\nimport java.nio.ByteBuffer;\n\npublic class AsyncDatagramSocket extends AsyncNetworkSocket {\n    public void disconnect() throws IOException {\n        socketAddress = null;\n        ((DatagramChannelWrapper)getChannel()).disconnect();\n    }\n\n    @Override\n    public InetSocketAddress getRemoteAddress() {\n        if (isOpen())\n            return super.getRemoteAddress();\n        return ((DatagramChannelWrapper)getChannel()).getRemoteAddress();\n    }\n\n    public void connect(InetSocketAddress address) throws IOException {\n        socketAddress = address;\n        ((DatagramChannelWrapper)getChannel()).mChannel.connect(address);\n    }\n\n    public void send(final String host, final int port, final ByteBuffer buffer) {\n        if (getServer().getAffinity() != Thread.currentThread()) {\n            getServer().run(new Runnable() {\n                @Override\n                public void run() {\n                    send(host, port, buffer);\n                }\n            });\n            return;\n        }\n\n        try {\n            ((DatagramChannelWrapper)getChannel()).mChannel.send(buffer, new InetSocketAddress(host, port));\n        }\n        catch (IOException e) {\n//            close();\n//            reportEndPending(e);\n//            reportClose(e);\n        }\n\n    }\n    public void send(final InetSocketAddress address, final ByteBuffer buffer) {\n        if (getServer().getAffinity() != Thread.currentThread()) {\n            getServer().run(new Runnable() {\n                @Override\n                public void run() {\n                    send(address, buffer);\n                }\n            });\n            return;\n        }\n\n        try {\n            int sent = ((DatagramChannelWrapper)getChannel()).mChannel.send(buffer, new InetSocketAddress(address.getHostName(), address.getPort()));\n        }\n        catch (IOException e) {\n//            Log.e(\"SEND\", \"send error\", e);\n//            close();\n//            reportEndPending(e);\n//            reportClose(e);\n        }\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/AsyncNetworkSocket.java",
    "content": "package com.koushikdutta.async;\n\nimport android.util.Log;\n\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.callback.DataCallback;\nimport com.koushikdutta.async.callback.WritableCallback;\nimport com.koushikdutta.async.util.Allocator;\n\nimport java.io.IOException;\nimport java.net.InetAddress;\nimport java.net.InetSocketAddress;\nimport java.net.Socket;\nimport java.nio.ByteBuffer;\nimport java.nio.channels.CancelledKeyException;\nimport java.nio.channels.DatagramChannel;\nimport java.nio.channels.SelectionKey;\nimport java.nio.channels.SocketChannel;\n\npublic class AsyncNetworkSocket implements AsyncSocket {\n    AsyncNetworkSocket() {\n    }\n\n    @Override\n    public void end() {\n        mChannel.shutdownOutput();\n    }\n\n    public boolean isChunked() {\n        return mChannel.isChunked();\n    }\n\n    InetSocketAddress socketAddress;\n    void attach(SocketChannel channel, InetSocketAddress socketAddress) throws IOException {\n        this.socketAddress = socketAddress;\n        allocator = new Allocator();\n        mChannel = new SocketChannelWrapper(channel);\n    }\n    \n    void attach(DatagramChannel channel) throws IOException {\n        mChannel = new DatagramChannelWrapper(channel);\n        // keep udp at roughly the mtu, which is 1540 or something\n        // letting it grow freaks out nio apparently.\n        allocator = new Allocator(8192);\n    }\n    \n    ChannelWrapper getChannel() {\n        return mChannel;\n    }\n    \n    public void onDataWritable() {\n        if (!mChannel.isChunked()) {\n            // turn write off\n            mKey.interestOps(~SelectionKey.OP_WRITE & mKey.interestOps());\n        }\n        if (mWriteableHandler != null)\n            mWriteableHandler.onWriteable();\n    }\n\n    private ChannelWrapper mChannel;\n    private SelectionKey mKey;\n    private AsyncServer mServer;\n    \n    void setup(AsyncServer server, SelectionKey key) {\n        mServer = server;\n        mKey = key;\n    }\n    \n    @Override\n    public void write(final ByteBufferList list) {\n        if (mServer.getAffinity() != Thread.currentThread()) {\n            mServer.run(new Runnable() {\n                @Override\n                public void run() {\n                    write(list);\n                }\n            });\n            return;\n        }\n        if (!mChannel.isConnected()) {\n            return;\n        }\n\n        try {\n            int before = list.remaining();\n            ByteBuffer[] arr = list.getAllArray();\n            mChannel.write(arr);\n            list.addAll(arr);\n            handleRemaining(list.remaining());\n            mServer.onDataSent(before - list.remaining());\n        }\n        catch (IOException e) {\n            closeInternal();\n            reportEndPending(e);\n            reportClose(e);\n        }\n    }\n    \n    private void handleRemaining(int remaining) throws IOException {\n        if (!mKey.isValid())\n            throw new IOException(new CancelledKeyException());\n        if (remaining > 0) {\n            // chunked channels should not fail\n            // register for a write notification if a write fails\n            // turn write on\n            mKey.interestOps(SelectionKey.OP_WRITE | mKey.interestOps());\n        }\n        else {\n            // turn write off\n            mKey.interestOps(~SelectionKey.OP_WRITE & mKey.interestOps());\n        }\n    }\n    private ByteBufferList pending = new ByteBufferList();\n//    private ByteBuffer[] buffers = new ByteBuffer[8];\n\n    Allocator allocator;\n    int onReadable() {\n        spitPending();\n        // even if the socket is paused,\n        // it may end up getting a queued readable event if it is\n        // already in the selector's ready queue.\n        if (mPaused)\n            return 0;\n        int total = 0;\n        boolean closed = false;\n\n//            ByteBufferList.obtainArray(buffers, Math.min(Math.max(mToAlloc, 2 << 11), maxAlloc));\n        ByteBuffer b = allocator.allocate();\n        // keep track of the max mount read during this read cycle\n        // so we can be quicker about allocations during the next\n        // time this socket reads.\n        long read;\n        try {\n            read = mChannel.read(b);\n        }\n        catch (Exception e) {\n            read = -1;\n            closeInternal();\n            reportEndPending(e);\n            reportClose(e);\n        }\n\n        if (read < 0) {\n            closeInternal();\n            closed = true;\n        }\n        else {\n            total += read;\n        }\n        if (read > 0) {\n            allocator.track(read);\n            b.flip();\n//                for (int i = 0; i < buffers.length; i++) {\n//                    ByteBuffer b = buffers[i];\n//                    buffers[i] = null;\n//                    b.flip();\n//                    pending.add(b);\n//                }\n            pending.add(b);\n            Util.emitAllData(this, pending);\n        }\n        else {\n            ByteBufferList.reclaim(b);\n        }\n\n        if (closed) {\n            reportEndPending(null);\n            reportClose(null);\n        }\n\n        return total;\n    }\n    \n    boolean closeReported;\n    protected void reportClose(Exception e) {\n        if (closeReported)\n            return;\n        closeReported = true;\n        if (mClosedHander != null) {\n            mClosedHander.onCompleted(e);\n            mClosedHander = null;\n        }\n    }\n\n    @Override\n    public void close() {\n        closeInternal();\n        reportClose(null);\n    }\n\n    private void closeInternal() {\n        mKey.cancel();\n        try {\n            mChannel.close();\n        }\n        catch (IOException e) {\n        }\n    }\n\n    WritableCallback mWriteableHandler;\n    @Override\n    public void setWriteableCallback(WritableCallback handler) {\n        mWriteableHandler = handler;        \n    }\n\n    DataCallback mDataHandler;\n    @Override\n    public void setDataCallback(DataCallback callback) {\n        mDataHandler = callback;\n    }\n\n    @Override\n    public DataCallback getDataCallback() {\n        return mDataHandler;\n    }\n\n    CompletedCallback mClosedHander;\n    @Override\n    public void setClosedCallback(CompletedCallback handler) {\n        mClosedHander = handler;       \n    }\n\n    @Override\n    public CompletedCallback getClosedCallback() {\n        return mClosedHander;\n    }\n\n    @Override\n    public WritableCallback getWriteableCallback() {\n        return mWriteableHandler;\n    }\n\n    void reportEnd(Exception e) {\n        if (mEndReported)\n            return;\n        mEndReported = true;\n        if (mCompletedCallback != null)\n            mCompletedCallback.onCompleted(e);\n        else if (e != null) {\n            Log.e(\"NIO\", \"Unhandled exception\", e);\n        }\n    }\n    boolean mEndReported;\n    Exception mPendingEndException;\n    void reportEndPending(Exception e) {\n        if (pending.hasRemaining()) {\n            mPendingEndException = e;\n            return;\n        }\n        reportEnd(e);\n    }\n    \n    private CompletedCallback mCompletedCallback;\n    @Override\n    public void setEndCallback(CompletedCallback callback) {\n        mCompletedCallback = callback;\n    }\n\n    @Override\n    public CompletedCallback getEndCallback() {\n        return mCompletedCallback;\n    }\n\n    @Override\n    public boolean isOpen() {\n        return mChannel.isConnected() && mKey.isValid();\n    }\n    \n    boolean mPaused = false;\n    @Override\n    public void pause() {\n        if (mServer.getAffinity() != Thread.currentThread()) {\n            mServer.run(new Runnable() {\n                @Override\n                public void run() {\n                    pause();\n                }\n            });\n            return;\n        }\n        \n        if (mPaused)\n            return;\n\n        mPaused = true;\n        try {\n            mKey.interestOps(~SelectionKey.OP_READ & mKey.interestOps());\n        }\n        catch (Exception ex) {\n        }\n    }\n    \n    private void spitPending() {\n        if (pending.hasRemaining()) {\n            Util.emitAllData(this, pending);\n        }\n    }\n    \n    @Override\n    public void resume() {\n        if (mServer.getAffinity() != Thread.currentThread()) {\n            mServer.run(new Runnable() {\n                @Override\n                public void run() {\n                    resume();\n                }\n            });\n            return;\n        }\n        \n        if (!mPaused)\n            return;\n        mPaused = false;\n        try {\n            mKey.interestOps(SelectionKey.OP_READ | mKey.interestOps());\n        }\n        catch (Exception ex) {\n        }\n        spitPending();\n        if (!isOpen())\n            reportEndPending(mPendingEndException);\n    }\n    \n    @Override\n    public boolean isPaused() {\n        return mPaused;\n    }\n\n    @Override\n    public AsyncServer getServer() {\n        return mServer;\n    }\n\n\n    public InetSocketAddress getRemoteAddress() {\n        return socketAddress;\n    }\n\n    public InetAddress getLocalAddress() {\n        return mChannel.getLocalAddress();\n    }\n\n    public int getLocalPort() {\n        return mChannel.getLocalPort();\n    }\n\n    public Object getSocket() {\n        return getChannel().getSocket();\n    }\n\n    @Override\n    public String charset() {\n        return null;\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/AsyncSSLException.java",
    "content": "package com.koushikdutta.async;\n\npublic class AsyncSSLException extends Exception {\n    public AsyncSSLException(Throwable cause) {\n        super(\"Peer not trusted by any of the system trust managers.\", cause);\n    }\n    private boolean mIgnore = false;\n    public void setIgnore(boolean ignore) {\n        mIgnore = ignore;\n    }\n    \n    public boolean getIgnore() {\n        return mIgnore;\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/AsyncSSLServerSocket.java",
    "content": "package com.koushikdutta.async;\n\nimport java.security.PrivateKey;\nimport java.security.cert.Certificate;\n\npublic interface AsyncSSLServerSocket extends AsyncServerSocket {\n    PrivateKey getPrivateKey();\n    Certificate getCertificate();\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/AsyncSSLSocket.java",
    "content": "package com.koushikdutta.async;\n\nimport java.security.cert.X509Certificate;\n\nimport javax.net.ssl.SSLEngine;\n\npublic interface AsyncSSLSocket extends AsyncSocket {\n    public X509Certificate[] getPeerCertificates();\n    public SSLEngine getSSLEngine();\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/AsyncSSLSocketWrapper.java",
    "content": "package com.koushikdutta.async;\n\nimport android.content.Context;\nimport android.os.Build;\nimport android.util.Base64;\nimport android.util.Log;\nimport android.util.Pair;\n\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.callback.ConnectCallback;\nimport com.koushikdutta.async.callback.DataCallback;\nimport com.koushikdutta.async.callback.ListenCallback;\nimport com.koushikdutta.async.callback.WritableCallback;\nimport com.koushikdutta.async.future.Cancellable;\nimport com.koushikdutta.async.future.SimpleCancellable;\nimport com.koushikdutta.async.http.SSLEngineSNIConfigurator;\nimport com.koushikdutta.async.util.Allocator;\nimport com.koushikdutta.async.util.StreamUtility;\nimport com.koushikdutta.async.wrapper.AsyncSocketWrapper;\n\nimport org.apache.http.conn.ssl.StrictHostnameVerifier;\nimport org.bouncycastle.asn1.ASN1ObjectIdentifier;\nimport org.bouncycastle.asn1.x500.X500Name;\nimport org.bouncycastle.asn1.x509.BasicConstraints;\nimport org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;\nimport org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;\nimport org.bouncycastle.jce.provider.BouncyCastleProvider;\nimport org.bouncycastle.operator.ContentSigner;\nimport org.bouncycastle.operator.OperatorCreationException;\nimport org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.math.BigInteger;\nimport java.net.InetAddress;\nimport java.nio.ByteBuffer;\nimport java.security.GeneralSecurityException;\nimport java.security.KeyFactory;\nimport java.security.KeyPair;\nimport java.security.KeyPairGenerator;\nimport java.security.KeyStore;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.PrivateKey;\nimport java.security.Provider;\nimport java.security.PublicKey;\nimport java.security.Security;\nimport java.security.cert.Certificate;\nimport java.security.cert.CertificateException;\nimport java.security.cert.CertificateFactory;\nimport java.security.cert.X509Certificate;\nimport java.security.spec.PKCS8EncodedKeySpec;\nimport java.security.spec.RSAPublicKeySpec;\nimport java.security.spec.X509EncodedKeySpec;\nimport java.util.Calendar;\nimport java.util.Date;\n\nimport javax.net.ssl.HostnameVerifier;\nimport javax.net.ssl.KeyManagerFactory;\nimport javax.net.ssl.SSLContext;\nimport javax.net.ssl.SSLEngine;\nimport javax.net.ssl.SSLEngineResult;\nimport javax.net.ssl.SSLEngineResult.HandshakeStatus;\nimport javax.net.ssl.SSLEngineResult.Status;\nimport javax.net.ssl.SSLException;\nimport javax.net.ssl.SSLSession;\nimport javax.net.ssl.TrustManager;\nimport javax.net.ssl.TrustManagerFactory;\nimport javax.net.ssl.X509TrustManager;\n\npublic class AsyncSSLSocketWrapper implements AsyncSocketWrapper, AsyncSSLSocket {\n    private static final String LOGTAG = \"AsyncSSLSocketWrapper\";\n\n    public interface HandshakeCallback {\n        public void onHandshakeCompleted(Exception e, AsyncSSLSocket socket);\n    }\n\n    static SSLContext defaultSSLContext;\n    static SSLContext trustAllSSLContext;\n    static TrustManager[] trustAllManagers;\n    static HostnameVerifier trustAllVerifier;\n\n    AsyncSocket mSocket;\n    BufferedDataSink mSink;\n    boolean mUnwrapping;\n    SSLEngine engine;\n    boolean finishedHandshake;\n    private int mPort;\n    private String mHost;\n    private boolean mWrapping;\n    HostnameVerifier hostnameVerifier;\n    HandshakeCallback handshakeCallback;\n    X509Certificate[] peerCertificates;\n    WritableCallback mWriteableCallback;\n    DataCallback mDataCallback;\n    TrustManager[] trustManagers;\n    boolean clientMode;\n\n    static {\n        // following is the \"trust the system\" certs setup\n        try {\n            // critical extension 2.5.29.15 is implemented improperly prior to 4.0.3.\n            // https://code.google.com/p/android/issues/detail?id=9307\n            // https://groups.google.com/forum/?fromgroups=#!topic/netty/UCfqPPk5O4s\n            // certs that use this extension will throw in Cipher.java.\n            // fallback is to use a custom SSLContext, and hack around the x509 extension.\n            if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)\n                throw new Exception();\n            defaultSSLContext = SSLContext.getInstance(\"Default\");\n        }\n        catch (Exception ex) {\n            try {\n                defaultSSLContext = SSLContext.getInstance(\"TLS\");\n                TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {\n                    public java.security.cert.X509Certificate[] getAcceptedIssuers() {\n                        return new X509Certificate[0];\n                    }\n\n                    public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {\n                    }\n\n                    public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {\n                        for (X509Certificate cert : certs) {\n                            if (cert != null && cert.getCriticalExtensionOIDs() != null)\n                                cert.getCriticalExtensionOIDs().remove(\"2.5.29.15\");\n                        }\n                    }\n                } };\n                defaultSSLContext.init(null, trustAllCerts, null);\n            }\n            catch (Exception ex2) {\n                ex.printStackTrace();\n                ex2.printStackTrace();\n            }\n        }\n\n\n        try {\n            trustAllSSLContext = SSLContext.getInstance(\"TLS\");\n            trustAllManagers = new TrustManager[] { new X509TrustManager() {\n                public java.security.cert.X509Certificate[] getAcceptedIssuers() {\n                    return new X509Certificate[0];\n                }\n\n                public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {\n                }\n\n                public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {\n                }\n            } };\n            trustAllSSLContext.init(null, trustAllManagers, null);\n            trustAllVerifier = (hostname, session) -> true;\n        }\n        catch (Exception ex2) {\n            ex2.printStackTrace();\n        }\n    }\n\n    public static SSLContext getDefaultSSLContext() {\n        return defaultSSLContext;\n    }\n\n    public static void handshake(AsyncSocket socket,\n                                 String host, int port,\n                                 SSLEngine sslEngine,\n                                 TrustManager[] trustManagers, HostnameVerifier verifier, boolean clientMode,\n                                 final HandshakeCallback callback) {\n        AsyncSSLSocketWrapper wrapper = new AsyncSSLSocketWrapper(socket, host, port, sslEngine, trustManagers, verifier, clientMode);\n        wrapper.handshakeCallback = callback;\n        socket.setClosedCallback(new CompletedCallback() {\n            @Override\n            public void onCompleted(Exception ex) {\n                if (ex != null)\n                    callback.onHandshakeCompleted(ex, null);\n                else\n                    callback.onHandshakeCompleted(new SSLException(\"socket closed during handshake\"), null);\n            }\n        });\n        try {\n            wrapper.engine.beginHandshake();\n            wrapper.handleHandshakeStatus(wrapper.engine.getHandshakeStatus());\n        } catch (SSLException e) {\n            wrapper.report(e);\n        }\n    }\n\n    public static Cancellable connectSocket(AsyncServer server, String host, int port, ConnectCallback callback) {\n        return connectSocket(server, host, port, false, callback);\n    }\n    public static Cancellable connectSocket(AsyncServer server, String host, int port, boolean trustAllCerts, ConnectCallback callback) {\n        SimpleCancellable cancellable = new SimpleCancellable();\n        Cancellable connect = server.connectSocket(host, port, (ex, netSocket) -> {\n            if (ex != null) {\n                if (cancellable.setComplete())\n                    callback.onConnectCompleted(ex, null);\n                return;\n            }\n\n            handshake(netSocket, host, port,\n                    (trustAllCerts ? trustAllSSLContext : defaultSSLContext).createSSLEngine(host, port),\n                    trustAllCerts ? trustAllManagers : null,\n                    trustAllCerts ? trustAllVerifier : null,\n                    true, (e, socket) -> {\n                if (!cancellable.setComplete()) {\n                    if (socket != null)\n                        socket.close();\n                    return;\n                }\n\n                if (e != null)\n                    callback.onConnectCompleted(e, null);\n                else\n                    callback.onConnectCompleted(null, socket);\n            });\n        });\n\n        cancellable.setParent(connect);\n        return cancellable;\n    }\n\n    boolean mEnded;\n    Exception mEndException;\n    final ByteBufferList pending = new ByteBufferList();\n\n    private AsyncSSLSocketWrapper(AsyncSocket socket,\n                                  String host, int port,\n                                  SSLEngine sslEngine,\n                                  TrustManager[] trustManagers, HostnameVerifier verifier, boolean clientMode) {\n        mSocket = socket;\n        hostnameVerifier = verifier;\n        this.clientMode = clientMode;\n        this.trustManagers = trustManagers;\n        this.engine = sslEngine;\n\n        mHost = host;\n        mPort = port;\n        engine.setUseClientMode(clientMode);\n        mSink = new BufferedDataSink(socket);\n        mSink.setWriteableCallback(new WritableCallback() {\n            @Override\n            public void onWriteable() {\n                if (mWriteableCallback != null)\n                    mWriteableCallback.onWriteable();\n            }\n        });\n\n        // on pause, the emitter is paused to prevent the buffered\n        // socket and itself from firing.\n        // on resume, emitter is resumed, ssl buffer is flushed as well\n        mSocket.setEndCallback(new CompletedCallback() {\n            @Override\n            public void onCompleted(Exception ex) {\n                if (mEnded)\n                    return;\n                mEnded = true;\n                mEndException = ex;\n                if (!pending.hasRemaining() && mEndCallback != null)\n                    mEndCallback.onCompleted(ex);\n            }\n        });\n\n        mSocket.setDataCallback(dataCallback);\n    }\n\n    final DataCallback dataCallback = new DataCallback() {\n        final Allocator allocator = new Allocator().setMinAlloc(8192);\n        final ByteBufferList buffered = new ByteBufferList();\n\n        @Override\n        public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n            if (mUnwrapping)\n                return;\n            try {\n                mUnwrapping = true;\n\n                bb.get(buffered);\n\n                if (buffered.hasRemaining()) {\n                    ByteBuffer all = buffered.getAll();\n                    buffered.add(all);\n                }\n\n                ByteBuffer b = ByteBufferList.EMPTY_BYTEBUFFER;\n                while (true) {\n                    if (b.remaining() == 0 && buffered.size() > 0) {\n                        b = buffered.remove();\n                    }\n                    int remaining = b.remaining();\n                    int before = pending.remaining();\n\n                    SSLEngineResult res;\n                    {\n                        // wrap to prevent access to the readBuf\n                        ByteBuffer readBuf = allocator.allocate();\n                        res = engine.unwrap(b, readBuf);\n                        addToPending(pending, readBuf);\n                        allocator.track(pending.remaining() - before);\n                    }\n                    if (res.getStatus() == Status.BUFFER_OVERFLOW) {\n                        allocator.setMinAlloc(allocator.getMinAlloc() * 2);\n                        remaining = -1;\n                    }\n                    else if (res.getStatus() == Status.BUFFER_UNDERFLOW) {\n                        buffered.addFirst(b);\n                        if (buffered.size() <= 1) {\n                            break;\n                        }\n                        // pack it\n                        remaining = -1;\n                        b = buffered.getAll();\n                        buffered.addFirst(b);\n                        b = ByteBufferList.EMPTY_BYTEBUFFER;\n                    }\n                    handleHandshakeStatus(res.getHandshakeStatus());\n                    if (b.remaining() == remaining && before == pending.remaining()) {\n                        buffered.addFirst(b);\n                        break;\n                    }\n                }\n\n                AsyncSSLSocketWrapper.this.onDataAvailable();\n            }\n            catch (SSLException ex) {\n//                ex.printStackTrace();\n                report(ex);\n            }\n            finally {\n                mUnwrapping = false;\n            }\n        }\n    };\n\n    public void onDataAvailable() {\n        Util.emitAllData(this, pending);\n\n        if (mEnded && !pending.hasRemaining() && mEndCallback != null)\n            mEndCallback.onCompleted(mEndException);\n    }\n\n\n    @Override\n    public SSLEngine getSSLEngine() {\n        return engine;\n    }\n\n    void addToPending(ByteBufferList out, ByteBuffer mReadTmp) {\n        mReadTmp.flip();\n        if (mReadTmp.hasRemaining()) {\n            out.add(mReadTmp);\n        }\n        else {\n            ByteBufferList.reclaim(mReadTmp);\n        }\n    }\n\n\n    @Override\n    public void end() {\n        mSocket.end();\n    }\n\n    public String getHost() {\n        return mHost;\n    }\n\n    public int getPort() {\n        return mPort;\n    }\n\n    private void handleHandshakeStatus(HandshakeStatus status) {\n        if (status == HandshakeStatus.NEED_TASK) {\n            final Runnable task = engine.getDelegatedTask();\n            task.run();\n        }\n\n        if (status == HandshakeStatus.NEED_WRAP) {\n            write(writeList);\n        }\n\n        if (status == HandshakeStatus.NEED_UNWRAP) {\n            dataCallback.onDataAvailable(this, new ByteBufferList());\n        }\n\n        try {\n            if (!finishedHandshake && (engine.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING || engine.getHandshakeStatus() == HandshakeStatus.FINISHED)) {\n                if (clientMode) {\n                    Exception peerUnverifiedCause = null;\n                    boolean trusted = false;\n                    try {\n                        peerCertificates = (X509Certificate[]) engine.getSession().getPeerCertificates();\n                        if (mHost != null) {\n                            if (hostnameVerifier == null) {\n                                StrictHostnameVerifier verifier = new StrictHostnameVerifier();\n                                verifier.verify(mHost, StrictHostnameVerifier.getCNs(peerCertificates[0]), StrictHostnameVerifier.getDNSSubjectAlts(peerCertificates[0]));\n                            }\n                            else {\n                                if (!hostnameVerifier.verify(mHost, engine.getSession())) {\n                                    throw new SSLException(\"hostname <\" + mHost + \"> has been denied\");\n                                }\n                            }\n                        }\n\n                        trusted = true;\n                    }\n                    catch (SSLException ex) {\n                        peerUnverifiedCause = ex;\n                    }\n\n                    finishedHandshake = true;\n                    if (!trusted) {\n                        AsyncSSLException e = new AsyncSSLException(peerUnverifiedCause);\n                        report(e);\n                        if (!e.getIgnore())\n                            throw e;\n                    }\n                }\n                else {\n                    finishedHandshake = true;\n                }\n                handshakeCallback.onHandshakeCompleted(null, this);\n                handshakeCallback = null;\n\n                mSocket.setClosedCallback(null);\n                // handshake can complete during a wrap, so make sure that the call\n                // stack and wrap flag is cleared before invoking writable\n                getServer().post(new Runnable() {\n                    @Override\n                    public void run() {\n                        if (mWriteableCallback != null)\n                            mWriteableCallback.onWriteable();\n                    }\n                });\n                onDataAvailable();\n            }\n        }\n        catch (Exception ex) {\n            report(ex);\n        }\n    }\n\n    int calculateAlloc(int remaining) {\n        // alloc 50% more than we need for writing\n        int alloc = remaining * 3 / 2;\n        if (alloc == 0)\n            alloc = 8192;\n        return alloc;\n    }\n\n    ByteBufferList writeList = new ByteBufferList();\n    @Override\n    public void write(ByteBufferList bb) {\n        if (mWrapping)\n            return;\n        if (mSink.remaining() > 0)\n            return;\n        mWrapping = true;\n        int remaining;\n        SSLEngineResult res = null;\n        ByteBuffer writeBuf = ByteBufferList.obtain(calculateAlloc(bb.remaining()));\n        do {\n            // if the handshake is finished, don't send\n            // 0 bytes of data, since that makes the ssl connection die.\n            // it wraps a 0 byte package, and craps out.\n            if (finishedHandshake && bb.remaining() == 0)\n                break;\n            remaining = bb.remaining();\n            try {\n                ByteBuffer[] arr = bb.getAllArray();\n                res = engine.wrap(arr, writeBuf);\n                bb.addAll(arr);\n                writeBuf.flip();\n                writeList.add(writeBuf);\n                if (writeList.remaining() > 0)\n                    mSink.write(writeList);\n                int previousCapacity = writeBuf.capacity();\n                writeBuf = null;\n                if (res.getStatus() == Status.BUFFER_OVERFLOW) {\n                    writeBuf = ByteBufferList.obtain(previousCapacity * 2);\n                    remaining = -1;\n                }\n                else {\n                    writeBuf = ByteBufferList.obtain(calculateAlloc(bb.remaining()));\n                    handleHandshakeStatus(res.getHandshakeStatus());\n                }\n            }\n            catch (SSLException e) {\n                report(e);\n            }\n        }\n        while ((remaining != bb.remaining() || (res != null && res.getHandshakeStatus() == HandshakeStatus.NEED_WRAP)) && mSink.remaining() == 0);\n        mWrapping = false;\n        ByteBufferList.reclaim(writeBuf);\n    }\n\n    @Override\n    public void setWriteableCallback(WritableCallback handler) {\n        mWriteableCallback = handler;\n    }\n\n    @Override\n    public WritableCallback getWriteableCallback() {\n        return mWriteableCallback;\n    }\n\n    private void report(Exception e) {\n        final HandshakeCallback hs = handshakeCallback;\n        if (hs != null) {\n            handshakeCallback = null;\n            mSocket.setDataCallback(new DataCallback.NullDataCallback());\n            mSocket.end();\n            // handshake sets this callback. unset it.\n            mSocket.setClosedCallback(null);\n            mSocket.close();\n            hs.onHandshakeCompleted(e, null);\n            return;\n        }\n\n        CompletedCallback cb = getEndCallback();\n        if (cb != null)\n            cb.onCompleted(e);\n    }\n\n    @Override\n    public void setDataCallback(DataCallback callback) {\n        mDataCallback = callback;\n    }\n\n    @Override\n    public DataCallback getDataCallback() {\n        return mDataCallback;\n    }\n\n    @Override\n    public boolean isChunked() {\n        return mSocket.isChunked();\n    }\n\n    @Override\n    public boolean isOpen() {\n        return mSocket.isOpen();\n    }\n\n    @Override\n    public void close() {\n        mSocket.close();\n    }\n\n    @Override\n    public void setClosedCallback(CompletedCallback handler) {\n        mSocket.setClosedCallback(handler);\n    }\n\n    @Override\n    public CompletedCallback getClosedCallback() {\n        return mSocket.getClosedCallback();\n    }\n\n    CompletedCallback mEndCallback;\n    @Override\n    public void setEndCallback(CompletedCallback callback) {\n        mEndCallback = callback;\n    }\n\n    @Override\n    public CompletedCallback getEndCallback() {\n        return mEndCallback;\n    }\n\n    @Override\n    public void pause() {\n        mSocket.pause();\n    }\n\n    @Override\n    public void resume() {\n        mSocket.resume();\n        onDataAvailable();\n    }\n\n    @Override\n    public boolean isPaused() {\n        return mSocket.isPaused();\n    }\n\n    @Override\n    public AsyncServer getServer() {\n        return mSocket.getServer();\n    }\n\n    @Override\n    public AsyncSocket getSocket() {\n        return mSocket;\n    }\n\n    @Override\n    public DataEmitter getDataEmitter() {\n        return mSocket;\n    }\n\n    @Override\n    public X509Certificate[] getPeerCertificates() {\n        return peerCertificates;\n    }\n\n    @Override\n    public String charset() {\n        return null;\n    }\n\n    private static Certificate selfSign(KeyPair keyPair, String subjectDN) throws Exception\n    {\n        Provider bcProvider = new BouncyCastleProvider();\n        Security.addProvider(bcProvider);\n\n        long now = System.currentTimeMillis();\n        Date startDate = new Date(now);\n\n        X500Name dnName = new X500Name(\"CN=\" + subjectDN);\n        BigInteger certSerialNumber = new BigInteger(Long.toString(now)); // <-- Using the current timestamp as the certificate serial number\n\n        Calendar calendar = Calendar.getInstance();\n        calendar.setTime(startDate);\n        calendar.add(Calendar.YEAR, 1); // <-- 1 Yr validity\n\n        Date endDate = calendar.getTime();\n\n        String signatureAlgorithm = \"SHA256WithRSA\"; // <-- Use appropriate signature algorithm based on your keyPair algorithm.\n\n        ContentSigner contentSigner = new JcaContentSignerBuilder(signatureAlgorithm).build(keyPair.getPrivate());\n\n        JcaX509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(dnName, certSerialNumber, startDate, endDate, dnName, keyPair.getPublic());\n\n        // Extensions --------------------------\n\n        // Basic Constraints\n        BasicConstraints basicConstraints = new BasicConstraints(true); // <-- true for CA, false for EndEntity\n\n        certBuilder.addExtension(new ASN1ObjectIdentifier(\"2.5.29.19\"), true, basicConstraints); // Basic Constraints is usually marked as critical.\n\n        // -------------------------------------\n\n        return new JcaX509CertificateConverter().setProvider(bcProvider).getCertificate(certBuilder.build(contentSigner));\n    }\n\n    public static Pair<KeyPair, Certificate> selfSignCertificate(final Context context, String subjectName) throws Exception {\n        File keyPath = context.getFileStreamPath(subjectName + \"-key.txt\");\n        KeyPair pair;\n        Certificate cert;\n        try {\n            String[] keyParts = StreamUtility.readFile(keyPath).split(\"\\n\");\n            X509EncodedKeySpec pub = new X509EncodedKeySpec(Base64.decode(keyParts[0], 0));\n            PKCS8EncodedKeySpec priv = new PKCS8EncodedKeySpec(Base64.decode(keyParts[1], 0));\n\n            cert = CertificateFactory.getInstance(\"X.509\").generateCertificate(new ByteArrayInputStream(Base64.decode(keyParts[2], 0)));\n\n            KeyFactory fact = KeyFactory.getInstance(\"RSA\");\n\n            pair = new KeyPair(fact.generatePublic(pub), fact.generatePrivate(priv));\n\n        }\n        catch (Exception e) {\n            KeyPairGenerator keyGen = KeyPairGenerator.getInstance(\"RSA\");\n            keyGen.initialize(2048);\n            pair = keyGen.generateKeyPair();\n\n            cert = selfSign(pair, subjectName);\n\n            StreamUtility.writeFile(keyPath,\n                    Base64.encodeToString(pair.getPublic().getEncoded(), Base64.NO_WRAP)\n                            + \"\\n\"\n                            + Base64.encodeToString(pair.getPrivate().getEncoded(), Base64.NO_WRAP)\n                            + \"\\n\"\n                            + Base64.encodeToString(cert.getEncoded(), Base64.NO_WRAP));\n        }\n\n        return new Pair<>(pair, cert);\n    }\n\n    public static AsyncSSLServerSocket listenSecure(final Context context, final AsyncServer server, final String subjectName, final InetAddress host, final int port, final ListenCallback handler) {\n        final ObjectHolder<AsyncSSLServerSocket> holder = new ObjectHolder<>();\n        server.run(() -> {\n            try {\n                Pair<KeyPair, Certificate> keyCert = selfSignCertificate(context, subjectName);\n                KeyPair pair = keyCert.first;\n                Certificate cert = keyCert.second;\n\n                holder.held = listenSecure(server, pair.getPrivate(), cert, host, port, handler);\n            }\n            catch (Exception e) {\n                handler.onCompleted(e);\n            }\n        });\n        return holder.held;\n    }\n\n    public static AsyncSSLServerSocket listenSecure(AsyncServer server, String keyDer, String certDer, final InetAddress host, final int port, final ListenCallback handler) {\n        return listenSecure(server, Base64.decode(keyDer, Base64.DEFAULT), Base64.decode(certDer, Base64.DEFAULT), host, port, handler);\n    }\n\n    private static class ObjectHolder<T> {\n        T held;\n    }\n\n    public static AsyncSSLServerSocket listenSecure(final AsyncServer server, final byte[] keyDer, final byte[] certDer, final InetAddress host, final int port, final ListenCallback handler) {\n        final ObjectHolder<AsyncSSLServerSocket> holder = new ObjectHolder<>();\n        server.run(() -> {\n            try {\n                PKCS8EncodedKeySpec key = new PKCS8EncodedKeySpec(keyDer);\n                Certificate cert = CertificateFactory.getInstance(\"X.509\").generateCertificate(new ByteArrayInputStream(certDer));\n\n                PrivateKey pk = KeyFactory.getInstance(\"RSA\").generatePrivate(key);\n\n                holder.held = listenSecure(server, pk, cert, host, port, handler);\n            }\n            catch (Exception e) {\n                handler.onCompleted(e);\n            }\n        });\n        return holder.held;\n    }\n\n    public static AsyncSSLServerSocket listenSecure(final AsyncServer server, final PrivateKey pk, final Certificate cert, final InetAddress host, final int port, final ListenCallback handler) {\n        final ObjectHolder<AsyncSSLServerSocket> holder = new ObjectHolder<>();\n        server.run(() -> {\n            try {\n                KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());\n                ks.load(null);\n\n                ks.setKeyEntry(\"key\", pk, null, new Certificate[] { cert });\n\n                KeyManagerFactory kmf = KeyManagerFactory.getInstance(\"X509\");\n                kmf.init(ks, \"\".toCharArray());\n\n                TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());\n                tmf.init(ks);\n\n                SSLContext sslContext = SSLContext.getInstance(\"TLS\");\n                sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);\n\n                final AsyncServerSocket socket = listenSecure(server, sslContext, host, port, handler);\n                holder.held = new AsyncSSLServerSocket() {\n                    @Override\n                    public PrivateKey getPrivateKey() {\n                        return pk;\n                    }\n\n                    @Override\n                    public Certificate getCertificate() {\n                        return cert;\n                    }\n\n                    @Override\n                    public void stop() {\n                        socket.stop();\n                    }\n\n                    @Override\n                    public int getLocalPort() {\n                        return socket.getLocalPort();\n                    }\n                };\n            }\n            catch (Exception e) {\n                handler.onCompleted(e);\n            }\n        });\n        return holder.held;\n    }\n\n    public static AsyncServerSocket listenSecure(AsyncServer server, final SSLContext sslContext, final InetAddress host, final int port, final ListenCallback handler) {\n        final SSLEngineSNIConfigurator conf = new SSLEngineSNIConfigurator() {\n            @Override\n            public SSLEngine createEngine(SSLContext sslContext, String peerHost, int peerPort) {\n                SSLEngine engine = super.createEngine(sslContext, peerHost, peerPort);\n//                String[] ciphers = engine.getEnabledCipherSuites();\n//                for (String cipher: ciphers) {\n//                    Log.i(LOGTAG, cipher);\n//                }\n\n                // todo: what's this for? some vestigal vysor code i think. required by audio mirroring?\n                engine.setEnabledCipherSuites(new String[] { \"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384\" });\n                return engine;\n            }\n        };\n        return server.listen(host, port, new ListenCallback() {\n            @Override\n            public void onAccepted(final AsyncSocket socket) {\n                AsyncSSLSocketWrapper.handshake(socket, null, port, conf.createEngine(sslContext, null, port), null, null, false,\n                        (e, sslSocket) -> {\n                            if (e != null) {\n                                // chrome seems to do some sort of SSL probe and cancels handshakes. not sure why.\n                                // i suspect it is to pick an optimal strong cipher.\n                                // seeing a lot of the following in the log (but no actual connection errors)\n                                // javax.net.ssl.SSLHandshakeException: error:10000416:SSL routines:OPENSSL_internal:SSLV3_ALERT_CERTIFICATE_UNKNOWN\n                                // seen on Shield TV running API 26\n                                // todo fix: conscrypt ssl context?\n//                                Log.e(LOGTAG, \"Error while handshaking\", e);\n                                socket.close();\n                                return;\n                            }\n                            handler.onAccepted(sslSocket);\n                        });\n            }\n\n            @Override\n            public void onListening(AsyncServerSocket socket) {\n                handler.onListening(socket);\n            }\n\n            @Override\n            public void onCompleted(Exception ex) {\n                handler.onCompleted(ex);\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/AsyncSemaphore.java",
    "content": "package com.koushikdutta.async;\n\nimport java.util.concurrent.Semaphore;\nimport java.util.concurrent.TimeUnit;\n\npublic class AsyncSemaphore {\n\n    Semaphore semaphore = new Semaphore(0);\n\n    public void acquire() throws InterruptedException {\n        ThreadQueue threadQueue = ThreadQueue.getOrCreateThreadQueue(Thread.currentThread());\n        AsyncSemaphore last = threadQueue.waiter;\n        threadQueue.waiter = this;\n        Semaphore queueSemaphore = threadQueue.queueSemaphore;\n        try {\n            if (semaphore.tryAcquire())\n                return;\n\n            while (true) {\n                // run the queue\n                while (true) {\n                    Runnable run = threadQueue.remove();\n                    if (run == null)\n                        break;\n//                        Log.i(LOGTAG, \"Pumping for AsyncSemaphore\");\n                    run.run();\n                }\n\n                int permits = Math.max(1, queueSemaphore.availablePermits());\n                queueSemaphore.acquire(permits);\n                if (semaphore.tryAcquire())\n                    break;\n            }\n        }\n        finally {\n            threadQueue.waiter = last;\n        }\n    }\n\n    public boolean tryAcquire(long timeout, TimeUnit timeunit) throws InterruptedException {\n        long timeoutMs = TimeUnit.MILLISECONDS.convert(timeout, timeunit);\n        ThreadQueue threadQueue = ThreadQueue.getOrCreateThreadQueue(Thread.currentThread());\n        AsyncSemaphore last = threadQueue.waiter;\n        threadQueue.waiter = this;\n        Semaphore queueSemaphore = threadQueue.queueSemaphore;\n\n        try {\n            if (semaphore.tryAcquire())\n                return true;\n\n            long start = System.currentTimeMillis();\n            do {\n                // run the queue\n                while (true) {\n                    Runnable run = threadQueue.remove();\n                    if (run == null)\n                        break;\n//                        Log.i(LOGTAG, \"Pumping for AsyncSemaphore\");\n                    run.run();\n                }\n\n                int permits = Math.max(1, queueSemaphore.availablePermits());\n                if (!queueSemaphore.tryAcquire(permits, timeoutMs, TimeUnit.MILLISECONDS))\n                    return false;\n                if (semaphore.tryAcquire())\n                    return true;\n            }\n            while (System.currentTimeMillis() - start < timeoutMs);\n            return false;\n        }\n        finally {\n            threadQueue.waiter = last;\n        }\n    }\n\n    public void release() {\n        semaphore.release();\n        ThreadQueue.release(this);\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/AsyncServer.java",
    "content": "package com.koushikdutta.async;\n\nimport android.os.Build;\nimport android.os.Handler;\nimport android.os.SystemClock;\nimport android.util.Log;\n\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.callback.ConnectCallback;\nimport com.koushikdutta.async.callback.ListenCallback;\nimport com.koushikdutta.async.callback.SocketCreateCallback;\nimport com.koushikdutta.async.callback.ValueFunction;\nimport com.koushikdutta.async.future.Cancellable;\nimport com.koushikdutta.async.future.Future;\nimport com.koushikdutta.async.future.FutureCallback;\nimport com.koushikdutta.async.future.SimpleCancellable;\nimport com.koushikdutta.async.future.SimpleFuture;\nimport com.koushikdutta.async.util.StreamUtility;\n\nimport java.io.IOException;\nimport java.net.Inet4Address;\nimport java.net.Inet6Address;\nimport java.net.InetAddress;\nimport java.net.InetSocketAddress;\nimport java.net.SocketAddress;\nimport java.nio.channels.CancelledKeyException;\nimport java.nio.channels.ClosedChannelException;\nimport java.nio.channels.ClosedSelectorException;\nimport java.nio.channels.DatagramChannel;\nimport java.nio.channels.SelectionKey;\nimport java.nio.channels.ServerSocketChannel;\nimport java.nio.channels.SocketChannel;\nimport java.nio.channels.spi.SelectorProvider;\nimport java.util.Arrays;\nimport java.util.Comparator;\nimport java.util.PriorityQueue;\nimport java.util.Set;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.concurrent.Semaphore;\nimport java.util.concurrent.ThreadFactory;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicInteger;\n\npublic class AsyncServer {\n    public static final String LOGTAG = \"NIO\";\n\n    private static class RunnableWrapper implements Runnable {\n        boolean hasRun;\n        Runnable runnable;\n        ThreadQueue threadQueue;\n        Handler handler;\n        @Override\n        public void run() {\n            synchronized (this) {\n                if (hasRun)\n                    return;\n                hasRun = true;\n            }\n            try {\n                runnable.run();\n            }\n            finally {\n                threadQueue.remove(this);\n                handler.removeCallbacks(this);\n\n                threadQueue = null;\n                handler = null;\n                runnable = null;\n            }\n        }\n    }\n\n    public static void post(Handler handler, Runnable runnable) {\n        RunnableWrapper wrapper = new RunnableWrapper();\n        ThreadQueue threadQueue = ThreadQueue.getOrCreateThreadQueue(handler.getLooper().getThread());\n        wrapper.threadQueue = threadQueue;\n        wrapper.handler = handler;\n        wrapper.runnable = runnable;\n\n        // run it in a blocking AsyncSemaphore or a Handler, whichever gets to it first.\n        threadQueue.add(wrapper);\n        handler.post(wrapper);\n\n        // run the queue if the thread is blocking\n        threadQueue.queueSemaphore.release();\n    }\n\n    static {\n        try {\n            if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.FROYO) {\n                java.lang.System.setProperty(\"java.net.preferIPv4Stack\", \"true\");\n                java.lang.System.setProperty(\"java.net.preferIPv6Addresses\", \"false\");\n            }\n        }\n        catch (Throwable ex) {\n        }\n    }\n\n    static AsyncServer mInstance = new AsyncServer();\n    public static AsyncServer getDefault() {\n        return mInstance;\n    }\n\n    private SelectorWrapper mSelector;\n\n    public boolean isRunning() {\n        return mSelector != null;\n    }\n\n    String mName;\n    public AsyncServer() {\n        this(null);\n    }\n\n    public AsyncServer(String name) {\n        if (name == null)\n            name = \"AsyncServer\";\n        mName = name;\n    }\n\n    private static ExecutorService synchronousWorkers = newSynchronousWorkers(\"AsyncServer-worker-\");\n    private static void wakeup(final SelectorWrapper selector) {\n        synchronousWorkers.execute(() -> {\n            try {\n                selector.wakeupOnce();\n            }\n            catch (Exception e) {\n            }\n        });\n    }\n\n    boolean killed;\n    public void kill() {\n        synchronized (this) {\n            killed = true;\n        }\n        stop(false);\n    }\n\n    int postCounter = 0;\n    public Cancellable postDelayed(Runnable runnable, long delay) {\n        Scheduled s;\n        synchronized (this) {\n            if (killed)\n                return SimpleCancellable.CANCELLED;\n\n            // Calculate when to run this queue item:\n            // If there is a delay (non-zero), add it to the current time\n            // When delay is zero, ensure that this follows all other\n            // zero-delay queue items. This is done by setting the\n            // \"time\" to the queue size. This will make sure it is before\n            // all time-delayed queue items (for all real world scenarios)\n            // as it will always be less than the current time and also remain\n            // behind all other immediately run queue items.\n            long time;\n            if (delay > 0)\n                time = SystemClock.elapsedRealtime() + delay;\n            else if (delay == 0)\n                time = postCounter++;\n            else if (mQueue.size() > 0)\n                time = Math.min(0, mQueue.peek().time - 1);\n            else\n                time = 0;\n            mQueue.add(s = new Scheduled(this, runnable, time));\n            // start the server up if necessary\n            if (mSelector == null)\n                run();\n            if (!isAffinityThread()) {\n                wakeup(mSelector);\n            }\n        }\n        return s;\n    }\n\n    public Cancellable postImmediate(Runnable runnable) {\n        if (Thread.currentThread() == getAffinity()) {\n            runnable.run();\n            return null;\n        }\n        return postDelayed(runnable, -1);\n    }\n\n    public Cancellable post(Runnable runnable) {\n        return postDelayed(runnable, 0);\n    }\n\n    public Cancellable post(final CompletedCallback callback, final Exception e) {\n        return post(() -> callback.onCompleted(e));\n    }\n\n    public void run(final Runnable runnable) {\n        if (Thread.currentThread() == mAffinity) {\n            post(runnable);\n            lockAndRunQueue(this, mQueue);\n            return;\n        }\n\n        final Semaphore semaphore;\n        synchronized (this) {\n            if (killed)\n                return;\n            semaphore = new Semaphore(0);\n            post(() -> {\n                runnable.run();\n                semaphore.release();\n            });\n        }\n        try {\n            semaphore.acquire();\n        }\n        catch (InterruptedException e) {\n            Log.e(LOGTAG, \"run\", e);\n        }\n    }\n\n    private static class Scheduled implements Cancellable, Runnable {\n        // this constructor is only called when the async execution should not be preserved\n        // ie... AsyncServer.stop.\n        public Scheduled(AsyncServer server, Runnable runnable, long time) {\n            this.server = server;\n            this.runnable = runnable;\n            this.time = time;\n        }\n        public AsyncServer server;\n        public Runnable runnable;\n        public long time;\n\n        @Override\n        public void run() {\n            this.runnable.run();\n        }\n\n        @Override\n        public boolean isDone() {\n            synchronized (server) {\n                return !cancelled && !server.mQueue.contains(this);\n            }\n        }\n\n        boolean cancelled;\n        @Override\n        public boolean isCancelled() {\n            return cancelled;\n        }\n\n        @Override\n        public boolean cancel() {\n            synchronized (server) {\n                return cancelled = server.mQueue.remove(this);\n            }\n        }\n    }\n    PriorityQueue<Scheduled> mQueue = new PriorityQueue<Scheduled>(1, Scheduler.INSTANCE);\n\n    static class Scheduler implements Comparator<Scheduled> {\n        public static Scheduler INSTANCE = new Scheduler();\n        private Scheduler() {\n        }\n        @Override\n        public int compare(Scheduled s1, Scheduled s2) {\n            // keep the smaller ones at the head, so they get tossed out quicker\n            if (s1.time == s2.time)\n                return 0;\n            if (s1.time > s2.time)\n                return 1;\n            return -1;\n        }\n    }\n\n\n    public void stop() {\n        stop(false);\n    }\n\n    public void stop(boolean wait) {\n//        Log.i(LOGTAG, \"****AsyncServer is shutting down.****\");\n        final SelectorWrapper currentSelector;\n        final Semaphore semaphore;\n        final boolean isAffinityThread;\n        synchronized (this) {\n            isAffinityThread = isAffinityThread();\n            currentSelector = mSelector;\n            if (currentSelector == null)\n                return;\n            semaphore = new Semaphore(0);\n\n            // post a shutdown and wait\n            mQueue.add(new Scheduled(this, new Runnable() {\n                @Override\n                public void run() {\n                    shutdownEverything(currentSelector);\n                    semaphore.release();\n                }\n            }, 0));\n            synchronousWorkers.execute(() -> {\n                try {\n                    currentSelector.wakeupOnce();\n                }\n                catch (Exception e) {\n                }\n            });\n\n            // force any existing connections to die\n            shutdownKeys(currentSelector);\n\n            mQueue = new PriorityQueue<>(1, Scheduler.INSTANCE);\n            mSelector = null;\n            mAffinity = null;\n        }\n        try {\n            if (!isAffinityThread && wait)\n                semaphore.acquire();\n        }\n        catch (Exception e) {\n        }\n    }\n\n    protected void onDataReceived(int transmitted) {\n    }\n\n    protected void onDataSent(int transmitted) {\n    }\n\n    private static class ObjectHolder<T> {\n        T held;\n    }\n    public AsyncServerSocket listen(final InetAddress host, final int port, final ListenCallback handler) {\n        final ObjectHolder<AsyncServerSocket> holder = new ObjectHolder<>();\n        run(new Runnable() {\n            @Override\n            public void run() {\n                ServerSocketChannel closeableServer = null;\n                ServerSocketChannelWrapper closeableWrapper = null;\n                try {\n                    closeableServer = ServerSocketChannel.open();\n                    closeableWrapper = new ServerSocketChannelWrapper(\n                            closeableServer);\n                    final ServerSocketChannel server = closeableServer;\n                    final ServerSocketChannelWrapper wrapper = closeableWrapper;\n                    InetSocketAddress isa;\n                    if (host == null)\n                        isa = new InetSocketAddress(port);\n                    else\n                        isa = new InetSocketAddress(host, port);\n                    server.socket().bind(isa);\n                    final SelectionKey key = wrapper.register(mSelector.getSelector());\n                    key.attach(handler);\n                    handler.onListening(holder.held = new AsyncServerSocket() {\n                        @Override\n                        public int getLocalPort() {\n                            return server.socket().getLocalPort();\n                        }\n\n                        @Override\n                        public void stop() {\n                            StreamUtility.closeQuietly(wrapper);\n                            try {\n                                key.cancel();\n                            }\n                            catch (Exception e) {\n                            }\n                        }\n                    });\n                }\n                catch (IOException e) {\n                    Log.e(LOGTAG, \"wtf\", e);\n                    StreamUtility.closeQuietly(closeableWrapper, closeableServer);\n                    handler.onCompleted(e);\n                }\n            }\n        });\n        return holder.held;\n    }\n\n    private class ConnectFuture extends SimpleFuture<AsyncNetworkSocket> {\n        @Override\n        protected void cancelCleanup() {\n            super.cancelCleanup();\n            try {\n                if (socket != null)\n                    socket.close();\n            }\n            catch (IOException e) {\n            }\n        }\n\n        SocketChannel socket;\n        ConnectCallback callback;\n    }\n\n    public Cancellable connectResolvedInetSocketAddress(final InetSocketAddress address, final ConnectCallback callback) {\n        return connectResolvedInetSocketAddress(address, callback, null);\n    }\n\n    public ConnectFuture connectResolvedInetSocketAddress(final InetSocketAddress address, final ConnectCallback callback, final SocketCreateCallback createCallback) {\n        final ConnectFuture cancel = new ConnectFuture();\n\n        post(new Runnable() {\n            @Override\n            public void run() {\n                if (cancel.isCancelled())\n                    return;\n\n                cancel.callback = callback;\n                SelectionKey ckey = null;\n                SocketChannel socket = null;\n                try {\n                    socket = cancel.socket = SocketChannel.open();\n                    socket.configureBlocking(false);\n                    ckey = socket.register(mSelector.getSelector(), SelectionKey.OP_CONNECT);\n                    ckey.attach(cancel);\n                    if (createCallback != null)\n                        createCallback.onSocketCreated(socket.socket().getLocalPort());\n                    socket.connect(address);\n                }\n                catch (Throwable e) {\n                    if (ckey != null)\n                        ckey.cancel();\n                    StreamUtility.closeQuietly(socket);\n                    cancel.setComplete(new RuntimeException(e));\n                }\n            }\n        });\n\n        return cancel;\n    }\n\n    public Cancellable connectSocket(final InetSocketAddress remote, final ConnectCallback callback) {\n        if (!remote.isUnresolved())\n            return connectResolvedInetSocketAddress(remote, callback);\n\n        final SimpleFuture<AsyncNetworkSocket> ret = new SimpleFuture<AsyncNetworkSocket>();\n\n        Future<InetAddress> lookup = getByName(remote.getHostName());\n        ret.setParent(lookup);\n        lookup\n        .setCallback(new FutureCallback<InetAddress>() {\n            @Override\n            public void onCompleted(Exception e, InetAddress result) {\n                if (e != null) {\n                    callback.onConnectCompleted(e, null);\n                    ret.setComplete(e);\n                    return;\n                }\n\n                ret.setComplete((ConnectFuture)connectResolvedInetSocketAddress(new InetSocketAddress(result, remote.getPort()), callback));\n            }\n        });\n        return ret;\n    }\n\n    public Cancellable connectSocket(final String host, final int port, final ConnectCallback callback) {\n        return connectSocket(InetSocketAddress.createUnresolved(host, port), callback);\n    }\n\n    private static ExecutorService newSynchronousWorkers(String prefix) {\n        ThreadFactory tf = new NamedThreadFactory(prefix);\n        ThreadPoolExecutor tpe = new ThreadPoolExecutor(0, 4, 10L,\n            TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), tf);\n        return tpe;\n    }\n\n    private static final Comparator<InetAddress> ipSorter = new Comparator<InetAddress>() {\n        @Override\n        public int compare(InetAddress lhs, InetAddress rhs) {\n            if (lhs instanceof Inet4Address && rhs instanceof Inet4Address)\n                return 0;\n            if (lhs instanceof Inet6Address && rhs instanceof Inet6Address)\n                return 0;\n            if (lhs instanceof Inet4Address && rhs instanceof Inet6Address)\n                return -1;\n            return 1;\n        }\n    };\n\n    private static ExecutorService synchronousResolverWorkers = newSynchronousWorkers(\"AsyncServer-resolver-\");\n    public Future<InetAddress[]> getAllByName(final String host) {\n        final SimpleFuture<InetAddress[]> ret = new SimpleFuture<InetAddress[]>();\n        synchronousResolverWorkers.execute(new Runnable() {\n            @Override\n            public void run() {\n                try {\n                    final InetAddress[] result = InetAddress.getAllByName(host);\n                    Arrays.sort(result, ipSorter);\n                    if (result == null || result.length == 0)\n                        throw new HostnameResolutionException(\"no addresses for host\");\n                    post(new Runnable() {\n                        @Override\n                        public void run() {\n                            ret.setComplete(null, result);\n                        }\n                    });\n                } catch (final Exception e) {\n                    post(new Runnable() {\n                        @Override\n                        public void run() {\n                            ret.setComplete(e, null);\n                        }\n                    });\n                }\n            }\n        });\n        return ret;\n    }\n\n    public Future<InetAddress> getByName(String host) {\n        return getAllByName(host).thenConvert(addresses -> addresses[0]);\n    }\n\n    private void handleSocket(final AsyncNetworkSocket handler) throws ClosedChannelException {\n        final ChannelWrapper sc = handler.getChannel();\n        SelectionKey ckey = sc.register(mSelector.getSelector());\n        ckey.attach(handler);\n        handler.setup(this, ckey);\n    }\n\n    public AsyncDatagramSocket connectDatagram(final String host, final int port) throws IOException {\n        final DatagramChannel socket = DatagramChannel.open();\n        final AsyncDatagramSocket handler = new AsyncDatagramSocket();\n        handler.attach(socket);\n        // ugh.. this should really be post to make it nonblocking...\n        // but i want datagrams to be immediately writable.\n        // they're not really used anyways.\n        run(new Runnable() {\n            @Override\n            public void run() {\n                try {\n                    final SocketAddress remote = new InetSocketAddress(host, port);\n                    handleSocket(handler);\n                    socket.connect(remote);\n                }\n                catch (IOException e) {\n                    Log.e(LOGTAG, \"Datagram error\", e);\n                    StreamUtility.closeQuietly(socket);\n                }\n            }\n        });\n        return handler;\n    }\n\n    public AsyncDatagramSocket openDatagram() {\n        return openDatagram(null, 0, false);\n    }\n\n    public Cancellable createDatagram(String address, int port, boolean reuseAddress, FutureCallback<AsyncDatagramSocket> callback) {\n        return createDatagram(() -> InetAddress.getByName(address), port, reuseAddress, callback);\n    }\n\n    public Cancellable createDatagram(InetAddress address, int port, boolean reuseAddress, FutureCallback<AsyncDatagramSocket> callback) {\n        return createDatagram(() -> address, port, reuseAddress, callback);\n    }\n\n    private Cancellable createDatagram(ValueFunction<InetAddress> inetAddressValueFunction, final int port, final boolean reuseAddress, FutureCallback<AsyncDatagramSocket> callback) {\n        SimpleFuture<AsyncDatagramSocket> ret = new SimpleFuture<>();\n        ret.setCallback(callback);\n        post(() -> {\n            DatagramChannel socket = null;\n            try {\n                socket = DatagramChannel.open();\n\n                final AsyncDatagramSocket handler = new AsyncDatagramSocket();\n                handler.attach(socket);\n\n                InetSocketAddress address;\n                if (inetAddressValueFunction == null)\n                    address = new InetSocketAddress(port);\n                else\n                    address = new InetSocketAddress(inetAddressValueFunction.getValue(), port);\n\n                if (reuseAddress)\n                    socket.socket().setReuseAddress(reuseAddress);\n                socket.socket().bind(address);\n                handleSocket(handler);\n                if (!ret.setComplete(handler))\n                    socket.close();\n            }\n            catch (Exception e) {\n                StreamUtility.closeQuietly(socket);\n                ret.setComplete(e);\n            }\n        });\n\n        return ret;\n    }\n\n    public AsyncDatagramSocket openDatagram(final InetAddress host, final int port, final boolean reuseAddress) {\n        final AsyncDatagramSocket handler = new AsyncDatagramSocket();\n        // ugh.. this should really be post to make it nonblocking...\n        // but i want datagrams to be immediately writable.\n        // they're not really used anyways.\n        Runnable runnable = () -> {\n            final DatagramChannel socket;\n            try {\n                socket = DatagramChannel.open();\n            }\n            catch (Exception e) {\n                return;\n            }\n            try {\n                handler.attach(socket);\n\n                InetSocketAddress address;\n                if (host == null)\n                    address = new InetSocketAddress(port);\n                else\n                    address = new InetSocketAddress(host, port);\n\n                if (reuseAddress)\n                    socket.socket().setReuseAddress(reuseAddress);\n                socket.socket().bind(address);\n                handleSocket(handler);\n            }\n            catch (IOException e) {\n                Log.e(LOGTAG, \"Datagram error\", e);\n                StreamUtility.closeQuietly(socket);\n            }\n        };\n\n        if (getAffinity() != Thread.currentThread()) {\n            run(runnable);\n            return handler;\n        }\n\n        runnable.run();\n        return handler;\n    }\n\n    public AsyncDatagramSocket connectDatagram(final SocketAddress remote) throws IOException {\n        final AsyncDatagramSocket handler = new AsyncDatagramSocket();\n        final DatagramChannel socket = DatagramChannel.open();\n        handler.attach(socket);\n        // ugh.. this should really be post to make it nonblocking...\n        // but i want datagrams to be immediately writable.\n        // they're not really used anyways.\n        Runnable runnable = () -> {\n            try {\n                handleSocket(handler);\n                socket.connect(remote);\n            }\n            catch (IOException e) {\n                StreamUtility.closeQuietly(socket);\n            }\n        };\n\n        if (getAffinity() != Thread.currentThread()) {\n            run(runnable);\n            return handler;\n        }\n\n        runnable.run();\n        return handler;\n    }\n\n    final private static ThreadLocal<AsyncServer> threadServer = new ThreadLocal<>();\n\n    public static AsyncServer getCurrentThreadServer() {\n        return threadServer.get();\n    }\n\n    Thread mAffinity;\n    private void run() {\n        final SelectorWrapper selector;\n        final PriorityQueue<Scheduled> queue;\n        synchronized (this) {\n            if (mSelector == null) {\n                try {\n                    selector = mSelector = new SelectorWrapper(SelectorProvider.provider().openSelector());\n                    queue = mQueue;\n                }\n                catch (IOException e) {\n                    throw new RuntimeException(\"unable to create selector?\", e);\n                }\n\n                mAffinity = new Thread(mName) {\n                    public void run() {\n                        try {\n                            threadServer.set(AsyncServer.this);\n                            AsyncServer.run(AsyncServer.this, selector, queue);\n                        }\n                        finally {\n                            threadServer.remove();\n                        }\n                    }\n                };\n\n                mAffinity.start();\n                // kicked off the new thread, let's bail.\n                return;\n            }\n\n            // this is a reentrant call\n            selector = mSelector;\n            queue = mQueue;\n\n            // fall through to outside of the synchronization scope\n            // to allow the thread to run without locking.\n        }\n\n        try {\n            runLoop(this, selector, queue);\n        }\n        catch (AsyncSelectorException e) {\n            Log.i(LOGTAG, \"Selector closed\", e);\n            try {\n                // StreamUtility.closeQuiety is throwing ArrayStoreException?\n                selector.getSelector().close();\n            }\n            catch (Exception ex) {\n            }\n        }\n    }\n\n    private static void run(final AsyncServer server, final SelectorWrapper selector, final PriorityQueue<Scheduled> queue) {\n//        Log.i(LOGTAG, \"****AsyncServer is starting.****\");\n        // at this point, this local queue and selector are owned\n        // by this thread.\n        // if a stop is called, the instance queue and selector\n        // will be replaced and nulled respectively.\n        // this will allow the old queue and selector to shut down\n        // gracefully, while also allowing a new selector thread\n        // to start up while the old one is still shutting down.\n        while(true) {\n            try {\n                runLoop(server, selector, queue);\n            }\n            catch (AsyncSelectorException e) {\n                if (!(e.getCause() instanceof ClosedSelectorException))\n                    Log.i(LOGTAG, \"Selector exception, shutting down\", e);\n                StreamUtility.closeQuietly(selector);\n            }\n            // see if we keep looping, this must be in a synchronized block since the queue is accessed.\n            synchronized (server) {\n                if (selector.isOpen() && (selector.keys().size() > 0 || queue.size() > 0))\n                    continue;\n\n                shutdownEverything(selector);\n                if (server.mSelector == selector) {\n                    server.mQueue = new PriorityQueue<Scheduled>(1, Scheduler.INSTANCE);\n                    server.mSelector = null;\n                    server.mAffinity = null;\n                }\n                break;\n            }\n        }\n//        Log.i(LOGTAG, \"****AsyncServer has shut down.****\");\n    }\n\n    private static void shutdownKeys(SelectorWrapper selector) {\n        try {\n            for (SelectionKey key: selector.keys()) {\n                StreamUtility.closeQuietly(key.channel());\n                try {\n                    key.cancel();\n                }\n                catch (Exception e) {\n                }\n            }\n        }\n        catch (Exception ex) {\n        }\n    }\n\n    private static void shutdownEverything(SelectorWrapper selector) {\n        shutdownKeys(selector);\n        // SHUT. DOWN. EVERYTHING.\n        StreamUtility.closeQuietly(selector);\n    }\n\n    private static final long QUEUE_EMPTY = Long.MAX_VALUE;\n    private static long lockAndRunQueue(final AsyncServer server, final PriorityQueue<Scheduled> queue) {\n        long wait = QUEUE_EMPTY;\n\n        // find the first item we can actually run\n        while (true) {\n            Scheduled run = null;\n\n            synchronized (server) {\n                long now = SystemClock.elapsedRealtime();\n\n                if (queue.size() > 0) {\n                    Scheduled s = queue.remove();\n                    if (s.time <= now) {\n                        run = s;\n                    }\n                    else {\n                        wait = s.time - now;\n                        queue.add(s);\n                    }\n                }\n            }\n\n            if (run == null)\n                break;\n\n            run.run();\n        }\n\n        server.postCounter = 0;\n        return wait;\n    }\n\n    private static class AsyncSelectorException extends IOException {\n        public AsyncSelectorException(Exception e) {\n            super(e);\n        }\n    }\n\n    private static void runLoop(final AsyncServer server, final SelectorWrapper selector, final PriorityQueue<Scheduled> queue) throws AsyncSelectorException {\n//        Log.i(LOGTAG, \"Keys: \" + selector.keys().size());\n        boolean needsSelect = true;\n\n        // run the queue to populate the selector with keys\n        long wait = lockAndRunQueue(server, queue);\n        try {\n            synchronized (server) {\n                // select now to see if anything is ready immediately. this\n                // also clears the canceled key queue.\n                int readyNow = selector.selectNow();\n                if (readyNow == 0) {\n                    // if there is nothing to select now, make sure we don't have an empty key set\n                    // which means it would be time to turn this thread off.\n                    if (selector.keys().size() == 0 && wait == QUEUE_EMPTY) {\n//                    Log.i(LOGTAG, \"Shutting down. keys: \" + selector.keys().size() + \" keepRunning: \" + keepRunning);\n                        return;\n                    }\n                }\n                else {\n                    needsSelect = false;\n                }\n            }\n\n            if (needsSelect) {\n                if (wait == QUEUE_EMPTY) {\n                    // wait until woken up\n                    selector.select();\n                }\n                else {\n                    // nothing to select immediately but there's something pending so let's block that duration and wait.\n                    selector.select(wait);\n                }\n            }\n        }\n        catch (Exception e) {\n            throw new AsyncSelectorException(e);\n        }\n\n        // process whatever keys are ready\n        Set<SelectionKey> readyKeys = selector.selectedKeys();\n        for (SelectionKey key: readyKeys) {\n            try {\n                if (key.isAcceptable()) {\n                    ServerSocketChannel nextReady = (ServerSocketChannel) key.channel();\n                    SocketChannel sc = null;\n                    SelectionKey ckey = null;\n                    try {\n                        sc = nextReady.accept();\n                        if (sc == null)\n                            continue;\n                        sc.configureBlocking(false);\n                        ckey = sc.register(selector.getSelector(), SelectionKey.OP_READ);\n                        ListenCallback serverHandler = (ListenCallback) key.attachment();\n                        AsyncNetworkSocket handler = new AsyncNetworkSocket();\n                        handler.attach(sc, (InetSocketAddress)sc.socket().getRemoteSocketAddress());\n                        handler.setup(server, ckey);\n                        ckey.attach(handler);\n                        serverHandler.onAccepted(handler);\n                    }\n                    catch (IOException e) {\n                        StreamUtility.closeQuietly(sc);\n                        if (ckey != null)\n                            ckey.cancel();\n                    }\n                }\n                else if (key.isReadable()) {\n                    AsyncNetworkSocket handler = (AsyncNetworkSocket) key.attachment();\n                    int transmitted = handler.onReadable();\n                    server.onDataReceived(transmitted);\n                }\n                else if (key.isWritable()) {\n                    AsyncNetworkSocket handler = (AsyncNetworkSocket) key.attachment();\n                    handler.onDataWritable();\n                }\n                else if (key.isConnectable()) {\n                    ConnectFuture cancel = (ConnectFuture) key.attachment();\n                    SocketChannel sc = (SocketChannel) key.channel();\n                    key.interestOps(SelectionKey.OP_READ);\n                    AsyncNetworkSocket newHandler;\n                    try {\n                        sc.finishConnect();\n                        newHandler = new AsyncNetworkSocket();\n                        newHandler.setup(server, key);\n                        newHandler.attach(sc, (InetSocketAddress)sc.socket().getRemoteSocketAddress());\n                        key.attach(newHandler);\n                    }\n                    catch (IOException ex) {\n                        key.cancel();\n                        StreamUtility.closeQuietly(sc);\n                        if (cancel.setComplete(ex))\n                            cancel.callback.onConnectCompleted(ex, null);\n                        continue;\n                    }\n                    if (cancel.setComplete(newHandler))\n                        cancel.callback.onConnectCompleted(null, newHandler);\n                }\n                else {\n                    Log.i(LOGTAG, \"wtf\");\n                    throw new RuntimeException(\"Unknown key state.\");\n                }\n            }\n            catch (CancelledKeyException ex) {\n            }\n        }\n        readyKeys.clear();\n    }\n\n    public void dump() {\n        post(new Runnable() {\n            @Override\n            public void run() {\n                if (mSelector == null) {\n                    Log.i(LOGTAG, \"Server dump not possible. No selector?\");\n                    return;\n                }\n                Log.i(LOGTAG, \"Key Count: \" + mSelector.keys().size());\n\n                for (SelectionKey key: mSelector.keys()) {\n                    Log.i(LOGTAG, \"Key: \" + key);\n                }\n            }\n        });\n    }\n\n    public Thread getAffinity() {\n        return mAffinity;\n    }\n\n    public boolean isAffinityThread() {\n        return mAffinity == Thread.currentThread();\n    }\n\n    public boolean isAffinityThreadOrStopped() {\n        Thread affinity = mAffinity;\n        return affinity == null || affinity == Thread.currentThread();\n    }\n\n    private static class NamedThreadFactory implements ThreadFactory {\n        private final ThreadGroup group;\n        private final AtomicInteger threadNumber = new AtomicInteger(1);\n        private final String namePrefix;\n\n        NamedThreadFactory(String namePrefix) {\n            SecurityManager s = System.getSecurityManager();\n            group = (s != null) ? s.getThreadGroup() :\n                Thread.currentThread().getThreadGroup();\n            this.namePrefix = namePrefix;\n        }\n\n        public Thread newThread(Runnable r) {\n            Thread t = new Thread(group, r,\n                namePrefix + threadNumber.getAndIncrement(), 0);\n            if (t.isDaemon()) t.setDaemon(false);\n            if (t.getPriority() != Thread.NORM_PRIORITY) {\n                t.setPriority(Thread.NORM_PRIORITY);\n            }\n            return t;\n        }\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/AsyncServerSocket.java",
    "content": "package com.koushikdutta.async;\n\npublic interface AsyncServerSocket {\n    public void stop();\n    public int getLocalPort();\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/AsyncSocket.java",
    "content": "package com.koushikdutta.async;\n\n\npublic interface AsyncSocket extends DataEmitter, DataSink {\n    public AsyncServer getServer();\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/BufferedDataSink.java",
    "content": "package com.koushikdutta.async;\n\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.callback.WritableCallback;\n\npublic class BufferedDataSink implements DataSink {\n    DataSink mDataSink;\n    public BufferedDataSink(DataSink datasink) {\n        setDataSink(datasink);\n    }\n\n    public boolean isBuffering() {\n        return mPendingWrites.hasRemaining() || forceBuffering;\n    }\n\n    public boolean isWritable() {\n        synchronized (mPendingWrites) {\n            return mPendingWrites.remaining() < mMaxBuffer;\n        }\n    }\n\n    public DataSink getDataSink() {\n        return mDataSink;\n    }\n\n    boolean forceBuffering;\n    public void forceBuffering(boolean forceBuffering) {\n        this.forceBuffering = forceBuffering;\n        if (!forceBuffering)\n            writePending();\n    }\n\n    public void setDataSink(DataSink datasink) {\n        mDataSink = datasink;\n        mDataSink.setWriteableCallback(this::writePending);\n    }\n\n    private void writePending() {\n        if (forceBuffering)\n            return;\n\n//        Log.i(\"NIO\", \"Writing to buffer...\");\n        boolean empty;\n        synchronized (mPendingWrites) {\n            mDataSink.write(mPendingWrites);\n            empty = mPendingWrites.isEmpty();\n        }\n        if (empty) {\n            if (endPending)\n                mDataSink.end();\n        }\n        if (empty && mWritable != null)\n            mWritable.onWriteable();\n    }\n    \n    final ByteBufferList mPendingWrites = new ByteBufferList();\n\n    // before the data is queued, let inheritors know. allows for filters, without\n    // issues with having to filter before writing which may fail in the buffer.\n    protected void onDataAccepted(ByteBufferList bb) {\n    }\n\n    @Override\n    public void write(final ByteBufferList bb) {\n        if (getServer().getAffinity() != Thread.currentThread()) {\n            synchronized (mPendingWrites) {\n                if (mPendingWrites.remaining() >= mMaxBuffer)\n                    return;\n                onDataAccepted(bb);\n                bb.get(mPendingWrites);\n            }\n            getServer().post(this::writePending);\n            return;\n        }\n\n        onDataAccepted(bb);\n\n        if (!isBuffering())\n            mDataSink.write(bb);\n\n        synchronized (mPendingWrites) {\n            bb.get(mPendingWrites);\n        }\n    }\n\n    WritableCallback mWritable;\n    @Override\n    public void setWriteableCallback(WritableCallback handler) {\n        mWritable = handler;\n    }\n\n    @Override\n    public WritableCallback getWriteableCallback() {\n        return mWritable;\n    }\n    \n    public int remaining() {\n        return mPendingWrites.remaining();\n    }\n    \n    int mMaxBuffer = Integer.MAX_VALUE;\n    public int getMaxBuffer() {\n        return mMaxBuffer;\n    }\n\n    public void setMaxBuffer(int maxBuffer) {\n        mMaxBuffer = maxBuffer;\n    }\n\n    @Override\n    public boolean isOpen() {\n        return mDataSink.isOpen();\n    }\n\n    boolean endPending;\n    @Override\n    public void end() {\n        if (getServer().getAffinity() != Thread.currentThread()) {\n            getServer().post(this::end);\n            return;\n        }\n\n        synchronized (mPendingWrites) {\n            if (mPendingWrites.hasRemaining()) {\n                endPending = true;\n                return;\n            }\n        }\n        mDataSink.end();\n    }\n\n    @Override\n    public void setClosedCallback(CompletedCallback handler) {\n        mDataSink.setClosedCallback(handler);\n    }\n\n    @Override\n    public CompletedCallback getClosedCallback() {\n        return mDataSink.getClosedCallback();\n    }\n\n    @Override\n    public AsyncServer getServer() {\n        return mDataSink.getServer();\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/ByteBufferList.java",
    "content": "package com.koushikdutta.async;\n\nimport android.annotation.TargetApi;\nimport android.os.Build;\nimport android.os.Looper;\n\nimport com.koushikdutta.async.util.ArrayDeque;\nimport com.koushikdutta.async.util.Charsets;\n\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.nio.charset.Charset;\nimport java.util.Comparator;\nimport java.util.PriorityQueue;\n\n@TargetApi(Build.VERSION_CODES.GINGERBREAD)\npublic class ByteBufferList {\n    ArrayDeque<ByteBuffer> mBuffers = new ArrayDeque<ByteBuffer>();\n    \n    ByteOrder order = ByteOrder.BIG_ENDIAN;\n    public ByteOrder order() {\n        return order;\n    }\n\n    public ByteBufferList order(ByteOrder order) {\n        this.order = order;\n        return this;\n    }\n\n    public ByteBufferList() {\n    }\n\n    public ByteBufferList(ByteBuffer... b) {\n        addAll(b);\n    }\n\n    public ByteBufferList(byte[] buf) {\n        super();\n        ByteBuffer b = ByteBuffer.wrap(buf);\n        add(b);\n    }\n\n    public ByteBufferList addAll(ByteBuffer... bb) {\n        for (ByteBuffer b: bb)\n            add(b);\n        return this;\n    }\n\n    public ByteBufferList addAll(ByteBufferList... bb) {\n        for (ByteBufferList b: bb)\n            b.get(this);\n        return this;\n    }\n\n    public byte[] getBytes(int length) {\n        byte[] ret = new byte[length];\n        get(ret);\n        return ret;\n    }\n\n    public byte[] getAllByteArray() {\n        byte[] ret = new byte[remaining()];\n        get(ret);\n        return ret;\n    }\n\n    public ByteBuffer[] getAllArray() {\n        ByteBuffer[] ret = new ByteBuffer[mBuffers.size()];\n        ret = mBuffers.toArray(ret);\n        mBuffers.clear();\n        remaining = 0;\n        return ret;\n    }\n\n    public boolean isEmpty() {\n        return remaining == 0;\n    }\n\n    private int remaining = 0;\n    public int remaining() {\n        return remaining;\n    }\n\n    public boolean hasRemaining() {\n        return remaining() > 0;\n    }\n\n    public short peekShort() {\n        return read(2).getShort(mBuffers.peekFirst().position());\n    }\n\n    public byte peek() {\n        return read(1).get(mBuffers.peekFirst().position());\n    }\n\n    public int peekInt() {\n        return read(4).getInt(mBuffers.peekFirst().position());\n    }\n\n    public long peekLong() {\n        return read(8).getLong(mBuffers.peekFirst().position());\n    }\n\n    public byte[] peekBytes(int size) {\n        byte[] ret = new byte[size];\n        read(size).get(ret, mBuffers.peekFirst().position(), ret.length);\n        return ret;\n    }\n\n    public ByteBufferList skip(int length) {\n        get(null, 0, length);\n        return this;\n    }\n\n    public int getInt() {\n        int ret = read(4).getInt();\n        remaining -= 4;\n        return ret;\n    }\n    \n    public char getByteChar() {\n        char ret = (char)read(1).get();\n        remaining--;\n        return ret;\n    }\n    \n    public short getShort() {\n        short ret = read(2).getShort();\n        remaining -= 2;\n        return ret;\n    }\n    \n    public byte get() {\n        byte ret = read(1).get();\n        remaining--;\n        return ret;\n    }\n    \n    public long getLong() {\n        long ret = read(8).getLong();\n        remaining -= 8;\n        return ret;\n    }\n\n    public void get(byte[] bytes) {\n        get(bytes, 0, bytes.length);\n    }\n\n    public void get(byte[] bytes, int offset, int length) {\n        if (remaining() < length)\n            throw new IllegalArgumentException(\"length\");\n\n        int need = length;\n        while (need > 0) {\n            ByteBuffer b = mBuffers.peek();\n            int read = Math.min(b.remaining(), need);\n            if (bytes != null){\n                b.get(bytes, offset, read);\n            } else {\n                //when bytes is null, just skip data.\n                b.position(b.position() + read);\n            }\n            need -= read;\n            offset += read;\n            if (b.remaining() == 0) {\n                ByteBuffer removed = mBuffers.remove();\n                reclaim(b);\n            }\n        }\n\n        remaining -= length;\n    }\n\n    public void get(ByteBufferList into, int length) {\n        if (remaining() < length)\n            throw new IllegalArgumentException(\"length\");\n        int offset = 0;\n\n        while (offset < length) {\n            ByteBuffer b = mBuffers.remove();\n            int remaining = b.remaining();\n\n            if (remaining == 0) {\n                reclaim(b);\n                continue;\n            }\n\n            if (offset + remaining > length) {\n                int need = length - offset;\n                // this is shared between both\n                ByteBuffer subset = obtain(need);\n                subset.limit(need);\n                b.get(subset.array(), 0, need);\n                into.add(subset);\n                mBuffers.addFirst(b);\n                break;\n            }\n            else {\n                // this belongs to the new list\n                into.add(b);\n            }\n\n            offset += remaining;\n        }\n\n        remaining -= length;\n    }\n\n    public void get(ByteBufferList into) {\n        get(into, remaining());\n    }\n\n    public ByteBufferList get(int length) {\n        ByteBufferList ret = new ByteBufferList();\n        get(ret, length);\n        return ret.order(order);\n    }\n\n    public ByteBuffer getAll() {\n        if (remaining() == 0)\n            return EMPTY_BYTEBUFFER;\n        read(remaining());\n        return remove();\n    }\n\n    private ByteBuffer read(int count) {\n        if (remaining() < count)\n            throw new IllegalArgumentException(\"count : \" + remaining() + \"/\" + count);\n\n        ByteBuffer first = mBuffers.peek();\n        while (first != null && !first.hasRemaining()) {\n            reclaim(mBuffers.remove());\n            first = mBuffers.peek();\n        }\n        \n        if (first == null) {\n            return EMPTY_BYTEBUFFER;\n        }\n\n        if (first.remaining() >= count) {\n            return first.order(order);\n        }\n\n        ByteBuffer ret = obtain(count);\n        ret.limit(count);\n        byte[] bytes = ret.array();\n        int offset = 0;\n        ByteBuffer bb = null;\n        while (offset < count) {\n            bb = mBuffers.remove();\n            int toRead = Math.min(count - offset, bb.remaining());\n            bb.get(bytes, offset, toRead);\n            offset += toRead;\n            if (bb.remaining() == 0) {\n                reclaim(bb);\n                bb = null;\n            }\n        }\n        // if there was still data left in the last buffer we popped\n        // toss it back into the head\n        if (bb != null && bb.remaining() > 0)\n            mBuffers.addFirst(bb);\n        mBuffers.addFirst(ret);\n        return ret.order(order);\n    }\n    \n    public void trim() {\n        // this clears out buffers that are empty in the beginning of the list\n        read(0);\n    }\n\n    public ByteBufferList add(ByteBufferList b) {\n        b.get(this);\n        return this;\n    }\n\n    public ByteBufferList add(ByteBuffer b) {\n        if (b.remaining() <= 0) {\n//            System.out.println(\"reclaiming remaining: \" + b.remaining());\n            reclaim(b);\n            return this;\n        }\n        addRemaining(b.remaining());\n        // see if we can fit the entirety of the buffer into the end\n        // of the current last buffer\n        if (mBuffers.size() > 0) {\n            ByteBuffer last = mBuffers.getLast();\n            if (last.capacity() - last.limit() >= b.remaining()) {\n                last.mark();\n                last.position(last.limit());\n                last.limit(last.capacity());\n                last.put(b);\n                last.limit(last.position());\n                last.reset();\n                reclaim(b);\n                trim();\n                return this;\n            }\n        }\n        mBuffers.add(b);\n        trim();\n        return this;\n    }\n\n    public void addFirst(ByteBuffer b) {\n        if (b.remaining() <= 0) {\n            reclaim(b);\n            return;\n        }\n        addRemaining(b.remaining());\n        // see if we can fit the entirety of the buffer into the beginning\n        // of the current first buffer\n        if (mBuffers.size() > 0) {\n            ByteBuffer first = mBuffers.getFirst();\n            if (first.position() >= b.remaining()) {\n                first.position(first.position() - b.remaining());\n                first.mark();\n                first.put(b);\n                first.reset();\n                reclaim(b);\n                return;\n            }\n        }\n        mBuffers.addFirst(b);\n    }\n\n    private void addRemaining(int remaining) {\n        if (this.remaining() >= 0)\n            this.remaining += remaining;\n    }\n\n    public void recycle() {\n        while (mBuffers.size() > 0) {\n            reclaim(mBuffers.remove());\n        }\n        remaining = 0;\n    }\n    \n    public ByteBuffer remove() {\n        ByteBuffer ret = mBuffers.remove();\n        remaining -= ret.remaining();\n        return ret;\n    }\n    \n    public int size() {\n        return mBuffers.size();\n    }\n\n    public void spewString() {\n        System.out.println(peekString());\n    }\n\n    public String peekString() {\n        return peekString(null);\n    }\n\n    // not doing toString as this is really nasty in the debugger...\n    public String peekString(Charset charset) {\n        if (charset == null)\n            charset = Charsets.UTF_8;\n        StringBuilder builder = new StringBuilder();\n        for (ByteBuffer bb: mBuffers) {\n            byte[] bytes;\n            int offset;\n            int length;\n            if (bb.isDirect()) {\n                bytes = new byte[bb.remaining()];\n                offset = 0;\n                length = bb.remaining();\n                bb.get(bytes);\n            }\n            else {\n                bytes = bb.array();\n                offset = bb.arrayOffset() + bb.position();\n                length = bb.remaining();\n            }\n            builder.append(new String(bytes, offset, length, charset));\n        }\n        return builder.toString();\n    }\n\n    public String readString() {\n        return readString(null);\n    }\n\n    public String readString(Charset charset) {\n        String ret = peekString(charset);\n        recycle();\n        return ret;\n    }\n\n    static class Reclaimer implements Comparator<ByteBuffer> {\n        @Override\n        public int compare(ByteBuffer byteBuffer, ByteBuffer byteBuffer2) {\n            // keep the smaller ones at the head, so they get tossed out quicker\n            if (byteBuffer.capacity() == byteBuffer2.capacity())\n                return 0;\n            if (byteBuffer.capacity() > byteBuffer2.capacity())\n                return 1;\n            return -1;\n        }\n    }\n\n    static PriorityQueue<ByteBuffer> reclaimed = new PriorityQueue<ByteBuffer>(8, new Reclaimer());\n\n    private static PriorityQueue<ByteBuffer> getReclaimed() {\n        Looper mainLooper = Looper.getMainLooper();\n        if (mainLooper != null) {\n            if (Thread.currentThread() == mainLooper.getThread())\n                return null;\n        }\n        return reclaimed;\n    }\n\n    private static int MAX_SIZE = 1024 * 1024;\n    public static int MAX_ITEM_SIZE = 1024 * 256;\n    static int currentSize = 0;\n    static int maxItem = 0;\n\n    public static void setMaxPoolSize(int size) {\n        MAX_SIZE = size;\n    }\n\n    public static void setMaxItemSize(int size) {\n        MAX_ITEM_SIZE = size;\n    }\n\n    private static boolean reclaimedContains(ByteBuffer b) {\n        for (ByteBuffer other: reclaimed) {\n            if (other == b)\n                return true;\n        }\n        return false;\n    }\n\n    public static void reclaim(ByteBuffer b) {\n        if (b == null || b.isDirect())\n            return;\n        if (b.arrayOffset() != 0 || b.array().length != b.capacity())\n            return;\n        if (b.capacity() < 8192)\n            return;\n        if (b.capacity() > MAX_ITEM_SIZE)\n            return;\n\n        PriorityQueue<ByteBuffer> r = getReclaimed();\n        if (r == null)\n            return;\n\n        synchronized (LOCK) {\n            while (currentSize > MAX_SIZE && r.size() > 0 && r.peek().capacity() < b.capacity()) {\n//                System.out.println(\"removing for better: \" + b.capacity());\n                ByteBuffer head = r.remove();\n                currentSize -= head.capacity();\n            }\n\n            if (currentSize > MAX_SIZE) {\n//                System.out.println(\"too full: \" + b.capacity());\n                return;\n            }\n\n            b.position(0);\n            b.limit(b.capacity());\n            currentSize += b.capacity();\n\n            r.add(b);\n\n            maxItem = Math.max(maxItem, b.capacity());\n        }\n    }\n\n    private static final Object LOCK = new Object();\n\n    public static ByteBuffer obtain(int size) {\n        if (size <= maxItem) {\n            PriorityQueue<ByteBuffer> r = getReclaimed();\n            if (r != null) {\n                synchronized (LOCK) {\n                    while (r.size() > 0) {\n                        ByteBuffer ret = r.remove();\n                        if (r.size() == 0)\n                            maxItem = 0;\n                        currentSize -= ret.capacity();\n                        if (ret.capacity() >= size) {\n//                            System.out.println(\"using \" + ret.capacity());\n                            return ret;\n                        }\n//                        System.out.println(\"dumping \" + ret.capacity());\n                    }\n                }\n            }\n        }\n\n//        System.out.println(\"alloc for \" + size);\n        ByteBuffer ret = ByteBuffer.allocate(Math.max(8192, size));\n        return ret;\n    }\n\n    public static void obtainArray(ByteBuffer[] arr, int size) {\n        PriorityQueue<ByteBuffer> r = getReclaimed();\n        int index = 0;\n        int total = 0;\n\n        if (r != null) {\n            synchronized (LOCK) {\n                while (r.size() > 0 && total < size && index < arr.length - 1) {\n                    ByteBuffer b = r.remove();\n                    currentSize -= b.capacity();\n                    int needed = Math.min(size - total, b.capacity());\n                    total += needed;\n                    arr[index++] = b;\n                }\n            }\n        }\n\n        if (total < size) {\n            ByteBuffer b = ByteBuffer.allocate(Math.max(8192, size - total));\n            arr[index++] = b;\n        }\n\n        for (int i = index; i < arr.length; i++) {\n            arr[i] = EMPTY_BYTEBUFFER;\n        }\n    }\n\n    public static ByteBuffer deepCopy(ByteBuffer copyOf) {\n        if (copyOf == null)\n            return null;\n        return (ByteBuffer)obtain(copyOf.remaining()).put(copyOf.duplicate()).flip();\n    }\n\n    public static final ByteBuffer EMPTY_BYTEBUFFER = ByteBuffer.allocate(0);\n\n    public static void writeOutputStream(OutputStream out, ByteBuffer b) throws IOException {\n        byte[] bytes;\n        int offset;\n        int length;\n        if (b.isDirect()) {\n            bytes = new byte[b.remaining()];\n            offset = 0;\n            length = b.remaining();\n            b.get(bytes);\n        }\n        else {\n            bytes = b.array();\n            offset = b.arrayOffset() + b.position();\n            length = b.remaining();\n        }\n        out.write(bytes, offset, length);\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/ChannelWrapper.java",
    "content": "package com.koushikdutta.async;\n\nimport java.io.IOException;\nimport java.net.InetAddress;\nimport java.nio.ByteBuffer;\nimport java.nio.channels.ClosedChannelException;\nimport java.nio.channels.ReadableByteChannel;\nimport java.nio.channels.ScatteringByteChannel;\nimport java.nio.channels.SelectionKey;\nimport java.nio.channels.Selector;\nimport java.nio.channels.spi.AbstractSelectableChannel;\n\nabstract class ChannelWrapper implements ReadableByteChannel, ScatteringByteChannel {\n    private AbstractSelectableChannel mChannel;\n    ChannelWrapper(AbstractSelectableChannel channel) throws IOException {\n        channel.configureBlocking(false);\n        mChannel = channel;\n    }\n\n    public abstract void shutdownInput();\n    public abstract void shutdownOutput();\n    \n    public abstract boolean isConnected();\n    \n    public abstract int write(ByteBuffer src) throws IOException;\n    public abstract int write(ByteBuffer[] src) throws IOException;\n\n    // register for default events appropriate for this channel\n    public abstract SelectionKey register(Selector sel) throws ClosedChannelException;\n\n    public SelectionKey register(Selector sel, int ops) throws ClosedChannelException {\n        return mChannel.register(sel, ops);\n    }\n\n    public boolean isChunked() {\n        return false;\n    }\n    \n    @Override\n    public boolean isOpen() {\n        return mChannel.isOpen();\n    }\n    \n    @Override\n    public void close() throws IOException {\n       mChannel.close();\n    }\n    \n    public abstract int getLocalPort();\n    public abstract InetAddress getLocalAddress();\n    public abstract Object getSocket();\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/DataEmitter.java",
    "content": "package com.koushikdutta.async;\n\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.callback.DataCallback;\n\npublic interface DataEmitter {\n    void setDataCallback(DataCallback callback);\n    DataCallback getDataCallback();\n    boolean isChunked();\n    void pause();\n    void resume();\n    void close();\n    boolean isPaused();\n    void setEndCallback(CompletedCallback callback);\n    CompletedCallback getEndCallback();\n    AsyncServer getServer();\n    String charset();\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/DataEmitterBase.java",
    "content": "package com.koushikdutta.async;\n\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.callback.DataCallback;\n\n/**\n * Created by koush on 5/27/13.\n */\npublic abstract class DataEmitterBase implements DataEmitter {\n    private boolean ended;\n    protected void report(Exception e) {\n        if (ended)\n            return;\n        ended = true;\n        if (getEndCallback() != null)\n            getEndCallback().onCompleted(e);\n    }\n\n    @Override\n    public final void setEndCallback(CompletedCallback callback) {\n        endCallback = callback;\n    }\n\n    CompletedCallback endCallback;\n    @Override\n    public final CompletedCallback getEndCallback() {\n        return endCallback;\n    }\n\n\n    DataCallback mDataCallback;\n    @Override\n    public void setDataCallback(DataCallback callback) {\n        mDataCallback = callback;\n    }\n\n    @Override\n    public DataCallback getDataCallback() {\n        return mDataCallback;\n    }\n\n    @Override\n    public String charset() {\n        return null;\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/DataEmitterReader.java",
    "content": "package com.koushikdutta.async;\n\nimport com.koushikdutta.async.callback.DataCallback;\n\npublic class DataEmitterReader implements com.koushikdutta.async.callback.DataCallback {\n    DataCallback mPendingRead;\n    int mPendingReadLength;\n    ByteBufferList mPendingData = new ByteBufferList();\n\n    public void read(int count, DataCallback callback) {\n        mPendingReadLength = count;\n        mPendingRead = callback;\n        mPendingData.recycle();\n    }\n\n    private boolean handlePendingData(DataEmitter emitter) {\n        if (mPendingReadLength > mPendingData.remaining())\n            return false;\n\n        DataCallback pendingRead = mPendingRead;\n        mPendingRead = null;\n        pendingRead.onDataAvailable(emitter, mPendingData);\n\n        return true;\n    }\n\n    public DataEmitterReader() {\n    }\n    @Override\n    public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n        // if we're registered for data, we must be waiting for a read\n        do {\n            int need = Math.min(bb.remaining(), mPendingReadLength - mPendingData.remaining());\n            bb.get(mPendingData, need);\n            bb.remaining();\n        }\n        while (handlePendingData(emitter) && mPendingRead != null);\n        bb.remaining();\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/DataSink.java",
    "content": "package com.koushikdutta.async;\n\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.callback.WritableCallback;\n\npublic interface DataSink {\n    public void write(ByteBufferList bb);\n    public void setWriteableCallback(WritableCallback handler);\n    public WritableCallback getWriteableCallback();\n    \n    public boolean isOpen();\n    public void end();\n    public void setClosedCallback(CompletedCallback handler);\n    public CompletedCallback getClosedCallback();\n    public AsyncServer getServer();\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/DataTrackingEmitter.java",
    "content": "package com.koushikdutta.async;\n\n/**\n * Created by koush on 5/28/13.\n */\npublic interface DataTrackingEmitter extends DataEmitter {\n    interface DataTracker {\n        void onData(int totalBytesRead);\n    }\n    void setDataTracker(DataTracker tracker);\n    DataTracker getDataTracker();\n    int getBytesRead();\n    void setDataEmitter(DataEmitter emitter);\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/DatagramChannelWrapper.java",
    "content": "package com.koushikdutta.async;\n\nimport java.io.IOException;\nimport java.net.InetAddress;\nimport java.net.InetSocketAddress;\nimport java.nio.ByteBuffer;\nimport java.nio.channels.ClosedChannelException;\nimport java.nio.channels.DatagramChannel;\nimport java.nio.channels.SelectionKey;\nimport java.nio.channels.Selector;\n\nclass DatagramChannelWrapper extends ChannelWrapper {\n    DatagramChannel mChannel;\n\n    @Override\n    public InetAddress getLocalAddress() {\n        return mChannel.socket().getLocalAddress();\n    }\n\n    @Override\n    public int getLocalPort() {\n        return mChannel.socket().getLocalPort();\n    }\n\n    InetSocketAddress address;\n    public InetSocketAddress getRemoteAddress() {\n        return address;\n    }\n    \n    public void disconnect() throws IOException {\n        mChannel.disconnect();\n    }\n    \n    DatagramChannelWrapper(DatagramChannel channel) throws IOException {\n        super(channel);\n        mChannel = channel;\n    }\n    @Override\n    public int read(ByteBuffer buffer) throws IOException {\n        if (!isConnected()) {\n            int position = buffer.position();\n            address = (InetSocketAddress)mChannel.receive(buffer);\n            if (address == null)\n                return -1;\n            return buffer.position() - position;\n        }\n        address = null;\n        return mChannel.read(buffer);\n    }\n    @Override\n    public boolean isConnected() {\n        return mChannel.isConnected();\n    }\n    @Override\n    public int write(ByteBuffer src) throws IOException {\n        return mChannel.write(src);\n    }\n    @Override\n    public int write(ByteBuffer[] src) throws IOException {\n        return (int)mChannel.write(src);\n    }\n    @Override\n    public SelectionKey register(Selector sel, int ops) throws ClosedChannelException {\n        return mChannel.register(sel, ops);\n    }\n    @Override\n    public boolean isChunked() {\n        return true;\n    }\n    @Override\n    public SelectionKey register(Selector sel) throws ClosedChannelException {\n        return register(sel, SelectionKey.OP_READ);\n    }\n\n    @Override\n    public void shutdownOutput() {\n    }\n\n    @Override\n    public void shutdownInput() {\n    }\n\n    @Override\n    public long read(ByteBuffer[] byteBuffers) throws IOException {\n        return mChannel.read(byteBuffers);\n    }\n\n    @Override\n    public long read(ByteBuffer[] byteBuffers, int i, int i2) throws IOException {\n        return mChannel.read(byteBuffers, i, i2);\n    }\n\n    @Override\n    public Object getSocket() {\n        return mChannel.socket();\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/FileDataEmitter.java",
    "content": "package com.koushikdutta.async;\n\nimport com.koushikdutta.async.callback.DataCallback;\nimport com.koushikdutta.async.util.StreamUtility;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.nio.ByteBuffer;\nimport java.nio.channels.FileChannel;\n\n/**\n * Created by koush on 5/22/13.\n */\npublic class FileDataEmitter extends DataEmitterBase {\n    AsyncServer server;\n    File file;\n    public FileDataEmitter(AsyncServer server, File file) {\n        this.server = server;\n        this.file = file;\n        paused = !server.isAffinityThread();\n        if (!paused)\n            doResume();\n    }\n\n    DataCallback callback;\n    @Override\n    public void setDataCallback(DataCallback callback) {\n        this.callback = callback;\n    }\n\n    @Override\n    public DataCallback getDataCallback() {\n        return callback;\n    }\n\n    @Override\n    public boolean isChunked() {\n        return false;\n    }\n\n    boolean paused;\n    @Override\n    public void pause() {\n        paused = true;\n    }\n\n    @Override\n    public void resume() {\n        paused = false;\n        doResume();\n    }\n\n    @Override\n    protected void report(Exception e) {\n        StreamUtility.closeQuietly(channel);\n        super.report(e);\n    }\n\n    ByteBufferList pending = new ByteBufferList();\n    FileChannel channel;\n    Runnable pumper = new Runnable() {\n        @Override\n        public void run() {\n            try {\n                if (channel == null)\n                    channel = new FileInputStream(file).getChannel();\n                if (!pending.isEmpty()) {\n                    Util.emitAllData(FileDataEmitter.this, pending);\n                    if (!pending.isEmpty())\n                        return;\n                }\n                ByteBuffer b;\n                do {\n                    b = ByteBufferList.obtain(8192);\n                    if (-1 == channel.read(b)) {\n                        report(null);\n                        return;\n                    }\n                    b.flip();\n                    pending.add(b);\n                    Util.emitAllData(FileDataEmitter.this, pending);\n                }\n                while (pending.remaining() == 0 && !isPaused());\n            }\n            catch (Exception e) {\n                report(e);\n            }\n        }\n    };\n\n    private void doResume() {\n        server.post(pumper);\n    }\n\n    @Override\n    public boolean isPaused() {\n        return paused;\n    }\n\n    @Override\n    public AsyncServer getServer() {\n        return server;\n    }\n\n    @Override\n    public void close() {\n        try {\n            channel.close();\n        }\n        catch (Exception e) {\n        }\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/FilteredDataEmitter.java",
    "content": "package com.koushikdutta.async;\n\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.callback.DataCallback;\nimport com.koushikdutta.async.wrapper.DataEmitterWrapper;\n\npublic class FilteredDataEmitter extends DataEmitterBase implements DataEmitter, DataCallback, DataEmitterWrapper, DataTrackingEmitter {\n    private DataEmitter mEmitter;\n    @Override\n    public DataEmitter getDataEmitter() {\n        return mEmitter;\n    }\n\n    @Override\n    public void setDataEmitter(DataEmitter emitter) {\n        if (mEmitter != null) {\n            mEmitter.setDataCallback(null);\n        }\n        mEmitter = emitter;\n        mEmitter.setDataCallback(this);\n        mEmitter.setEndCallback(new CompletedCallback() {\n            @Override\n            public void onCompleted(Exception ex) {\n                report(ex);\n            }\n        });\n    }\n\n    @Override\n    public int getBytesRead() {\n        return totalRead;\n    }\n\n    @Override\n    public DataTracker getDataTracker() {\n        return tracker;\n    }\n\n    @Override\n    public void setDataTracker(DataTracker tracker) {\n        this.tracker = tracker;\n    }\n\n    private DataTracker tracker;\n    private int totalRead;\n    @Override\n    public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n        if (closed) {\n            // this emitter was closed but for some reason data is still being spewed...\n            // eat it, nom nom.\n            bb.recycle();\n            return;\n        }\n        if (bb != null)\n            totalRead += bb.remaining();\n        Util.emitAllData(this, bb);\n        if (bb != null)\n            totalRead -= bb.remaining();\n        if (tracker != null && bb != null)\n            tracker.onData(totalRead);\n        // if there's data after the emitting, and it is paused... the underlying implementation\n        // is obligated to cache the byte buffer list.\n    }\n\n    @Override\n    public boolean isChunked() {\n        return mEmitter.isChunked();\n    }\n\n    @Override\n    public void pause() {\n        mEmitter.pause();\n    }\n\n    @Override\n    public void resume() {\n        mEmitter.resume();\n    }\n\n    @Override\n    public boolean isPaused() {\n        return mEmitter.isPaused();\n    }\n\n    @Override\n    public AsyncServer getServer() {\n        return mEmitter.getServer();\n    }\n\n    boolean closed;\n    @Override\n    public void close() {\n        closed = true;\n        if (mEmitter != null)\n            mEmitter.close();\n    }\n\n    @Override\n    public String charset() {\n        if (mEmitter == null)\n            return null;\n        return mEmitter.charset();\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/FilteredDataSink.java",
    "content": "package com.koushikdutta.async;\n\npublic class FilteredDataSink extends BufferedDataSink {\n    public FilteredDataSink(DataSink sink) {\n        super(sink);\n        setMaxBuffer(0);\n    }\n    \n    public ByteBufferList filter(ByteBufferList bb) {\n        return bb;\n    }\n\n    @Override\n    protected void onDataAccepted(ByteBufferList bb) {\n        ByteBufferList filtered = filter(bb);\n        // filtering may return the same byte buffer, so watch for that.\n        if (filtered != bb) {\n            bb.recycle();\n            filtered.get(bb);\n        }\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/HostnameResolutionException.java",
    "content": "package com.koushikdutta.async;\n\npublic class HostnameResolutionException extends Exception {\n    public HostnameResolutionException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/LineEmitter.java",
    "content": "package com.koushikdutta.async;\n\nimport com.koushikdutta.async.callback.DataCallback;\n\nimport java.nio.ByteBuffer;\nimport java.nio.charset.Charset;\n\npublic class LineEmitter implements DataCallback {\n    public interface StringCallback {\n        void onStringAvailable(String s);\n    }\n\n    public LineEmitter() {\n        this(null);\n    }\n\n    public LineEmitter(Charset charset) {\n        this.charset = charset;\n    }\n\n    Charset charset;\n\n    ByteBufferList data = new ByteBufferList();\n\n    StringCallback mLineCallback;\n    public void setLineCallback(StringCallback callback) {\n        mLineCallback = callback;\n    }\n\n    public StringCallback getLineCallback() {\n        return mLineCallback;\n    }\n\n    @Override\n    public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n        ByteBuffer buffer = ByteBuffer.allocate(bb.remaining());\n        while (bb.remaining() > 0) {\n            byte b = bb.get();\n            if (b == '\\n') {\n                buffer.flip();\n                data.add(buffer);\n                mLineCallback.onStringAvailable(data.readString(charset));\n                data = new ByteBufferList();\n                return;\n            }\n            else {\n                buffer.put(b);\n            }\n        }\n        buffer.flip();\n        data.add(buffer);\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/PushParser.java",
    "content": "package com.koushikdutta.async;\n\nimport android.util.Log;\nimport com.koushikdutta.async.callback.DataCallback;\n\nimport java.lang.reflect.Method;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.util.ArrayList;\nimport java.util.Hashtable;\nimport java.util.LinkedList;\n\npublic class PushParser implements DataCallback {\n\n    public interface ParseCallback<T> {\n        public void parsed(T data);\n    }\n\n    static abstract class Waiter {\n        int length;\n        public Waiter(int length) {\n            this.length = length;\n        }\n        /**\n         * Consumes received data, and/or returns next waiter to continue reading instead of this waiter.\n         * @param bb received data, bb.remaining >= length\n         * @return - a waiter that should continue reading right away, or null if this waiter is finished\n         */\n        public abstract Waiter onDataAvailable(DataEmitter emitter, ByteBufferList bb);\n    }\n\n    static class IntWaiter extends Waiter {\n        ParseCallback<Integer> callback;\n        public IntWaiter(ParseCallback<Integer> callback) {\n            super(4);\n            this.callback = callback;\n        }\n\n        @Override\n        public Waiter onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n            callback.parsed(bb.getInt());\n            return null;\n        }\n    }\n\n    static class ByteArrayWaiter extends Waiter {\n        ParseCallback<byte[]> callback;\n        public ByteArrayWaiter(int length, ParseCallback<byte[]> callback) {\n            super(length);\n            if (length <= 0)\n                throw new IllegalArgumentException(\"length should be > 0\");\n            this.callback = callback;\n        }\n\n        @Override\n        public Waiter onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n            byte[] bytes = new byte[length];\n            bb.get(bytes);\n            callback.parsed(bytes);\n            return null;\n        }\n    }\n\n    static class LenByteArrayWaiter extends Waiter {\n        private final ParseCallback<byte[]> callback;\n\n        public LenByteArrayWaiter(ParseCallback<byte[]> callback) {\n            super(4);\n            this.callback = callback;\n        }\n\n        @Override\n        public Waiter onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n            int length = bb.getInt();\n            if (length == 0) {\n                callback.parsed(new byte[0]);\n                return null;\n            }\n            return new ByteArrayWaiter(length, callback);\n        }\n    }\n\n\n    static class ByteBufferListWaiter extends Waiter {\n        ParseCallback<ByteBufferList> callback;\n        public ByteBufferListWaiter(int length, ParseCallback<ByteBufferList> callback) {\n            super(length);\n            if (length <= 0) throw new IllegalArgumentException(\"length should be > 0\");\n            this.callback = callback;\n        }\n\n        @Override\n        public Waiter onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n            callback.parsed(bb.get(length));\n            return null;\n        }\n    }\n\n    static class LenByteBufferListWaiter extends Waiter {\n        private final ParseCallback<ByteBufferList> callback;\n\n        public LenByteBufferListWaiter(ParseCallback<ByteBufferList> callback) {\n            super(4);\n            this.callback = callback;\n        }\n\n        @Override\n        public Waiter onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n            int length = bb.getInt();\n            return new ByteBufferListWaiter(length, callback);\n        }\n    }\n\n    static class UntilWaiter extends Waiter {\n\n        byte value;\n        DataCallback callback;\n        public UntilWaiter(byte value, DataCallback callback) {\n            super(1);\n            this.value = value;\n            this.callback = callback;\n        }\n\n        @Override\n        public Waiter onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n            boolean found = true;\n            ByteBufferList cb = new ByteBufferList();\n            while (bb.size() > 0) {\n                ByteBuffer b = bb.remove();\n                b.mark();\n                int index = 0;\n                while (b.remaining() > 0 && !(found = (b.get() == value))) {\n                    index++;\n                }\n                b.reset();\n                if (found) {\n                    bb.addFirst(b);\n                    bb.get(cb, index);\n                    // eat the one we're waiting on\n                    bb.get();\n                    break;\n                } else {\n                    cb.add(b);\n                }\n            }\n\n            callback.onDataAvailable(emitter, cb);\n\n            if (found) {\n                return null;\n            } else {\n                return this;\n            }\n        }\n    }\n\n    private class TapWaiter extends Waiter {\n        private final TapCallback callback;\n\n        public TapWaiter(TapCallback callback) {\n            super(0);\n            this.callback = callback;\n        }\n\n        @Override\n        public Waiter onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n            Method method = getTap(callback);\n            method.setAccessible(true);\n            try {\n                method.invoke(callback, args.toArray());\n            } catch (Exception e) {\n                Log.e(\"PushParser\", \"Error while invoking tap callback\", e);\n            }\n            args.clear();\n            return null;\n        }\n    }\n\n    private Waiter noopArgWaiter = new Waiter(0) {\n        @Override\n        public Waiter onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n            args.add(null);\n            return null;\n        }\n    };\n\n    private Waiter byteArgWaiter = new Waiter(1) {\n        @Override\n        public Waiter onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n            args.add(bb.get());\n            return null;\n        }\n    };\n\n    private Waiter shortArgWaiter = new Waiter(2) {\n        @Override\n        public Waiter onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n            args.add(bb.getShort());\n            return null;\n        }\n    };\n\n    private Waiter intArgWaiter = new Waiter(4) {\n        @Override\n        public Waiter onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n            args.add(bb.getInt());\n            return null;\n        }\n    };\n\n    private Waiter longArgWaiter = new Waiter(8) {\n        @Override\n        public Waiter onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n            args.add(bb.getLong());\n            return null;\n        }\n    };\n\n    private ParseCallback<byte[]> byteArrayArgCallback = new ParseCallback<byte[]>() {\n        @Override\n        public void parsed(byte[] data) {\n            args.add(data);\n        }\n    };\n\n    private ParseCallback<ByteBufferList> byteBufferListArgCallback = new ParseCallback<ByteBufferList>() {\n        @Override\n        public void parsed(ByteBufferList data) {\n            args.add(data);\n        }\n    };\n\n    private ParseCallback<byte[]> stringArgCallback = new ParseCallback<byte[]>() {\n        @Override\n        public void parsed(byte[] data) {\n            args.add(new String(data));\n        }\n    };\n\n    DataEmitter mEmitter;\n    private LinkedList<Waiter> mWaiting = new LinkedList<Waiter>();\n    private ArrayList<Object> args = new ArrayList<Object>();\n    ByteOrder order = ByteOrder.BIG_ENDIAN;\n\n    public PushParser setOrder(ByteOrder order) {\n        this.order = order;\n        return this;\n    }\n\n    public PushParser(DataEmitter s) {\n        mEmitter = s;\n        mEmitter.setDataCallback(this);\n    }\n\n    public PushParser readInt(ParseCallback<Integer> callback) {\n        mWaiting.add(new IntWaiter(callback));\n        return this;\n    }\n\n    public PushParser readByteArray(int length, ParseCallback<byte[]> callback) {\n        mWaiting.add(new ByteArrayWaiter(length, callback));\n        return this;\n    }\n\n    public PushParser readByteBufferList(int length, ParseCallback<ByteBufferList> callback) {\n        mWaiting.add(new ByteBufferListWaiter(length, callback));\n        return this;\n    }\n\n    public PushParser until(byte b, DataCallback callback) {\n        mWaiting.add(new UntilWaiter(b, callback));\n        return this;\n    }\n\n    public PushParser readByte() {\n        mWaiting.add(byteArgWaiter);\n        return this;\n    }\n\n    public PushParser readShort() {\n        mWaiting.add(shortArgWaiter);\n        return this;\n    }\n\n    public PushParser readInt() {\n        mWaiting.add(intArgWaiter);\n        return this;\n    }\n\n    public PushParser readLong() {\n        mWaiting.add(longArgWaiter);\n        return this;\n    }\n\n    public PushParser readByteArray(int length) {\n        return (length == -1) ? readLenByteArray() : readByteArray(length, byteArrayArgCallback);\n    }\n\n    public PushParser readLenByteArray() {\n        mWaiting.add(new LenByteArrayWaiter(byteArrayArgCallback));\n        return this;\n    }\n\n    public PushParser readByteBufferList(int length) {\n        return (length == -1) ? readLenByteBufferList() : readByteBufferList(length, byteBufferListArgCallback);\n    }\n\n    public PushParser readLenByteBufferList() {\n        return readLenByteBufferList(byteBufferListArgCallback);\n    }\n\n    public PushParser readLenByteBufferList(ParseCallback<ByteBufferList> callback) {\n        mWaiting.add(new LenByteBufferListWaiter(callback));\n        return this;\n    }\n\n    public PushParser readString() {\n        mWaiting.add(new LenByteArrayWaiter(stringArgCallback));\n        return this;\n    }\n\n    public PushParser noop() {\n        mWaiting.add(noopArgWaiter);\n        return this;\n    }\n\n    ByteBufferList pending = new ByteBufferList();\n    @Override\n    public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n        bb.get(pending);\n        while (mWaiting.size() > 0 && pending.remaining() >= mWaiting.peek().length) {\n            pending.order(order);\n            Waiter next = mWaiting.poll().onDataAvailable(emitter, pending);\n            if (next != null) mWaiting.addFirst(next);\n        }\n        if (mWaiting.size() == 0)\n            pending.get(bb);\n    }\n\n    public void tap(TapCallback callback) {\n        mWaiting.add(new TapWaiter(callback));\n    }\n\n    static Hashtable<Class, Method> mTable = new Hashtable<Class, Method>();\n    static Method getTap(TapCallback callback) {\n        Method found = mTable.get(callback.getClass());\n        if (found != null)\n            return found;\n\n        for (Method method : callback.getClass().getMethods()) {\n            if (\"tap\".equals(method.getName())) {\n                mTable.put(callback.getClass(), method);\n                return method;\n            }\n        }\n\n        // try the proguard friendly route, take the first/only method\n        // in case \"tap\" has been renamed\n        Method[] candidates = callback.getClass().getDeclaredMethods();\n        if (candidates.length == 1)\n            return candidates[0];\n\n        String fail =\n            \"-keep class * extends com.koushikdutta.async.TapCallback {\\n\" +\n                    \"    *;\\n\" +\n                    \"}\\n\";\n\n        //null != \"AndroidAsync: tap callback could not be found. Proguard? Use this in your proguard config:\\n\" + fail;\n        throw new AssertionError(fail);\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/SelectorWrapper.java",
    "content": "package com.koushikdutta.async;\n\nimport java.io.Closeable;\nimport java.io.IOException;\nimport java.nio.channels.SelectionKey;\nimport java.nio.channels.Selector;\nimport java.util.Set;\nimport java.util.concurrent.Semaphore;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\n/**\n * Created by koush on 2/13/14.\n */\nclass SelectorWrapper implements Closeable {\n    private Selector selector;\n    public AtomicBoolean isWaking = new AtomicBoolean(false);\n    Semaphore semaphore = new Semaphore(0);\n    public Selector getSelector() {\n        return selector;\n    }\n\n    public SelectorWrapper(Selector selector) {\n        this.selector = selector;\n    }\n\n    public int selectNow() throws IOException {\n        return selector.selectNow();\n    }\n\n    public void select() throws IOException {\n        select(0);\n    }\n\n    public void select(long timeout) throws IOException {\n        try {\n            semaphore.drainPermits();\n            selector.select(timeout);\n        }\n        finally {\n            semaphore.release(Integer.MAX_VALUE);\n        }\n    }\n\n    public Set<SelectionKey> keys() {\n        return selector.keys();\n    }\n\n    public Set<SelectionKey> selectedKeys() {\n        return selector.selectedKeys();\n    }\n\n    @Override\n    public void close() throws IOException {\n        selector.close();\n    }\n\n    public boolean isOpen() {\n        return selector.isOpen();\n    }\n\n    public void wakeupOnce() {\n        // see if it is selecting, ie, can't acquire a permit\n        boolean selecting = !semaphore.tryAcquire();\n        selector.wakeup();\n        // if it was selecting, then the wakeup definitely worked.\n        if (selecting)\n            return;\n\n        // now, we NEED to wait for the select to start to forcibly wake it.\n        if (isWaking.getAndSet(true)) {\n            selector.wakeup();\n            return;\n        }\n\n        try {\n            waitForSelect();\n            selector.wakeup();\n        } finally {\n            isWaking.set(false);\n        }\n    }\n\n    public boolean waitForSelect() {\n        // try to wake up 10 times\n        for (int i = 0; i < 100; i++) {\n            try {\n                if (semaphore.tryAcquire(10, TimeUnit.MILLISECONDS)) {\n                    // successfully acquiring means the selector is NOT selecting, since select\n                    // will drain all permits.\n                    continue;\n                }\n            } catch (InterruptedException e) {\n                // an InterruptedException means the acquire failed a select is in progress,\n                // since it holds all permits\n                return true;\n            }\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/ServerSocketChannelWrapper.java",
    "content": "package com.koushikdutta.async;\n\nimport java.io.IOException;\nimport java.net.InetAddress;\nimport java.nio.ByteBuffer;\nimport java.nio.channels.ClosedChannelException;\nimport java.nio.channels.SelectionKey;\nimport java.nio.channels.Selector;\nimport java.nio.channels.ServerSocketChannel;\n\nclass ServerSocketChannelWrapper extends ChannelWrapper {\n    ServerSocketChannel mChannel;\n\n    @Override\n    public void shutdownOutput() {\n    }\n\n    @Override\n    public void shutdownInput() {\n    }\n\n    @Override\n    public InetAddress getLocalAddress() {\n        return mChannel.socket().getInetAddress();\n    }\n\n    @Override\n    public int getLocalPort() {\n        return mChannel.socket().getLocalPort();\n    }\n\n    ServerSocketChannelWrapper(ServerSocketChannel channel) throws IOException {\n        super(channel);\n        mChannel = channel;\n    }\n\n    @Override\n    public int read(ByteBuffer buffer) throws IOException {\n        final String msg = \"Can't read ServerSocketChannel\";\n        throw new IOException(msg);\n    }\n\n    @Override\n    public boolean isConnected() {\n        return false;\n    }\n\n    @Override\n    public int write(ByteBuffer src) throws IOException {\n        final String msg = \"Can't write ServerSocketChannel\";\n        throw new IOException(msg);\n    }\n\n    @Override\n    public SelectionKey register(Selector sel) throws ClosedChannelException {\n        return mChannel.register(sel, SelectionKey.OP_ACCEPT);\n    }\n\n    @Override\n    public int write(ByteBuffer[] src) throws IOException {\n        final String msg = \"Can't write ServerSocketChannel\";\n        throw new IOException(msg);\n    }\n\n    @Override\n    public long read(ByteBuffer[] byteBuffers) throws IOException {\n        final String msg = \"Can't read ServerSocketChannel\";\n        throw new IOException(msg);\n    }\n\n    @Override\n    public long read(ByteBuffer[] byteBuffers, int i, int i2) throws IOException {\n        final String msg = \"Can't read ServerSocketChannel\";\n        throw new IOException(msg);\n    }\n\n    @Override\n    public Object getSocket() {\n        return mChannel.socket();\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/SocketChannelWrapper.java",
    "content": "package com.koushikdutta.async;\n\nimport java.io.IOException;\nimport java.net.InetAddress;\nimport java.nio.ByteBuffer;\nimport java.nio.channels.ClosedChannelException;\nimport java.nio.channels.SelectionKey;\nimport java.nio.channels.Selector;\nimport java.nio.channels.SocketChannel;\n\nclass SocketChannelWrapper extends ChannelWrapper {\n    SocketChannel mChannel;\n\n    @Override\n    public InetAddress getLocalAddress() {\n        return mChannel.socket().getLocalAddress();\n    }\n\n    @Override\n    public int getLocalPort() {\n        return mChannel.socket().getLocalPort();\n    }\n\n    SocketChannelWrapper(SocketChannel channel) throws IOException {\n        super(channel);\n        mChannel = channel;\n    }\n    @Override\n    public int read(ByteBuffer buffer) throws IOException {\n        return mChannel.read(buffer);\n    }\n    @Override\n    public boolean isConnected() {\n        return mChannel.isConnected();\n    }\n    @Override\n    public int write(ByteBuffer src) throws IOException {\n        return mChannel.write(src);\n    }\n    @Override\n    public int write(ByteBuffer[] src) throws IOException {\n        return (int)mChannel.write(src);\n    }\n    @Override\n    public SelectionKey register(Selector sel) throws ClosedChannelException {\n        return register(sel, SelectionKey.OP_CONNECT);\n    }\n\n    @Override\n    public void shutdownOutput() {\n        try {\n            mChannel.socket().shutdownOutput();\n        }\n        catch (Exception e) {\n        }\n    }\n\n    @Override\n    public void shutdownInput() {\n        try {\n            mChannel.socket().shutdownInput();\n        }\n        catch (Exception e) {\n        }\n    }\n\n    @Override\n    public long read(ByteBuffer[] byteBuffers) throws IOException {\n        return mChannel.read(byteBuffers);\n    }\n\n    @Override\n    public long read(ByteBuffer[] byteBuffers, int i, int i2) throws IOException {\n        return mChannel.read(byteBuffers, i, i2);\n    }\n\n    @Override\n    public Object getSocket() {\n        return mChannel.socket();\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/TapCallback.java",
    "content": "package com.koushikdutta.async;\n\n\npublic interface TapCallback {\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/ThreadQueue.java",
    "content": "package com.koushikdutta.async;\n\nimport java.util.LinkedList;\nimport java.util.WeakHashMap;\nimport java.util.concurrent.Semaphore;\n\nclass ThreadQueue extends LinkedList<Runnable> {\n    final private static WeakHashMap<Thread, ThreadQueue> mThreadQueues = new WeakHashMap<Thread, ThreadQueue>();\n\n    static ThreadQueue getOrCreateThreadQueue(Thread thread) {\n        ThreadQueue queue;\n        synchronized (mThreadQueues) {\n            queue = mThreadQueues.get(thread);\n            if (queue == null) {\n                queue = new ThreadQueue();\n                mThreadQueues.put(thread, queue);\n            }\n        }\n\n        return queue;\n    }\n\n    static void release(AsyncSemaphore semaphore) {\n        synchronized (mThreadQueues) {\n            for (ThreadQueue threadQueue: mThreadQueues.values()) {\n                if (threadQueue.waiter == semaphore)\n                    threadQueue.queueSemaphore.release();\n            }\n        }\n    }\n\n    AsyncSemaphore waiter;\n    Semaphore queueSemaphore = new Semaphore(0);\n\n    @Override\n    public boolean add(Runnable object) {\n        synchronized (this) {\n            return super.add(object);\n        }\n    }\n\n    @Override\n    public boolean remove(Object object) {\n        synchronized (this) {\n            return super.remove(object);\n        }\n    }\n\n    @Override\n    public Runnable remove() {\n        synchronized (this) {\n            if (this.isEmpty())\n                return null;\n            return super.remove();\n        }\n    }\n}"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/Util.java",
    "content": "package com.koushikdutta.async;\n\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.callback.DataCallback;\nimport com.koushikdutta.async.callback.WritableCallback;\nimport com.koushikdutta.async.util.Allocator;\nimport com.koushikdutta.async.util.StreamUtility;\nimport com.koushikdutta.async.wrapper.AsyncSocketWrapper;\nimport com.koushikdutta.async.wrapper.DataEmitterWrapper;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.ByteBuffer;\n\npublic class Util {\n    public static boolean SUPRESS_DEBUG_EXCEPTIONS = false;\n    public static void emitAllData(DataEmitter emitter, ByteBufferList list) {\n        int remaining;\n        DataCallback handler = null;\n        while (!emitter.isPaused() && (handler = emitter.getDataCallback()) != null && (remaining = list.remaining()) > 0) {\n            handler.onDataAvailable(emitter, list);\n            if (remaining == list.remaining() && handler == emitter.getDataCallback() && !emitter.isPaused()) {\n                // this is generally indicative of failure...\n\n                // 1) The data callback has not changed\n                // 2) no data was consumed\n                // 3) the data emitter was not paused\n\n                // call byteBufferList.recycle() or read all the data to prevent this assertion.\n                // this is nice to have, as it identifies protocol or parsing errors.\n\n//                System.out.println(\"Data: \" + list.peekString());\n                System.out.println(\"handler: \" + handler);\n                list.recycle();\n                if (SUPRESS_DEBUG_EXCEPTIONS)\n                    return;\n                throw new RuntimeException(\"mDataHandler failed to consume data, yet remains the mDataHandler.\");\n            }\n        }\n        if (list.remaining() != 0 && !emitter.isPaused()) {\n            // not all the data was consumed...\n            // call byteBufferList.recycle() or read all the data to prevent this assertion.\n            // this is nice to have, as it identifies protocol or parsing errors.\n//            System.out.println(\"Data: \" + list.peekString());\n            System.out.println(\"handler: \" + handler);\n            System.out.println(\"emitter: \" + emitter);\n            list.recycle();\n            if (SUPRESS_DEBUG_EXCEPTIONS)\n                return;\n//            throw new AssertionError(\"Not all data was consumed by Util.emitAllData\");\n        }\n    }\n\n    public static void pump(final InputStream is, final DataSink ds, final CompletedCallback callback) {\n        pump(is, Integer.MAX_VALUE, ds, callback);\n    }\n\n    public static void pump(final InputStream is, final long max, final DataSink ds, final CompletedCallback callback) {\n        final CompletedCallback wrapper = new CompletedCallback() {\n            boolean reported;\n            @Override\n            public void onCompleted(Exception ex) {\n                if (reported)\n                    return;\n                reported = true;\n                callback.onCompleted(ex);\n            }\n        };\n\n        final WritableCallback cb = new WritableCallback() {\n            int totalRead = 0;\n            private void cleanup() {\n                ds.setClosedCallback(null);\n                ds.setWriteableCallback(null);\n                pending.recycle();\n                StreamUtility.closeQuietly(is);\n            }\n            ByteBufferList pending = new ByteBufferList();\n            Allocator allocator = new Allocator().setMinAlloc((int)Math.min(2 << 19, max));\n\n            @Override\n            public void onWriteable() {\n                try {\n                    do {\n                        if (!pending.hasRemaining()) {\n                            ByteBuffer b = allocator.allocate();\n\n                            long toRead = Math.min(max - totalRead, b.capacity());\n                            int read = is.read(b.array(), 0, (int)toRead);\n                            if (read == -1 || totalRead == max) {\n                                cleanup();\n                                wrapper.onCompleted(null);\n                                return;\n                            }\n                            allocator.track(read);\n                            totalRead += read;\n                            b.position(0);\n                            b.limit(read);\n                            pending.add(b);\n                        }\n                        \n                        ds.write(pending);\n                    }\n                    while (!pending.hasRemaining());\n                }\n                catch (Exception e) {\n                    cleanup();\n                    wrapper.onCompleted(e);\n                }\n            }\n        };\n        ds.setWriteableCallback(cb);\n\n        ds.setClosedCallback(wrapper);\n        \n        cb.onWriteable();\n    }\n    \n    public static void pump(final DataEmitter emitter, final DataSink sink, final CompletedCallback callback) {\n        final DataCallback dataCallback = new DataCallback() {\n            @Override\n            public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n                sink.write(bb);\n                if (bb.remaining() > 0)\n                    emitter.pause();\n            }\n        };\n        emitter.setDataCallback(dataCallback);\n        sink.setWriteableCallback(new WritableCallback() {\n            @Override\n            public void onWriteable() {\n                emitter.resume();\n            }\n        });\n\n        final CompletedCallback wrapper = new CompletedCallback() {\n            boolean reported;\n            @Override\n            public void onCompleted(Exception ex) {\n                if (reported)\n                    return;\n                reported = true;\n                emitter.setDataCallback(null);\n                emitter.setEndCallback(null);\n                sink.setClosedCallback(null);\n                sink.setWriteableCallback(null);\n                callback.onCompleted(ex);\n            }\n        };\n\n        emitter.setEndCallback(wrapper);\n        sink.setClosedCallback(new CompletedCallback() {\n            @Override\n            public void onCompleted(Exception ex) {\n                if (ex == null)\n                    ex = new IOException(\"sink was closed before emitter ended\");\n                wrapper.onCompleted(ex);\n            }\n        });\n    }\n    \n    public static void stream(AsyncSocket s1, AsyncSocket s2, CompletedCallback callback) {\n        pump(s1, s2, callback);\n        pump(s2, s1, callback);\n    }\n    \n    public static void pump(final File file, final DataSink ds, final CompletedCallback callback) {\n        try {\n            if (file == null || ds == null) {\n                callback.onCompleted(null);\n                return;\n            }\n            final InputStream is = new FileInputStream(file);\n            pump(is, ds, new CompletedCallback() {\n                @Override\n                public void onCompleted(Exception ex) {\n                    try {\n                        is.close();\n                        callback.onCompleted(ex);\n                    }\n                    catch (IOException e) {\n                        callback.onCompleted(e);\n                    }\n                }\n            });\n        }\n        catch (Exception e) {\n            callback.onCompleted(e);\n        }\n    }\n\n    public static void writeAll(final DataSink sink, final ByteBufferList bb, final CompletedCallback callback) {\n        WritableCallback wc;\n        sink.setWriteableCallback(wc = new WritableCallback() {\n            @Override\n            public void onWriteable() {\n                sink.write(bb);\n                if (bb.remaining() == 0 && callback != null) {\n                    sink.setWriteableCallback(null);\n                    callback.onCompleted(null);\n                }\n            }\n        });\n        wc.onWriteable();\n    }\n    public static void writeAll(DataSink sink, byte[] bytes, CompletedCallback callback) {\n        ByteBuffer bb = ByteBufferList.obtain(bytes.length);\n        bb.put(bytes);\n        bb.flip();\n        ByteBufferList bbl = new ByteBufferList();\n        bbl.add(bb);\n        writeAll(sink, bbl, callback);\n    }\n\n    public static <T extends AsyncSocket> T getWrappedSocket(AsyncSocket socket, Class<T> wrappedClass) {\n        if (wrappedClass.isInstance(socket))\n            return (T)socket;\n        while (socket instanceof AsyncSocketWrapper) {\n            socket = ((AsyncSocketWrapper)socket).getSocket();\n            if (wrappedClass.isInstance(socket))\n                return (T)socket;\n        }\n        return null;\n    }\n\n    public static DataEmitter getWrappedDataEmitter(DataEmitter emitter, Class wrappedClass) {\n        if (wrappedClass.isInstance(emitter))\n            return emitter;\n        while (emitter instanceof DataEmitterWrapper) {\n            emitter = ((AsyncSocketWrapper)emitter).getSocket();\n            if (wrappedClass.isInstance(emitter))\n                return emitter;\n        }\n        return null;\n    }\n\n    public static void end(DataEmitter emitter, Exception e) {\n        if (emitter == null)\n            return;\n        end(emitter.getEndCallback(), e);\n    }\n\n    public static void end(CompletedCallback end, Exception e) {\n        if (end != null)\n            end.onCompleted(e);\n    }\n\n    public static void writable(DataSink emitter) {\n        if (emitter == null)\n            return;\n        writable(emitter.getWriteableCallback());\n    }\n\n    public static void writable(WritableCallback writable) {\n        if (writable != null)\n            writable.onWriteable();\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/ZipDataSink.java",
    "content": "package com.koushikdutta.async;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipOutputStream;\n\nimport com.koushikdutta.async.callback.CompletedCallback;\n\npublic class ZipDataSink extends FilteredDataSink {\n    public ZipDataSink(DataSink sink) {\n        super(sink);\n    }\n\n    ByteArrayOutputStream bout = new ByteArrayOutputStream();\n    ZipOutputStream zop = new ZipOutputStream(bout);\n\n    public void putNextEntry(ZipEntry ze) throws IOException {\n        zop.putNextEntry(ze);\n    }\n\n    public void closeEntry() throws IOException {\n        zop.closeEntry();\n    }\n    \n    protected void report(Exception e) {\n        CompletedCallback closed = getClosedCallback();\n        if (closed != null)\n            closed.onCompleted(e);\n    }\n\n    @Override\n    public void end() {\n        try {\n            zop.close();\n        }\n        catch (IOException e) {\n            report(e);\n            return;\n        }\n        setMaxBuffer(Integer.MAX_VALUE);\n        write(new ByteBufferList());\n        super.end();\n    }\n\n    @Override\n    public ByteBufferList filter(ByteBufferList bb) {\n        try {\n            if (bb != null) {\n                while (bb.size() > 0) {\n                    ByteBuffer b = bb.remove();\n                    ByteBufferList.writeOutputStream(zop, b);\n                    ByteBufferList.reclaim(b);\n                }\n            }\n            ByteBufferList ret = new ByteBufferList(bout.toByteArray());\n            bout.reset();\n            return ret;\n        }\n        catch (IOException e) {\n            report(e);\n            return null;\n        }\n        finally {\n            if (bb != null)\n                bb.recycle();\n        }\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/callback/CompletedCallback.java",
    "content": "package com.koushikdutta.async.callback;\n\npublic interface CompletedCallback {\n    public class NullCompletedCallback implements CompletedCallback {\n        @Override\n        public void onCompleted(Exception ex) {\n\n        }\n    }\n\n    public void onCompleted(Exception ex);\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/callback/ConnectCallback.java",
    "content": "package com.koushikdutta.async.callback;\n\nimport com.koushikdutta.async.AsyncSocket;\n\npublic interface ConnectCallback {\n    public void onConnectCompleted(Exception ex, AsyncSocket socket);\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/callback/ContinuationCallback.java",
    "content": "package com.koushikdutta.async.callback;\n\nimport com.koushikdutta.async.future.Continuation;\n\npublic interface ContinuationCallback {\n    public void onContinue(Continuation continuation, CompletedCallback next) throws Exception;\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/callback/DataCallback.java",
    "content": "package com.koushikdutta.async.callback;\n\nimport com.koushikdutta.async.ByteBufferList;\nimport com.koushikdutta.async.DataEmitter;\n\n\npublic interface DataCallback {\n    public class NullDataCallback implements DataCallback {\n        @Override\n        public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n            bb.recycle();\n        }\n    }\n\n    public void onDataAvailable(DataEmitter emitter, ByteBufferList bb);\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/callback/ListenCallback.java",
    "content": "package com.koushikdutta.async.callback;\n\nimport com.koushikdutta.async.AsyncServerSocket;\nimport com.koushikdutta.async.AsyncSocket;\n\n\npublic interface ListenCallback extends CompletedCallback {\n    public void onAccepted(AsyncSocket socket);\n    public void onListening(AsyncServerSocket socket);\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/callback/ResultCallback.java",
    "content": "package com.koushikdutta.async.callback;\n\npublic interface ResultCallback<S, T> {\n    public void onCompleted(Exception e, S source, T result);\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/callback/SocketCreateCallback.java",
    "content": "package com.koushikdutta.async.callback;\n\nimport com.koushikdutta.async.AsyncNetworkSocket;\n\npublic interface SocketCreateCallback {\n    void onSocketCreated(int localPort);\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/callback/ValueCallback.java",
    "content": "package com.koushikdutta.async.callback;\n\n/**\n * Created by koush on 7/5/16.\n */\npublic interface ValueCallback<T> {\n    void onResult(T value);\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/callback/ValueFunction.java",
    "content": "package com.koushikdutta.async.callback;\n\npublic interface ValueFunction<T> {\n    T getValue() throws Exception;\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/callback/WritableCallback.java",
    "content": "package com.koushikdutta.async.callback;\n\npublic interface WritableCallback {\n    public void onWriteable();\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/dns/Dns.java",
    "content": "package com.koushikdutta.async.dns;\n\nimport com.koushikdutta.async.AsyncDatagramSocket;\nimport com.koushikdutta.async.AsyncServer;\nimport com.koushikdutta.async.ByteBufferList;\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.callback.DataCallback;\nimport com.koushikdutta.async.future.Cancellable;\nimport com.koushikdutta.async.future.Future;\nimport com.koushikdutta.async.future.FutureCallback;\nimport com.koushikdutta.async.future.SimpleFuture;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.net.DatagramSocket;\nimport java.net.InetAddress;\nimport java.net.InetSocketAddress;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.util.Random;\n\n/**\n * Created by koush on 10/20/13.\n */\npublic class Dns {\n    public static Future<DnsResponse> lookup(String host) {\n        return lookup(AsyncServer.getDefault(), host, false, null);\n    }\n\n    private static int setFlag(int flags, int value, int offset) {\n        return flags | (value << offset);\n    }\n\n    private static int setQuery(int flags) {\n        return setFlag(flags, 0, 0);\n    }\n\n    private static int setRecursion(int flags) {\n        return setFlag(flags, 1, 8);\n    }\n\n    private static void addName(ByteBuffer bb, String name) {\n        String[] parts = name.split(\"\\\\.\");\n        for (String part: parts) {\n            bb.put((byte)part.length());\n            bb.put(part.getBytes());\n        }\n        bb.put((byte)0);\n    }\n\n    public static Future<DnsResponse> lookup(AsyncServer server, String host) {\n        return lookup(server, host, false, null);\n    }\n\n    public static Cancellable multicastLookup(AsyncServer server, String host, FutureCallback<DnsResponse> callback) {\n        return lookup(server, host, true, callback);\n    }\n\n    public static Cancellable multicastLookup(String host, FutureCallback<DnsResponse> callback) {\n        return multicastLookup(AsyncServer.getDefault(), host, callback);\n    }\n\n    public static Future<DnsResponse> lookup(AsyncServer server, String host, final boolean multicast, final FutureCallback<DnsResponse> callback) {\n        if (!server.isAffinityThread()) {\n            SimpleFuture<DnsResponse> ret = new SimpleFuture<>();\n            server.post(() -> ret.setComplete(lookup(server, host, multicast, callback)));\n            return ret;\n        }\n        ByteBuffer packet = ByteBufferList.obtain(1024).order(ByteOrder.BIG_ENDIAN);\n        short id = (short)new Random().nextInt();\n        short flags = (short)setQuery(0);\n        if (!multicast)\n            flags = (short)setRecursion(flags);\n\n        packet.putShort(id);\n        packet.putShort(flags);\n        // number questions\n        packet.putShort(multicast ? (short)1 : (short)2);\n        // number answer rr\n        packet.putShort((short)0);\n        // number authority rr\n        packet.putShort((short)0);\n        // number additional rr\n        packet.putShort((short)0);\n\n        addName(packet, host);\n        // query\n        packet.putShort(multicast ? (short)12 : (short)1);\n        // request internet address\n        packet.putShort((short)1);\n\n        if (!multicast) {\n            addName(packet, host);\n            // AAAA query\n            packet.putShort((short) 28);\n            // request internet address\n            packet.putShort((short)1);\n        }\n\n        packet.flip();\n\n\n        try {\n            final AsyncDatagramSocket dgram;\n            // todo, use the dns server...\n            if (!multicast) {\n                dgram = server.connectDatagram(new InetSocketAddress(\"8.8.8.8\", 53));\n            }\n            else {\n//                System.out.println(\"multicast dns...\");\n                dgram = AsyncServer.getDefault().openDatagram(null, 0, true);\n                Field field = DatagramSocket.class.getDeclaredField(\"impl\");\n                field.setAccessible(true);\n                Object impl = field.get(dgram.getSocket());\n                Method method = impl.getClass().getDeclaredMethod(\"join\", InetAddress.class);\n                method.setAccessible(true);\n                method.invoke(impl, InetAddress.getByName(\"224.0.0.251\"));\n                ((DatagramSocket)dgram.getSocket()).setBroadcast(true);\n            }\n            final SimpleFuture<DnsResponse> ret = new SimpleFuture<DnsResponse>() {\n                @Override\n                protected void cleanup() {\n                    super.cleanup();\n//                    System.out.println(\"multicast dns cleanup...\");\n                    dgram.close();\n                }\n            };\n            dgram.setDataCallback(new DataCallback() {\n                @Override\n                public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n                    try {\n//                        System.out.println(dgram.getRemoteAddress());\n                        DnsResponse response = DnsResponse.parse(bb);\n//                        System.out.println(response);\n                        response.source = dgram.getRemoteAddress();\n\n                        if (!multicast) {\n                            dgram.close();\n                            ret.setComplete(response);\n                        }\n                        else {\n                            callback.onCompleted(null, response);\n                        }\n                    }\n                    catch (Exception e) {\n                    }\n                    bb.recycle();\n                }\n            });\n            if (!multicast)\n                dgram.write(new ByteBufferList(packet));\n            else\n                dgram.send(new InetSocketAddress(\"224.0.0.251\", 5353), packet);\n            return ret;\n        }\n        catch (Exception e) {\n            SimpleFuture<DnsResponse> ret = new SimpleFuture<DnsResponse>();\n            ret.setComplete(e);\n            if (multicast)\n                callback.onCompleted(e, null);\n            return ret;\n        }\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/dns/DnsResponse.java",
    "content": "package com.koushikdutta.async.dns;\n\nimport com.koushikdutta.async.ByteBufferList;\nimport com.koushikdutta.async.http.Multimap;\n\nimport java.net.InetAddress;\nimport java.net.InetSocketAddress;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.util.ArrayList;\n\n/**\n * Created by koush on 10/20/13.\n */\npublic class DnsResponse {\n    public ArrayList<InetAddress> addresses = new ArrayList<InetAddress>();\n    public ArrayList<String> names = new ArrayList<String>();\n    public Multimap txt = new Multimap();\n    public InetSocketAddress source;\n\n    private static String parseName(ByteBufferList bb, ByteBuffer backReference) {\n        bb.order(ByteOrder.BIG_ENDIAN);\n        String ret = \"\";\n\n        int len;\n        while (0 != (len = bb.get() & 0x00FF)) {\n            // compressed\n            if ((len & 0x00c0) == 0x00c0) {\n                int offset = ((len & ~0xFFFFFFc0) << 8) | (bb.get() & 0x00FF);\n                if (ret.length() > 0)\n                    ret += \".\";\n                ByteBufferList sub = new ByteBufferList();\n                ByteBuffer duplicate = backReference.duplicate();\n                duplicate.get(new byte[offset]);\n                sub.add(duplicate);\n                return ret + parseName(sub, backReference);\n            }\n\n            byte[] bytes = new byte[len];\n            bb.get(bytes);\n            if (ret.length() > 0)\n                ret += \".\";\n            ret += new String(bytes);\n        }\n\n        return ret;\n    }\n\n    public static DnsResponse parse(ByteBufferList bb) {\n        ByteBuffer b = bb.getAll();\n        bb.add(b.duplicate());\n        // naive parsing...\n        bb.order(ByteOrder.BIG_ENDIAN);\n\n        // id\n        bb.getShort();\n        // flags\n        bb.getShort();\n\n        // number questions\n        int questions = bb.getShort();\n        // number answer rr\n        int answers = bb.getShort();\n        // number authority rr\n        int authorities = bb.getShort();\n        // number additional rr\n        int additionals = bb.getShort();\n\n        for (int i = 0; i < questions; i++) {\n            parseName(bb, b);\n            // type\n            bb.getShort();\n            // class\n            bb.getShort();\n        }\n\n        DnsResponse response = new DnsResponse();\n        for (int i = 0; i < answers; i++) {\n            String name = parseName(bb, b);\n            // type\n            int type = bb.getShort();\n            // class\n            int clazz = bb.getShort();\n            // ttl\n            int ttl = bb.getInt();\n            // length of address\n            int length = bb.getShort();\n            try {\n                if (type == 1) {\n                    // data\n                    byte[] data = new byte[length];\n                    bb.get(data);\n                    response.addresses.add(InetAddress.getByAddress(data));\n                }\n                else if (type == 0x000c) {\n                    response.names.add(parseName(bb, b));\n                }\n                else if (type == 16) {\n                    ByteBufferList txt = new ByteBufferList();\n                    bb.get(txt, length);\n                    response.parseTxt(txt);\n                }\n                else {\n                    bb.get(new byte[length]);\n                }\n            }\n            catch (Exception e) {\n//                e.printStackTrace();\n            }\n        }\n\n        // authorities\n        for (int i = 0; i < authorities; i++) {\n            String name = parseName(bb, b);\n            // type\n            int type = bb.getShort();\n            // class\n            int clazz = bb.getShort();\n            // ttl\n            int ttl = bb.getInt();\n            // length of address\n            int length = bb.getShort();\n            try {\n                bb.get(new byte[length]);\n            }\n            catch (Exception e) {\n//                e.printStackTrace();\n            }\n        }\n\n        // additionals\n        for (int i = 0; i < additionals; i++) {\n            String name = parseName(bb, b);\n            // type\n            int type = bb.getShort();\n            // class\n            int clazz = bb.getShort();\n            // ttl\n            int ttl = bb.getInt();\n            // length of address\n            int length = bb.getShort();\n            try {\n                if (type == 16) {\n                    ByteBufferList txt = new ByteBufferList();\n                    bb.get(txt, length);\n                    response.parseTxt(txt);\n                }\n                else {\n                    bb.get(new byte[length]);\n                }\n            }\n            catch (Exception e) {\n//                e.printStackTrace();\n            }\n        }\n\n        return response;\n    }\n\n    void parseTxt(ByteBufferList bb) {\n        while (bb.hasRemaining()) {\n            int length = (int)bb.get() & 0x00FF;\n            byte [] bytes = new byte[length];\n            bb.get(bytes);\n            String string = new String(bytes);\n            String[] pair = string.split(\"=\");\n            txt.add(pair[0], pair[1]);\n        }\n    }\n\n    @Override\n    public String toString() {\n        String ret = \"addresses:\\n\";\n        for (InetAddress address: addresses)\n            ret += address.toString() + \"\\n\";\n        ret += \"names:\\n\";\n        for (String name: names)\n            ret += name + \"\\n\";\n        return ret;\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/future/Cancellable.java",
    "content": "package com.koushikdutta.async.future;\n\npublic interface Cancellable {\n    /**\n     * Check whether this asynchronous operation completed successfully.\n     * @return\n     */\n    boolean isDone();\n\n    /**\n     * Check whether this asynchronous operation has been cancelled.\n     * @return\n     */\n    boolean isCancelled();\n\n    /**\n     * Attempt to cancel this asynchronous operation.\n     * @return The return value is whether the operation cancelled successfully.\n     */\n    boolean cancel();\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/future/Continuation.java",
    "content": "package com.koushikdutta.async.future;\n\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.callback.ContinuationCallback;\n\nimport java.util.LinkedList;\n\npublic class Continuation extends SimpleCancellable implements ContinuationCallback, Runnable, Cancellable {\n    CompletedCallback callback;\n    Runnable cancelCallback;\n    \n    public CompletedCallback getCallback() {\n        return callback;\n    }\n    public void setCallback(CompletedCallback callback) {\n        this.callback = callback;\n    }\n    \n    public Runnable getCancelCallback() {\n        return cancelCallback;\n    }\n    public void setCancelCallback(Runnable cancelCallback) {\n        this.cancelCallback = cancelCallback;\n    }\n    public void setCancelCallback(final Cancellable cancel) {\n        if (cancel == null) {\n            this.cancelCallback = null;\n            return;\n        }\n        this.cancelCallback = new Runnable() {\n            @Override\n            public void run() {\n                cancel.cancel();\n            }\n        };\n    }\n    \n    public Continuation() {\n        this(null);\n    }\n    public Continuation(CompletedCallback callback) {\n        this(callback, null);\n    }\n    public Continuation(CompletedCallback callback, Runnable cancelCallback) {\n        this.cancelCallback = cancelCallback;\n        this.callback = callback;\n    }\n    \n    private CompletedCallback wrap() {\n        return new CompletedCallback() {\n            boolean mThisCompleted;\n            @Override\n            public void onCompleted(Exception ex) {\n                // onCompleted may be called more than once... buggy code.\n                // only accept the first (timeouts, etc)\n                if (mThisCompleted)\n                    return;\n                mThisCompleted = true;\n                waiting = false;\n                if (ex == null) {\n                    next();\n                    return;\n                }\n                reportCompleted(ex);\n            }\n        };\n    }\n    \n    void reportCompleted(Exception ex) {\n        if (!setComplete())\n            return;\n        if (callback != null)\n            callback.onCompleted(ex);        \n    }\n    \n    LinkedList<ContinuationCallback> mCallbacks = new LinkedList<ContinuationCallback>();\n    \n    private ContinuationCallback hook(ContinuationCallback callback) {\n        if (callback instanceof DependentCancellable) {\n            DependentCancellable child = (DependentCancellable)callback;\n            child.setParent(this);\n        }\n        return callback;\n    }\n    \n    public Continuation add(ContinuationCallback callback) {\n        mCallbacks.add(hook(callback));\n        return this;\n    }\n    \n    public Continuation insert(ContinuationCallback callback) {\n        mCallbacks.add(0, hook(callback));\n        return this;\n    }\n   \n    public Continuation add(final DependentFuture future) {\n        future.setParent(this);\n        add(new ContinuationCallback() {\n            @Override\n            public void onContinue(Continuation continuation, CompletedCallback next) throws Exception {\n                future.get();\n                next.onCompleted(null);\n            }\n        });\n        return this;\n    }\n    \n    private boolean inNext;\n    private boolean waiting;\n    private void next() {\n        if (inNext)\n            return;\n        while (mCallbacks.size() > 0 && !waiting && !isDone() && !isCancelled()) {\n            ContinuationCallback cb = mCallbacks.remove();\n            try {\n                inNext = true;\n                waiting = true;\n                cb.onContinue(this, wrap());\n            }\n            catch (Exception e) {\n                reportCompleted(e);\n            }\n            finally {\n                inNext = false;\n            }\n        }\n        if (waiting)\n            return;\n        if (isDone())\n            return;\n        if (isCancelled())\n            return;\n\n        reportCompleted(null);\n    }\n\n    @Override\n    public boolean cancel() {\n        if (!super.cancel())\n            return false;\n        \n        if (cancelCallback != null)\n            cancelCallback.run();\n        \n        return true;\n    }\n    \n    boolean started;\n    public Continuation start() {\n        if (started)\n            throw new IllegalStateException(\"already started\");\n        started = true;\n        next();\n        return this;\n    }\n\n    @Override\n    public void onContinue(Continuation continuation, CompletedCallback next) throws Exception {\n        setCallback(next);\n        start();\n    }\n\n    @Override\n    public void run() {\n        start();\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/future/Converter.java",
    "content": "package com.koushikdutta.async.future;\n\nimport android.text.TextUtils;\n\nimport com.koushikdutta.async.ByteBufferList;\n\nimport org.json.JSONObject;\n\nimport java.io.InvalidObjectException;\nimport java.nio.ByteBuffer;\nimport java.util.ArrayDeque;\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.LinkedHashMap;\n\npublic class Converter<R> {\n    public static <T> Converter<T> convert(Future<T> future, String mime) {\n        return new Converter<>(future, mime);\n    }\n\n    public static <T> Converter<T> convert(Future<T> future) {\n        return convert(future, null);\n    }\n\n    static class MimedData<T> {\n        public MimedData(T data, String mime) {\n            this.data = data;\n            this.mime = mime;\n        }\n        T data;\n        String mime;\n    }\n\n    static class MultiTransformer<T, F> extends MultiTransformFuture<MimedData<Future<T>>, MimedData<Future<F>>> {\n        TypeConverter<T, F> converter;\n        String converterMime;\n        int distance;\n        public MultiTransformer(TypeConverter<T, F> converter, String converterMime, int distance) {\n            this.converter = converter;\n            this.converterMime = converterMime;\n            this.distance = distance;\n        }\n\n        @Override\n        protected void transform(MimedData<Future<F>> converting) {\n            // transform will only ever be called once, and is called immediately,\n            // the transform is on the future itself, and not a pending value.\n            // so there's no risk of running the converter twice.\n            final String mime = converting.mime;\n\n            // this future will receive the eventual actual value.\n            final MultiFuture<T> converted = new MultiFuture<>();\n\n            // this marks the conversion as \"complete\". the conversion will start\n            // as soon as the value is ready.\n            setComplete(new MimedData<>(converted, mimeReplace(mime, converterMime)));\n\n            // wait on the incoming value and convert it\n            converting.data.thenConvert(data -> converter.convert(data, mime)).\n            setCallback((e, result1) -> {\n                if (e != null)\n                    converted.setComplete(e);\n                else\n                    converted.setComplete(result1);\n            });\n        }\n    }\n\n    static abstract class EnsureHashMap<K, V> extends LinkedHashMap<K, V> {\n        synchronized V ensure(K k) {\n            if (!containsKey(k)) {\n                put(k, makeDefault());\n            }\n            return get(k);\n        }\n\n        protected abstract V makeDefault();\n    }\n\n    static class MimedType<T> {\n        MimedType(Class<T> type, String mime) {\n            this.type = type;\n            this.mime = mime;\n        }\n        Class<T> type;\n        String mime;\n\n        @Override\n        public int hashCode() {\n            return type.hashCode() ^ mime.hashCode();\n        }\n\n        @Override\n        public boolean equals(Object obj) {\n            MimedType other = (MimedType)obj;\n            return type.equals(other.type) && mime.equals(other.mime);\n        }\n\n        // check if this mimed type is the same or more specific than this mimed type\n        public boolean isTypeOf(MimedType other) {\n            // check the type, this type must be less specific than the other type\n            if (!this.type.isAssignableFrom(other.type))\n                return false;\n\n            return isTypeOf(other.mime);\n        }\n\n        public String primary() {\n            return mime.split(\"/\")[0];\n        }\n\n        public String secondary() {\n            return mime.split(\"/\")[1];\n        }\n\n        // check if this mimed type is convertible to another mimed type\n        public boolean isTypeOf(String mime) {\n            String[] otherParts = mime.split(\"/\");\n            String[] myParts = this.mime.split(\"/\");\n\n            // ensure the other type is the same OR this type is fine with a wildcard\n            if (!\"*\".equals(myParts[0]) && !otherParts[0].equals(myParts[0]))\n                return false;\n\n            if (!\"*\".equals(myParts[1]) && !otherParts[1].equals(myParts[1]))\n                return false;\n\n            return true;\n        }\n\n        @Override\n        public String toString() {\n            return type.getSimpleName() + \" \" + mime;\n        }\n    }\n\n    static class ConverterTransformers<F, T> extends LinkedHashMap<MimedType<T>, MultiTransformer<T, F>> {\n    }\n\n    static class Converters<F, T> extends EnsureHashMap<MimedType<F>, ConverterTransformers<F, T>> {\n        @Override\n        protected ConverterTransformers makeDefault() {\n            return new ConverterTransformers();\n        }\n\n        private static <F, T> void add(ConverterTransformers<F, T> set, ConverterTransformers<F, T> more) {\n            if (more == null)\n                return;\n            set.putAll(more);\n        }\n        public ConverterTransformers<F, T> getAll(MimedType<T> mimedType) {\n            ConverterTransformers<F, T> ret = new ConverterTransformers<>();\n\n            for (MimedType candidate: keySet()) {\n                if (candidate.isTypeOf(mimedType))\n                    add(ret, get(candidate));\n            }\n\n            return ret;\n        }\n    }\n\n    Converters<Object, Object> outputs;\n\n    protected ConverterEntries getConverters() {\n        return new ConverterEntries(Converters);\n    }\n\n    MultiFuture<R> future = new MultiFuture<>();\n    String futureMime;\n    protected Converter(Future future, String mime) {\n        if (TextUtils.isEmpty(mime))\n            mime = MIME_ALL;\n        this.futureMime = mime;\n        this.future.setComplete(future);\n    }\n\n    synchronized private final <T> Future<T> to(Object value, Class<T> clazz, String mime) {\n        if (clazz.isInstance(value))\n            return new SimpleFuture<>((T) value);\n        return to(value.getClass(), clazz, mime);\n    }\n\n    synchronized private final <T> Future<T> to(Class fromClass, Class<T> clazz, String mime) {\n        if (TextUtils.isEmpty(mime))\n            mime = MIME_ALL;\n\n        if (outputs == null) {\n            outputs = new Converters<>();\n            ConverterEntries converters = getConverters();\n            for (ConverterEntry entry: converters.list) {\n                outputs.ensure(entry.from).put(entry.to, new MultiTransformer<>(entry.typeConverter, entry.to.mime, entry.distance));\n            }\n        }\n\n        MimedType<T> target = new MimedType<>(clazz, mime);\n        ArrayDeque<PathInfo> bestMatch = new ArrayDeque<>();\n        ArrayDeque<PathInfo> currentPath = new ArrayDeque<>();\n        if (search(target, bestMatch, currentPath, new MimedType(fromClass, futureMime), new HashSet<MimedType>())) {\n            PathInfo current = bestMatch.removeFirst();\n\n            new SimpleFuture<>(new MimedData<>((Future<Object>)future, futureMime)).setCallback(current.transformer);\n\n            while (!bestMatch.isEmpty()) {\n                PathInfo next = bestMatch.removeFirst();\n                current.transformer.setCallback(next.transformer);\n                current = next;\n            }\n\n            return ((MultiTransformer<T, Object>)current.transformer).then(from -> from.data);\n        }\n\n        return new SimpleFuture<>(new InvalidObjectException(\"unable to find converter\"));\n    }\n\n    static class PathInfo {\n        MultiTransformer<Object, Object> transformer;\n        String mime;\n        MimedType candidate;\n\n        static int distance(ArrayDeque<PathInfo> path) {\n            int distance = 0;\n            for (PathInfo entry: path) {\n                distance += entry.transformer.distance;\n            }\n            return distance;\n        }\n    }\n\n    static String mimeReplace(String mime1, String mime2) {\n        String[] parts = mime2.split(\"/\");\n        String[] myParts = mime1.split(\"/\");\n\n        // a wildcard mime converter adopts the mime of the converted type\n        String primary = !\"*\".equals(parts[0]) ? parts[0] : myParts[0];\n        String secondary = !\"*\".equals(parts[1]) ? parts[1] : myParts[1];\n\n        return primary + \"/\" + secondary;\n    }\n\n    public final <T> Future<T> to(Class<T> clazz) {\n        return to(clazz, null);\n    }\n\n    private <T> boolean search(MimedType<T> target, ArrayDeque<PathInfo> bestMatch, ArrayDeque<PathInfo> currentPath, MimedType currentSearch, HashSet<MimedType> searched) {\n        if (target.isTypeOf(currentSearch)) {\n            bestMatch.clear();\n            bestMatch.addAll(currentPath);\n            return true;\n        }\n\n        // the current path must have potential to be better than the best match\n        if (!bestMatch.isEmpty() && PathInfo.distance(currentPath) >= PathInfo.distance(bestMatch))\n            return false;\n\n        // prevent reentrancy\n        if (searched.contains(currentSearch))\n            return false;\n\n        boolean found = false;\n        searched.add(currentSearch);\n        ConverterTransformers<Object, Object> converterTransformers = outputs.getAll(currentSearch);\n        for (MimedType candidate: converterTransformers.keySet()) {\n            // this simulates the mime results of a transform\n            MimedType newSearch = new MimedType(candidate.type, mimeReplace(currentSearch.mime, candidate.mime));\n\n            PathInfo path = new PathInfo();\n            path.transformer = converterTransformers.get(candidate);\n            path.mime = newSearch.mime;\n            path.candidate = candidate;\n            currentPath.addLast(path);\n            try {\n                found |= search(target, bestMatch, currentPath, newSearch, searched);\n            }\n            finally {\n                currentPath.removeLast();\n            }\n        }\n\n        if (found) {\n            // if this resulted in a success,\n            // clear this from the currentSearch list, because we know this leads\n            // to a potential solution. maybe we can arrive here faster.\n            searched.remove(currentSearch);\n        }\n\n        return found;\n    }\n\n    private static final String MIME_ALL = \"*/*\";\n    public <T> Future<T> to(Class<T> clazz, String mime) {\n        return future.then(from -> to(from, clazz, mime));\n    }\n\n    static class ConverterEntry<F, T> {\n        ConverterEntry(Class<F> from, String fromMime, Class<T> to, String toMime, int distance, TypeConverter<T, F> typeConverter) {\n            this.from = new MimedType<>(from, fromMime);\n            this.to = new MimedType<>(to, toMime);\n            this.distance = distance;\n            this.typeConverter = typeConverter;\n        }\n        MimedType<F> from;\n        MimedType<T> to;\n        int distance;\n        TypeConverter<T, F> typeConverter;\n\n        @Override\n        public int hashCode() {\n            return from.hashCode() ^ to.hashCode();\n        }\n\n        @Override\n        public boolean equals(Object obj) {\n            ConverterEntry other = (ConverterEntry)obj;\n            return from.equals(other.from) && to.equals(other.to);\n        }\n    }\n\n    public static class ConverterEntries {\n        public ArrayList<ConverterEntry> list = new ArrayList<>();\n        public ConverterEntries() {\n        }\n\n        public ConverterEntries(ConverterEntries other) {\n            list.addAll(other.list);\n        }\n\n        public synchronized <F, T> void addConverter(Class<F> from, String fromMime, Class<T> to, String toMime, TypeConverter<T, F> typeConverter) {\n            addConverter(from, fromMime, to, toMime, 1, typeConverter);\n        }\n        public synchronized <F, T> void addConverter(Class<F> from, String fromMime, Class<T> to, String toMime, int distance, TypeConverter<T, F> typeConverter) {\n            if (TextUtils.isEmpty(fromMime))\n                fromMime = MIME_ALL;\n            if (TextUtils.isEmpty(toMime))\n                toMime = MIME_ALL;\n\n            list.add(new ConverterEntry<>(from, fromMime, to, toMime, distance, typeConverter));\n        }\n\n        public synchronized boolean removeConverter(TypeConverter typeConverter) {\n            for (ConverterEntry entry: list) {\n                if (entry.typeConverter == typeConverter)\n                    return list.remove(entry);\n            }\n            return false;\n        }\n    }\n\n    public final static ConverterEntries Converters = new ConverterEntries();\n\n    static {\n        // ensure byte buffer operations are idempotent. do deep copies.\n        final TypeConverter<ByteBufferList, byte[]> ByteArrayToByteBufferList = (from, fromMime) ->\n                new SimpleFuture<>(new ByteBufferList(ByteBufferList.deepCopy(ByteBuffer.wrap(from))));\n        final TypeConverter<byte[], ByteBufferList> ByteBufferListToByteArray = (from, fromMime) ->\n                new SimpleFuture<>(from.getAllByteArray());\n        final TypeConverter<ByteBuffer, ByteBufferList> ByteBufferListToByteBuffer = (from, fromMime) ->\n                new SimpleFuture<>(from.getAll());\n        final TypeConverter<String, ByteBufferList> ByteBufferListToString = (from, fromMime) ->\n                new SimpleFuture<>(from.peekString());\n        final TypeConverter<ByteBuffer, byte[]> ByteArrayToByteBuffer = (from, fromMime) ->\n                new SimpleFuture<>(ByteBufferList.deepCopy(ByteBuffer.wrap(from)));\n        final TypeConverter<ByteBufferList, ByteBuffer> ByteBufferToByteBufferList = (from, fromMime) ->\n                new SimpleFuture<>(new ByteBufferList(ByteBufferList.deepCopy(from)));\n\n        final TypeConverter<byte[], String> StringToByteArray = (from, fromMime) -> new SimpleFuture<>(from.getBytes());\n        final TypeConverter<JSONObject, String> StringToJSONObject = (from, fromMime) -> new SimpleFuture<>(from).thenConvert(JSONObject::new);\n        final TypeConverter<String, JSONObject> JSONObjectToString = (from, fromMime) -> new SimpleFuture<>(from).thenConvert(JSONObject::toString);\n        final TypeConverter<String, byte[]> ByteArrayToString = (from, fromMime) -> new SimpleFuture<>(new String(from));\n\n        Converters.addConverter(ByteBuffer.class, null, ByteBufferList.class, null, ByteBufferToByteBufferList);\n        Converters.addConverter(String.class, null, byte[].class, \"text/plain\", StringToByteArray);\n        Converters.addConverter(byte[].class, null, ByteBufferList.class, null, ByteArrayToByteBufferList);\n        Converters.addConverter(ByteBufferList.class, null, byte[].class, null, ByteBufferListToByteArray);\n        Converters.addConverter(ByteBufferList.class, null, ByteBuffer.class, null, ByteBufferListToByteBuffer);\n        Converters.addConverter(ByteBufferList.class, \"text/plain\", String.class, null, ByteBufferListToString);\n        Converters.addConverter(byte[].class, null, ByteBuffer.class, null, ByteArrayToByteBuffer);\n        Converters.addConverter(String.class, \"application/json\", JSONObject.class, null, StringToJSONObject);\n        Converters.addConverter(JSONObject.class, null, String.class, \"application/json\", JSONObjectToString);\n        Converters.addConverter(byte[].class, \"text/plain\", String.class, null, ByteArrayToString);\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/future/DependentCancellable.java",
    "content": "package com.koushikdutta.async.future;\n\npublic interface DependentCancellable extends Cancellable {\n    boolean setParent(Cancellable parent);\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/future/DependentFuture.java",
    "content": "package com.koushikdutta.async.future;\n\npublic interface DependentFuture<T> extends Future<T>, DependentCancellable {\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/future/DoneCallback.java",
    "content": "package com.koushikdutta.async.future;\n\npublic interface DoneCallback<T> {\n    void done(Exception e, T result) throws Exception;\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/future/FailCallback.java",
    "content": "package com.koushikdutta.async.future;\n\npublic interface FailCallback {\n    /**\n     * Callback that is invoked when a future completes with an error.\n     * The error should be rethrown to pass it along.\n     * @param e\n     * @throws Exception\n     */\n    void fail(Exception e) throws Exception;\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/future/FailConvertCallback.java",
    "content": "package com.koushikdutta.async.future;\n\npublic interface FailConvertCallback<T> {\n    /**\n     * Callback that is invoked when a future completes with an error.\n     * The error should be rethrown, or a new value should be returned.\n     * @param e\n     * @return\n     * @throws Exception\n     */\n    T fail(Exception e) throws Exception;\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/future/FailRecoverCallback.java",
    "content": "package com.koushikdutta.async.future;\n\npublic interface FailRecoverCallback<T> {\n    /**\n     * Callback that is invoked when a future completes with an error.\n     * The error should be rethrown, or a new future value should be returned.\n     * @param e\n     * @return\n     * @throws Exception\n     */\n    Future<T> fail(Exception e) throws Exception;\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/future/Future.java",
    "content": "package com.koushikdutta.async.future;\n\n\nimport java.util.concurrent.Executor;\n\npublic interface Future<T> extends Cancellable, java.util.concurrent.Future<T> {\n    /**\n     * Set a callback to be invoked when this Future completes.\n     * @param callback\n     * @return\n     */\n    void setCallback(FutureCallback<T> callback);\n\n    /**\n     * Set a callback to be invoked when the Future completes\n     * with an error or a result.\n     * The existing error or result will be passed down the chain, or a new error\n     * may be thrown.\n     * @param done\n     * @return\n     */\n    Future<T> done(DoneCallback<T> done);\n\n    /**\n     * Set a callback to be invoked when this Future completes successfully.\n     * @param callback\n     * @return A future that will resolve once the success callback completes,\n     * which may contain any errors thrown by the success callback.\n     */\n    Future<T> success(SuccessCallback<T> callback);\n\n    /**\n     * Set a callback to be invoked when this Future completes successfully.\n     * @param then\n     * @param <R>\n     * @return A future containing all exceptions that happened prior or during\n     * the callback, or the successful result.\n     */\n    <R> Future<R> then(ThenFutureCallback<R, T> then);\n\n    /**\n     * Set a callback to be invoked when this Future completes successfully.\n     * @param then\n     * @param <R>\n     * @return A future containing all exceptions that happened prior or during\n     * the callback, or the successful result.\n     */\n    <R> Future<R> thenConvert(ThenCallback<R, T> then);\n\n    /**\n     * Set a callback to be invoked when this future completes with a failure.\n     * The failure can be observered and rethrown, otherwise it is considered handled.\n     * The exception will be nulled for subsequent callbacks in the chain.\n     * @param fail\n     * @return\n     */\n    Future<T> fail(FailCallback fail);\n\n    /**\n     * Set a callback to be invoked when this future completes with a failure.\n     * The failure can be observered and rethrown, or handled by returning\n     * a new fallback value of the same type.\n     * @param fail\n     * @return\n     */\n    Future<T> failConvert(FailConvertCallback<T> fail);\n\n    /**\n     * Set a callback to be invoked when this future completes with a failure.\n     * The failure should be observered and rethrown, or handled by returning\n     * a new future of the same type.\n     * @param fail\n     * @return\n     */\n    Future<T> failRecover(FailRecoverCallback<T> fail);\n\n    /**\n     * Get the result, if any. Returns null if still in progress.\n     * @return\n     */\n    T tryGet();\n\n    /**\n     * Get the exception, if any. Returns null if still in progress.\n     * @return\n     */\n    Exception tryGetException();\n\n    /**\n     * Get the result on the executor thread.\n     * @param executor\n     * @return\n     */\n    default Future<T> executorThread(Executor executor) {\n        SimpleFuture<T> ret = new SimpleFuture<>();\n        executor.execute(() -> ret.setComplete(Future.this));\n        return ret;\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/future/FutureCallback.java",
    "content": "package com.koushikdutta.async.future;\n\n/**\n * Created by koush on 5/20/13.\n */\npublic interface FutureCallback<T> {\n    /**\n     * onCompleted is called by the Future with the result or exception of the asynchronous operation.\n     * @param e Exception encountered by the operation\n     * @param result Result returned from the operation\n     */\n    public void onCompleted(Exception e, T result);\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/future/FutureRunnable.java",
    "content": "package com.koushikdutta.async.future;\n\n/**\n * Created by koush on 12/22/13.\n */\npublic interface FutureRunnable<T> {\n    T run() throws Exception;\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/future/FutureThread.java",
    "content": "package com.koushikdutta.async.future;\n\nimport java.util.concurrent.ExecutorService;\n\n/**\n * Created by koush on 12/22/13.\n */\npublic class FutureThread<T> extends SimpleFuture<T> {\n    public FutureThread(final FutureRunnable<T> runnable) {\n        this(runnable, \"FutureThread\");\n    }\n\n    public FutureThread(final ExecutorService pool, final FutureRunnable<T> runnable) {\n        pool.submit(new Runnable() {\n            @Override\n            public void run() {\n                try {\n                    setComplete(runnable.run());\n                }\n                catch (Exception e) {\n                    setComplete(e);\n                }\n            }\n        });\n    }\n\n    public FutureThread(final FutureRunnable<T> runnable, String name) {\n        new Thread(new Runnable() {\n            @Override\n            public void run() {\n                try {\n                    setComplete(runnable.run());\n                }\n                catch (Exception e) {\n                    setComplete(e);\n                }\n            }\n        }, name).start();\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/future/Futures.java",
    "content": "package com.koushikdutta.async.future;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Iterator;\nimport java.util.List;\n\npublic class Futures {\n    public static <T> Future<List<T>> waitAll(final List<Future<T>> futures) {\n        final ArrayList<T> results = new ArrayList<>();\n        final SimpleFuture<List<T>> ret = new SimpleFuture<>();\n\n        if (futures.isEmpty()) {\n            ret.setComplete(results);\n            return ret;\n        }\n\n        FutureCallback<T> cb = new FutureCallback<T>() {\n            int count = 0;\n\n            @Override\n            public void onCompleted(Exception e, T result) {\n                results.add(result);\n                count++;\n                if (count < futures.size())\n                    futures.get(count).setCallback(this);\n                else\n                    ret.setComplete(results);\n            }\n        };\n\n        futures.get(0).setCallback(cb);\n\n        return ret;\n    }\n\n    public static <T> Future<List<T>> waitAll(final Future<T>... futures) {\n        return waitAll(Arrays.asList(futures));\n    }\n\n\n    private static <T, F> void loopUntil(final Iterator<F> values, ThenFutureCallback<T, F> callback, SimpleFuture<T> ret, Exception lastException) {\n        while (values.hasNext()) {\n            try {\n                callback.then(values.next())\n                        .success(ret::setComplete)\n                        .fail(e -> loopUntil(values, callback, ret, e));\n                return;\n            } catch (Exception e) {\n                lastException = e;\n            }\n        }\n\n        if (lastException == null)\n            ret.setComplete(new Exception(\"empty list\"));\n        else\n            ret.setComplete(lastException);\n    }\n\n    public static <T, F> Future<T> loopUntil(final Iterable<F> values, ThenFutureCallback<T, F> callback) {\n        SimpleFuture<T> ret = new SimpleFuture<>();\n        loopUntil(values.iterator(), callback, ret, null);\n        return ret;\n    }\n\n    public static <T, F> Future<T> loopUntil(final F[] values, ThenFutureCallback<T, F> callback) {\n        return loopUntil(Arrays.asList(values), callback);\n    }\n}"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/future/HandlerFuture.java",
    "content": "package com.koushikdutta.async.future;\n\nimport android.os.Handler;\nimport android.os.Looper;\n\n/**\n * Created by koush on 12/25/13.\n */\npublic class HandlerFuture<T> extends SimpleFuture<T> {\n    Handler handler;\n\n    public HandlerFuture() {\n        Looper looper = Looper.myLooper();\n        if (looper == null)\n            looper = Looper.getMainLooper();\n        handler = new Handler(looper);\n    }\n\n    @Override\n    public void setCallback(final FutureCallback<T> callback) {\n        FutureCallback<T> wrapped = new FutureCallback<T>() {\n            @Override\n            public void onCompleted(final Exception e, final T result) {\n                if (Looper.myLooper() == handler.getLooper()) {\n                    callback.onCompleted(e, result);\n                    return;\n                }\n\n                handler.post(new Runnable() {\n                    @Override\n                    public void run() {\n                        onCompleted(e, result);\n                    }\n                });\n            }\n        };\n        super.setCallback(wrapped);\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/future/MultiFuture.java",
    "content": "package com.koushikdutta.async.future;\n\nimport java.util.ArrayList;\n\n/**\n * Created by koush on 2/25/14.\n */\npublic class MultiFuture<T> extends SimpleFuture<T> {\n    private ArrayList<FutureCallbackInternal<T>> internalCallbacks;\n\n    public MultiFuture() {\n    }\n\n    public MultiFuture(T value) {\n        super(value);\n    }\n\n    public MultiFuture(Exception e) {\n        super(e);\n    }\n\n    public MultiFuture(Future<T> future) {\n        super(future);\n    }\n\n    private final FutureCallbackInternal<T> internalCallback = (e, result, callsite) -> {\n        ArrayList<FutureCallbackInternal<T>> callbacks;\n        synchronized (MultiFuture.this) {\n            callbacks = MultiFuture.this.internalCallbacks;\n            MultiFuture.this.internalCallbacks = null;\n        }\n\n        if (callbacks == null)\n            return;\n        for (FutureCallbackInternal<T> cb : callbacks) {\n            cb.onCompleted(e, result, callsite);\n        }\n    };\n\n    @Override\n    protected void setCallbackInternal(FutureCallsite callsite, FutureCallbackInternal<T> internalCallback) {\n        synchronized (this) {\n            if (internalCallback != null) {\n                if (internalCallbacks == null)\n                    internalCallbacks = new ArrayList<>();\n                internalCallbacks.add(internalCallback);\n            }\n        }\n        // so, there is a race condition where this internal callback could get\n        // executed twice, if two callbacks are added at the same time.\n        // however, it doesn't matter, as the actual retrieval and nulling\n        // of the callback list is done in another sync block.\n        // one of the invocations will actually invoke all the callbacks,\n        // while the other will not get a list back.\n\n        // race:\n        // 1-ADD\n        // 2-ADD\n        // 1-INVOKE LIST\n        // 2-INVOKE NULL\n\n        super.setCallbackInternal(callsite, this.internalCallback);\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/future/MultiTransformFuture.java",
    "content": "package com.koushikdutta.async.future;\n\npublic abstract class MultiTransformFuture<T, F> extends MultiFuture<T> implements FutureCallback<F> {\n    @Override\n    public void onCompleted(Exception e, F result) {\n        if (isCancelled())\n            return;\n        if (e != null) {\n            error(e);\n            return;\n        }\n\n        try {\n            transform(result);\n        }\n        catch (Exception ex) {\n            error(ex);\n        }\n    }\n\n    protected void error(Exception e) {\n        setComplete(e);\n    }\n\n    protected abstract void transform(F result) throws Exception;\n}"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/future/SimpleCancellable.java",
    "content": "package com.koushikdutta.async.future;\n\npublic class SimpleCancellable implements DependentCancellable {\n    boolean complete;\n    @Override\n    public boolean isDone() {\n        return complete;\n    }\n\n    protected void cancelCleanup() {\n    }\n\n    protected void cleanup() {\n    }\n\n    protected void completeCleanup() {\n    }\n\n    public boolean setComplete() {\n        synchronized (this) {\n            if (cancelled)\n                return false;\n            if (complete) {\n                // don't allow a Cancellable to complete twice...\n                return false;\n            }\n            complete = true;\n            parent = null;\n        }\n        completeCleanup();\n        cleanup();\n        return true;\n    }\n\n    @Override\n    public boolean cancel() {\n        Cancellable parent;\n        synchronized (this) {\n            if (complete)\n                return false;\n            if (cancelled)\n                return true;\n            cancelled = true;\n            parent = this.parent;\n            // null out the parent to allow garbage collection\n            this.parent = null;\n        }\n        if (parent != null)\n            parent.cancel();\n        cancelCleanup();\n        cleanup();\n        return true;\n    }\n    boolean cancelled;\n\n    private Cancellable parent;\n    @Override\n    public boolean setParent(Cancellable parent) {\n        synchronized (this) {\n            if (isDone())\n                return false;\n            this.parent = parent;\n            return true;\n        }\n    }\n\n    @Override\n    public boolean isCancelled() {\n        synchronized (this) {\n            return cancelled || (parent != null && parent.isCancelled());\n        }\n    }\n\n    public static final Cancellable COMPLETED = new SimpleCancellable() {\n        {\n            setComplete();\n        }\n    };\n\n    public static final Cancellable CANCELLED = new SimpleCancellable() {\n        {\n            cancel();\n        }\n    };\n\n    public Cancellable reset() {\n        cancel();\n        complete = false;\n        cancelled = false;\n        return this;\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/future/SimpleFuture.java",
    "content": "package com.koushikdutta.async.future;\n\nimport com.koushikdutta.async.AsyncSemaphore;\n\nimport java.util.concurrent.CancellationException;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.TimeoutException;\n\npublic class SimpleFuture<T> extends SimpleCancellable implements DependentFuture<T> {\n    private AsyncSemaphore waiter;\n    private Exception exception;\n    private T result;\n    private boolean silent;\n    private FutureCallbackInternal<T> internalCallback;\n\n    protected interface FutureCallbackInternal<T> {\n        void onCompleted(Exception e, T result, FutureCallsite next);\n    }\n\n    public SimpleFuture() {\n    }\n\n    public SimpleFuture(T value) {\n        setComplete(value);\n    }\n\n    public SimpleFuture(Exception e) {\n        setComplete(e);\n    }\n\n    public SimpleFuture(Future<T> future) {\n        setComplete(future);\n    }\n\n    @Override\n    public boolean cancel(boolean mayInterruptIfRunning) {\n        return cancel();\n    }\n\n    private boolean cancelInternal(boolean silent) {\n        if (!super.cancel())\n            return false;\n        // still need to release any pending waiters\n        FutureCallbackInternal<T> internalCallback;\n        synchronized (this) {\n            exception = new CancellationException();\n            releaseWaiterLocked();\n            internalCallback = handleInternalCompleteLocked();\n            this.silent = silent;\n        }\n        handleCallbackUnlocked(null, internalCallback);\n        return true;\n    }\n\n    public boolean cancelSilently() {\n        return cancelInternal(true);\n    }\n\n    @Override\n    public boolean cancel() {\n        return cancelInternal(silent);\n    }\n\n    @Override\n    public T get() throws InterruptedException, ExecutionException {\n        AsyncSemaphore waiter;\n        synchronized (this) {\n            if (isCancelled() || isDone())\n                return getResultOrThrow();\n            waiter = ensureWaiterLocked();\n        }\n        waiter.acquire();\n        return getResultOrThrow();\n    }\n\n    private T getResultOrThrow() throws ExecutionException {\n        if (exception != null)\n            throw new ExecutionException(exception);\n        return result;\n    }\n\n    @Override\n    public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {\n        AsyncSemaphore waiter;\n        synchronized (this) {\n            if (isCancelled() || isDone())\n                return getResultOrThrow();\n            waiter = ensureWaiterLocked();\n        }\n        if (!waiter.tryAcquire(timeout, unit))\n            throw new TimeoutException();\n        return getResultOrThrow();\n    }\n\n    @Override\n    public boolean setComplete() {\n        return setComplete((T)null);\n    }\n\n    private FutureCallbackInternal<T> handleInternalCompleteLocked() {\n        // don't execute the callback inside the sync block... possible hangup\n        // read the callback value, and then call it outside the block.\n        // can't simply call this.callback.onCompleted directly outside the block,\n        // because that may result in a race condition where the callback changes once leaving\n        // the block.\n        FutureCallbackInternal<T> callback = this.internalCallback;\n        // null out members to allow garbage collection\n        this.internalCallback = null;\n        return callback;\n    }\n\n    static class FutureCallsite {\n        Exception e;\n        Object result;\n        FutureCallbackInternal callback;\n\n        void loop() {\n            while (callback != null) {\n                // these values always start non null.\n                FutureCallbackInternal callback = this.callback;\n                Exception e = this.e;\n                Object result = this.result;\n\n                // null them out for reentrancy\n                this.callback = null;\n                this.e = null;\n                this.result = null;\n\n                callback.onCompleted(e, result, this);\n            }\n        }\n    }\n\n    private void handleCallbackUnlocked(FutureCallsite callsite, FutureCallbackInternal<T> internalCallback) {\n        if (silent)\n            return;\n\n        if (internalCallback == null)\n            return;\n\n        boolean needsLoop = false;\n        if (callsite == null) {\n            needsLoop = true;\n            callsite = new FutureCallsite();\n        }\n\n        callsite.callback = internalCallback;\n        callsite.e = exception;\n        callsite.result = result;\n\n        if (needsLoop)\n            callsite.loop();\n    }\n\n    void releaseWaiterLocked() {\n        if (waiter != null) {\n            waiter.release();\n            waiter = null;\n        }\n    }\n\n    AsyncSemaphore ensureWaiterLocked() {\n        if (waiter == null)\n            waiter = new AsyncSemaphore();\n        return waiter;\n    }\n\n    public boolean setComplete(Exception e) {\n        return setComplete(e, null, null);\n    }\n    public boolean setCompleteException(Exception e) { return setComplete(e, null, null); }\n\n    public boolean setComplete(T value) {\n        return setComplete(null, value, null);\n    }\n    public boolean setCompleteValue(T value) {\n        return setComplete(null, value, null);\n    }\n\n    public boolean setComplete(Exception e, T value) {\n        return setComplete(e, value, null);\n    }\n\n    private boolean setComplete(Exception e, T value, FutureCallsite callsite) {\n        FutureCallbackInternal<T> internalCallback;\n        synchronized (this) {\n            if (!super.setComplete())\n                return false;\n            result = value;\n            exception = e;\n            releaseWaiterLocked();\n            internalCallback = handleInternalCompleteLocked();\n        }\n        handleCallbackUnlocked(callsite, internalCallback);\n        return true;\n    }\n\n    void setCallbackInternal(FutureCallsite callsite, FutureCallbackInternal<T> internalCallback) {\n        // callback can only be changed or read/used inside a sync block\n        synchronized (this) {\n            this.internalCallback = internalCallback;\n            if (!isDone() && !isCancelled())\n                return;\n\n            internalCallback = handleInternalCompleteLocked();\n        }\n        handleCallbackUnlocked(callsite, internalCallback);\n    }\n\n    @Override\n    public void setCallback(FutureCallback<T> callback) {\n        if (callback == null)\n            setCallbackInternal(null, null);\n        else\n            setCallbackInternal(null, (e, result, next) -> callback.onCompleted(e, result));\n    }\n\n    private Future<T> setComplete(Future<T> future, FutureCallsite callsite) {\n        setParent(future);\n\n        SimpleFuture<T> ret = new SimpleFuture<>();\n        if (future instanceof SimpleFuture) {\n            ((SimpleFuture<T>)future).setCallbackInternal(callsite,\n                    (e, result, next) ->\n                            ret.setComplete(SimpleFuture.this.setComplete(e, result, next) ? null : new CancellationException(), result, next));\n        }\n        else {\n            future.setCallback((e, result) -> ret.setComplete(SimpleFuture.this.setComplete(e, result, null) ? null : new CancellationException()));\n        }\n        return ret;\n    }\n\n    /**\n     * Complete a future with another future. Returns a future that reports whether the completion\n     * was successful. If the future was not completed due to cancellation, the callback\n     * will be called with a CancellationException, and the original future result, if one was provided.\n     * @param future\n     * @return\n     */\n    public Future<T> setComplete(Future<T> future) {\n        return setComplete(future, null);\n    }\n\n    public Future<T> setCompleteFuture(Future<T> future) {\n        return setComplete(future, null);\n    }\n\n\n    /**\n     * THIS METHOD IS FOR TEST USE ONLY\n     * @return\n     */\n    @Deprecated\n    public Object getCallback() {\n        return internalCallback;\n    }\n\n    @Override\n    public Future<T> done(DoneCallback<T> done) {\n        final SimpleFuture<T> ret = new SimpleFuture<>();\n        ret.setParent(this);\n        setCallbackInternal(null, (e, result, next) -> {\n            if (e == null) {\n                try {\n                    done.done(e, result);\n                }\n                catch (Exception callbackException) {\n                    e = callbackException;\n                    // note that the result is not nulled out. this is useful for managed resources, like sockets.\n                    // for example: a successful socket connection was made, but the request can be cancelled.\n                    // so, returning an error along with a socket object allows for failure cleanup.\n                }\n            }\n            ret.setComplete(e, result, next);\n        });\n        return ret;\n    }\n\n    @Override\n    public Future<T> success(SuccessCallback<T> callback) {\n        final SimpleFuture<T> ret = new SimpleFuture<>();\n        ret.setParent(this);\n        setCallbackInternal(null, (e, result, next) -> {\n            if (e == null) {\n                try {\n                    callback.success(result);\n                }\n                catch (Exception callbackException) {\n                    e = callbackException;\n                    // note that the result is not nulled out. this is useful for managed resources, like sockets.\n                    // for example: a successful socket connection was made, but the request can be cancelled.\n                    // so, returning an error along with a socket object allows for failure cleanup.\n                }\n            }\n            ret.setComplete(e, result, next);\n        });\n        return ret;\n    }\n\n    @Override\n    public <R> Future<R> then(ThenFutureCallback<R, T> then) {\n        final SimpleFuture<R> ret = new SimpleFuture<>();\n        ret.setParent(this);\n        setCallbackInternal(null, (e, result, next) -> {\n            if (e != null) {\n                ret.setComplete(e, null, next);\n                return;\n            }\n            Future<R> out;\n            try {\n                out = then.then(result);\n            }\n            catch (Exception callbackException) {\n                ret.setComplete(callbackException, null, next);\n                return;\n            }\n            ret.setComplete(out, next);\n\n        });\n        return ret;\n    }\n\n    @Override\n    public <R> Future<R> thenConvert(final ThenCallback<R, T> callback) {\n        return then(from -> new SimpleFuture<>(callback.then(from)));\n    }\n\n    @Override\n    public Future<T> fail(FailCallback fail) {\n        return failRecover(e -> {\n            fail.fail(e);\n            return new SimpleFuture<>((T)null);\n        });\n    }\n\n    @Override\n    public Future<T> failRecover(FailRecoverCallback<T> fail) {\n        SimpleFuture<T> ret = new SimpleFuture<>();\n        ret.setParent(this);\n        setCallbackInternal(null, (e, result, next) -> {\n            if (e == null) {\n                ret.setComplete(e, result, next);\n                return;\n            }\n            Future<T> out;\n            try {\n                out = fail.fail(e);\n            }\n            catch (Exception callbackException) {\n                ret.setComplete(callbackException, null, next);\n                return;\n            }\n            ret.setComplete(out, next);\n        });\n        return ret;\n    }\n\n    @Override\n    public Future<T> failConvert(FailConvertCallback<T> fail) {\n        return failRecover(e -> new SimpleFuture<>(fail.fail(e)));\n    }\n\n    @Override\n    public boolean setParent(Cancellable parent) {\n        return super.setParent(parent);\n    }\n\n    /**\n     * Reset the future for reuse.\n     * @return\n     */\n    public SimpleFuture<T> reset() {\n        super.reset();\n\n        result = null;\n        exception = null;\n        waiter = null;\n        internalCallback = null;\n        silent = false;\n\n        return this;\n    }\n\n    @Override\n    public Exception tryGetException() {\n        return exception;\n    }\n\n    @Override\n    public T tryGet() {\n        return result;\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/future/SuccessCallback.java",
    "content": "package com.koushikdutta.async.future;\n\npublic interface SuccessCallback<T> {\n    void success(T value) throws Exception;\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/future/ThenCallback.java",
    "content": "package com.koushikdutta.async.future;\n\npublic interface ThenCallback<T, F> {\n    /**\n     * Callback that is invoked when Future.then completes,\n     * and converts a value F to value T.\n     * @param from\n     * @return\n     * @throws Exception\n     */\n    T then(F from) throws Exception;\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/future/ThenFutureCallback.java",
    "content": "package com.koushikdutta.async.future;\n\npublic interface ThenFutureCallback<T, F> {\n    /**\n     * Callback that is invoked when Future.then completes,\n     * and converts a value F to a Future<T>.\n     * @param from\n     * @return\n     * @throws Exception\n     */\n    Future<T> then(F from) throws Exception;\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/future/TransformFuture.java",
    "content": "package com.koushikdutta.async.future;\n\npublic abstract class TransformFuture<T, F> extends SimpleFuture<T> implements FutureCallback<F> {\n    public TransformFuture(F from) {\n        onCompleted(null, from);\n    }\n\n    public TransformFuture() {\n    }\n\n    @Override\n    public void onCompleted(Exception e, F result) {\n        if (isCancelled())\n            return;\n        if (e != null) {\n            error(e);\n            return;\n        }\n\n        try {\n            transform(result);\n        }\n        catch (Exception ex) {\n            error(ex);\n        }\n    }\n\n    protected void error(Exception e) {\n        setComplete(e);\n    }\n\n    protected abstract void transform(F result) throws Exception;\n}"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/future/TypeConverter.java",
    "content": "package com.koushikdutta.async.future;\n\npublic interface TypeConverter<T, F> {\n    Future<T> convert(F from, String fromMime) throws Exception;\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpClient.java",
    "content": "package com.koushikdutta.async.http;\n\nimport android.annotation.SuppressLint;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.text.TextUtils;\n\nimport com.koushikdutta.async.AsyncSSLException;\nimport com.koushikdutta.async.AsyncServer;\nimport com.koushikdutta.async.AsyncSocket;\nimport com.koushikdutta.async.ByteBufferList;\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.callback.ConnectCallback;\nimport com.koushikdutta.async.callback.DataCallback;\nimport com.koushikdutta.async.future.Cancellable;\nimport com.koushikdutta.async.future.Future;\nimport com.koushikdutta.async.future.SimpleFuture;\nimport com.koushikdutta.async.http.callback.HttpConnectCallback;\nimport com.koushikdutta.async.http.callback.RequestCallback;\nimport com.koushikdutta.async.parser.AsyncParser;\nimport com.koushikdutta.async.parser.ByteBufferListParser;\nimport com.koushikdutta.async.parser.JSONArrayParser;\nimport com.koushikdutta.async.parser.JSONObjectParser;\nimport com.koushikdutta.async.parser.StringParser;\nimport com.koushikdutta.async.stream.OutputStreamDataCallback;\n\nimport org.json.JSONArray;\nimport org.json.JSONObject;\n\nimport java.io.BufferedOutputStream;\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.net.HttpURLConnection;\nimport java.net.InetAddress;\nimport java.net.InetSocketAddress;\nimport java.net.Proxy;\nimport java.net.ProxySelector;\nimport java.net.URI;\nimport java.net.URL;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.concurrent.CopyOnWriteArrayList;\nimport java.util.concurrent.TimeoutException;\n\npublic class AsyncHttpClient {\n    private static AsyncHttpClient mDefaultInstance;\n    public static AsyncHttpClient getDefaultInstance() {\n        if (mDefaultInstance == null)\n            mDefaultInstance = new AsyncHttpClient(AsyncServer.getDefault());\n\n        return mDefaultInstance;\n    }\n\n    final List<AsyncHttpClientMiddleware> mMiddleware = new CopyOnWriteArrayList<>();\n    public Collection<AsyncHttpClientMiddleware> getMiddleware() {\n        return mMiddleware;\n    }\n    public void insertMiddleware(AsyncHttpClientMiddleware middleware) {\n        mMiddleware.add(0, middleware);\n    }\n\n    AsyncSSLSocketMiddleware sslSocketMiddleware;\n    AsyncSocketMiddleware socketMiddleware;\n    HttpTransportMiddleware httpTransportMiddleware;\n    AsyncServer mServer;\n    public AsyncHttpClient(AsyncServer server) {\n        mServer = server;\n        insertMiddleware(socketMiddleware = new AsyncSocketMiddleware(this));\n        insertMiddleware(sslSocketMiddleware = new AsyncSSLSocketMiddleware(this));\n        insertMiddleware(httpTransportMiddleware = new HttpTransportMiddleware());\n        sslSocketMiddleware.addEngineConfigurator(new SSLEngineSNIConfigurator());\n    }\n\n    @SuppressLint(\"NewApi\")\n    private static void setupAndroidProxy(AsyncHttpRequest request) {\n        // using a explicit proxy?\n        if (request.proxyHost != null)\n            return;\n\n        List<Proxy> proxies;\n        try {\n            proxies = ProxySelector.getDefault().select(URI.create(request.getUri().toString()));\n        }\n        catch (Exception e) {\n            // uri parsing craps itself sometimes.\n            return;\n        }\n        if (proxies.isEmpty())\n            return;\n        Proxy proxy = proxies.get(0);\n        if (proxy.type() != Proxy.Type.HTTP)\n            return;\n        if (!(proxy.address() instanceof InetSocketAddress))\n            return;\n        InetSocketAddress proxyAddress = (InetSocketAddress) proxy.address();\n        String proxyHost;\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {\n            proxyHost = proxyAddress.getHostString();\n        }\n        else {\n            InetAddress address = proxyAddress.getAddress();\n            if (address!=null)\n                proxyHost = address.getHostAddress();\n            else\n                proxyHost = proxyAddress.getHostName();\n        }\n        request.enableProxy(proxyHost, proxyAddress.getPort());\n    }\n\n    public AsyncSocketMiddleware getSocketMiddleware() {\n        return socketMiddleware;\n    }\n\n    public AsyncSSLSocketMiddleware getSSLSocketMiddleware() {\n        return sslSocketMiddleware;\n    }\n\n    public Future<AsyncHttpResponse> execute(final AsyncHttpRequest request, final HttpConnectCallback callback) {\n        FutureAsyncHttpResponse ret;\n        execute(request, 0, ret = new FutureAsyncHttpResponse(), callback);\n        return ret;\n    }\n\n    public Future<AsyncHttpResponse> execute(String uri, final HttpConnectCallback callback) {\n        return execute(new AsyncHttpGet(uri), callback);\n    }\n\n    private static final String LOGTAG = \"AsyncHttp\";\n    private class FutureAsyncHttpResponse extends SimpleFuture<AsyncHttpResponse> {\n        public AsyncSocket socket;\n        public Cancellable scheduled;\n        public Runnable timeoutRunnable;\n\n        @Override\n        public boolean cancel() {\n            if (!super.cancel())\n                return false;\n\n            if (socket != null) {\n                socket.setDataCallback(new DataCallback.NullDataCallback());\n                socket.close();\n            }\n\n            if (scheduled != null)\n                scheduled.cancel();\n\n            return true;\n        }\n    }\n\n    private void reportConnectedCompleted(FutureAsyncHttpResponse cancel, Exception ex, AsyncHttpResponseImpl response, AsyncHttpRequest request, final HttpConnectCallback callback) {\n        cancel.scheduled.cancel();\n        boolean complete;\n        if (ex != null) {\n            request.loge(\"Connection error\", ex);\n            complete = cancel.setComplete(ex);\n        }\n        else {\n            request.logd(\"Connection successful\");\n            complete = cancel.setComplete(response);\n        }\n        if (complete) {\n            callback.onConnectCompleted(ex, response);\n            return;\n        }\n\n        if (response != null) {\n            // the request was cancelled, so close up shop, and eat any pending data\n            response.setDataCallback(new DataCallback.NullDataCallback());\n            response.close();\n        }\n    }\n\n    private void execute(final AsyncHttpRequest request, final int redirectCount, final FutureAsyncHttpResponse cancel, final HttpConnectCallback callback) {\n        if (mServer.isAffinityThread()) {\n            executeAffinity(request, redirectCount, cancel, callback);\n        }\n        else {\n            mServer.post(new Runnable() {\n                @Override\n                public void run() {\n                    executeAffinity(request, redirectCount, cancel, callback);\n                }\n            });\n        }\n    }\n\n    private static long getTimeoutRemaining(AsyncHttpRequest request) {\n        // need a better way to calculate this.\n        // a timer of sorts that stops/resumes.\n        return request.getTimeout();\n    }\n\n    private static void copyHeader(AsyncHttpRequest from, AsyncHttpRequest to, String header) {\n        String value = from.getHeaders().get(header);\n        if (!TextUtils.isEmpty(value))\n            to.getHeaders().set(header, value);\n    }\n\n    private void executeAffinity(final AsyncHttpRequest request, final int redirectCount, final FutureAsyncHttpResponse cancel, final HttpConnectCallback callback) {\n        if (redirectCount > 15) {\n            reportConnectedCompleted(cancel, new RedirectLimitExceededException(\"too many redirects\"), null, request, callback);\n            return;\n        }\n        final Uri uri = request.getUri();\n        final AsyncHttpClientMiddleware.OnResponseCompleteData data = new AsyncHttpClientMiddleware.OnResponseCompleteData();\n        request.executionTime = System.currentTimeMillis();\n        data.request = request;\n\n        request.logd(\"Executing request.\");\n\n        for (AsyncHttpClientMiddleware middleware: mMiddleware) {\n            middleware.onRequest(data);\n        }\n\n        // flow:\n        // 1) set a connect timeout\n        // 2) wait for connect\n        // 3) on connect, cancel timeout\n        // 4) wait for request to be sent fully\n        // 5) after request is sent, set a header timeout\n        // 6) wait for headers\n        // 7) on headers, cancel timeout\n        // 8) TODO: response can take as long as it wants to arrive?\n\n        if (request.getTimeout() > 0) {\n            // set connect timeout\n            cancel.timeoutRunnable = new Runnable() {\n                @Override\n                public void run() {\n                    // we've timed out, kill the connections\n                    if (data.socketCancellable != null) {\n                        data.socketCancellable.cancel();\n                        if (data.socket != null)\n                            data.socket.close();\n                    }\n                    reportConnectedCompleted(cancel, new TimeoutException(), null, request, callback);\n                }\n            };\n            cancel.scheduled = mServer.postDelayed(cancel.timeoutRunnable, getTimeoutRemaining(request));\n        }\n\n        // 2) wait for a connect\n        data.connectCallback = new ConnectCallback() {\n            boolean reported;\n            @Override\n            public void onConnectCompleted(Exception ex, AsyncSocket socket) {\n                if (reported) {\n                    if (socket != null) {\n                        socket.setDataCallback(new DataCallback.NullDataCallback());\n                        socket.setEndCallback(new CompletedCallback.NullCompletedCallback());\n                        socket.close();\n                        throw new AssertionError(\"double connect callback\");\n                    }\n                }\n                reported = true;\n\n                request.logv(\"socket connected\");\n                if (cancel.isCancelled()) {\n                    if (socket != null)\n                        socket.close();\n                    return;\n                }\n\n                // 3) on connect, cancel timeout\n                if (cancel.timeoutRunnable != null)\n                    cancel.scheduled.cancel();\n\n                if (ex != null) {\n                    reportConnectedCompleted(cancel, ex, null, request, callback);\n                    return;\n                }\n\n                data.socket = socket;\n                cancel.socket = socket;\n\n                executeSocket(request, redirectCount, cancel, callback, data);\n            }\n        };\n\n        // set up the system default proxy and connect\n        setupAndroidProxy(request);\n\n        // set the implicit content type\n        if (request.getBody() != null) {\n            if (request.getHeaders().get(\"Content-Type\") == null)\n                request.getHeaders().set(\"Content-Type\", request.getBody().getContentType());\n        }\n\n        final Exception unsupportedURI;\n        for (AsyncHttpClientMiddleware middleware: mMiddleware) {\n            Cancellable socketCancellable = middleware.getSocket(data);\n            if (socketCancellable != null) {\n                data.socketCancellable = socketCancellable;\n                cancel.setParent(socketCancellable);\n                return;\n            }\n        }\n        unsupportedURI = new IllegalArgumentException(\"invalid uri=\"+request.getUri()+\" middlewares=\"+mMiddleware);\n        reportConnectedCompleted(cancel, unsupportedURI, null, request, callback);\n    }\n\n    private void executeSocket(final AsyncHttpRequest request, final int redirectCount,\n                               final FutureAsyncHttpResponse cancel, final HttpConnectCallback callback,\n                               final AsyncHttpClientMiddleware.OnResponseCompleteData data) {\n        // 4) wait for request to be sent fully\n        // and\n        // 6) wait for headers\n        final AsyncHttpResponseImpl ret = new AsyncHttpResponseImpl(request) {\n            @Override\n            protected void onRequestCompleted(Exception ex) {\n                if (ex != null) {\n                    reportConnectedCompleted(cancel, ex, null, request, callback);\n                    return;\n                }\n\n                request.logv(\"request completed\");\n                if (cancel.isCancelled())\n                    return;\n                // 5) after request is sent, set a header timeout\n                if (cancel.timeoutRunnable != null && mHeaders == null) {\n                    cancel.scheduled.cancel();\n                    cancel.scheduled = mServer.postDelayed(cancel.timeoutRunnable, getTimeoutRemaining(request));\n                }\n\n                for (AsyncHttpClientMiddleware middleware: mMiddleware) {\n                    middleware.onRequestSent(data);\n                }\n            }\n\n            @Override\n            public void setDataEmitter(DataEmitter emitter) {\n                data.bodyEmitter = emitter;\n                for (AsyncHttpClientMiddleware middleware: mMiddleware) {\n                    middleware.onBodyDecoder(data);\n                }\n\n                super.setDataEmitter(data.bodyEmitter);\n\n                for (AsyncHttpClientMiddleware middleware: mMiddleware) {\n                    AsyncHttpRequest newReq = middleware.onResponseReady(data);\n                    if (newReq != null) {\n                        newReq.executionTime = request.executionTime;\n                        newReq.logLevel = request.logLevel;\n                        newReq.LOGTAG = request.LOGTAG;\n                        newReq.proxyHost = request.proxyHost;\n                        newReq.proxyPort = request.proxyPort;\n                        setupAndroidProxy(newReq);\n\n                        request.logi(\"Response intercepted by middleware\");\n                        newReq.logi(\"Request initiated by middleware intercept by middleware\");\n                        // post to allow reuse of socket.\n                        mServer.post(() -> execute(newReq, redirectCount, cancel, callback));\n                        setDataCallback(new NullDataCallback());\n                        return;\n                    }\n                }\n\n                Headers headers = mHeaders;\n                int responseCode = code();\n                if ((responseCode == HttpURLConnection.HTTP_MOVED_PERM || responseCode == HttpURLConnection.HTTP_MOVED_TEMP || responseCode == 307) && request.getFollowRedirect()) {\n                    String location = headers.get(\"Location\");\n                    Uri redirect;\n                    try {\n                        redirect = Uri.parse(location);\n                        if (redirect.getScheme() == null) {\n                            redirect = Uri.parse(new URL(new URL(request.getUri().toString()), location).toString());\n                        }\n                    }\n                    catch (Exception e) {\n                        reportConnectedCompleted(cancel, e, this, request, callback);\n                        return;\n                    }\n                    final String method = request.getMethod().equals(AsyncHttpHead.METHOD) ? AsyncHttpHead.METHOD : AsyncHttpGet.METHOD;\n                    AsyncHttpRequest newReq = new AsyncHttpRequest(redirect, method);\n                    newReq.executionTime = request.executionTime;\n                    newReq.logLevel = request.logLevel;\n                    newReq.LOGTAG = request.LOGTAG;\n                    newReq.proxyHost = request.proxyHost;\n                    newReq.proxyPort = request.proxyPort;\n                    setupAndroidProxy(newReq);\n                    copyHeader(request, newReq, \"User-Agent\");\n                    copyHeader(request, newReq, \"Range\");\n                    request.logi(\"Redirecting\");\n                    newReq.logi(\"Redirected\");\n                    mServer.post(() -> execute(newReq, redirectCount + 1, cancel, callback));\n\n                    setDataCallback(new NullDataCallback());\n                    return;\n                }\n\n                request.logv(\"Final (post cache response) headers:\\n\" + toString());\n\n                // at this point the headers are done being modified\n                reportConnectedCompleted(cancel, null, this, request, callback);\n            }\n\n            protected void onHeadersReceived() {\n                super.onHeadersReceived();\n                if (cancel.isCancelled())\n                    return;\n\n                // 7) on headers, cancel timeout\n                if (cancel.timeoutRunnable != null)\n                    cancel.scheduled.cancel();\n\n                // allow the middleware to massage the headers before the body is decoded\n                request.logv(\"Received headers:\\n\" + toString());\n\n                for (AsyncHttpClientMiddleware middleware: mMiddleware) {\n                    middleware.onHeadersReceived(data);\n                }\n\n                // drop through, and setDataEmitter will be called for the body decoder.\n                // headers will be further massaged in there.\n            }\n\n            @Override\n            protected void report(Exception ex) {\n                if (ex != null)\n                    request.loge(\"exception during response\", ex);\n                if (cancel.isCancelled())\n                    return;\n                if (ex instanceof AsyncSSLException) {\n                    request.loge(\"SSL Exception\", ex);\n                    AsyncSSLException ase = (AsyncSSLException)ex;\n                    request.onHandshakeException(ase);\n                    if (ase.getIgnore())\n                        return;\n                }\n                final AsyncSocket socket = socket();\n                if (socket == null)\n                    return;\n                super.report(ex);\n                if (!socket.isOpen() || ex != null) {\n                    if (headers() == null && ex != null)\n                        reportConnectedCompleted(cancel, ex, null, request, callback);\n                }\n\n                data.exception = ex;\n                for (AsyncHttpClientMiddleware middleware: mMiddleware) {\n                    middleware.onResponseComplete(data);\n                }\n            }\n\n            @Override\n            public AsyncSocket detachSocket() {\n                request.logd(\"Detaching socket\");\n                AsyncSocket socket = socket();\n                if (socket == null)\n                    return null;\n                socket.setWriteableCallback(null);\n                socket.setClosedCallback(null);\n                socket.setEndCallback(null);\n                socket.setDataCallback(null);\n                setSocket(null);\n                return socket;\n            }\n        };\n\n        data.sendHeadersCallback = new CompletedCallback() {\n            @Override\n            public void onCompleted(Exception ex) {\n                if (ex != null)\n                    ret.report(ex);\n                else\n                    ret.onHeadersSent();\n            }\n        };\n        data.receiveHeadersCallback = new CompletedCallback() {\n            @Override\n            public void onCompleted(Exception ex) {\n                if (ex != null)\n                    ret.report(ex);\n                else\n                    ret.onHeadersReceived();\n            }\n        };\n        data.response = ret;\n        ret.setSocket(data.socket);\n\n        for (AsyncHttpClientMiddleware middleware : mMiddleware) {\n            if (middleware.exchangeHeaders(data))\n                break;\n        }\n    }\n\n    public static abstract class RequestCallbackBase<T> implements RequestCallback<T> {\n        @Override\n        public void onProgress(AsyncHttpResponse response, long downloaded, long total) {\n        }\n        @Override\n        public void onConnect(AsyncHttpResponse response) {\n        }\n    }\n\n    public static abstract class DownloadCallback extends RequestCallbackBase<ByteBufferList> {\n    }\n\n    public static abstract class StringCallback extends RequestCallbackBase<String> {\n    }\n\n    public static abstract class JSONObjectCallback extends RequestCallbackBase<JSONObject> {\n    }\n    \n    public static abstract class JSONArrayCallback extends RequestCallbackBase<JSONArray> {\n    }\n\n    public static abstract class FileCallback extends RequestCallbackBase<File> {\n    }\n\n    public Future<ByteBufferList> executeByteBufferList(AsyncHttpRequest request, DownloadCallback callback) {\n        return execute(request, new ByteBufferListParser(), callback);\n    }\n\n    public Future<String> executeString(AsyncHttpRequest req, final StringCallback callback) {\n        return execute(req, new StringParser(), callback);\n    }\n\n    public Future<JSONObject> executeJSONObject(AsyncHttpRequest req, final JSONObjectCallback callback) {\n        return execute(req, new JSONObjectParser(), callback);\n    }\n\n    public Future<JSONArray> executeJSONArray(AsyncHttpRequest req, final JSONArrayCallback callback) {\n        return execute(req, new JSONArrayParser(), callback);\n    }\n\n    private <T> void invokeWithAffinity(final RequestCallback<T> callback, SimpleFuture<T> future, final AsyncHttpResponse response, final Exception e, final T result) {\n        boolean complete;\n        if (e != null)\n            complete = future.setComplete(e);\n        else\n            complete = future.setComplete(result);\n        if (!complete)\n            return;\n        if (callback != null)\n            callback.onCompleted(e, response, result);\n    }\n\n    private <T> void invoke(final RequestCallback<T> callback, final SimpleFuture<T> future, final AsyncHttpResponse response, final Exception e, final T result) {\n        Runnable runnable = new Runnable() {\n            @Override\n            public void run() {\n                invokeWithAffinity(callback, future, response, e, result);\n            }\n        };\n        mServer.post(runnable);\n    }\n\n    private void invokeProgress(final RequestCallback callback, final AsyncHttpResponse response, final long downloaded, final long total) {\n        if (callback != null)\n            callback.onProgress(response, downloaded, total);\n    }\n\n    private void invokeConnect(final RequestCallback callback, final AsyncHttpResponse response) {\n        if (callback != null)\n            callback.onConnect(response);\n    }\n\n    public Future<File> executeFile(AsyncHttpRequest req, final String filename, final FileCallback callback) {\n        final File file = new File(filename);\n        file.getParentFile().mkdirs();\n        final OutputStream fout;\n        try {\n            fout = new BufferedOutputStream(new FileOutputStream(file), 8192);\n        }\n        catch (FileNotFoundException e) {\n            SimpleFuture<File> ret = new SimpleFuture<File>();\n            ret.setComplete(e);\n            return ret;\n        }\n        final FutureAsyncHttpResponse cancel = new FutureAsyncHttpResponse();\n        final SimpleFuture<File> ret = new SimpleFuture<File>() {\n            @Override\n            public void cancelCleanup() {\n                try {\n                    cancel.get().setDataCallback(new DataCallback.NullDataCallback());\n                    cancel.get().close();\n                }\n                catch (Exception e) {\n                }\n                try {\n                    fout.close();\n                }\n                catch (Exception e) {\n                }\n                file.delete();\n            }\n        };\n        ret.setParent(cancel);\n        execute(req, 0, cancel, new HttpConnectCallback() {\n            long mDownloaded = 0;\n\n            @Override\n            public void onConnectCompleted(Exception ex, final AsyncHttpResponse response) {\n                if (ex != null) {\n                    try {\n                        fout.close();\n                    }\n                    catch (IOException e) {\n                    }\n                    file.delete();\n                    invoke(callback, ret, response, ex, null);\n                    return;\n                }\n                invokeConnect(callback, response);\n\n                final long contentLength = HttpUtil.contentLength(response.headers());\n\n                response.setDataCallback(new OutputStreamDataCallback(fout) {\n                    @Override\n                    public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n                        mDownloaded += bb.remaining();\n                        super.onDataAvailable(emitter, bb);\n                        invokeProgress(callback, response, mDownloaded, contentLength);\n                    }\n                });\n                response.setEndCallback(new CompletedCallback() {\n                    @Override\n                    public void onCompleted(Exception ex) {\n                        try {\n                            fout.close();\n                        }\n                        catch (IOException e) {\n                            ex = e;\n                        }\n                        if (ex != null) {\n                            file.delete();\n                            invoke(callback, ret, response, ex, null);\n                        }\n                        else {\n                            invoke(callback, ret, response, null, file);\n                        }\n                    }\n                });\n            }\n        });\n        return ret;\n    }\n\n    public <T> SimpleFuture<T> execute(AsyncHttpRequest req, final AsyncParser<T> parser, final RequestCallback<T> callback) {\n        final FutureAsyncHttpResponse cancel = new FutureAsyncHttpResponse();\n        final SimpleFuture<T> ret = new SimpleFuture<T>();\n        execute(req, 0, cancel, (ex, response) -> {\n            if (ex != null) {\n                invoke(callback, ret, response, ex, null);\n                return;\n            }\n            invokeConnect(callback, response);\n\n            Future<T> parsed = parser.parse(response);\n            parsed.setCallback((e, result) -> invoke(callback, ret, response, e, result));\n\n            // reparent to the new parser future\n            ret.setParent(parsed);\n        });\n        ret.setParent(cancel);\n        return ret;\n    }\n\n    public interface WebSocketConnectCallback {\n        void onCompleted(Exception ex, WebSocket webSocket);\n    }\n\n    public Future<WebSocket> websocket(final AsyncHttpRequest req, String protocol, final WebSocketConnectCallback callback) {\n        return websocket(req, protocol != null ? new String[] { protocol } : null, callback);\n    }\n\n    public Future<WebSocket> websocket(final AsyncHttpRequest req, String[] protocols, final WebSocketConnectCallback callback) {\n        WebSocketImpl.addWebSocketUpgradeHeaders(req, protocols);\n        final SimpleFuture<WebSocket> ret = new SimpleFuture<>();\n        Cancellable connect = execute(req, (ex, response) -> {\n            if (ex != null) {\n                if (ret.setComplete(ex)) {\n                    if (callback != null)\n                        callback.onCompleted(ex, null);\n                }\n                return;\n            }\n            WebSocket ws = WebSocketImpl.finishHandshake(req.getHeaders(), response);\n            if (ws == null) {\n                ex = new WebSocketHandshakeException(\"Unable to complete websocket handshake\");\n                response.close();\n                if (!ret.setComplete(ex))\n                    return;\n            }\n            else {\n                if (!ret.setComplete(ws))\n                    return;\n            }\n            if (callback != null)\n                callback.onCompleted(ex, ws);\n        });\n\n        ret.setParent(connect);\n        return ret;\n    }\n\n    public Future<WebSocket> websocket(String uri, String protocol, final WebSocketConnectCallback callback) {\n        final AsyncHttpGet get = new AsyncHttpGet(uri.replace(\"ws://\", \"http://\").replace(\"wss://\", \"https://\"));\n        return websocket(get, protocol, callback);\n    }\n\n    public Future<WebSocket> websocket(String uri, String[] protocols, final WebSocketConnectCallback callback) {\n        final AsyncHttpGet get = new AsyncHttpGet(uri.replace(\"ws://\", \"http://\").replace(\"wss://\", \"https://\"));\n        return websocket(get, protocols, callback);\n    }\n\n    public AsyncServer getServer() {\n        return mServer;\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpClientMiddleware.java",
    "content": "package com.koushikdutta.async.http;\n\nimport com.koushikdutta.async.AsyncSocket;\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.DataSink;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.callback.ConnectCallback;\nimport com.koushikdutta.async.future.Cancellable;\nimport com.koushikdutta.async.util.UntypedHashtable;\n\n/**\n * AsyncHttpClientMiddleware is used by AsyncHttpClient to\n * inspect, manipulate, and handle http requests.\n */\npublic interface AsyncHttpClientMiddleware {\n    interface ResponseHead  {\n        AsyncSocket socket();\n        String protocol();\n        String message();\n        int code();\n        ResponseHead protocol(String protocol);\n        ResponseHead message(String message);\n        ResponseHead code(int code);\n        Headers headers();\n        ResponseHead headers(Headers headers);\n        DataSink sink();\n        ResponseHead sink(DataSink sink);\n        DataEmitter emitter();\n        ResponseHead emitter(DataEmitter emitter);\n    }\n\n    class OnRequestData {\n        public UntypedHashtable state = new UntypedHashtable();\n        public AsyncHttpRequest request;\n    }\n\n    class GetSocketData extends OnRequestData {\n        public ConnectCallback connectCallback;\n        public Cancellable socketCancellable;\n        public String protocol;\n    }\n\n    class OnExchangeHeaderData extends GetSocketData {\n        public AsyncSocket socket;\n        public ResponseHead response;\n        public CompletedCallback sendHeadersCallback;\n        public CompletedCallback receiveHeadersCallback;\n    }\n\n    class OnRequestSentData extends OnExchangeHeaderData {\n    }\n\n    class OnHeadersReceivedData extends OnRequestSentData {\n    }\n\n    class OnBodyDecoderData extends OnHeadersReceivedData {\n        public DataEmitter bodyEmitter;\n    }\n\n    class OnResponseReadyData extends OnBodyDecoderData {\n    }\n\n    class OnResponseCompleteData extends OnResponseReadyData {\n        public Exception exception;\n    }\n\n    /**\n     * Called immediately upon request execution\n     * @param data\n     */\n    void onRequest(OnRequestData data);\n\n    /**\n     * Called to retrieve the socket that will fulfill this request\n     * @param data\n     * @return\n     */\n    Cancellable getSocket(GetSocketData data);\n\n    /**\n     * Called before when the headers are sent and received via the socket.\n     * Implementers return true to denote they will manage header exchange.\n     * @param data\n     * @return\n     */\n    boolean exchangeHeaders(OnExchangeHeaderData data);\n\n    /**\n     * Called once the headers and any optional request body has\n     * been sent\n     * @param data\n     */\n    void onRequestSent(OnRequestSentData data);\n\n    /**\n     * Called once the headers have been received via the socket\n     * @param data\n     */\n    void onHeadersReceived(OnHeadersReceivedData data);\n\n    /**\n     * Called before the body is decoded\n     * @param data\n     */\n    void onBodyDecoder(OnBodyDecoderData data);\n\n    /**\n     * Called before the response is returned to the client. Return a new AsyncHttpRequest\n     * to end the current request and start a new one. Can be used to implement redirect strategies\n     * or multileg authentication, such as digest.\n     * @param data\n     * @return\n     */\n    AsyncHttpRequest onResponseReady(OnResponseReadyData data);\n\n    /**\n     * Called once the request is complete and response has been received,\n     * or if an error occurred\n     * @param data\n     */\n    void onResponseComplete(OnResponseCompleteData data);\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpDelete.java",
    "content": "package com.koushikdutta.async.http;\n\nimport android.net.Uri;\n\npublic class AsyncHttpDelete extends AsyncHttpRequest {\n    public static final String METHOD = \"DELETE\";\n\n    public AsyncHttpDelete(String uri) {\n        this(Uri.parse(uri));\n    }\n\n    public AsyncHttpDelete(Uri uri) {\n        super(uri, METHOD);\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpGet.java",
    "content": "package com.koushikdutta.async.http;\n\nimport android.net.Uri;\n\npublic class AsyncHttpGet extends AsyncHttpRequest {\n    public static final String METHOD = \"GET\";\n    \n    public AsyncHttpGet(String uri) {\n        super(Uri.parse(uri), METHOD);\n    }\n\n    public AsyncHttpGet(Uri uri) {\n        super(uri, METHOD);\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpHead.java",
    "content": "package com.koushikdutta.async.http;\n\nimport android.net.Uri;\n\n/**\n * Created by koush on 8/25/13.\n */\npublic class AsyncHttpHead extends AsyncHttpRequest {\n    public AsyncHttpHead(Uri uri) {\n        super(uri, METHOD);\n    }\n\n    @Override\n    public boolean hasBody() {\n        return false;\n    }\n\n    public static final String METHOD = \"HEAD\";\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpPost.java",
    "content": "package com.koushikdutta.async.http;\n\nimport android.net.Uri;\n\npublic class AsyncHttpPost extends AsyncHttpRequest {\n    public static final String METHOD = \"POST\";\n    \n    public AsyncHttpPost(String uri) {\n        this(Uri.parse(uri));\n    }\n\n    public AsyncHttpPost(Uri uri) {\n        super(uri, METHOD);\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpPut.java",
    "content": "package com.koushikdutta.async.http;\n\nimport android.net.Uri;\n\npublic class AsyncHttpPut extends AsyncHttpRequest {\n    public static final String METHOD = \"PUT\";\n    \n    public AsyncHttpPut(String uri) {\n        this(Uri.parse(uri));\n    }\n\n    public AsyncHttpPut(Uri uri) {\n        super(uri, METHOD);\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpRequest.java",
    "content": "package com.koushikdutta.async.http;\n\nimport android.net.Uri;\nimport android.util.Log;\n\nimport com.koushikdutta.async.AsyncSSLException;\nimport com.koushikdutta.async.http.body.AsyncHttpRequestBody;\n\nimport java.util.Locale;\n\npublic class AsyncHttpRequest {\n    public RequestLine getRequestLine() {\n        return new RequestLine() {\n            @Override\n            public String getUri() {\n                return AsyncHttpRequest.this.getUri().toString();\n            }\n            \n            @Override\n            public ProtocolVersion getProtocolVersion() {\n                return new ProtocolVersion(\"HTTP\", 1, 1);\n            }\n            \n            @Override\n            public String getMethod() {\n                return mMethod;\n            }\n\n            @Override\n            public String toString() {\n                if (proxyHost != null)\n                    return String.format(Locale.ENGLISH, \"%s %s %s\", mMethod, AsyncHttpRequest.this.getUri(), requestLineProtocol);\n                String path = getPath();\n                if (path == null || path.length() == 0)\n                    path = \"/\";\n                String query = AsyncHttpRequest.this.getUri().getEncodedQuery();\n                if (query != null && query.length() != 0) {\n                    path += \"?\" + query;\n                }\n                return String.format(Locale.ENGLISH, \"%s %s %s\", mMethod, path, requestLineProtocol);\n            }\n        };\n    }\n\n    public boolean hasBody() {\n        return true;\n    }\n\n    public String getPath() {\n        return AsyncHttpRequest.this.getUri().getEncodedPath();\n    }\n\n    protected static String getDefaultUserAgent() {\n        String agent = System.getProperty(\"http.agent\");\n        return agent != null ? agent : (\"Java\" + System.getProperty(\"java.version\"));\n    }\n\n    private String requestLineProtocol = \"HTTP/1.1\";\n    private String mMethod;\n    public String getMethod() {\n       return mMethod; \n    }\n\n    public void setRequestLineProtocol(String scheme) {\n        this.requestLineProtocol = scheme;\n    }\n\n    public String getRequestLineProtocol() {\n        return requestLineProtocol;\n    }\n\n    public AsyncHttpRequest setMethod(String method) {\n        if (getClass() != AsyncHttpRequest.class)\n            throw new UnsupportedOperationException(\"can't change method on a subclass of AsyncHttpRequest\");\n        mMethod = method;\n        return this;\n    }\n\n    public AsyncHttpRequest(Uri uri, String method) {\n        this(uri, method, null);\n    }\n\n    public static void setDefaultHeaders(Headers ret, Uri uri) {\n        if (uri != null) {\n            String host = uri.getHost();\n            if (uri.getPort() != -1)\n                host = host + \":\" + uri.getPort();\n            if (host != null)\n                ret.set(\"Host\", host);\n        }\n        ret.set(\"User-Agent\", getDefaultUserAgent());\n        ret.set(\"Accept-Encoding\", \"gzip, deflate\");\n        ret.set(\"Connection\", \"keep-alive\");\n        ret.set(\"Accept\", HEADER_ACCEPT_ALL);\n    }\n\n    public static final String HEADER_ACCEPT_ALL = \"*/*\";\n\n    public AsyncHttpRequest(Uri uri, String method, Headers headers) {\n        mMethod = method;\n        this.uri = uri;\n        if (headers == null)\n            mRawHeaders = new Headers();\n        else\n            mRawHeaders = headers;\n        if (headers == null)\n            setDefaultHeaders(mRawHeaders, uri);\n    }\n\n    Uri uri;\n    public Uri getUri() {\n        return uri;\n    }\n    \n    private Headers mRawHeaders = new Headers();\n\n    public Headers getHeaders() {\n        return mRawHeaders;\n    }\n\n    private boolean mFollowRedirect = true;\n    public boolean getFollowRedirect() {\n        return mFollowRedirect;\n    }\n    public AsyncHttpRequest setFollowRedirect(boolean follow) {\n        mFollowRedirect = follow;\n        return this;\n    }\n    \n    private AsyncHttpRequestBody mBody;\n    public void setBody(AsyncHttpRequestBody body) {\n        mBody = body;\n    }\n    \n    public AsyncHttpRequestBody getBody() {\n        return mBody;\n    }\n    \n    public void onHandshakeException(AsyncSSLException e) {\n    }\n\n    public static final int DEFAULT_TIMEOUT = 30000;\n    int mTimeout = DEFAULT_TIMEOUT;\n    public int getTimeout() {\n        return mTimeout;\n    }\n    \n    public AsyncHttpRequest setTimeout(int timeout) {\n        mTimeout = timeout;\n        return this;\n    }\n\n    public AsyncHttpRequest setHeader(String name, String value) {\n        getHeaders().set(name, value);\n        return this;\n    }\n\n    public AsyncHttpRequest addHeader(String name, String value) {\n        getHeaders().add(name, value);\n        return this;\n    }\n\n    String proxyHost;\n    int proxyPort = -1;\n    public void enableProxy(String host, int port) {\n        proxyHost = host;\n        proxyPort = port;\n    }\n\n    public void disableProxy() {\n        proxyHost = null;\n        proxyPort = -1;\n    }\n\n    public String getProxyHost() {\n        return proxyHost;\n    }\n\n    public int getProxyPort() {\n        return proxyPort;\n    }\n\n    @Override\n    public String toString() {\n        if (mRawHeaders == null)\n            return super.toString();\n        return mRawHeaders.toPrefixString(uri.toString());\n    }\n\n    public void setLogging(String tag, int level) {\n        LOGTAG = tag;\n        logLevel = level;\n    }\n    // request level logging\n    String LOGTAG;\n    int logLevel;\n    public int getLogLevel() {\n        return logLevel;\n    }\n    public String getLogTag() {\n        return LOGTAG;\n    }\n    long executionTime;\n    private String getLogMessage(String message) {\n        long elapsed;\n        if (executionTime != 0)\n            elapsed = System.currentTimeMillis() - executionTime;\n        else\n            elapsed = 0;\n        return String.format(Locale.ENGLISH, \"(%d ms) %s: %s\", elapsed, getUri(), message);\n    }\n    public void logi(String message) {\n        if (LOGTAG == null)\n            return;\n        if (logLevel > Log.INFO)\n            return;\n        Log.i(LOGTAG, getLogMessage(message));\n    }\n    public void logv(String message) {\n        if (LOGTAG == null)\n            return;\n        if (logLevel > Log.VERBOSE)\n            return;\n        Log.v(LOGTAG, getLogMessage(message));\n    }\n    public void logw(String message) {\n        if (LOGTAG == null)\n            return;\n        if (logLevel > Log.WARN)\n            return;\n        Log.w(LOGTAG, getLogMessage(message));\n    }\n    public void logd(String message) {\n        if (LOGTAG == null)\n            return;\n        if (logLevel > Log.DEBUG)\n            return;\n        Log.d(LOGTAG, getLogMessage(message));\n    }\n    public void logd(String message, Exception e) {\n        if (LOGTAG == null)\n            return;\n        if (logLevel > Log.DEBUG)\n            return;\n        Log.d(LOGTAG, getLogMessage(message));\n        Log.d(LOGTAG, e.getMessage(), e);\n    }\n    public void loge(String message) {\n        if (LOGTAG == null)\n            return;\n        if (logLevel > Log.ERROR)\n            return;\n        Log.e(LOGTAG, getLogMessage(message));\n    }\n    public void loge(String message, Exception e) {\n        if (LOGTAG == null)\n            return;\n        if (logLevel > Log.ERROR)\n            return;\n        Log.e(LOGTAG, getLogMessage(message));\n        Log.e(LOGTAG, e.getMessage(), e);\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpResponse.java",
    "content": "package com.koushikdutta.async.http;\n\nimport com.koushikdutta.async.AsyncSocket;\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.callback.CompletedCallback;\n\npublic interface AsyncHttpResponse extends DataEmitter {\n    public String protocol();\n    public String message();\n    public int code();\n    public Headers headers();\n    public AsyncSocket detachSocket();\n    public AsyncHttpRequest getRequest();\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpResponseImpl.java",
    "content": "package com.koushikdutta.async.http;\n\nimport com.koushikdutta.async.AsyncServer;\nimport com.koushikdutta.async.AsyncSocket;\nimport com.koushikdutta.async.ByteBufferList;\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.DataSink;\nimport com.koushikdutta.async.FilteredDataEmitter;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.callback.WritableCallback;\nimport com.koushikdutta.async.http.body.AsyncHttpRequestBody;\n\nimport java.nio.charset.Charset;\n\nabstract class AsyncHttpResponseImpl extends FilteredDataEmitter implements DataEmitter, AsyncHttpResponse, AsyncHttpClientMiddleware.ResponseHead {\n    public AsyncSocket socket() {\n        return mSocket;\n    }\n\n    @Override\n    public AsyncHttpRequest getRequest() {\n        return mRequest;\n    }\n\n    void setSocket(AsyncSocket exchange) {\n        mSocket = exchange;\n        if (mSocket == null)\n            return;\n\n        mSocket.setEndCallback(mReporter);\n    }\n\n    protected void onHeadersSent() {\n        AsyncHttpRequestBody requestBody = mRequest.getBody();\n        if (requestBody != null) {\n            requestBody.write(mRequest, mSink, new CompletedCallback() {\n                @Override\n                public void onCompleted(Exception ex) {\n                    onRequestCompleted(ex);\n                }\n            });\n        } else {\n            onRequestCompleted(null);\n        }\n    }\n\n    protected void onRequestCompleted(Exception ex) {\n    }\n    \n    private CompletedCallback mReporter = new CompletedCallback() {\n        @Override\n        public void onCompleted(Exception error) {\n            if (headers() == null) {\n                report(new ConnectionClosedException(\"connection closed before headers received.\", error));\n            }\n            else if (error != null && !mCompleted) {\n                report(new ConnectionClosedException(\"connection closed before response completed.\", error));\n            }\n            else {\n                report(error);\n            }\n        }\n    };\n\n    protected void onHeadersReceived() {\n    }\n\n\n    @Override\n    public DataEmitter emitter() {\n        return getDataEmitter();\n    }\n\n    @Override\n    public AsyncHttpClientMiddleware.ResponseHead emitter(DataEmitter emitter) {\n        setDataEmitter(emitter);\n        return this;\n    }\n\n    private void terminate() {\n        // DISCONNECT. EVERYTHING.\n        // should not get any data after this point...\n        // if so, eat it and disconnect.\n        mSocket.setDataCallback(new NullDataCallback() {\n            @Override\n            public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n                super.onDataAvailable(emitter, bb);\n                mSocket.close();\n            }\n        });\n    }\n\n    @Override\n    protected void report(Exception e) {\n        super.report(e);\n\n        terminate();\n        mSocket.setWriteableCallback(null);\n        mSocket.setClosedCallback(null);\n        mSocket.setEndCallback(null);\n        mCompleted = true;\n    }\n\n    @Override\n    public void close() {\n        super.close();\n        terminate();\n    }\n\n    private AsyncHttpRequest mRequest;\n    private AsyncSocket mSocket;\n    protected Headers mHeaders;\n    public AsyncHttpResponseImpl(AsyncHttpRequest request) {\n        mRequest = request;\n    }\n\n    boolean mCompleted = false;\n\n    @Override\n    public Headers headers() {\n        return mHeaders;\n    }\n\n    @Override\n    public AsyncHttpClientMiddleware.ResponseHead headers(Headers headers) {\n        mHeaders = headers;\n        return this;\n    }\n\n    int code;\n    @Override\n    public int code() {\n        return code;\n    }\n\n    @Override\n    public AsyncHttpClientMiddleware.ResponseHead code(int code) {\n        this.code = code;\n        return this;\n    }\n\n    @Override\n    public AsyncHttpClientMiddleware.ResponseHead protocol(String protocol) {\n        this.protocol = protocol;\n        return this;\n    }\n\n    @Override\n    public AsyncHttpClientMiddleware.ResponseHead message(String message) {\n        this.message = message;\n        return this;\n    }\n\n    String protocol;\n    @Override\n    public String protocol() {\n        return protocol;\n    }\n\n    String message;\n    @Override\n    public String message() {\n        return message;\n    }\n\n    @Override\n    public String toString() {\n        if (mHeaders == null)\n            return super.toString();\n        return mHeaders.toPrefixString(protocol + \" \" + code + \" \" + message);\n    }\n\n    private boolean mFirstWrite = true;\n    private void assertContent() {\n        if (!mFirstWrite)\n            return;\n        mFirstWrite = false;\n    }\n\n    DataSink mSink;\n\n    @Override\n    public DataSink sink() {\n        return mSink;\n    }\n\n    @Override\n    public AsyncHttpClientMiddleware.ResponseHead sink(DataSink sink) {\n        mSink = sink;\n        return this;\n    }\n\n    @Override\n    public AsyncServer getServer() {\n        return mSocket.getServer();\n    }\n\n    @Override\n    public String charset() {\n        Multimap mm = Multimap.parseSemicolonDelimited(headers().get(\"Content-Type\"));\n        String cs;\n        if (mm != null && null != (cs = mm.getString(\"charset\")) && Charset.isSupported(cs)) {\n            return cs;\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/AsyncSSLEngineConfigurator.java",
    "content": "package com.koushikdutta.async.http;\n\nimport javax.net.ssl.SSLContext;\nimport javax.net.ssl.SSLEngine;\n\npublic interface AsyncSSLEngineConfigurator {\n    SSLEngine createEngine(SSLContext sslContext, String peerHost, int peerPort);\n    void configureEngine(SSLEngine engine, AsyncHttpClientMiddleware.GetSocketData data, String host, int port);\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/AsyncSSLSocketMiddleware.java",
    "content": "package com.koushikdutta.async.http;\n\nimport android.net.Uri;\nimport android.os.Build;\nimport android.text.TextUtils;\n\nimport com.koushikdutta.async.AsyncSSLSocket;\nimport com.koushikdutta.async.AsyncSSLSocketWrapper;\nimport com.koushikdutta.async.AsyncSocket;\nimport com.koushikdutta.async.LineEmitter;\nimport com.koushikdutta.async.Util;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.callback.ConnectCallback;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Locale;\n\nimport javax.net.ssl.HostnameVerifier;\nimport javax.net.ssl.SSLContext;\nimport javax.net.ssl.SSLEngine;\nimport javax.net.ssl.TrustManager;\n\npublic class AsyncSSLSocketMiddleware extends AsyncSocketMiddleware {\n    public AsyncSSLSocketMiddleware(AsyncHttpClient client) {\n        super(client, \"https\", 443);\n    }\n\n    protected SSLContext sslContext;\n\n    public void setSSLContext(SSLContext sslContext) {\n        this.sslContext = sslContext;\n    }\n\n    public SSLContext getSSLContext() {\n        return sslContext != null ? sslContext : AsyncSSLSocketWrapper.getDefaultSSLContext();\n    }\n\n    protected TrustManager[] trustManagers;\n\n    public void setTrustManagers(TrustManager[] trustManagers) {\n        this.trustManagers = trustManagers;\n    }\n\n    protected HostnameVerifier hostnameVerifier;\n\n    public void setHostnameVerifier(HostnameVerifier hostnameVerifier) {\n        this.hostnameVerifier = hostnameVerifier;\n    }\n\n    protected List<AsyncSSLEngineConfigurator> engineConfigurators = new ArrayList<AsyncSSLEngineConfigurator>();\n\n    public void addEngineConfigurator(AsyncSSLEngineConfigurator engineConfigurator) {\n        engineConfigurators.add(engineConfigurator);\n    }\n\n    public void clearEngineConfigurators() {\n        engineConfigurators.clear();\n    }\n\n    protected SSLEngine createConfiguredSSLEngine(GetSocketData data, String host, int port) {\n        SSLContext sslContext = getSSLContext();\n        SSLEngine sslEngine = null;\n\n        for (AsyncSSLEngineConfigurator configurator : engineConfigurators) {\n            sslEngine = configurator.createEngine(sslContext, host, port);\n            if (sslEngine != null)\n                break;\n        }\n\n        for (AsyncSSLEngineConfigurator configurator : engineConfigurators) {\n            configurator.configureEngine(sslEngine, data, host, port);\n        }\n\n        return sslEngine;\n    }\n\n    protected AsyncSSLSocketWrapper.HandshakeCallback createHandshakeCallback(final GetSocketData data, final ConnectCallback callback) {\n        return new AsyncSSLSocketWrapper.HandshakeCallback() {\n            @Override\n            public void onHandshakeCompleted(Exception e, AsyncSSLSocket socket) {\n                callback.onConnectCompleted(e, socket);\n            }\n        };\n    }\n\n    protected void tryHandshake(AsyncSocket socket, GetSocketData data, final Uri uri, final int port, final ConnectCallback callback) {\n        AsyncSSLSocketWrapper.handshake(socket, uri.getHost(), port,\n        createConfiguredSSLEngine(data, uri.getHost(), port),\n        trustManagers, hostnameVerifier, true,\n        createHandshakeCallback(data, callback));\n    }\n\n    @Override\n    protected ConnectCallback wrapCallback(final GetSocketData data, final Uri uri, final int port, final boolean proxied, final ConnectCallback callback) {\n        return new ConnectCallback() {\n            @Override\n            public void onConnectCompleted(Exception ex, final AsyncSocket socket) {\n                if (ex != null) {\n                    callback.onConnectCompleted(ex, socket);\n                    return;\n                }\n\n                if (!proxied) {\n                    tryHandshake(socket, data, uri, port, callback);\n                    return;\n                }\n\n                // this SSL connection is proxied, must issue a CONNECT request to the proxy server\n                // http://stackoverflow.com/a/6594880/704837\n                // some proxies also require 'Host' header, it should be safe to provide it every time\n                String connect = String.format(Locale.ENGLISH, \"CONNECT %s:%s HTTP/1.1\\r\\nHost: %s\\r\\n\\r\\n\", uri.getHost(), port, uri.getHost());\n                data.request.logv(\"Proxying: \" + connect);\n                Util.writeAll(socket, connect.getBytes(), new CompletedCallback() {\n                    @Override\n                    public void onCompleted(Exception ex) {\n                        if (ex != null) {\n                            callback.onConnectCompleted(ex, socket);\n                            return;\n                        }\n\n                        LineEmitter liner = new LineEmitter();\n                        liner.setLineCallback(new LineEmitter.StringCallback() {\n                            String statusLine;\n                            @Override\n                            public void onStringAvailable(String s) {\n                                data.request.logv(s);\n                                if (statusLine == null) {\n                                    statusLine = s.trim();\n                                    if (!statusLine.matches(\"HTTP/1.\\\\d 2\\\\d\\\\d .*\")) { // connect response is allowed to have any 2xx status code\n                                        socket.setDataCallback(null);\n                                        socket.setEndCallback(null);\n                                        callback.onConnectCompleted(new IOException(\"non 2xx status line: \" + statusLine), socket);\n                                    }\n                                }\n                                else if (TextUtils.isEmpty(s.trim())) { // skip all headers, complete handshake once empty line is received\n                                    socket.setDataCallback(null);\n                                    socket.setEndCallback(null);\n                                    tryHandshake(socket, data, uri, port, callback);\n                                }\n                            }\n                        });\n\n                        socket.setDataCallback(liner);\n\n                        socket.setEndCallback(new CompletedCallback() {\n                            @Override\n                            public void onCompleted(Exception ex) {\n                                if (!socket.isOpen() && ex == null)\n                                    ex = new IOException(\"socket closed before proxy connect response\");\n                                callback.onConnectCompleted(ex, socket);\n                            }\n                        });\n                    }\n                });\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/AsyncSocketMiddleware.java",
    "content": "package com.koushikdutta.async.http;\n\nimport android.net.Uri;\n\nimport com.koushikdutta.async.AsyncSocket;\nimport com.koushikdutta.async.ByteBufferList;\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.callback.ConnectCallback;\nimport com.koushikdutta.async.callback.DataCallback;\nimport com.koushikdutta.async.future.Cancellable;\nimport com.koushikdutta.async.future.Future;\nimport com.koushikdutta.async.future.Futures;\nimport com.koushikdutta.async.future.SimpleCancellable;\nimport com.koushikdutta.async.future.SimpleFuture;\nimport com.koushikdutta.async.util.ArrayDeque;\n\nimport java.net.InetSocketAddress;\nimport java.util.Hashtable;\nimport java.util.Locale;\n\npublic class AsyncSocketMiddleware extends SimpleMiddleware {\n    String scheme;\n    int port;\n    // 5 min idle timeout\n    int idleTimeoutMs = 300 * 1000;\n\n    public AsyncSocketMiddleware(AsyncHttpClient client, String scheme, int port) {\n        mClient = client;\n        this.scheme = scheme;\n        this.port = port;\n    }\n\n    public void setIdleTimeoutMs(int idleTimeoutMs) {\n        this.idleTimeoutMs = idleTimeoutMs;\n    }\n    \n    public int getSchemePort(Uri uri) {\n        if (uri.getScheme() == null || !uri.getScheme().equals(scheme))\n            return -1;\n        if (uri.getPort() == -1) {\n            return port;\n        }\n        else {\n            return uri.getPort();\n        }\n    }\n\n    public AsyncSocketMiddleware(AsyncHttpClient client) {\n        this(client, \"http\", 80);\n    }\n\n    protected AsyncHttpClient mClient;\n\n    protected ConnectCallback wrapCallback(GetSocketData data, Uri uri, int port, boolean proxied, ConnectCallback callback) {\n        return callback;\n    }\n\n    boolean connectAllAddresses;\n    public boolean getConnectAllAddresses() {\n        return connectAllAddresses;\n    }\n\n    public void setConnectAllAddresses(boolean connectAllAddresses) {\n        this.connectAllAddresses = connectAllAddresses;\n    }\n\n    String proxyHost;\n    int proxyPort;\n    InetSocketAddress proxyAddress;\n\n    public void disableProxy() {\n        proxyPort = -1;\n        proxyHost = null;\n        proxyAddress = null;\n    }\n\n    public void enableProxy(String host, int port) {\n        proxyHost = host;\n        proxyPort = port;\n        proxyAddress = null;\n    }\n\n    String computeLookup(Uri uri, int port, String proxyHost, int proxyPort) {\n        String proxy;\n        if (proxyHost != null)\n            proxy = proxyHost + \":\" + proxyPort;\n        else\n            proxy = \"\";\n\n        if (proxyHost != null)\n            proxy = proxyHost + \":\" + proxyPort;\n\n        return uri.getScheme() + \"//\" + uri.getHost() + \":\" + port + \"?proxy=\" + proxy;\n    }\n\n    class IdleSocketHolder {\n        public IdleSocketHolder(AsyncSocket socket) {\n            this.socket = socket;\n        }\n        AsyncSocket socket;\n        long idleTime = System.currentTimeMillis();\n    }\n\n    static class ConnectionInfo {\n        int openCount;\n        ArrayDeque<GetSocketData> queue = new ArrayDeque<GetSocketData>();\n        ArrayDeque<IdleSocketHolder> sockets = new ArrayDeque<IdleSocketHolder>();\n    }\n    Hashtable<String, ConnectionInfo> connectionInfo = new Hashtable<String, ConnectionInfo>();\n\n    int maxConnectionCount = Integer.MAX_VALUE;\n\n    public int getMaxConnectionCount() {\n        return maxConnectionCount;\n    }\n\n    public void setMaxConnectionCount(int maxConnectionCount) {\n        this.maxConnectionCount = maxConnectionCount;\n    }\n\n    @Override\n    public Cancellable getSocket(final GetSocketData data) {\n        final Uri uri = data.request.getUri();\n        final int port = getSchemePort(data.request.getUri());\n        if (port == -1) {\n            return null;\n        }\n\n        data.state.put(\"socket-owner\", this);\n\n        final String lookup = computeLookup(uri, port, data.request.getProxyHost(), data.request.getProxyPort());\n        ConnectionInfo info = getOrCreateConnectionInfo(lookup);\n        synchronized (AsyncSocketMiddleware.this) {\n            if (info.openCount >= maxConnectionCount) {\n                // wait for a connection queue to free up\n                SimpleCancellable queueCancel = new SimpleCancellable();\n                info.queue.add(data);\n                return queueCancel;\n            }\n\n            info.openCount++;\n\n            while (!info.sockets.isEmpty()) {\n                IdleSocketHolder idleSocketHolder = info.sockets.pop();\n                final AsyncSocket socket = idleSocketHolder.socket;\n                if (idleSocketHolder.idleTime + idleTimeoutMs < System.currentTimeMillis()) {\n                    socket.setClosedCallback(null);\n                    socket.close();\n                    continue;\n                }\n                if (!socket.isOpen())\n                    continue;\n\n                data.request.logd(\"Reusing keep-alive socket\");\n                data.connectCallback.onConnectCompleted(null, socket);\n\n                // just a noop/dummy, as this can't actually be cancelled.\n                SimpleCancellable ret = new SimpleCancellable();\n                ret.setComplete();\n                return ret;\n            }\n        }\n\n        if (!connectAllAddresses || proxyHost != null || data.request.getProxyHost() != null) {\n            // just default to connecting to a single address\n            data.request.logd(\"Connecting socket\");\n            String unresolvedHost;\n            int unresolvedPort;\n            boolean proxied = false;\n            if (data.request.getProxyHost() == null && proxyHost != null)\n                data.request.enableProxy(proxyHost, proxyPort);\n            if (data.request.getProxyHost() != null) {\n                unresolvedHost = data.request.getProxyHost();\n                unresolvedPort = data.request.getProxyPort();\n                proxied = true;\n            }\n            else {\n                unresolvedHost = uri.getHost();\n                unresolvedPort = port;\n            }\n            if (proxied) {\n                data.request.logv(\"Using proxy: \" + unresolvedHost + \":\" + unresolvedPort);\n            }\n            return mClient.getServer().connectSocket(unresolvedHost, unresolvedPort,\n                wrapCallback(data, uri, port, proxied, data.connectCallback));\n        }\n\n        // try to connect to everything...\n        data.request.logv(\"Resolving domain and connecting to all available addresses\");\n\n        final SimpleFuture<AsyncSocket> checkedReturnValue = new SimpleFuture<>();\n\n        Future<AsyncSocket> socket = mClient.getServer().getAllByName(uri.getHost())\n        .then(addresses -> Futures.loopUntil(addresses, address -> {\n            SimpleFuture<AsyncSocket> loopResult = new SimpleFuture<>();\n\n            final String inetSockAddress = String.format(Locale.ENGLISH, \"%s:%s\", address, port);\n            data.request.logv(\"attempting connection to \" + inetSockAddress);\n\n            mClient.getServer().connectSocket(new InetSocketAddress(address, port), loopResult::setComplete);\n            return loopResult;\n        }))\n        // handle failures here (wrap the callback)\n        .fail(e -> wrapCallback(data, uri, port, false, data.connectCallback).onConnectCompleted(e, null));\n\n        checkedReturnValue.setComplete(socket)\n        .setCallback((e, successfulSocket) -> {\n            if (successfulSocket == null)\n                return;\n            // SimpleFuture.setComplete(Future) returns a future as to whether\n            // the completion was successful, or the future has been cancelled,\n            // thus the completion failed.\n            // The exception value will only ever be a CancellationException.\n            if (e == null) {\n                // handle successes here (wrap the callback)\n                wrapCallback(data, uri, port, false, data.connectCallback).onConnectCompleted(null, successfulSocket);\n                return;\n            }\n            data.request.logd(\"Recycling extra socket leftover from cancelled operation\");\n            idleSocket(successfulSocket);\n            recycleSocket(successfulSocket, data.request);\n        });\n\n        return checkedReturnValue;\n    }\n\n    private ConnectionInfo getOrCreateConnectionInfo(String lookup) {\n        ConnectionInfo info = connectionInfo.get(lookup);\n        if (info == null) {\n            info = new ConnectionInfo();\n            connectionInfo.put(lookup, info);\n        }\n        return info;\n    }\n\n    private void maybeCleanupConnectionInfo(String lookup) {\n        ConnectionInfo info = connectionInfo.get(lookup);\n        if (info == null)\n            return;\n        while (!info.sockets.isEmpty()) {\n            IdleSocketHolder idleSocketHolder = info.sockets.peekLast();\n            AsyncSocket socket = idleSocketHolder.socket;\n            if (idleSocketHolder.idleTime + idleTimeoutMs > System.currentTimeMillis())\n                break;\n            info.sockets.pop();\n            // remove the callback, prevent reentrancy.\n            socket.setClosedCallback(null);\n            socket.close();\n        }\n        if (info.openCount == 0 && info.queue.isEmpty() && info.sockets.isEmpty())\n            connectionInfo.remove(lookup);\n    }\n\n    private void recycleSocket(final AsyncSocket socket, AsyncHttpRequest request) {\n        if (socket == null)\n            return;\n        Uri uri = request.getUri();\n        int port = getSchemePort(uri);\n        final String lookup = computeLookup(uri, port, request.getProxyHost(), request.getProxyPort());\n        final ArrayDeque<IdleSocketHolder> sockets;\n        final IdleSocketHolder idleSocketHolder = new IdleSocketHolder(socket);\n        synchronized (AsyncSocketMiddleware.this) {\n            ConnectionInfo info = getOrCreateConnectionInfo(lookup);\n            sockets = info.sockets;\n            sockets.push(idleSocketHolder);\n        }\n        socket.setClosedCallback(new CompletedCallback() {\n            @Override\n            public void onCompleted(Exception ex) {\n                synchronized (AsyncSocketMiddleware.this) {\n                    sockets.remove(idleSocketHolder);\n                    maybeCleanupConnectionInfo(lookup);\n                }\n            }\n        });\n    }\n\n    private void idleSocket(final AsyncSocket socket) {\n        // must listen for socket close, otherwise log will get spammed.\n        socket.setEndCallback(new CompletedCallback() {\n            @Override\n            public void onCompleted(Exception ex) {\n                socket.setClosedCallback(null);\n                socket.close();\n            }\n        });\n        socket.setWriteableCallback(null);\n        // should not get any data after this point...\n        // if so, eat it and disconnect.\n        socket.setDataCallback(new DataCallback.NullDataCallback() {\n            @Override\n            public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n                super.onDataAvailable(emitter, bb);\n                bb.recycle();\n                socket.setClosedCallback(null);\n                socket.close();\n            }\n        });\n    }\n\n    private void nextConnection(AsyncHttpRequest request) {\n        Uri uri = request.getUri();\n        final int port = getSchemePort(uri);\n        String key = computeLookup(uri, port, request.getProxyHost(), request.getProxyPort());\n        synchronized (AsyncSocketMiddleware.this) {\n            ConnectionInfo info = connectionInfo.get(key);\n            if (info == null)\n                return;\n            --info.openCount;\n            while (info.openCount < maxConnectionCount && info.queue.size() > 0) {\n                GetSocketData gsd = info.queue.remove();\n                SimpleCancellable socketCancellable = (SimpleCancellable)gsd.socketCancellable;\n                if (socketCancellable.isCancelled())\n                    continue;\n                Cancellable connect = getSocket(gsd);\n                socketCancellable.setParent(connect);\n            }\n            maybeCleanupConnectionInfo(key);\n        }\n    }\n\n    protected boolean isKeepAlive(OnResponseCompleteData data) {\n        return HttpUtil.isKeepAlive(data.response.protocol(), data.response.headers()) && HttpUtil.isKeepAlive(Protocol.HTTP_1_1, data.request.getHeaders());\n    }\n\n    @Override\n    public void onResponseComplete(final OnResponseCompleteData data) {\n        if (data.state.get(\"socket-owner\") != this)\n            return;\n\n        try {\n            idleSocket(data.socket);\n\n            if (data.exception != null || !data.socket.isOpen()) {\n                data.request.logv(\"closing out socket (exception)\");\n                data.socket.setClosedCallback(null);\n                data.socket.close();\n                return;\n            }\n            if (!isKeepAlive(data)) {\n                data.request.logv(\"closing out socket (not keep alive)\");\n                data.socket.setClosedCallback(null);\n                data.socket.close();\n                return;\n            }\n            data.request.logd(\"Recycling keep-alive socket\");\n            recycleSocket(data.socket, data.request);\n        }\n        finally {\n            nextConnection(data.request);\n        }\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/BasicNameValuePair.java",
    "content": "/*\n * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/BasicNameValuePair.java $\n * $Revision: 604625 $\n * $Date: 2007-12-16 06:11:11 -0800 (Sun, 16 Dec 2007) $\n *\n * ====================================================================\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements.  See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership.  The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License.  You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied.  See the License for the\n * specific language governing permissions and limitations\n * under the License.\n * ====================================================================\n *\n * This software consists of voluntary contributions made by many\n * individuals on behalf of the Apache Software Foundation.  For more\n * information on the Apache Software Foundation, please see\n * <http://www.apache.org/>.\n *\n */\n\npackage com.koushikdutta.async.http;\n\nimport android.text.TextUtils;\n\n/**\n * A simple class encapsulating an attribute/value pair.\n * <p>\n *  This class comforms to the generic grammar and formatting rules outlined in the \n *  <a href=\"http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2\">Section 2.2</a>\n *  and  \n *  <a href=\"http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6\">Section 3.6</a>\n *  of <a href=\"http://www.w3.org/Protocols/rfc2616/rfc2616.txt\">RFC 2616</a>\n * </p>\n * <h>2.2 Basic Rules</h>\n * <p>\n *  The following rules are used throughout this specification to describe basic parsing constructs. \n *  The US-ASCII coded character set is defined by ANSI X3.4-1986.\n * </p>\n * <pre>\n *     OCTET          = <any 8-bit sequence of data>\n *     CHAR           = <any US-ASCII character (octets 0 - 127)>\n *     UPALPHA        = <any US-ASCII uppercase letter \"A\"..\"Z\">\n *     LOALPHA        = <any US-ASCII lowercase letter \"a\"..\"z\">\n *     ALPHA          = UPALPHA | LOALPHA\n *     DIGIT          = <any US-ASCII digit \"0\"..\"9\">\n *     CTL            = <any US-ASCII control character\n *                      (octets 0 - 31) and DEL (127)>\n *     CR             = <US-ASCII CR, carriage return (13)>\n *     LF             = <US-ASCII LF, linefeed (10)>\n *     SP             = <US-ASCII SP, space (32)>\n *     HT             = <US-ASCII HT, horizontal-tab (9)>\n *     <\">            = <US-ASCII double-quote mark (34)>\n * </pre>\n * <p>\n *  Many HTTP/1.1 header field values consist of words separated by LWS or special \n *  characters. These special characters MUST be in a quoted string to be used within \n *  a parameter value (as defined in section 3.6).\n * <p>\n * <pre>\n * token          = 1*<any CHAR except CTLs or separators>\n * separators     = \"(\" | \")\" | \"<\" | \">\" | \"@\"\n *                | \",\" | \";\" | \":\" | \"\\\" | <\">\n *                | \"/\" | \"[\" | \"]\" | \"?\" | \"=\"\n *                | \"{\" | \"}\" | SP | HT\n * </pre>\n * <p>\n *  A string of text is parsed as a single word if it is quoted using double-quote marks.\n * </p>\n * <pre>\n * quoted-string  = ( <\"> *(qdtext | quoted-pair ) <\"> )\n * qdtext         = <any TEXT except <\">>\n * </pre>\n * <p>\n *  The backslash character (\"\\\") MAY be used as a single-character quoting mechanism only \n *  within quoted-string and comment constructs.\n * </p>\n * <pre>\n * quoted-pair    = \"\\\" CHAR\n * </pre>\n * <h>3.6 Transfer Codings</h>\n * <p>\n *  Parameters are in the form of attribute/value pairs.\n * </p>\n * <pre>\n * parameter               = attribute \"=\" value\n * attribute               = token\n * value                   = token | quoted-string\n * </pre> \n * \n * @author <a href=\"mailto:oleg at ural.com\">Oleg Kalnichevski</a>\n * \n */\npublic class BasicNameValuePair implements NameValuePair, Cloneable {\n\n    private final String name;\n    private final String value;\n\n    /**\n     * Default Constructor taking a name and a value. The value may be null.\n     * \n     * @param name The name.\n     * @param value The value.\n     */\n    public BasicNameValuePair(final String name, final String value) {\n        super();\n        if (name == null) {\n            throw new IllegalArgumentException(\"Name may not be null\");\n        }\n        this.name = name;\n        this.value = value;\n    }\n\n    /**\n     * Returns the name.\n     *\n     * @return String name The name\n     */\n    public String getName() {\n        return this.name;\n    }\n\n    /**\n     * Returns the value.\n     *\n     * @return String value The current value.\n     */\n    public String getValue() {\n        return this.value;\n    }\n\n    \n    /**\n     * Get a string representation of this pair.\n     * \n     * @return A string representation.\n     */\n    public String toString() {\n        return name + \"=\" + value;\n    }\n\n    public boolean equals(final Object object) {\n        if (object == null) return false;\n        if (this == object) return true;\n        if (object instanceof NameValuePair) {\n            BasicNameValuePair that = (BasicNameValuePair) object;\n            return this.name.equals(that.name)\n                  && TextUtils.equals(this.value, that.value);\n        } else {\n            return false;\n        }\n    }\n\n    public int hashCode() {\n        return name.hashCode() ^ value.hashCode();\n    }\n    \n    public Object clone() throws CloneNotSupportedException {\n        return super.clone();\n    }\n \n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/BodyDecoderException.java",
    "content": "package com.koushikdutta.async.http;\n\npublic class BodyDecoderException extends Exception {\n    public BodyDecoderException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/ConnectionClosedException.java",
    "content": "package com.koushikdutta.async.http;\n\npublic class ConnectionClosedException extends Exception {\n    public ConnectionClosedException(String message) {\n        super(message);\n    }\n\n    public ConnectionClosedException(String detailMessage, Throwable throwable) {\n        super(detailMessage, throwable);\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/ConnectionFailedException.java",
    "content": "package com.koushikdutta.async.http;\n\npublic class ConnectionFailedException extends Exception {\n    public ConnectionFailedException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/Headers.java",
    "content": "package com.koushikdutta.async.http;\n\n\nimport android.text.TextUtils;\n\nimport com.koushikdutta.async.util.TaggedList;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\n\n/**\n * Created by koush on 7/21/14.\n */\npublic class Headers {\n    public Headers() {\n    }\n\n    public Headers(Map<String, List<String>> mm) {\n        for (String key: mm.keySet()) {\n            addAll(key, mm.get(key));\n        }\n    }\n\n    final Multimap map = new Multimap() {\n        @Override\n        protected List<String> newList() {\n            return new TaggedList<String>();\n        }\n    };\n    public Multimap getMultiMap() {\n        return map;\n    }\n\n    public List<String> getAll(String header) {\n        return map.get(header.toLowerCase(Locale.US));\n    }\n\n    public String get(String header) {\n        return map.getString(header.toLowerCase(Locale.US));\n    }\n\n    public Headers set(String header, String value) {\n        if (value != null && (value.contains(\"\\n\") || value.contains(\"\\r\")))\n            throw new IllegalArgumentException(\"value must not contain a new line or line feed\");\n        String lc = header.toLowerCase(Locale.US);\n        map.put(lc, value);\n        TaggedList<String> list = (TaggedList<String>)map.get(lc);\n        list.tagNull(header);\n        return this;\n    }\n\n    public Headers add(String header, String value) {\n        String lc = header.toLowerCase(Locale.US);\n        map.add(lc, value);\n        TaggedList<String> list = (TaggedList<String>)map.get(lc);\n        list.tagNull(header);\n        return this;\n    }\n\n    public Headers addLine(String line) {\n        if (line != null) {\n            line = line.trim();\n            String[] parts = line.split(\":\", 2);\n            if (parts.length == 2)\n                add(parts[0].trim(), parts[1].trim());\n            else\n                add(parts[0].trim(), \"\");\n        }\n        return this;\n    }\n\n    public Headers addAll(String header, List<String> values) {\n        for (String v: values) {\n            add(header, v);\n        }\n        return this;\n    }\n\n    public Headers addAll(Map<String, List<String>> m) {\n        for (String key: m.keySet()) {\n            for (String value: m.get(key)) {\n                add(key, value);\n            }\n        }\n        return this;\n    }\n\n    public Headers addAllMap(Map<String, String> m) {\n        for (String key: m.keySet()) {\n            add(key, m.get(key));\n        }\n        return this;\n    }\n\n    public Headers addAll(Headers headers) {\n        // safe to addall since this is another Headers object\n        map.putAll(headers.map);\n        return this;\n    }\n\n    public List<String> removeAll(String header) {\n        return map.remove(header.toLowerCase(Locale.US));\n    }\n\n    public String remove(String header) {\n        List<String> r = removeAll(header.toLowerCase(Locale.US));\n        if (r == null || r.size() == 0)\n            return null;\n        return r.get(0);\n    }\n\n    public Headers removeAll(Collection<String> headers) {\n        for (String header: headers) {\n            remove(header);\n        }\n        return this;\n    }\n\n    public StringBuilder toStringBuilder() {\n        StringBuilder result = new StringBuilder(256);\n        for (String key: map.keySet()) {\n            TaggedList<String> list = (TaggedList<String>)map.get(key);\n            for (String v: list) {\n                result.append((String)list.tag())\n                .append(\": \")\n                .append(v)\n                .append(\"\\r\\n\");\n            }\n        }\n        result.append(\"\\r\\n\");\n        return result;\n    }\n\n    @Override\n    public String toString() {\n        return toStringBuilder().toString();\n    }\n\n    public String toPrefixString(String prefix) {\n        return\n        toStringBuilder()\n        .insert(0, prefix + \"\\r\\n\")\n        .toString();\n    }\n\n    public static Headers parse(String payload) {\n        String[] lines = payload.split(\"\\n\");\n\n        Headers headers = new Headers();\n        for (String line: lines) {\n            line = line.trim();\n            if (TextUtils.isEmpty(line))\n                continue;\n\n            headers.addLine(line);\n        }\n        return headers;\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/HttpDate.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.koushikdutta.async.http;\n\nimport java.text.DateFormat;\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.Locale;\nimport java.util.TimeZone;\n\n/**\n * Best-effort parser for HTTP dates.\n */\npublic final class HttpDate {\n\n    /**\n     * Most websites serve cookies in the blessed format. Eagerly create the parser to ensure such\n     * cookies are on the fast path.\n     */\n    private static final ThreadLocal<DateFormat> STANDARD_DATE_FORMAT\n            = new ThreadLocal<DateFormat>() {\n        @Override protected DateFormat initialValue() {\n            DateFormat rfc1123 = new SimpleDateFormat(\"EEE, dd MMM yyyy HH:mm:ss zzz\", Locale.US);\n            rfc1123.setTimeZone(TimeZone.getTimeZone(\"UTC\"));\n            return rfc1123;\n        }\n    };\n\n    /**\n     * If we fail to parse a date in a non-standard format, try each of these formats in sequence.\n     */\n    private static final String[] BROWSER_COMPATIBLE_DATE_FORMATS = new String[] {\n            /* This list comes from  {@code org.apache.http.impl.cookie.BrowserCompatSpec}. */\n            \"EEEE, dd-MMM-yy HH:mm:ss zzz\", // RFC 1036\n            \"EEE MMM d HH:mm:ss yyyy\", // ANSI C asctime()\n            \"EEE, dd-MMM-yyyy HH:mm:ss z\",\n            \"EEE, dd-MMM-yyyy HH-mm-ss z\",\n            \"EEE, dd MMM yy HH:mm:ss z\",\n            \"EEE dd-MMM-yyyy HH:mm:ss z\",\n            \"EEE dd MMM yyyy HH:mm:ss z\",\n            \"EEE dd-MMM-yyyy HH-mm-ss z\",\n            \"EEE dd-MMM-yy HH:mm:ss z\",\n            \"EEE dd MMM yy HH:mm:ss z\",\n            \"EEE,dd-MMM-yy HH:mm:ss z\",\n            \"EEE,dd-MMM-yyyy HH:mm:ss z\",\n            \"EEE, dd-MM-yyyy HH:mm:ss z\",\n\n            /* RI bug 6641315 claims a cookie of this format was once served by www.yahoo.com */\n            \"EEE MMM d yyyy HH:mm:ss z\",\n    };\n\n    /**\n     * Returns the date for {@code value}. Returns null if the value couldn't be\n     * parsed.\n     */\n    public static Date parse(String value) {\n        if (value == null)\n            return null;\n        try {\n            return STANDARD_DATE_FORMAT.get().parse(value);\n        } catch (ParseException ignore) {\n        }\n        for (String formatString : BROWSER_COMPATIBLE_DATE_FORMATS) {\n            try {\n                return new SimpleDateFormat(formatString, Locale.US).parse(value);\n            } catch (ParseException ignore) {\n            }\n        }\n        return null;\n    }\n\n    /**\n     * Returns the string for {@code value}.\n     */\n    public static String format(Date value) {\n        return STANDARD_DATE_FORMAT.get().format(value);\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/HttpTransportMiddleware.java",
    "content": "package com.koushikdutta.async.http;\n\nimport android.text.TextUtils;\n\nimport com.koushikdutta.async.AsyncSocket;\nimport com.koushikdutta.async.BufferedDataSink;\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.DataSink;\nimport com.koushikdutta.async.LineEmitter;\nimport com.koushikdutta.async.Util;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.http.body.AsyncHttpRequestBody;\nimport com.koushikdutta.async.http.filter.ChunkedOutputFilter;\n\nimport java.io.IOException;\n\n/**\n * Created by koush on 7/24/14.\n */\npublic class HttpTransportMiddleware extends SimpleMiddleware {\n    @Override\n    public boolean exchangeHeaders(final OnExchangeHeaderData data) {\n        Protocol p = Protocol.get(data.protocol);\n        if (p != null && p != Protocol.HTTP_1_0 && p != Protocol.HTTP_1_1)\n            return super.exchangeHeaders(data);\n\n        AsyncHttpRequest request = data.request;\n        AsyncHttpRequestBody requestBody = data.request.getBody();\n\n        if (requestBody != null) {\n            if (requestBody.length() >= 0) {\n                request.getHeaders().set(\"Content-Length\", String.valueOf(requestBody.length()));\n                data.response.sink(data.socket);\n            }\n            else if (\"close\".equals(request.getHeaders().get(\"Connection\"))) {\n                data.response.sink(data.socket);\n            }\n            else {\n                request.getHeaders().set(\"Transfer-Encoding\", \"Chunked\");\n                data.response.sink(new ChunkedOutputFilter(data.socket));\n            }\n        }\n\n        String rl = request.getRequestLine().toString();\n        String rs = request.getHeaders().toPrefixString(rl);\n\n        byte[] rsBytes = rs.getBytes();\n\n        // try to get the request body in the same packet as the request headers... if it will fit\n        // in the max MTU (1540 or whatever).\n        final boolean waitForBody = requestBody != null && requestBody.length() >= 0 && requestBody.length() + rsBytes.length < 1024;\n        final BufferedDataSink bsink;\n        final DataSink headerSink;\n        if (waitForBody) {\n            // force buffering of headers\n            bsink = new BufferedDataSink(data.response.sink());\n            bsink.forceBuffering(true);\n            data.response.sink(bsink);\n            headerSink = bsink;\n        }\n        else {\n            bsink = null;\n            headerSink = data.socket;\n        }\n\n        request.logv(\"\\n\" + rs);\n\n        final CompletedCallback sentCallback = data.sendHeadersCallback;\n        Util.writeAll(headerSink, rsBytes, new CompletedCallback() {\n            @Override\n            public void onCompleted(Exception ex) {\n                Util.end(sentCallback, ex);\n                // flush headers and any request body that was written by the callback\n                if (bsink != null) {\n                    bsink.forceBuffering(false);\n                    bsink.setMaxBuffer(0);\n                }\n            }\n        });\n\n        LineEmitter.StringCallback headerCallback = new LineEmitter.StringCallback() {\n            Headers mRawHeaders = new Headers();\n            String statusLine;\n\n            @Override\n            public void onStringAvailable(String s) {\n                try {\n                    s = s.trim();\n                    if (statusLine == null) {\n                        statusLine = s;\n                    }\n                    else if (!TextUtils.isEmpty(s)) {\n                        mRawHeaders.addLine(s);\n                    }\n                    else {\n                        String[] parts = statusLine.split(\" \", 3);\n                        if (parts.length < 2)\n                            throw new Exception(new IOException(\"Not HTTP\"));\n\n                        data.response.headers(mRawHeaders);\n                        String protocol = parts[0];\n                        data.response.protocol(protocol);\n                        data.response.code(Integer.parseInt(parts[1]));\n                        data.response.message(parts.length == 3 ? parts[2] : \"\");\n                        data.receiveHeadersCallback.onCompleted(null);\n\n                        // socket may get detached after headers (websocket)\n                        AsyncSocket socket = data.response.socket();\n                        if (socket == null)\n                            return;\n                        DataEmitter emitter;\n                        // HEAD requests must not return any data. They still may\n                        // return content length, etc, which will confuse the body decoder\n                        if (!data.request.hasBody()) {\n                            emitter = HttpUtil.EndEmitter.create(socket.getServer(), null);\n                        }\n                        else if (responseIsEmpty(data.response.code())) {\n                            emitter = HttpUtil.EndEmitter.create(socket.getServer(), null);\n                        }\n                        else {\n                            emitter = HttpUtil.getBodyDecoder(socket, Protocol.get(protocol), mRawHeaders, false);\n                        }\n                        data.response.emitter(emitter);\n                    }\n                }\n                catch (Exception ex) {\n                    data.receiveHeadersCallback.onCompleted(ex);\n                }\n            }\n        };\n\n        LineEmitter liner = new LineEmitter();\n        data.socket.setDataCallback(liner);\n        liner.setLineCallback(headerCallback);\n        return true;\n    }\n\n    static boolean responseIsEmpty(int code) {\n        return (code >= 100 && code <= 199) || code == 204 || code == 304;\n    }\n\n    @Override\n    public void onRequestSent(OnRequestSentData data) {\n        Protocol p = Protocol.get(data.protocol);\n        if (p != null && p != Protocol.HTTP_1_0 && p != Protocol.HTTP_1_1)\n            return;\n\n        if (data.response.sink() instanceof ChunkedOutputFilter)\n            data.response.sink().end();\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/HttpUtil.java",
    "content": "package com.koushikdutta.async.http;\n\nimport com.koushikdutta.async.AsyncServer;\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.FilteredDataEmitter;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.http.body.AsyncHttpRequestBody;\nimport com.koushikdutta.async.http.body.JSONObjectBody;\nimport com.koushikdutta.async.http.body.MultipartFormDataBody;\nimport com.koushikdutta.async.http.body.StringBody;\nimport com.koushikdutta.async.http.body.UrlEncodedFormBody;\nimport com.koushikdutta.async.http.filter.ChunkedInputFilter;\nimport com.koushikdutta.async.http.filter.ContentLengthFilter;\nimport com.koushikdutta.async.http.filter.GZIPInputFilter;\nimport com.koushikdutta.async.http.filter.InflaterInputFilter;\n\npublic class HttpUtil {\n    public static AsyncHttpRequestBody getBody(DataEmitter emitter, CompletedCallback reporter, Headers headers) {\n        String contentType = headers.get(\"Content-Type\");\n        if (contentType != null) {\n            String[] values = contentType.split(\";\");\n            for (int i = 0; i < values.length; i++) {\n                values[i] = values[i].trim();\n            }\n            for (String ct: values) {\n                if (UrlEncodedFormBody.CONTENT_TYPE.equals(ct)) {\n                    return new UrlEncodedFormBody();\n                }\n                if (JSONObjectBody.CONTENT_TYPE.equals(ct)) {\n                    return new JSONObjectBody();\n                }\n                if (StringBody.CONTENT_TYPE.equals(ct)) {\n                    return new StringBody();\n                }\n                if (ct != null && ct.startsWith(MultipartFormDataBody.PRIMARY_TYPE)) {\n                    return new MultipartFormDataBody(contentType);\n                }\n            }\n        }\n\n        return null;\n    }\n    \n    static class EndEmitter extends FilteredDataEmitter {\n        private EndEmitter() {\n        }\n        \n        public static EndEmitter create(AsyncServer server, final Exception e) {\n            final EndEmitter ret = new EndEmitter();\n            // don't need to worry about any race conditions with post and this return value\n            // since we are in the server thread.\n            server.post(new Runnable() {\n                @Override\n                public void run() {\n                    ret.report(e);\n                }\n            });\n            return ret;\n        }\n    }\n    \n    public static DataEmitter getBodyDecoder(DataEmitter emitter, Protocol protocol, Headers headers, boolean server) {\n        long _contentLength = -1;\n        try {\n            String header = headers.get(\"Content-Length\");\n            if (header != null)\n                _contentLength = Long.parseLong(header);\n        }\n        catch (NumberFormatException ex) {\n        }\n        final long contentLength = _contentLength;\n        if (-1 != contentLength) {\n            if (contentLength < 0) {\n                EndEmitter ender = EndEmitter.create(emitter.getServer(), new BodyDecoderException(\"not using chunked encoding, and no content-length found.\"));\n                ender.setDataEmitter(emitter);\n                emitter = ender;\n                return emitter;\n            }\n            if (contentLength == 0) {\n                EndEmitter ender = EndEmitter.create(emitter.getServer(), null);\n                ender.setDataEmitter(emitter);\n                emitter = ender;\n                return emitter;\n            }\n            ContentLengthFilter contentLengthWatcher = new ContentLengthFilter(contentLength);\n            contentLengthWatcher.setDataEmitter(emitter);\n            emitter = contentLengthWatcher;\n        }\n        else if (\"chunked\".equalsIgnoreCase(headers.get(\"Transfer-Encoding\"))) {\n            ChunkedInputFilter chunker = new ChunkedInputFilter();\n            chunker.setDataEmitter(emitter);\n            emitter = chunker;\n        }\n        else if (server) {\n            // if this is the server, and the client has not indicated a request body, the client is done\n            EndEmitter ender = EndEmitter.create(emitter.getServer(), null);\n            ender.setDataEmitter(emitter);\n            emitter = ender;\n            return emitter;\n        }\n\n        if (\"gzip\".equals(headers.get(\"Content-Encoding\"))) {\n            GZIPInputFilter gunzipper = new GZIPInputFilter();\n            gunzipper.setDataEmitter(emitter);\n            emitter = gunzipper;\n        }        \n        else if (\"deflate\".equals(headers.get(\"Content-Encoding\"))) {\n            InflaterInputFilter inflater = new InflaterInputFilter();\n            inflater.setDataEmitter(emitter);\n            emitter = inflater;\n        }\n\n        // conversely, if this is the client (http 1.0), and the server has not indicated a request body, we do not report\n        // the close/end event until the server actually closes the connection.\n        return emitter;\n    }\n\n    public static boolean isKeepAlive(Protocol protocol, Headers headers) {\n        // connection is always keep alive as this is an http/1.1 client\n        String connection = headers.get(\"Connection\");\n        if (connection == null)\n            return protocol == Protocol.HTTP_1_1;\n        return \"keep-alive\".equalsIgnoreCase(connection);\n    }\n\n    public static boolean isKeepAlive(String protocol, Headers headers) {\n        // connection is always keep alive as this is an http/1.1 client\n        String connection = headers.get(\"Connection\");\n        if (connection == null)\n            return Protocol.get(protocol) == Protocol.HTTP_1_1;\n        return \"keep-alive\".equalsIgnoreCase(connection);\n    }\n\n    public static long contentLength(Headers headers) {\n        String cl = headers.get(\"Content-Length\");\n        if (cl == null)\n            return -1;\n        try {\n            return Long.parseLong(cl);\n        }\n        catch (NumberFormatException e) {\n            return -1;\n        }\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/HybiParser.java",
    "content": "//\n// HybiParser.java: draft-ietf-hybi-thewebsocketprotocol-13 parser\n//\n// Based on code from the faye project.\n// https://github.com/faye/faye-websocket-node\n// Copyright (c) 2009-2012 James Coglan\n//\n// Ported from Javascript to Java by Eric Butler <eric@codebutler.com>\n//\n// (The MIT License)\n//\n// Permission is hereby granted, free of charge, to any person obtaining\n// a copy of this software and associated documentation files (the\n// \"Software\"), to deal in the Software without restriction, including\n// without limitation the rights to use, copy, modify, merge, publish,\n// distribute, sublicense, and/or sell copies of the Software, and to\n// permit persons to whom the Software is furnished to do so, subject to\n// the following conditions:\n//\n// The above copyright notice and this permission notice shall be\n// included in all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\npackage com.koushikdutta.async.http;\n\nimport android.util.Log;\n\nimport com.koushikdutta.async.ByteBufferList;\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.DataEmitterReader;\nimport com.koushikdutta.async.callback.DataCallback;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.UnsupportedEncodingException;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.zip.DataFormatException;\nimport java.util.zip.Inflater;\n\nabstract class HybiParser {\n    private static final String TAG = \"HybiParser\";\n\n    private boolean mMasking = true;\n    private boolean mDeflate = false;\n\n    private int     mStage;\n\n    private boolean mFinal;\n    private boolean mMasked;\n    private boolean mDeflated;\n    private int     mOpcode;\n    private int     mLengthSize;\n    private int     mLength;\n    private int     mMode;\n\n    private byte[] mMask    = new byte[0];\n    private byte[] mPayload = new byte[0];\n\n    private boolean mClosed = false;\n\n    private ByteArrayOutputStream mBuffer = new ByteArrayOutputStream();\n    private Inflater mInflater = new Inflater(true);\n    private byte[] mInflateBuffer = new byte[4096];\n\n    private static final int BYTE   = 255;\n    private static final int FIN    = 128;\n    private static final int MASK   = 128;\n    private static final int RSV1   =  64;\n    private static final int RSV2   =  32;\n    private static final int RSV3   =  16;\n    private static final int OPCODE =  15;\n    private static final int LENGTH = 127;\n\n    private static final int MODE_TEXT   = 1;\n    private static final int MODE_BINARY = 2;\n\n    private static final int OP_CONTINUATION =  0;\n    private static final int OP_TEXT         =  1;\n    private static final int OP_BINARY       =  2;\n    private static final int OP_CLOSE        =  8;\n    private static final int OP_PING         =  9;\n    private static final int OP_PONG         = 10;\n\n    private static final List<Integer> OPCODES = Arrays.asList(\n        OP_CONTINUATION,\n        OP_TEXT,\n        OP_BINARY,\n        OP_CLOSE,\n        OP_PING,\n        OP_PONG\n    );\n\n    private static final List<Integer> FRAGMENTED_OPCODES = Arrays.asList(\n        OP_CONTINUATION, OP_TEXT, OP_BINARY\n    );\n//\n//    public HybiParser(WebSocketClient client) {\n//        mClient = client;\n//    }\n\n    private static byte[] mask(byte[] payload, byte[] mask, int offset) {\n        if (mask.length == 0) return payload;\n\n        for (int i = 0; i < payload.length - offset; i++) {\n            payload[offset + i] = (byte) (payload[offset + i] ^ mask[i % 4]);\n        }\n        return payload;\n    }\n\n    private byte[] inflate(byte[] payload) throws DataFormatException {\n        ByteArrayOutputStream inflated = new ByteArrayOutputStream();\n\n        mInflater.setInput(payload);\n        while (!mInflater.needsInput()) {\n            int chunkSize = mInflater.inflate(mInflateBuffer);\n            inflated.write(mInflateBuffer, 0, chunkSize);\n        }\n\n        mInflater.setInput(new byte[] { 0, 0, -1, -1 });\n        while (!mInflater.needsInput()) {\n            int chunkSize = mInflater.inflate(mInflateBuffer);\n            inflated.write(mInflateBuffer, 0, chunkSize);\n        }\n\n        return inflated.toByteArray();\n    }\n\n    public void setMasking(boolean masking) {\n        mMasking = masking;\n    }\n\n    public void setDeflate(boolean deflate) {\n        mDeflate = deflate;\n    }\n\n    DataCallback mStage0 = new DataCallback() {\n        @Override\n        public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n            try {\n                parseOpcode(bb.get());\n            }\n            catch (ProtocolError e) {\n                report(e);\n                e.printStackTrace();\n            }\n            parse();\n        }\n    };\n\n    DataCallback mStage1 = new DataCallback() {\n        @Override\n        public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n            parseLength(bb.get());\n            parse();\n        }\n    };\n\n    DataCallback mStage2 = new DataCallback() {\n        @Override\n        public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n            byte[] bytes = new byte[mLengthSize];\n            bb.get(bytes);\n            try {\n                parseExtendedLength(bytes);\n            }\n            catch (ProtocolError e) {\n                report(e);\n                e.printStackTrace();\n            }\n            parse();\n        }\n    };\n\n    DataCallback mStage3 = new DataCallback() {\n        @Override\n        public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n            mMask = new byte[4];\n            bb.get(mMask);\n            mStage = 4;\n            parse();\n        }\n    };\n\n    DataCallback mStage4 = new DataCallback() {\n        @Override\n        public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n            mPayload = new byte[mLength];\n            bb.get(mPayload);\n            try {\n                emitFrame();\n            }\n            catch (IOException e) {\n                report(e);\n                e.printStackTrace();\n            }\n            mStage = 0;\n            parse();\n        }\n    };\n\n    void parse() {\n        switch (mStage) {\n        case 0:\n            mReader.read(1, mStage0);\n            break;\n        case 1:\n            mReader.read(1, mStage1);\n            break;\n        case 2:\n            mReader.read(mLengthSize, mStage2);\n            break;\n        case 3:\n            mReader.read(4, mStage3);\n            break;\n        case 4:\n            mReader.read(mLength, mStage4);\n            break;\n        }\n    }\n\n    private DataEmitterReader mReader = new DataEmitterReader();\n\n\tprivate static final long BASE = 2;\n\n\tprivate static final long _2_TO_8_ = BASE << 7;\n\n\tprivate static final long _2_TO_16_ = BASE << 15;\n\n\tprivate static final long _2_TO_24 = BASE << 23;\n\n\tprivate static final long _2_TO_32_ = BASE << 31;\n\n\tprivate static final long _2_TO_40_ = BASE << 39;\n\n\tprivate static final long _2_TO_48_ = BASE << 47;\n\n\tprivate static final long _2_TO_56_ = BASE << 55;\n    public HybiParser(DataEmitter socket) {\n        socket.setDataCallback(mReader);\n        parse();\n    }\n\n    private void parseOpcode(byte data) throws ProtocolError {\n        boolean rsv1 = (data & RSV1) == RSV1;\n        boolean rsv2 = (data & RSV2) == RSV2;\n        boolean rsv3 = (data & RSV3) == RSV3;\n\n        if ((!mDeflate && rsv1) || rsv2 || rsv3) {\n            throw new ProtocolError(\"RSV not zero\");\n        }\n\n        mFinal   = (data & FIN) == FIN;\n        mOpcode  = (data & OPCODE);\n        mDeflated = rsv1;\n        mMask    = new byte[0];\n        mPayload = new byte[0];\n\n        if (!OPCODES.contains(mOpcode)) {\n            throw new ProtocolError(\"Bad opcode\");\n        }\n\n        if (!FRAGMENTED_OPCODES.contains(mOpcode) && !mFinal) {\n            throw new ProtocolError(\"Expected non-final packet\");\n        }\n\n        mStage = 1;\n    }\n\n    private void parseLength(byte data) {\n        mMasked = (data & MASK) == MASK;\n        mLength = (data & LENGTH);\n\n        if (mLength >= 0 && mLength <= 125) {\n            mStage = mMasked ? 3 : 4;\n        } else {\n            mLengthSize = (mLength == 126) ? 2 : 8;\n            mStage      = 2;\n        }\n    }\n\n    private void parseExtendedLength(byte[] buffer) throws ProtocolError {\n        mLength = getInteger(buffer);\n        mStage  = mMasked ? 3 : 4;\n    }\n\n    public byte[] frame(String data) {\n        return frame(OP_TEXT, data, -1);\n    }\n\n    public byte[] frame(byte[] data) {\n        return frame(OP_BINARY, data, -1);\n    }\n\n    public byte[] frame(byte[] data, int offset, int length) {\n    \treturn frame(OP_BINARY, data, -1, offset, length);\n    }\n\n    public byte[] pingFrame(String data) {\n        return frame(OP_PING, data, -1);\n    }\n\n    public byte[] pongFrame(String data) {\n        return frame(OP_PONG, data, -1);\n    }\n\n    /**\n     * Flip the opcode so to avoid the name collision with the public method\n     *\n     * @param opcode\n     * @param data\n     * @param errorCode\n     * @return\n     */\n    private byte[] frame(int opcode, byte[] data, int errorCode)  {\n        return frame(opcode, data, errorCode, 0, data.length);\n    }\n\n    /**\n     * Don't actually need the flipped method signature, trying to keep it in line with the byte[] version\n     *\n     * @param opcode\n     * @param data\n     * @param errorCode\n     * @return\n     */\n    private byte[] frame(int opcode, String data, int errorCode) {\n        return frame(opcode, decode(data), errorCode);\n    }\n\n    private byte[] frame(int opcode, byte [] data, int errorCode, int dataOffset, int dataLength) {\n        if (mClosed) return null;\n\n//        Log.d(TAG, \"Creating frame for: \" + data + \" op: \" + opcode + \" err: \" + errorCode);\n        byte[] buffer = data;\n        int insert = (errorCode > 0) ? 2 : 0;\n        int length = dataLength + insert - dataOffset;\n        int header = (length <= 125) ? 2 : (length <= 65535 ? 4 : 10);\n        int offset = header + (mMasking ? 4 : 0);\n        int masked = mMasking ? MASK : 0;\n        byte[] frame = new byte[length + offset];\n\n        frame[0] = (byte) ((byte)FIN | (byte)opcode);\n\n        if (length <= 125) {\n            frame[1] = (byte) (masked | length);\n        } else if (length <= 65535) {\n            frame[1] = (byte) (masked | 126);\n            frame[2] = (byte) (length / 256);\n            frame[3] = (byte) (length & BYTE);\n        } else {\n\n        \tframe[1] = (byte) (masked | 127);\n            frame[2] = (byte) (( length / _2_TO_56_) & BYTE);\n            frame[3] = (byte) (( length / _2_TO_48_) & BYTE);\n            frame[4] = (byte) (( length / _2_TO_40_) & BYTE);\n            frame[5] = (byte) (( length / _2_TO_32_) & BYTE);\n            frame[6] = (byte) (( length / _2_TO_24) & BYTE);\n            frame[7] = (byte) (( length / _2_TO_16_) & BYTE);\n            frame[8] = (byte) (( length / _2_TO_8_)  & BYTE);\n            frame[9] = (byte) (length & BYTE);\n        }\n\n        if (errorCode > 0) {\n            frame[offset] = (byte) ((errorCode / 256) & BYTE);\n            frame[offset+1] = (byte) (errorCode & BYTE);\n        }\n\n        System.arraycopy(buffer, dataOffset, frame, offset + insert, dataLength - dataOffset);\n\n        if (mMasking) {\n            byte[] mask = {\n                (byte) Math.floor(Math.random() * 256), (byte) Math.floor(Math.random() * 256),\n                (byte) Math.floor(Math.random() * 256), (byte) Math.floor(Math.random() * 256)\n            };\n            System.arraycopy(mask, 0, frame, header, mask.length);\n            mask(frame, mask, offset);\n        }\n\n        return frame;\n    }\n\n    public void close(int code, String reason) {\n        if (mClosed) return;\n        sendFrame(frame(OP_CLOSE, reason, code));\n        mClosed = true;\n    }\n\n    private void emitFrame() throws IOException {\n        byte[] payload = mask(mPayload, mMask, 0);\n        if (mDeflated) {\n            try {\n                payload = inflate(payload);\n            } catch (DataFormatException e) {\n                throw new IOException(\"Invalid deflated data\");\n            }\n        }\n        int opcode = mOpcode;\n\n        if (opcode == OP_CONTINUATION) {\n            if (mMode == 0) {\n                throw new ProtocolError(\"Mode was not set.\");\n            }\n            mBuffer.write(payload);\n            if (mFinal) {\n                byte[] message = mBuffer.toByteArray();\n                if (mMode == MODE_TEXT) {\n                    onMessage(encode(message));\n                } else {\n                    onMessage(message);\n                }\n                reset();\n            }\n\n        } else if (opcode == OP_TEXT) {\n            if (mFinal) {\n                String messageText = encode(payload);\n                onMessage(messageText);\n            } else {\n                mMode = MODE_TEXT;\n                mBuffer.write(payload);\n            }\n\n        } else if (opcode == OP_BINARY) {\n            if (mFinal) {\n                onMessage(payload);\n            } else {\n                mMode = MODE_BINARY;\n                mBuffer.write(payload);\n            }\n\n        } else if (opcode == OP_CLOSE) {\n            int    code   = (payload.length >= 2) ? 256 * (payload[0] & 0xFF) + (payload[1] & 0xFF) : 0;\n            String reason = (payload.length >  2) ? encode(slice(payload, 2))     : null;\n//            Log.d(TAG, \"Got close op! \" + code + \" \" + reason);\n            onDisconnect(code, reason);\n\n        } else if (opcode == OP_PING) {\n            if (payload.length > 125) { throw new ProtocolError(\"Ping payload too large\"); }\n//            Log.d(TAG, \"Sending pong!!\");\n            String message = encode(payload);\n            sendFrame(frame(OP_PONG, payload, -1));\n            onPing(message);\n        } else if (opcode == OP_PONG) {\n            String message = encode(payload);\n            onPong(message);\n//            Log.d(TAG, \"Got pong! \" + message);\n        }\n    }\n\n    protected abstract void onMessage(byte[] payload);\n    protected abstract void onMessage(String payload);\n    protected abstract void onPong(String payload);\n    protected abstract void onPing(String payload);\n    protected abstract void onDisconnect(int code, String reason);\n    protected abstract void report(Exception ex);\n\n    protected abstract void sendFrame(byte[] frame);\n\n    private void reset() {\n        mMode = 0;\n        mBuffer.reset();\n    }\n\n    private String encode(byte[] buffer) {\n        try {\n            return new String(buffer, \"UTF-8\");\n        } catch (UnsupportedEncodingException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    private byte[] decode(String string) {\n        try {\n            return (string).getBytes(\"UTF-8\");\n        } catch (UnsupportedEncodingException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    private int getInteger(byte[] bytes) throws ProtocolError {\n        long i = byteArrayToLong(bytes, 0, bytes.length);\n        if (i < 0 || i > Integer.MAX_VALUE) {\n            throw new ProtocolError(\"Bad integer: \" + i);\n        }\n        return (int) i;\n    }\n\n    private byte[] slice(byte[] array, int start) {\n        byte[] copy = new byte[array.length - start];\n        System.arraycopy(array, start, copy, 0, array.length - start);\n        return copy;\n    }\n\n    @Override\n    protected void finalize() throws Throwable {\n        Inflater inflater = mInflater;\n\n        if (inflater != null) {\n            try {\n                inflater.end();\n            } catch (Exception e) {\n                Log.e(TAG, \"inflater.end failed\", e);\n            }\n        }\n\n        super.finalize();\n    }\n\n    public static class ProtocolError extends IOException {\n        public ProtocolError(String detailMessage) {\n            super(detailMessage);\n        }\n    }\n\n    private static long byteArrayToLong(byte[] b, int offset, int length) {\n        if (b.length < length)\n            throw new IllegalArgumentException(\"length must be less than or equal to b.length\");\n\n        long value = 0;\n        for (int i = 0; i < length; i++) {\n            int shift = (length - 1 - i) * 8;\n            value += (b[i + offset] & 0x000000FF) << shift;\n        }\n        return value;\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/Multimap.java",
    "content": "package com.koushikdutta.async.http;\n\nimport android.net.Uri;\nimport android.text.TextUtils;\n\nimport com.koushikdutta.async.util.TaggedList;\n\nimport java.net.URLDecoder;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Created by koush on 5/27/13.\n */\npublic class Multimap extends LinkedHashMap<String, List<String>> implements Iterable<NameValuePair> {\n    public Multimap() {\n    }\n\n    protected List<String> newList() {\n        return new ArrayList<String>();\n    }\n\n    public String getString(String name) {\n        List<String> ret = get(name);\n        if (ret == null || ret.size() == 0)\n            return null;\n        return ret.get(0);\n    }\n\n    public String getAllString(String name, String delimiter) {\n        List<String> ret = get(name);\n        if (ret == null || ret.size() == 0)\n            return null;\n        StringBuilder builder = new StringBuilder();\n        boolean first = true;\n        for (String value: ret) {\n            if (!first)\n                builder.append(delimiter);\n\n            builder.append(value);\n            first = false;\n        }\n        return builder.toString();\n    }\n\n    public List<String> ensure(String name) {\n        List<String> ret = get(name);\n        if (ret == null) {\n            ret = newList();\n            put(name, ret);\n        }\n        return ret;\n    }\n\n    public void add(String name, String value) {\n        ensure(name).add(value);\n    }\n\n    public void put(String name, String value) {\n        List<String> ret = newList();\n        ret.add(value);\n        put(name, ret);\n    }\n\n    public Multimap(List<NameValuePair> pairs) {\n        for (NameValuePair pair: pairs)\n            add(pair.getName(), pair.getValue());\n    }\n\n    public Multimap(Multimap m) {\n        putAll(m);\n    }\n\n    public interface StringDecoder {\n        public String decode(String s);\n    }\n\n    public static Multimap parse(String value, String delimiter, boolean unquote, StringDecoder decoder) {\n        return parse(value, delimiter, \"=\", unquote, decoder);\n    }\n\n    public static Multimap parse(String value, String delimiter, String assigner, boolean unquote, StringDecoder decoder) {\n        Multimap map = new Multimap();\n        if (value == null)\n            return map;\n        String[] parts = value.split(delimiter);\n        for (String part: parts) {\n            String[] pair = part.split(assigner, 2);\n            String key = pair[0].trim();\n            // watch for empty string or trailing delimiter\n            if (TextUtils.isEmpty(key))\n                continue;\n            String v = null;\n            if (pair.length > 1)\n                v = pair[1];\n            if (v != null && unquote && v.endsWith(\"\\\"\") && v.startsWith(\"\\\"\"))\n                v = v.substring(1, v.length() - 1);\n            if (v != null && decoder != null) {\n                key = decoder.decode(key);\n                v = decoder.decode(v);\n            }\n            map.add(key, v);\n        }\n        return map;\n    }\n\n    public static Multimap parseSemicolonDelimited(String header) {\n        return parse(header, \";\", true, null);\n    }\n\n    public static Multimap parseCommaDelimited(String header) {\n        return parse(header, \",\", true, null);\n    }\n\n    public static final StringDecoder QUERY_DECODER = new StringDecoder() {\n        @Override\n        public String decode(String s) {\n            return Uri.decode(s);\n        }\n    };\n\n    public static Multimap parseQuery(String query) {\n        return parse(query, \"&\", false, QUERY_DECODER);\n    }\n\n    public static final StringDecoder URL_DECODER = new StringDecoder() {\n        @Override\n        public String decode(String s) {\n            return URLDecoder.decode(s);\n        }\n    };\n\n    public static Multimap parseUrlEncoded(String query) {\n        return parse(query, \"&\", false, URL_DECODER);\n    }\n\n    @Override\n    public Iterator<NameValuePair> iterator() {\n        ArrayList<NameValuePair> ret = new ArrayList<NameValuePair>();\n        for (String name: keySet()) {\n            List<String> values = get(name);\n            for (String value: values) {\n                ret.add(new BasicNameValuePair(name, value));\n            }\n        }\n        return ret.iterator();\n    }\n\n    public Map<String, String> toSingleMap() {\n        HashMap<String, String> ret = new HashMap<>();\n        for (String key: keySet()) {\n            ret.put(key, getString(key));\n        }\n        return ret;\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/NameValuePair.java",
    "content": "/*\n * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/NameValuePair.java $\n * $Revision: 496070 $\n * $Date: 2007-01-14 04:18:34 -0800 (Sun, 14 Jan 2007) $\n *\n * ====================================================================\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements.  See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership.  The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License.  You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied.  See the License for the\n * specific language governing permissions and limitations\n * under the License.\n * ====================================================================\n *\n * This software consists of voluntary contributions made by many\n * individuals on behalf of the Apache Software Foundation.  For more\n * information on the Apache Software Foundation, please see\n * <http://www.apache.org/>.\n *\n */\n\npackage com.koushikdutta.async.http;\n\n/**\n * A simple class encapsulating an attribute/value pair.\n * <p>\n *  This class comforms to the generic grammar and formatting rules outlined in the \n *  <a href=\"http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2\">Section 2.2</a>\n *  and  \n *  <a href=\"http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6\">Section 3.6</a>\n *  of <a href=\"http://www.w3.org/Protocols/rfc2616/rfc2616.txt\">RFC 2616</a>\n * </p>\n * <h>2.2 Basic Rules</h>\n * <p>\n *  The following rules are used throughout this specification to describe basic parsing constructs. \n *  The US-ASCII coded character set is defined by ANSI X3.4-1986.\n * </p>\n * <pre>\n *     OCTET          = <any 8-bit sequence of data>\n *     CHAR           = <any US-ASCII character (octets 0 - 127)>\n *     UPALPHA        = <any US-ASCII uppercase letter \"A\"..\"Z\">\n *     LOALPHA        = <any US-ASCII lowercase letter \"a\"..\"z\">\n *     ALPHA          = UPALPHA | LOALPHA\n *     DIGIT          = <any US-ASCII digit \"0\"..\"9\">\n *     CTL            = <any US-ASCII control character\n *                      (octets 0 - 31) and DEL (127)>\n *     CR             = <US-ASCII CR, carriage return (13)>\n *     LF             = <US-ASCII LF, linefeed (10)>\n *     SP             = <US-ASCII SP, space (32)>\n *     HT             = <US-ASCII HT, horizontal-tab (9)>\n *     <\">            = <US-ASCII double-quote mark (34)>\n * </pre>\n * <p>\n *  Many HTTP/1.1 header field values consist of words separated by LWS or special \n *  characters. These special characters MUST be in a quoted string to be used within \n *  a parameter value (as defined in section 3.6).\n * <p>\n * <pre>\n * token          = 1*<any CHAR except CTLs or separators>\n * separators     = \"(\" | \")\" | \"<\" | \">\" | \"@\"\n *                | \",\" | \";\" | \":\" | \"\\\" | <\">\n *                | \"/\" | \"[\" | \"]\" | \"?\" | \"=\"\n *                | \"{\" | \"}\" | SP | HT\n * </pre>\n * <p>\n *  A string of text is parsed as a single word if it is quoted using double-quote marks.\n * </p>\n * <pre>\n * quoted-string  = ( <\"> *(qdtext | quoted-pair ) <\"> )\n * qdtext         = <any TEXT except <\">>\n * </pre>\n * <p>\n *  The backslash character (\"\\\") MAY be used as a single-character quoting mechanism only \n *  within quoted-string and comment constructs.\n * </p>\n * <pre>\n * quoted-pair    = \"\\\" CHAR\n * </pre>\n * <h>3.6 Transfer Codings</h>\n * <p>\n *  Parameters are in the form of attribute/value pairs.\n * </p>\n * <pre>\n * parameter               = attribute \"=\" value\n * attribute               = token\n * value                   = token | quoted-string\n * </pre> \n *\n * @author <a href=\"mailto:oleg at ural.com\">Oleg Kalnichevski</a>\n *\n */\npublic interface NameValuePair {\n\n    String getName();\n\n    String getValue();\n\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/Protocol.java",
    "content": "package com.koushikdutta.async.http;\n\nimport java.util.Hashtable;\nimport java.util.Locale;\n\n/**\n * Protocols that OkHttp implements for <a\n * href=\"http://tools.ietf.org/html/draft-agl-tls-nextprotoneg-04\">NPN</a> and\n * <a href=\"http://tools.ietf.org/html/draft-ietf-tls-applayerprotoneg\">ALPN</a>\n * selection.\n * <p/>\n * <h3>Protocol vs Scheme</h3>\n * Despite its name, {@link java.net.URL#getProtocol()} returns the\n * {@linkplain java.net.URI#getScheme() scheme} (http, https, etc.) of the URL, not\n * the protocol (http/1.1, spdy/3.1, etc.). OkHttp uses the word <i>protocol</i>\n * to identify how HTTP messages are framed.\n */\npublic enum Protocol {\n    /**\n     * An obsolete plaintext framing that does not use persistent sockets by\n     * default.\n     */\n    HTTP_1_0(\"http/1.0\"),\n\n    /**\n     * A plaintext framing that includes persistent connections.\n     * <p/>\n     * <p>This version of OkHttp implements <a\n     * href=\"http://www.ietf.org/rfc/rfc2616.txt\">RFC 2616</a>, and tracks\n     * revisions to that spec.\n     */\n    HTTP_1_1(\"http/1.1\"),\n\n    /**\n     * Chromium's binary-framed protocol that includes header compression,\n     * multiplexing multiple requests on the same socket, and server-push.\n     * HTTP/1.1 semantics are layered on SPDY/3.\n     * <p/>\n     * <p>This version of OkHttp implements SPDY 3 <a\n     * href=\"http://dev.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3-1\">draft\n     * 3.1</a>. Future releases of OkHttp may use this identifier for a newer draft\n     * of the SPDY spec.\n     */\n    SPDY_3(\"spdy/3.1\") {\n        @Override\n        public boolean needsSpdyConnection() {\n            return true;\n        }\n    },\n\n    /**\n     * The IETF's binary-framed protocol that includes header compression,\n     * multiplexing multiple requests on the same socket, and server-push.\n     * HTTP/1.1 semantics are layered on HTTP/2.\n     * <p/>\n     * <p>This version of OkHttp implements HTTP/2 <a\n     * href=\"http://tools.ietf.org/html/draft-ietf-httpbis-http2-13\">draft 12</a>\n     * with HPACK <a\n     * href=\"http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-08\">draft\n     * 6</a>. Future releases of OkHttp may use this identifier for a newer draft\n     * of these specs.\n     */\n    HTTP_2(\"h2-13\") {\n        @Override\n        public boolean needsSpdyConnection() {\n            return true;\n        }\n    };\n\n    private final String protocol;\n    private static final Hashtable<String, Protocol> protocols = new Hashtable<String, Protocol>();\n\n    static {\n        protocols.put(HTTP_1_0.toString(), HTTP_1_0);\n        protocols.put(HTTP_1_1.toString(), HTTP_1_1);\n        protocols.put(SPDY_3.toString(), SPDY_3);\n        protocols.put(HTTP_2.toString(), HTTP_2);\n    }\n\n\n    Protocol(String protocol) {\n        this.protocol = protocol;\n    }\n\n    /**\n     * Returns the protocol identified by {@code protocol}.\n     */\n    public static Protocol get(String protocol) {\n        if (protocol == null)\n            return null;\n        return protocols.get(protocol.toLowerCase(Locale.US));\n    }\n\n    /**\n     * Returns the string used to identify this protocol for ALPN and NPN, like\n     * \"http/1.1\", \"spdy/3.1\" or \"h2-13\".\n     */\n    @Override\n    public String toString() {\n        return protocol;\n    }\n\n    public boolean needsSpdyConnection() {\n        return false;\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/ProtocolVersion.java",
    "content": "/*\n * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/ProtocolVersion.java $\n * $Revision: 609106 $\n * $Date: 2008-01-05 01:15:42 -0800 (Sat, 05 Jan 2008) $\n *\n * ====================================================================\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements.  See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership.  The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License.  You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied.  See the License for the\n * specific language governing permissions and limitations\n * under the License.\n * ====================================================================\n *\n * This software consists of voluntary contributions made by many\n * individuals on behalf of the Apache Software Foundation.  For more\n * information on the Apache Software Foundation, please see\n * <http://www.apache.org/>.\n *\n */\n\npackage com.koushikdutta.async.http;\n\nimport java.io.Serializable;\n\n\n/**\n * Represents a protocol version, as specified in RFC 2616.\n * RFC 2616 specifies only HTTP versions, like \"HTTP/1.1\" and \"HTTP/1.0\".\n * RFC 3261 specifies a message format that is identical to HTTP except\n * for the protocol name. It defines a protocol version \"SIP/2.0\".\n * There are some nitty-gritty differences between the interpretation\n * of versions in HTTP and SIP. In those cases, HTTP takes precedence.\n * <p>\n * This class defines a protocol version as a combination of\n * protocol name, major version number, and minor version number.\n * Note that {@link #equals} and {@link #hashCode} are defined as\n * final here, they cannot be overridden in derived classes.\n * \n * @author <a href=\"mailto:oleg@ural.ru\">Oleg Kalnichevski</a>\n * @author <a href=\"mailto:rolandw at apache.org\">Roland Weber</a>\n * \n * @version $Revision: 609106 $\n */\npublic class ProtocolVersion implements Serializable, Cloneable {\n\n    private static final long serialVersionUID = 8950662842175091068L;\n\n\n    /** Name of the protocol. */\n    protected final String protocol;\n\n    /** Major version number of the protocol */\n    protected final int major;\n\n    /** Minor version number of the protocol */\n    protected final int minor;\n\n    \n    /**\n     * Create a protocol version designator.\n     *\n     * @param protocol   the name of the protocol, for example \"HTTP\"\n     * @param major      the major version number of the protocol\n     * @param minor      the minor version number of the protocol\n     */\n    public ProtocolVersion(String protocol, int major, int minor) {\n        if (protocol == null) {\n            throw new IllegalArgumentException\n                (\"Protocol name must not be null.\");\n        }\n        if (major < 0) {\n            throw new IllegalArgumentException\n                (\"Protocol major version number must not be negative.\");\n        }\n        if (minor < 0) {\n            throw new IllegalArgumentException\n                (\"Protocol minor version number may not be negative\");\n        }\n        this.protocol = protocol;\n        this.major = major;\n        this.minor = minor;\n    }\n\n    /**\n     * Returns the name of the protocol.\n     * \n     * @return the protocol name\n     */\n    public final String getProtocol() {\n        return protocol;\n    }\n\n    /**\n     * Returns the major version number of the protocol.\n     * \n     * @return the major version number.\n     */\n    public final int getMajor() {\n        return major;\n    }\n\n    /**\n     * Returns the minor version number of the HTTP protocol.\n     * \n     * @return the minor version number.\n     */\n    public final int getMinor() {\n        return minor;\n    }\n\n\n    /**\n     * Obtains a specific version of this protocol.\n     * This can be used by derived classes to instantiate themselves instead\n     * of the base class, and to define constants for commonly used versions.\n     * <br/>\n     * The default implementation in this class returns <code>this</code>\n     * if the version matches, and creates a new {@link ProtocolVersion}\n     * otherwise.\n     *\n     * @param major     the major version\n     * @param minor     the minor version\n     *\n     * @return  a protocol version with the same protocol name\n     *          and the argument version\n     */\n    public ProtocolVersion forVersion(int major, int minor) {\n\n        if ((major == this.major) && (minor == this.minor)) {\n            return this;\n        }\n\n        // argument checking is done in the constructor\n        return new ProtocolVersion(this.protocol, major, minor);\n    }\n\n\n    /**\n     * Obtains a hash code consistent with {@link #equals}.\n     *\n     * @return  the hashcode of this protocol version\n     */\n    public final int hashCode() {\n        return this.protocol.hashCode() ^ (this.major * 100000) ^ this.minor;\n    }\n\n        \n    /**\n     * Checks equality of this protocol version with an object.\n     * The object is equal if it is a protocl version with the same\n     * protocol name, major version number, and minor version number.\n     * The specific class of the object is <i>not</i> relevant,\n     * instances of derived classes with identical attributes are\n     * equal to instances of the base class and vice versa.\n     *\n     * @param obj       the object to compare with\n     *\n     * @return  <code>true</code> if the argument is the same protocol version,\n     *          <code>false</code> otherwise\n     */\n    public final boolean equals(Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (!(obj instanceof ProtocolVersion)) {\n            return false;\n        }\n        ProtocolVersion that = (ProtocolVersion) obj;\n\n        return ((this.protocol.equals(that.protocol)) &&\n                (this.major == that.major) &&\n                (this.minor == that.minor));\n    }\n\n\n    /**\n     * Checks whether this protocol can be compared to another one.\n     * Only protocol versions with the same protocol name can be\n     * {@link #compareToVersion compared}.\n     *\n     * @param that      the protocol version to consider\n     *\n     * @return  <code>true</code> if {@link #compareToVersion compareToVersion}\n     *          can be called with the argument, <code>false</code> otherwise\n     */\n    public boolean isComparable(ProtocolVersion that) {\n        return (that != null) && this.protocol.equals(that.protocol);\n    }\n\n\n    /**\n     * Compares this protocol version with another one.\n     * Only protocol versions with the same protocol name can be compared.\n     * This method does <i>not</i> define a total ordering, as it would be\n     * required for {@link java.lang.Comparable}.\n     *\n     * @param that      the protocl version to compare with\n     *  \n     * @return   a negative integer, zero, or a positive integer\n     *           as this version is less than, equal to, or greater than\n     *           the argument version.\n     *\n     * @throws IllegalArgumentException\n     *         if the argument has a different protocol name than this object,\n     *         or if the argument is <code>null</code>\n     */\n    public int compareToVersion(ProtocolVersion that) {\n        if (that == null) {\n            throw new IllegalArgumentException\n                (\"Protocol version must not be null.\"); \n        }\n        if (!this.protocol.equals(that.protocol)) {\n            throw new IllegalArgumentException\n                (\"Versions for different protocols cannot be compared. \" +\n                 this + \" \" + that);\n        }\n\n        int delta = getMajor() - that.getMajor();\n        if (delta == 0) {\n            delta = getMinor() - that.getMinor();\n        }\n        return delta;\n    }\n\n\n    /**\n     * Tests if this protocol version is greater or equal to the given one.\n     *\n     * @param version   the version against which to check this version\n     *\n     * @return  <code>true</code> if this protocol version is\n     *          {@link #isComparable comparable} to the argument\n     *          and {@link #compareToVersion compares} as greater or equal,\n     *          <code>false</code> otherwise\n     */\n    public final boolean greaterEquals(ProtocolVersion version) {\n        return isComparable(version) && (compareToVersion(version) >= 0);\n    }\n\n\n    /**\n     * Tests if this protocol version is less or equal to the given one.\n     *\n     * @param version   the version against which to check this version\n     *\n     * @return  <code>true</code> if this protocol version is\n     *          {@link #isComparable comparable} to the argument\n     *          and {@link #compareToVersion compares} as less or equal,\n     *          <code>false</code> otherwise\n     */\n    public final boolean lessEquals(ProtocolVersion version) {\n        return isComparable(version) && (compareToVersion(version) <= 0);\n    }\n\n\n    /**\n     * Converts this protocol version to a string.\n     *\n     * @return  a protocol version string, like \"HTTP/1.1\"\n     */\n    public String toString() {\n        StringBuilder buffer = new StringBuilder();\n        buffer.append(this.protocol); \n        buffer.append('/'); \n        buffer.append(Integer.toString(this.major)); \n        buffer.append('.'); \n        buffer.append(Integer.toString(this.minor)); \n        return buffer.toString();\n    }\n\n    public Object clone() throws CloneNotSupportedException {\n        return super.clone();\n    }\n    \n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/RedirectLimitExceededException.java",
    "content": "package com.koushikdutta.async.http;\n\npublic class RedirectLimitExceededException extends Exception {\n    public RedirectLimitExceededException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/RequestLine.java",
    "content": "/*\n * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/RequestLine.java $\n * $Revision: 573864 $\n * $Date: 2007-09-08 08:53:25 -0700 (Sat, 08 Sep 2007) $\n *\n * ====================================================================\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements.  See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership.  The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License.  You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied.  See the License for the\n * specific language governing permissions and limitations\n * under the License.\n * ====================================================================\n *\n * This software consists of voluntary contributions made by many\n * individuals on behalf of the Apache Software Foundation.  For more\n * information on the Apache Software Foundation, please see\n * <http://www.apache.org/>.\n *\n */\n\npackage com.koushikdutta.async.http;\n\n/**\n * The first line of an {@link HttpRequest HttpRequest}.\n * It contains the method, URI, and HTTP version of the request.\n * For details, see RFC 2616.\n *\n * @author <a href=\"mailto:oleg at ural.ru\">Oleg Kalnichevski</a>\n *\n * @version $Revision: 573864 $\n * \n * @since 4.0\n */\npublic interface RequestLine {\n\n    String getMethod();\n\n    ProtocolVersion getProtocolVersion();\n\n    String getUri();\n    \n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/SSLEngineSNIConfigurator.java",
    "content": "package com.koushikdutta.async.http;\n\nimport android.os.Build;\n\nimport java.lang.reflect.Field;\nimport java.util.Hashtable;\n\nimport javax.net.ssl.SSLContext;\nimport javax.net.ssl.SSLEngine;\n\n/**\n * Created by koush on 12/8/14.\n */\npublic class SSLEngineSNIConfigurator implements AsyncSSLEngineConfigurator {\n    private static class EngineHolder implements AsyncSSLEngineConfigurator {\n        Field peerHost;\n        Field peerPort;\n        Field sslParameters;\n        Field useSni;\n        boolean skipReflection;\n\n        @Override\n        public SSLEngine createEngine(SSLContext sslContext, String peerHost, int peerPort) {\n            return null;\n        }\n\n        public EngineHolder(Class engineClass) {\n            try {\n                peerHost = engineClass.getSuperclass().getDeclaredField(\"peerHost\");\n                peerHost.setAccessible(true);\n\n                peerPort = engineClass.getSuperclass().getDeclaredField(\"peerPort\");\n                peerPort.setAccessible(true);\n\n                sslParameters = engineClass.getDeclaredField(\"sslParameters\");\n                sslParameters.setAccessible(true);\n\n                useSni = sslParameters.getType().getDeclaredField(\"useSni\");\n                useSni.setAccessible(true);\n            }\n            catch (NoSuchFieldException e) {\n            }\n        }\n\n        @Override\n        public void configureEngine(SSLEngine engine, AsyncHttpClientMiddleware.GetSocketData data, String host, int port) {\n            if (useSni == null || skipReflection)\n                return;\n            try {\n                peerHost.set(engine, host);\n                peerPort.set(engine, port);\n                Object sslp = sslParameters.get(engine);\n                useSni.set(sslp, true);\n            }\n            catch (IllegalAccessException e) {\n            }\n        }\n    }\n\n    Hashtable<String, EngineHolder> holders = new Hashtable<String, EngineHolder>();\n\n    @Override\n    public SSLEngine createEngine(SSLContext sslContext, String peerHost, int peerPort) {\n        // pre M, must use reflection to enable SNI, otherwise createSSLEngine(peerHost, peerPort) works.\n        SSLEngine engine;\n        boolean skipReflection = \"GmsCore_OpenSSL\".equals(sslContext.getProvider().getName()) || Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;\n        if (skipReflection)\n            engine = sslContext.createSSLEngine(peerHost, peerPort);\n        else\n            engine = sslContext.createSSLEngine();\n//        ensureHolder(engine).skipReflection = skipReflection;\n        return engine;\n    }\n\n    EngineHolder ensureHolder(SSLEngine engine) {\n        String name = engine.getClass().getCanonicalName();\n        EngineHolder holder = holders.get(name);\n        if (holder == null) {\n            holder = new EngineHolder(engine.getClass());\n            holders.put(name, holder);\n        }\n        return holder;\n    }\n\n    @Override\n    public void configureEngine(SSLEngine engine, AsyncHttpClientMiddleware.GetSocketData data, String host, int port) {\n        EngineHolder holder = ensureHolder(engine);\n        holder.configureEngine(engine, data, host, port);\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/SimpleMiddleware.java",
    "content": "package com.koushikdutta.async.http;\n\nimport com.koushikdutta.async.future.Cancellable;\n\npublic class SimpleMiddleware implements AsyncHttpClientMiddleware {\n    @Override\n    public void onRequest(OnRequestData data) {\n    }\n\n    @Override\n    public Cancellable getSocket(GetSocketData data) {\n        return null;\n    }\n\n    @Override\n    public boolean exchangeHeaders(OnExchangeHeaderData data) {\n        return false;\n    }\n\n    @Override\n    public void onRequestSent(OnRequestSentData data) {\n    }\n\n    @Override\n    public void onHeadersReceived(OnHeadersReceivedData data) {\n    }\n\n    @Override\n    public void onBodyDecoder(OnBodyDecoderData data) {\n    }\n\n    @Override\n    public AsyncHttpRequest onResponseReady(OnResponseReadyData data) {\n        return null;\n    }\n\n    @Override\n    public void onResponseComplete(OnResponseCompleteData data) {\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/WebSocket.java",
    "content": "package com.koushikdutta.async.http;\n\nimport com.koushikdutta.async.AsyncSocket;\n\npublic interface WebSocket extends AsyncSocket {\n    interface StringCallback {\n        void onStringAvailable(String s);\n    }\n    interface PingCallback {\n        void onPingReceived(String s);\n    }\n    interface PongCallback {\n        void onPongReceived(String s);\n    }\n\n    void send(byte[] bytes);\n    void send(String string);\n    void send(byte [] bytes, int offset, int len);\n    void ping(String message);\n    void pong(String message);\n    \n    void setStringCallback(StringCallback callback);\n    StringCallback getStringCallback();\n\n    void setPingCallback(PingCallback callback);\n    \n    void setPongCallback(PongCallback callback);\n    PongCallback getPongCallback();\n\n    boolean isBuffering();\n    String getProtocol();\n    \n    AsyncSocket getSocket();\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/WebSocketHandshakeException.java",
    "content": "package com.koushikdutta.async.http;\n\npublic class WebSocketHandshakeException extends Exception {\n    public WebSocketHandshakeException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/WebSocketImpl.java",
    "content": "package com.koushikdutta.async.http;\n\nimport android.text.TextUtils;\nimport android.util.Base64;\n\nimport com.koushikdutta.async.AsyncServer;\nimport com.koushikdutta.async.AsyncSocket;\nimport com.koushikdutta.async.BufferedDataSink;\nimport com.koushikdutta.async.ByteBufferList;\nimport com.koushikdutta.async.Util;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.callback.DataCallback;\nimport com.koushikdutta.async.callback.WritableCallback;\nimport com.koushikdutta.async.http.server.AsyncHttpServerRequest;\nimport com.koushikdutta.async.http.server.AsyncHttpServerResponse;\n\nimport java.nio.ByteBuffer;\nimport java.nio.LongBuffer;\nimport java.security.MessageDigest;\nimport java.util.LinkedList;\nimport java.util.UUID;\n\npublic class WebSocketImpl implements WebSocket {\n    @Override\n    public void end() {\n        mSocket.end();\n    }\n    \n    private static byte[] toByteArray(UUID uuid) {\n    \tbyte[] byteArray = new byte[(Long.SIZE / Byte.SIZE) * 2];\n    \tByteBuffer buffer = ByteBuffer.wrap(byteArray);\n    \tLongBuffer longBuffer = buffer.asLongBuffer();\n    \tlongBuffer.put(new long[] { uuid.getMostSignificantBits(),uuid.getLeastSignificantBits() });\n    \treturn byteArray;\n    }\n\n    private static String SHA1(String text) {\n        try {\n            MessageDigest md = MessageDigest.getInstance(\"SHA-1\");\n            md.update(text.getBytes(\"iso-8859-1\"), 0, text.length());\n            byte[] sha1hash = md.digest();\n            return Base64.encodeToString(sha1hash, Base64.NO_WRAP);\n        }\n        catch (Exception ex) {\n            return null;\n        }\n    }\n\n    final static String MAGIC = \"258EAFA5-E914-47DA-95CA-C5AB0DC85B11\";\n    \n    private LinkedList<ByteBufferList> pending;\n\n    private void addAndEmit(ByteBufferList bb) {\n        if (pending == null) {\n            Util.emitAllData(this, bb);\n            if (bb.remaining() > 0) {\n                pending = new LinkedList<ByteBufferList>();\n                pending.add(bb);\n            }\n            return;\n        }\n        \n        while (!isPaused()) {\n            bb = pending.remove();\n            Util.emitAllData(this, bb);\n            if (bb.remaining() > 0)\n                pending.add(0, bb);\n        }\n        if (pending.size() == 0)\n            pending = null;\n    }\n\n    private void setupParser(boolean masking, boolean deflate) {\n        mParser = new HybiParser(mSocket) {\n            @Override\n            protected void report(Exception ex) {\n                if (WebSocketImpl.this.mExceptionCallback != null)\n                    WebSocketImpl.this.mExceptionCallback.onCompleted(ex);\n            }\n            @Override\n            protected void onMessage(byte[] payload) {\n                addAndEmit(new ByteBufferList(payload));\n            }\n\n            @Override\n            protected void onMessage(String payload) {\n                if (WebSocketImpl.this.mStringCallback != null)\n                    WebSocketImpl.this.mStringCallback.onStringAvailable(payload);\n            }\n            @Override\n            protected void onDisconnect(int code, String reason) {\n                mSocket.close();\n//                if (WebSocketImpl.this.mClosedCallback != null)\n//                    WebSocketImpl.this.mClosedCallback.onCompleted(null);\n            }\n            @Override\n            protected void sendFrame(byte[] frame) {\n                mSink.write(new ByteBufferList(frame));\n            }\n\n            @Override\n            protected void onPing(String payload) {\n                if (WebSocketImpl.this.mPingCallback != null)\n                    WebSocketImpl.this.mPingCallback.onPingReceived(payload);\n            }\n\n            @Override\n            protected void onPong(String payload) {\n                if (WebSocketImpl.this.mPongCallback != null)\n                    WebSocketImpl.this.mPongCallback.onPongReceived(payload);\n            }\n        };\n        mParser.setMasking(masking);\n        mParser.setDeflate(deflate);\n        if (mSocket.isPaused())\n            mSocket.resume();\n    }\n    \n    private AsyncSocket mSocket;\n    BufferedDataSink mSink;\n    public WebSocketImpl(AsyncHttpServerRequest request, AsyncHttpServerResponse response) {\n        this(request.getSocket());\n        \n        String key = request.getHeaders().get(\"Sec-WebSocket-Key\");\n        String concat = key + MAGIC;\n        String sha1 = SHA1(concat);\n        String origin = request.getHeaders().get(\"Origin\");\n        \n        response.code(101);\n        response.getHeaders().set(\"Upgrade\", \"WebSocket\");\n        response.getHeaders().set(\"Connection\", \"Upgrade\");\n        response.getHeaders().set(\"Sec-WebSocket-Accept\", sha1);\n        String protocol = request.getHeaders().get(\"Sec-WebSocket-Protocol\");\n        // match the protocol (sanity checking and enforcement is done in the caller)\n        if (!TextUtils.isEmpty(protocol))\n            response.getHeaders().set(\"Sec-WebSocket-Protocol\", protocol);\n//        if (origin != null)\n//            response.getHeaders().getHeaders().set(\"Access-Control-Allow-Origin\", \"http://\" + origin);\n        response.writeHead();\n        \n        setupParser(false, false);\n    }\n\n    String protocol;\n    @Override\n    public String getProtocol() {\n        return protocol;\n    }\n\n    public static void addWebSocketUpgradeHeaders(AsyncHttpRequest req, String... protocols) {\n        Headers headers = req.getHeaders();\n        final String key = Base64.encodeToString(toByteArray(UUID.randomUUID()),Base64.NO_WRAP);\n        headers.set(\"Sec-WebSocket-Version\", \"13\");\n        headers.set(\"Sec-WebSocket-Key\", key);\n        headers.set(\"Sec-WebSocket-Extensions\", \"x-webkit-deflate-frame\");\n        headers.set(\"Connection\", \"Upgrade\");\n        headers.set(\"Upgrade\", \"websocket\");\n        if (protocols != null) {\n            for (String protocol: protocols) {\n                headers.add(\"Sec-WebSocket-Protocol\", protocol);\n            }\n        }\n        headers.set(\"Pragma\", \"no-cache\");\n        headers.set(\"Cache-Control\", \"no-cache\");\n        if (TextUtils.isEmpty(req.getHeaders().get(\"User-Agent\")))\n            req.getHeaders().set(\"User-Agent\", \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.15 Safari/537.36\");\n    }\n    \n    public WebSocketImpl(AsyncSocket socket) {\n        mSocket = socket;\n        mSink = new BufferedDataSink(mSocket);\n    }\n    \n    public static WebSocket finishHandshake(Headers requestHeaders, AsyncHttpResponse response) {\n        if (response == null)\n            return null;\n        if (response.code() != 101)\n            return null;\n        if (!\"websocket\".equalsIgnoreCase(response.headers().get(\"Upgrade\")))\n            return null;\n        \n        String sha1 = response.headers().get(\"Sec-WebSocket-Accept\");\n        if (sha1 == null)\n            return null;\n        String key = requestHeaders.get(\"Sec-WebSocket-Key\");\n        if (key == null)\n            return null;\n        String concat = key + MAGIC;\n        String expected = SHA1(concat).trim();\n        if (!sha1.equalsIgnoreCase(expected))\n            return null;\n        String extensions = requestHeaders.get(\"Sec-WebSocket-Extensions\");\n        boolean deflate = false;\n        if (extensions != null) {\n            if (extensions.equals(\"x-webkit-deflate-frame\"))\n                deflate = true;\n            // is this right? do we want to crap out here? Commenting out\n            // as I suspect this caused a regression.\n//            else\n//                return null;\n        }\n\n        WebSocketImpl ret = new WebSocketImpl(response.detachSocket());\n        ret.protocol = response.headers().get(\"Sec-WebSocket-Protocol\");\n        ret.setupParser(true, deflate);\n        return ret;\n    }\n    \n    HybiParser mParser;\n\n    @Override\n    public void close() {\n        mSocket.close();\n    }\n\n    @Override\n    public void setClosedCallback(CompletedCallback handler) {\n        mSocket.setClosedCallback(handler);\n    }\n\n    @Override\n    public CompletedCallback getClosedCallback() {\n        return mSocket.getClosedCallback();\n    }\n\n    CompletedCallback mExceptionCallback;\n    @Override\n    public void setEndCallback(CompletedCallback callback) {\n        mExceptionCallback = callback;\n    }\n\n    @Override\n    public CompletedCallback getEndCallback() {\n        return mExceptionCallback;\n    }\n\n    @Override\n    public void send(byte[] bytes) {\n        getServer().post(() -> mSink.write(new ByteBufferList((mParser.frame(bytes)))));\n    }\n    \n    @Override\n    public void send(byte[] bytes, int offset, int len) {\n        getServer().post(() -> mSink.write(new ByteBufferList(mParser.frame(bytes, offset, len))));\n    }\n\n    @Override\n    public void send(String string) {\n        getServer().post(() -> mSink.write(new ByteBufferList((mParser.frame(string)))));\n    }\n\n    @Override\n    public void ping(String string) {\n        getServer().post(() -> mSink.write(new ByteBufferList(ByteBuffer.wrap(mParser.pingFrame(string)))));\n    }\n\n    @Override\n    public void pong(String string) {\n        getServer().post(() -> mSink.write(new ByteBufferList(ByteBuffer.wrap(mParser.pongFrame(string)))));\n    }\n\n    private StringCallback mStringCallback;\n    @Override\n    public void setStringCallback(StringCallback callback) {\n        mStringCallback = callback;\n    }\n\n    private DataCallback mDataCallback;\n    @Override\n    public void setDataCallback(DataCallback callback) {\n        mDataCallback = callback;\n    }\n\n    @Override\n    public StringCallback getStringCallback() {\n        return mStringCallback;\n    }\n\n    private PingCallback mPingCallback;\n    @Override\n    public void setPingCallback(PingCallback callback) {\n        mPingCallback = callback;\n    }\n\n    private PongCallback mPongCallback;\n    @Override\n    public void setPongCallback(PongCallback callback) {\n        mPongCallback = callback;\n    }\n\n    @Override\n    public PongCallback getPongCallback() {\n        return mPongCallback;\n    }\n\n    @Override\n    public DataCallback getDataCallback() {\n        return mDataCallback;\n    }\n\n    @Override\n    public boolean isOpen() {\n        return mSocket.isOpen();\n    }\n    \n    @Override\n    public boolean isBuffering() {\n        return mSink.remaining() > 0;\n    }\n\n    @Override\n    public void write(ByteBufferList bb) {\n        byte[] buf = bb.getAllByteArray();\n        send(buf);\n    }\n\n    @Override\n    public void setWriteableCallback(WritableCallback handler) {\n        mSink.setWriteableCallback(handler);\n    }\n\n    @Override\n    public WritableCallback getWriteableCallback() {\n        return mSink.getWriteableCallback();\n    }\n    \n    @Override\n    public AsyncSocket getSocket() {\n        return mSocket;\n    }\n\n    @Override\n    public AsyncServer getServer() {\n        return mSocket.getServer();\n    }\n\n    @Override\n    public boolean isChunked() {\n        return false;\n    }\n\n    @Override\n    public void pause() {\n        mSocket.pause();\n    }\n\n    @Override\n    public void resume() {\n        mSocket.resume();\n    }\n\n    @Override\n    public boolean isPaused() {\n        return mSocket.isPaused();\n    }\n\n    @Override\n    public String charset() {\n        return null;\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/body/AsyncHttpRequestBody.java",
    "content": "package com.koushikdutta.async.http.body;\n\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.DataSink;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.http.AsyncHttpRequest;\n\npublic interface AsyncHttpRequestBody<T> {\n    public void write(AsyncHttpRequest request, DataSink sink, CompletedCallback completed);\n    public void parse(DataEmitter emitter, CompletedCallback completed);\n    public String getContentType();\n    public boolean readFullyOnRequest();\n    public int length();\n    public T get();\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/body/ByteBufferListRequestBody.java",
    "content": "package com.koushikdutta.async.http.body;\n\nimport com.koushikdutta.async.ByteBufferList;\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.DataSink;\nimport com.koushikdutta.async.Util;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.http.AsyncHttpRequest;\nimport com.koushikdutta.async.parser.ByteBufferListParser;\n\npublic class ByteBufferListRequestBody implements AsyncHttpRequestBody<ByteBufferList> {\n    public ByteBufferListRequestBody() {\n    }\n\n    ByteBufferList bb;\n    public ByteBufferListRequestBody(ByteBufferList bb) {\n        this.bb = bb;\n    }\n    @Override\n    public void write(AsyncHttpRequest request, DataSink sink, CompletedCallback completed) {\n        Util.writeAll(sink, bb, completed);\n    }\n\n    @Override\n    public void parse(DataEmitter emitter, CompletedCallback completed) {\n        new ByteBufferListParser().parse(emitter).setCallback((e, result) -> {\n            bb = result;\n            completed.onCompleted(e);\n        });\n    }\n\n    public static String CONTENT_TYPE = \"application/binary\";\n\n    @Override\n    public String getContentType() {\n        return CONTENT_TYPE;\n    }\n\n    @Override\n    public boolean readFullyOnRequest() {\n        return true;\n    }\n\n    @Override\n    public int length() {\n        return bb.remaining();\n    }\n\n    @Override\n    public ByteBufferList get() {\n        return bb;\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/body/DocumentBody.java",
    "content": "package com.koushikdutta.async.http.body;\n\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.DataSink;\nimport com.koushikdutta.async.Util;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.future.FutureCallback;\nimport com.koushikdutta.async.http.AsyncHttpRequest;\nimport com.koushikdutta.async.parser.DocumentParser;\nimport com.koushikdutta.async.util.Charsets;\n\nimport org.w3c.dom.Document;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.OutputStreamWriter;\n\nimport javax.xml.transform.Transformer;\nimport javax.xml.transform.TransformerFactory;\nimport javax.xml.transform.dom.DOMSource;\nimport javax.xml.transform.stream.StreamResult;\n\n/**\n * Created by koush on 8/30/13.\n */\npublic class DocumentBody implements AsyncHttpRequestBody<Document> {\n    public DocumentBody() {\n        this(null);\n    }\n\n    public DocumentBody(Document document) {\n        this.document = document;\n    }\n\n    ByteArrayOutputStream bout;\n    private void prepare() {\n        if (bout != null)\n            return;\n\n        try {\n            DOMSource source = new DOMSource(document);\n            TransformerFactory tf = TransformerFactory.newInstance();\n            Transformer transformer = tf.newTransformer();\n            bout = new ByteArrayOutputStream();\n            OutputStreamWriter writer = new OutputStreamWriter(bout, Charsets.UTF_8);\n            StreamResult result = new StreamResult(writer);\n            transformer.transform(source, result);\n            writer.flush();\n        }\n        catch (Exception e) {\n        }\n    }\n\n    @Override\n    public void write(AsyncHttpRequest request, DataSink sink, CompletedCallback completed) {\n        prepare();\n        byte[] bytes = bout.toByteArray();\n        Util.writeAll(sink, bytes, completed);\n    }\n\n    @Override\n    public void parse(DataEmitter emitter, final CompletedCallback completed) {\n        new DocumentParser().parse(emitter).setCallback(new FutureCallback<Document>() {\n            @Override\n            public void onCompleted(Exception e, Document result) {\n                document = result;\n                completed.onCompleted(e);\n            }\n        });\n    }\n\n    public static final String CONTENT_TYPE = \"application/xml\";\n\n    @Override\n    public String getContentType() {\n        return CONTENT_TYPE;\n    }\n\n    @Override\n    public boolean readFullyOnRequest() {\n        return true;\n    }\n\n    @Override\n    public int length() {\n        prepare();\n        return bout.size();\n    }\n\n    Document document;\n    @Override\n    public Document get() {\n        return document;\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/body/FileBody.java",
    "content": "package com.koushikdutta.async.http.body;\n\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.DataSink;\nimport com.koushikdutta.async.Util;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.http.AsyncHttpRequest;\n\nimport java.io.File;\n\n/**\n * Created by koush on 10/14/13.\n */\npublic class FileBody implements AsyncHttpRequestBody<File> {\n    public static final String CONTENT_TYPE = \"application/binary\";\n\n    File file;\n    String contentType = CONTENT_TYPE;\n\n    public FileBody(File file) {\n        this.file = file;\n    }\n\n    public FileBody(File file, String contentType) {\n        this.file = file;\n        this.contentType = contentType;\n    }\n\n    @Override\n    public void write(AsyncHttpRequest request, DataSink sink, CompletedCallback completed) {\n        Util.pump(file, sink, completed);\n    }\n\n    @Override\n    public void parse(DataEmitter emitter, CompletedCallback completed) {\n        throw new AssertionError(\"not implemented\");\n    }\n\n    @Override\n    public String getContentType() {\n        return contentType;\n    }\n\n    public void setContentType(String contentType) {\n        this.contentType = contentType;\n    }\n\n    @Override\n    public boolean readFullyOnRequest() {\n        throw new AssertionError(\"not implemented\");\n    }\n\n    @Override\n    public int length() {\n        return (int)file.length();\n    }\n\n    @Override\n    public File get() {\n        return file;\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/body/FilePart.java",
    "content": "package com.koushikdutta.async.http.body;\n\nimport com.koushikdutta.async.http.BasicNameValuePair;\nimport com.koushikdutta.async.http.NameValuePair;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.ArrayList;\n\npublic class FilePart extends StreamPart {\n    File file;\n    public FilePart(String name, final File file) {\n        super(name, (int)file.length(), new ArrayList<NameValuePair>() {\n            {\n                add(new BasicNameValuePair(\"filename\", file.getName()));\n            }\n        });\n\n//        getRawHeaders().set(\"Content-Type\", \"application/xml\");\n\n        this.file = file;\n    }\n\n    @Override\n    protected InputStream getInputStream() throws IOException {\n        return new FileInputStream(file);\n    }\n\n    @Override\n    public String toString() {\n        return getName();\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/body/JSONArrayBody.java",
    "content": "package com.koushikdutta.async.http.body;\n\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.DataSink;\nimport com.koushikdutta.async.Util;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.future.FutureCallback;\nimport com.koushikdutta.async.http.AsyncHttpRequest;\nimport com.koushikdutta.async.parser.JSONArrayParser;\n\nimport org.json.JSONArray;\n\npublic class JSONArrayBody implements AsyncHttpRequestBody<JSONArray> {\n    public JSONArrayBody() {\n    }\n\n    byte[] mBodyBytes;\n    JSONArray json;\n    public JSONArrayBody(JSONArray json) {\n        this();\n        this.json = json;\n    }\n\n    @Override\n    public void parse(DataEmitter emitter, final CompletedCallback completed) {\n        new JSONArrayParser().parse(emitter).setCallback(new FutureCallback<JSONArray>() {\n            @Override\n            public void onCompleted(Exception e, JSONArray result) {\n                json = result;\n                completed.onCompleted(e);\n            }\n        });\n    }\n\n    @Override\n    public void write(AsyncHttpRequest request, DataSink sink, final CompletedCallback completed) {\n        Util.writeAll(sink, mBodyBytes, completed);\n    }\n\n    @Override\n    public String getContentType() {\n        return \"application/json\";\n    }\n\n    @Override\n    public boolean readFullyOnRequest() {\n        return true;\n    }\n\n    @Override\n    public int length() {\n        mBodyBytes = json.toString().getBytes();\n        return mBodyBytes.length;\n    }\n\n    public static final String CONTENT_TYPE = \"application/json\";\n\n    @Override\n    public JSONArray get() {\n        return json;\n    }\n}\n\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/body/JSONObjectBody.java",
    "content": "package com.koushikdutta.async.http.body;\n\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.DataSink;\nimport com.koushikdutta.async.Util;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.future.FutureCallback;\nimport com.koushikdutta.async.http.AsyncHttpRequest;\nimport com.koushikdutta.async.parser.JSONObjectParser;\n\nimport org.json.JSONObject;\n\npublic class JSONObjectBody implements AsyncHttpRequestBody<JSONObject> {\n    public JSONObjectBody() {\n    }\n    \n    byte[] mBodyBytes;\n    JSONObject json;\n    public JSONObjectBody(JSONObject json) {\n        this();\n        this.json = json;\n    }\n\n    @Override\n    public void parse(DataEmitter emitter, final CompletedCallback completed) {\n        new JSONObjectParser().parse(emitter).setCallback(new FutureCallback<JSONObject>() {\n            @Override\n            public void onCompleted(Exception e, JSONObject result) {\n                json = result;\n                completed.onCompleted(e);\n            }\n        });\n    }\n\n    @Override\n    public void write(AsyncHttpRequest request, DataSink sink, final CompletedCallback completed) {\n        Util.writeAll(sink, mBodyBytes, completed);\n    }\n\n    @Override\n    public String getContentType() {\n        return CONTENT_TYPE;\n    }\n\n    @Override\n    public boolean readFullyOnRequest() {\n        return true;\n    }\n\n    @Override\n    public int length() {\n        mBodyBytes = json.toString().getBytes();\n        return mBodyBytes.length;\n    }\n\n    public static final String CONTENT_TYPE = \"application/json\";\n\n    @Override\n    public JSONObject get() {\n        return json;\n    }\n}\n\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/body/MultipartFormDataBody.java",
    "content": "package com.koushikdutta.async.http.body;\n\nimport android.text.TextUtils;\n\nimport com.koushikdutta.async.ByteBufferList;\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.DataSink;\nimport com.koushikdutta.async.LineEmitter;\nimport com.koushikdutta.async.LineEmitter.StringCallback;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.callback.ContinuationCallback;\nimport com.koushikdutta.async.callback.DataCallback;\nimport com.koushikdutta.async.future.Continuation;\nimport com.koushikdutta.async.http.AsyncHttpRequest;\nimport com.koushikdutta.async.http.Headers;\nimport com.koushikdutta.async.http.Multimap;\nimport com.koushikdutta.async.http.server.BoundaryEmitter;\n\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.UUID;\n\npublic class MultipartFormDataBody extends BoundaryEmitter implements AsyncHttpRequestBody<Multimap> {\n    LineEmitter liner;\n    Headers formData;\n    ByteBufferList lastData;\n    Part lastPart;\n\n    public interface MultipartCallback {\n        public void onPart(Part part);\n    }\n\n    @Override\n    public void parse(DataEmitter emitter, final CompletedCallback completed) {\n        setDataEmitter(emitter);\n        setEndCallback(completed);\n    }\n\n    void handleLast() {\n        if (lastData == null)\n            return;\n        \n        if (formData == null)\n            formData = new Headers();\n\n        String value = lastData.peekString();\n        String name = TextUtils.isEmpty(lastPart.getName()) ? \"unnamed\" : lastPart.getName();\n        StringPart part = new StringPart(name, value);\n        part.mHeaders = lastPart.mHeaders;\n        addPart(part);\n\n        formData.add(name, value);\n\n        lastPart = null;\n        lastData = null;\n    }\n    \n    public String getField(String name) {\n        if (formData == null)\n            return null;\n        return formData.get(name);\n    }\n    \n    @Override\n    protected void onBoundaryEnd() {\n        super.onBoundaryEnd();\n        handleLast();\n    }\n\n    @Override\n    protected void onBoundaryStart() {\n        final Headers headers = new Headers();\n        liner = new LineEmitter();\n        liner.setLineCallback(new StringCallback() {\n            @Override\n            public void onStringAvailable(String s) {\n                if (!\"\\r\".equals(s)){\n                    headers.addLine(s);\n                }\n                else {\n                    handleLast();\n                    \n                    liner = null;\n                    setDataCallback(null);\n                    Part part = new Part(headers);\n                    if (mCallback != null)\n                        mCallback.onPart(part);\n                    if (getDataCallback() == null) {\n//                        if (part.isFile()) {\n//                            setDataCallback(new NullDataCallback());\n//                            return;\n//                        }\n\n                        lastPart = part;\n                        lastData = new ByteBufferList();\n                        setDataCallback(new DataCallback() {\n                            @Override\n                            public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n                                bb.get(lastData);\n                            }\n                        });\n                    }\n                }\n            }\n        });\n        setDataCallback(liner);\n    }\n\n    public static final String PRIMARY_TYPE = \"multipart/\";\n    public static final String CONTENT_TYPE = PRIMARY_TYPE + \"form-data\";\n    String contentType = CONTENT_TYPE;\n    public MultipartFormDataBody(String contentType) {\n        Multimap map = Multimap.parseSemicolonDelimited(contentType);\n        String boundary = map.getString(\"boundary\");\n        if (boundary == null)\n            report(new Exception (\"No boundary found for multipart/form-data\"));\n        else\n            setBoundary(boundary);\n    }\n\n    MultipartCallback mCallback;\n    public void setMultipartCallback(MultipartCallback callback) {\n        mCallback = callback;\n    }\n    \n    public MultipartCallback getMultipartCallback() {\n        return mCallback;\n    }\n\n    int written;\n    @Override\n    public void write(AsyncHttpRequest request, final DataSink sink, final CompletedCallback completed) {\n        if (mParts == null)\n            return;\n\n        Continuation c = new Continuation(new CompletedCallback() {\n            @Override\n            public void onCompleted(Exception ex) {\n                completed.onCompleted(ex);\n//                if (ex == null)\n//                    sink.end();\n//                else\n//                    sink.close();\n            }\n        });\n\n        for (final Part part: mParts) {\n            c.add(new ContinuationCallback() {\n                @Override\n                public void onContinue(Continuation continuation, CompletedCallback next) throws Exception {\n                    byte[] bytes = part.getRawHeaders().toPrefixString(getBoundaryStart()).getBytes();\n                    com.koushikdutta.async.Util.writeAll(sink, bytes, next);\n                    written += bytes.length;\n                }\n            })\n            .add(new ContinuationCallback() {\n                @Override\n                public void onContinue(Continuation continuation, CompletedCallback next) throws Exception {\n                    long partLength = part.length();\n                    if (partLength >= 0)\n                        written += partLength;\n                    part.write(sink, next);\n                }\n            })\n            .add(new ContinuationCallback() {\n                @Override\n                public void onContinue(Continuation continuation, CompletedCallback next) throws Exception {\n                    byte[] bytes = \"\\r\\n\".getBytes();\n                    com.koushikdutta.async.Util.writeAll(sink, bytes, next);\n                    written += bytes.length;\n                }\n            });\n        }\n        c.add(new ContinuationCallback() {\n            @Override\n            public void onContinue(Continuation continuation, CompletedCallback next) throws Exception {\n                byte[] bytes = (getBoundaryEnd()).getBytes();\n                com.koushikdutta.async.Util.writeAll(sink, bytes, next);\n                written += bytes.length;\n            }\n        });\n        c.start();\n    }\n\n    @Override\n    public String getContentType() {\n        if (getBoundary() == null) {\n            setBoundary(\"----------------------------\" + UUID.randomUUID().toString().replace(\"-\", \"\"));\n        }\n        return contentType + \"; boundary=\" + getBoundary();\n    }\n\n    @Override\n    public boolean readFullyOnRequest() {\n        return false;\n    }\n\n    int totalToWrite;\n    @Override\n    public int length() {\n        if (getBoundary() == null) {\n            setBoundary(\"----------------------------\" + UUID.randomUUID().toString().replace(\"-\", \"\"));\n        }\n\n        int length = 0;\n        for (final Part part: mParts) {\n            String partHeader = part.getRawHeaders().toPrefixString(getBoundaryStart());\n            if (part.length() == -1)\n                return -1;\n            length += part.length() + partHeader.getBytes().length + \"\\r\\n\".length();\n        }\n        length += (getBoundaryEnd()).getBytes().length;\n        return totalToWrite = length;\n    }\n    \n    public MultipartFormDataBody() {\n    }\n\n    public void setContentType(String contentType) {\n        this.contentType = contentType;\n    }\n\n    public List<Part> getParts() {\n        if (mParts == null)\n            return null;\n        return new ArrayList<>(mParts);\n    }\n\n    public void addFilePart(String name, File file) {\n        addPart(new FilePart(name, file));\n    }\n    \n    public void addStringPart(String name, String value) {\n        addPart(new StringPart(name, value));\n    }\n    \n    private ArrayList<Part> mParts;\n    public void addPart(Part part) {\n        if (mParts == null)\n            mParts = new ArrayList<Part>();\n        mParts.add(part);\n    }\n\n    @Override\n    public Multimap get() {\n        return new Multimap(formData.getMultiMap());\n    }\n\n    @Override\n    public String toString() {\n        for (Part part: getParts()) {\n            return part.toString();\n        }\n        return \"multipart content is empty\";\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/body/Part.java",
    "content": "package com.koushikdutta.async.http.body;\n\nimport com.koushikdutta.async.DataSink;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.http.Headers;\nimport com.koushikdutta.async.http.Multimap;\nimport com.koushikdutta.async.http.NameValuePair;\n\nimport java.io.File;\nimport java.util.List;\nimport java.util.Locale;\n\npublic class Part {\n    public static final String CONTENT_DISPOSITION = \"Content-Disposition\";\n    \n    Headers mHeaders;\n    Multimap mContentDisposition;\n    public Part(Headers headers) {\n        mHeaders = headers;\n        mContentDisposition = Multimap.parseSemicolonDelimited(mHeaders.get(CONTENT_DISPOSITION));\n    }\n    \n    public String getName() {\n        return mContentDisposition.getString(\"name\");\n    }\n    \n    private long length = -1;\n    public Part(String name, long length, List<NameValuePair> contentDisposition) {\n        this.length = length;\n        mHeaders = new Headers();\n        StringBuilder builder = new StringBuilder(String.format(Locale.ENGLISH, \"form-data; name=\\\"%s\\\"\", name));\n        if (contentDisposition != null) {\n            for (NameValuePair pair: contentDisposition) {\n                builder.append(String.format(Locale.ENGLISH, \"; %s=\\\"%s\\\"\", pair.getName(), pair.getValue()));\n            }\n        }\n        mHeaders.set(CONTENT_DISPOSITION, builder.toString());\n        mContentDisposition = Multimap.parseSemicolonDelimited(mHeaders.get(CONTENT_DISPOSITION));\n    }\n\n    public Headers getRawHeaders() {\n        return mHeaders;\n    }\n\n    public String getContentType() {\n        return mHeaders.get(\"Content-Type\");\n    }\n\n    public void setContentType(String contentType) {\n        mHeaders.set(\"Content-Type\", contentType);\n    }\n\n    public String getFilename() {\n        String file = mContentDisposition.getString(\"filename\");\n        if (file == null)\n            return null;\n        return new File(file).getName();\n    }\n\n    public boolean isFile() {\n        return mContentDisposition.containsKey(\"filename\");\n    }\n    \n    public long length() {\n        return length;\n    }\n    \n    public void write(DataSink sink, CompletedCallback callback) {\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/body/StreamBody.java",
    "content": "package com.koushikdutta.async.http.body;\n\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.DataSink;\nimport com.koushikdutta.async.Util;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.http.AsyncHttpRequest;\n\nimport java.io.InputStream;\n\npublic class StreamBody implements AsyncHttpRequestBody<InputStream> {\n    InputStream stream;\n    int length;\n    String contentType = CONTENT_TYPE;\n\n    /**\n     * Construct an http body from a stream\n     * @param stream\n     * @param length Length of stream to read, or value < 0 to read to end\n     */\n    public StreamBody(InputStream stream, int length) {\n        this.stream = stream;\n        this.length = length;\n    }\n\n    @Override\n    public void write(AsyncHttpRequest request, DataSink sink, CompletedCallback completed) {\n        Util.pump(stream, length < 0 ? Integer.MAX_VALUE : length, sink, completed);\n    }\n\n    @Override\n    public void parse(DataEmitter emitter, CompletedCallback completed) {\n        throw new AssertionError(\"not implemented\");\n    }\n\n    public static final String CONTENT_TYPE = \"application/binary\";\n    @Override\n    public String getContentType() {\n        return contentType;\n    }\n    public StreamBody setContentType(String contentType) {\n        this.contentType = contentType;\n        return this;\n    }\n\n    @Override\n    public boolean readFullyOnRequest() {\n        throw new AssertionError(\"not implemented\");\n    }\n\n    @Override\n    public int length() {\n        return length;\n    }\n\n    @Override\n    public InputStream get() {\n        return stream;\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/body/StreamPart.java",
    "content": "package com.koushikdutta.async.http.body;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.List;\n\nimport com.koushikdutta.async.DataSink;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.http.NameValuePair;\n\npublic abstract class StreamPart extends Part {\n    public StreamPart(String name, long length, List<NameValuePair> contentDisposition) {\n        super(name, length, contentDisposition);\n    }\n    \n    @Override\n    public void write(DataSink sink, CompletedCallback callback) {\n        try {\n            InputStream is = getInputStream();\n            com.koushikdutta.async.Util.pump(is, sink, callback);\n        }\n        catch (Exception e) {\n            callback.onCompleted(e);\n        }\n    }\n    \n    protected abstract InputStream getInputStream() throws IOException;\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/body/StringBody.java",
    "content": "package com.koushikdutta.async.http.body;\n\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.DataSink;\nimport com.koushikdutta.async.Util;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.future.FutureCallback;\nimport com.koushikdutta.async.http.AsyncHttpRequest;\nimport com.koushikdutta.async.parser.StringParser;\n\npublic class StringBody implements AsyncHttpRequestBody<String> {\n    public StringBody() {\n    }\n\n    byte[] mBodyBytes;\n    String string;\n    public StringBody(String string) {\n        this();\n        this.string = string;\n    }\n\n    @Override\n    public void parse(DataEmitter emitter, final CompletedCallback completed) {\n        new StringParser().parse(emitter).setCallback(new FutureCallback<String>() {\n            @Override\n            public void onCompleted(Exception e, String result) {\n                string = result;\n                completed.onCompleted(e);\n            }\n        });\n    }\n\n    public static final String CONTENT_TYPE = \"text/plain\";\n\n    @Override\n    public void write(AsyncHttpRequest request, DataSink sink, final CompletedCallback completed) {\n        if (mBodyBytes == null)\n            mBodyBytes = string.getBytes();\n        Util.writeAll(sink, mBodyBytes, completed);\n    }\n\n    @Override\n    public String getContentType() {\n        return \"text/plain\";\n    }\n\n    @Override\n    public boolean readFullyOnRequest() {\n        return true;\n    }\n\n    @Override\n    public int length() {\n        if (mBodyBytes == null)\n            mBodyBytes = string.getBytes();\n        return mBodyBytes.length;\n    }\n\n    @Override\n    public String toString() {\n        return string;\n    }\n\n    @Override\n    public String get() {\n        return toString();\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/body/StringPart.java",
    "content": "package com.koushikdutta.async.http.body;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\n\npublic class StringPart extends StreamPart {\n    String value;\n    public StringPart(String name, String value) {\n        super(name, value.getBytes().length, null);\n        this.value = value;\n    }\n\n    @Override\n    protected InputStream getInputStream() throws IOException {\n        return new ByteArrayInputStream(value.getBytes());\n    }\n\n    public String getValue() {\n        return value;\n    }\n\n    @Override\n    public String toString() {\n        return value;\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/body/UrlEncodedFormBody.java",
    "content": "package com.koushikdutta.async.http.body;\n\nimport com.koushikdutta.async.ByteBufferList;\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.DataSink;\nimport com.koushikdutta.async.Util;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.callback.DataCallback;\nimport com.koushikdutta.async.http.AsyncHttpRequest;\nimport com.koushikdutta.async.http.Multimap;\nimport com.koushikdutta.async.http.NameValuePair;\nimport com.koushikdutta.async.util.Charsets;\n\nimport java.io.UnsupportedEncodingException;\nimport java.net.URLEncoder;\nimport java.util.List;\n\npublic class UrlEncodedFormBody implements AsyncHttpRequestBody<Multimap> {\n    private Multimap mParameters;\n    private byte[] mBodyBytes;\n\n    public UrlEncodedFormBody(Multimap parameters) {\n        mParameters = parameters;\n    }\n\n    public UrlEncodedFormBody(List<NameValuePair> parameters) {\n        mParameters = new Multimap(parameters);\n    }\n\n    private void buildData() {\n        boolean first = true;\n        StringBuilder b = new StringBuilder();\n        try {\n            for (NameValuePair pair: mParameters) {\n                if (pair.getValue() == null)\n                    continue;\n                if (!first)\n                    b.append('&');\n                first = false;\n\n                b.append(URLEncoder.encode(pair.getName(), \"UTF-8\"));\n                b.append('=');\n                b.append(URLEncoder.encode(pair.getValue(), \"UTF-8\"));\n            }\n            mBodyBytes = b.toString().getBytes(\"UTF-8\");\n        }\n        catch (UnsupportedEncodingException e) {\n            throw new AssertionError(e);\n        }\n    }\n    \n    @Override\n    public void write(AsyncHttpRequest request, final DataSink response, final CompletedCallback completed) {\n        if (mBodyBytes == null)\n            buildData();\n        Util.writeAll(response, mBodyBytes, completed);\n    }\n\n    public static final String CONTENT_TYPE = \"application/x-www-form-urlencoded\";\n    @Override\n    public String getContentType() {\n        return CONTENT_TYPE + \"; charset=utf-8\";\n    }\n\n    @Override\n    public void parse(DataEmitter emitter, final CompletedCallback completed) {\n        final ByteBufferList data = new ByteBufferList();\n        emitter.setDataCallback(new DataCallback() {\n            @Override\n            public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n                bb.get(data);\n            }\n        });\n        emitter.setEndCallback(new CompletedCallback() {\n            @Override\n            public void onCompleted(Exception ex) {\n                try {\n                    if (ex != null)\n                        throw ex;\n                    mParameters = Multimap.parseUrlEncoded(data.readString());\n                }\n                catch (Exception e) {\n                    completed.onCompleted(e);\n                    return;\n                }\n                completed.onCompleted(null);\n            }\n        });\n    }\n\n    public UrlEncodedFormBody() {\n    }\n\n    @Override\n    public boolean readFullyOnRequest() {\n        return true;\n    }\n\n    @Override\n    public int length() {\n        if (mBodyBytes == null)\n            buildData();\n        return mBodyBytes.length;\n    }\n\n    @Override\n    public Multimap get() {\n        return mParameters;\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/cache/HeaderParser.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.koushikdutta.async.http.cache;\n\nfinal class HeaderParser {\n\n    public interface CacheControlHandler {\n        void handle(String directive, String parameter);\n    }\n\n    /**\n     * Parse a comma-separated list of cache control header values.\n     */\n    public static void parseCacheControl(String value, CacheControlHandler handler) {\n        if (value == null)\n            return;\n        int pos = 0;\n        while (pos < value.length()) {\n            int tokenStart = pos;\n            pos = skipUntil(value, pos, \"=,\");\n            String directive = value.substring(tokenStart, pos).trim();\n\n            if (pos == value.length() || value.charAt(pos) == ',') {\n                pos++; // consume ',' (if necessary)\n                handler.handle(directive, null);\n                continue;\n            }\n\n            pos++; // consume '='\n            pos = skipWhitespace(value, pos);\n\n            String parameter;\n\n            // quoted string\n            if (pos < value.length() && value.charAt(pos) == '\\\"') {\n                pos++; // consume '\"' open quote\n                int parameterStart = pos;\n                pos = skipUntil(value, pos, \"\\\"\");\n                parameter = value.substring(parameterStart, pos);\n                pos++; // consume '\"' close quote (if necessary)\n\n            // unquoted string\n            } else {\n                int parameterStart = pos;\n                pos = skipUntil(value, pos, \",\");\n                parameter = value.substring(parameterStart, pos).trim();\n            }\n\n            handler.handle(directive, parameter);\n        }\n    }\n\n    /**\n     * Returns the next index in {@code input} at or after {@code pos} that\n     * contains a character from {@code characters}. Returns the input length if\n     * none of the requested characters can be found.\n     */\n    private static int skipUntil(String input, int pos, String characters) {\n        for (; pos < input.length(); pos++) {\n            if (characters.indexOf(input.charAt(pos)) != -1) {\n                break;\n            }\n        }\n        return pos;\n    }\n\n    /**\n     * Returns the next non-whitespace character in {@code input} that is white\n     * space. Result is undefined if input contains newline characters.\n     */\n    private static int skipWhitespace(String input, int pos) {\n        for (; pos < input.length(); pos++) {\n            char c = input.charAt(pos);\n            if (c != ' ' && c != '\\t') {\n                break;\n            }\n        }\n        return pos;\n    }\n\n    /**\n     * Returns {@code value} as a positive integer, or 0 if it is negative, or\n     * -1 if it cannot be parsed.\n     */\n    public static int parseSeconds(String value) {\n        try {\n            long seconds = Long.parseLong(value);\n            if (seconds > Integer.MAX_VALUE) {\n                return Integer.MAX_VALUE;\n            } else if (seconds < 0) {\n                return 0;\n            } else {\n                return (int) seconds;\n            }\n        } catch (NumberFormatException e) {\n            return -1;\n        }\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/cache/Objects.java",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.koushikdutta.async.http.cache;\n\nfinal class Objects {\n    private Objects() {}\n\n    /**\n     * Returns true if two possibly-null objects are equal.\n     */\n    public static boolean equal(Object a, Object b) {\n        return a == b || (a != null && a.equals(b));\n    }\n\n    public static int hashCode(Object o) {\n        return (o == null) ? 0 : o.hashCode();\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/cache/RawHeaders.java",
    "content": "package com.koushikdutta.async.http.cache;\n\n/*\n *  Licensed to the Apache Software Foundation (ASF) under one or more\n *  contributor license agreements.  See the NOTICE file distributed with\n *  this work for additional information regarding copyright ownership.\n *  The ASF licenses this file to You under the Apache License, Version 2.0\n *  (the \"License\"); you may not use this file except in compliance with\n *  the License.  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\nimport android.text.TextUtils;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Set;\nimport java.util.TreeMap;\n\n/**\n * The HTTP status and unparsed header fields of a single HTTP message. Values\n * are represented as uninterpreted strings; use {@link RequestHeaders} and\n * {@link ResponseHeaders} for interpreted headers. This class maintains the\n * order of the header fields within the HTTP message.\n *\n * <p>This class tracks fields line-by-line. A field with multiple comma-\n * separated values on the same line will be treated as a field with a single\n * value by this class. It is the caller's responsibility to detect and split\n * on commas if their field permits multiple values. This simplifies use of\n * single-valued fields whose values routinely contain commas, such as cookies\n * or dates.\n *\n * <p>This class trims whitespace from values. It never returns values with\n * leading or trailing whitespace.\n */\nfinal class RawHeaders {\n    private static final Comparator<String> FIELD_NAME_COMPARATOR = new Comparator<String>() {\n        @Override public int compare(String a, String b) {\n            if (a == b) {\n                return 0;\n            } else if (a == null) {\n                return -1;\n            } else if (b == null) {\n                return 1;\n            } else {\n                return String.CASE_INSENSITIVE_ORDER.compare(a, b);\n            }\n        }\n    };\n\n    private final List<String> namesAndValues = new ArrayList<String>(20);\n    private String statusLine;\n    private int httpMinorVersion = 1;\n    private int responseCode = -1;\n    private String responseMessage;\n\n    public RawHeaders() {}\n\n    public RawHeaders(RawHeaders copyFrom) {\n        copy(copyFrom);\n    }\n\n    public void copy(RawHeaders copyFrom) {\n        namesAndValues.addAll(copyFrom.namesAndValues);\n        statusLine = copyFrom.statusLine;\n        httpMinorVersion = copyFrom.httpMinorVersion;\n        responseCode = copyFrom.responseCode;\n        responseMessage = copyFrom.responseMessage;\n    }\n\n    /**\n     * Sets the response status line (like \"HTTP/1.0 200 OK\") or request line\n     * (like \"GET / HTTP/1.1\").\n     */\n    public void setStatusLine(String statusLine) {\n        statusLine = statusLine.trim();\n        this.statusLine = statusLine;\n\n        if (statusLine == null || !statusLine.startsWith(\"HTTP/\")) {\n            return;\n        }\n        statusLine = statusLine.trim();\n        int mark = statusLine.indexOf(\" \") + 1;\n        if (mark == 0) {\n            return;\n        }\n        if (statusLine.charAt(mark - 2) != '1') {\n            this.httpMinorVersion = 0;\n        }\n        int last = mark + 3;\n        if (last > statusLine.length()) {\n            last = statusLine.length();\n        }\n        this.responseCode = Integer.parseInt(statusLine.substring(mark, last));\n        if (last + 1 <= statusLine.length()) {\n            this.responseMessage = statusLine.substring(last + 1);\n        }\n    }\n\n    public String getStatusLine() {\n        return statusLine;\n    }\n\n    /**\n     * Returns the status line's HTTP minor version. This returns 0 for HTTP/1.0\n     * and 1 for HTTP/1.1. This returns 1 if the HTTP version is unknown.\n     */\n    public int getHttpMinorVersion() {\n        return httpMinorVersion != -1 ? httpMinorVersion : 1;\n    }\n\n    /**\n     * Returns the HTTP status code or -1 if it is unknown.\n     */\n    public int getResponseCode() {\n        return responseCode;\n    }\n\n    /**\n     * Returns the HTTP status message or null if it is unknown.\n     */\n    public String getResponseMessage() {\n        return responseMessage;\n    }\n\n    /**\n     * Add an HTTP header line containing a field name, a literal colon, and a\n     * value.\n     */\n    public void addLine(String line) {\n        int index = line.indexOf(\":\");\n        if (index == -1) {\n            add(\"\", line);\n        } else {\n            add(line.substring(0, index), line.substring(index + 1));\n        }\n    }\n\n    /**\n     * Add a field with the specified value.\n     */\n    public void add(String fieldName, String value) {\n        if (fieldName == null) {\n            throw new IllegalArgumentException(\"fieldName == null\");\n        }\n        if (value == null) {\n            /*\n             * Given null values, the RI sends a malformed field line like\n             * \"Accept\\r\\n\". For platform compatibility and HTTP compliance, we\n             * print a warning and ignore null values.\n             */\n            System.err.println(\"Ignoring HTTP header field '\" + fieldName + \"' because its value is null\");\n            return;\n        }\n        namesAndValues.add(fieldName);\n        namesAndValues.add(value.trim());\n    }\n\n    public void removeAll(String fieldName) {\n        for (int i = 0; i < namesAndValues.size(); i += 2) {\n            if (fieldName.equalsIgnoreCase(namesAndValues.get(i))) {\n                namesAndValues.remove(i); // field name\n                namesAndValues.remove(i); // value\n            }\n        }\n    }\n\n    public void addAll(String fieldName, List<String> headerFields) {\n        for (String value : headerFields) {\n            add(fieldName, value);\n        }\n    }\n\n    /**\n     * Set a field with the specified value. If the field is not found, it is\n     * added. If the field is found, the existing values are replaced.\n     */\n    public void set(String fieldName, String value) {\n        removeAll(fieldName);\n        add(fieldName, value);\n    }\n\n    /**\n     * Returns the number of field values.\n     */\n    public int length() {\n        return namesAndValues.size() / 2;\n    }\n\n    /**\n     * Returns the field at {@code position} or null if that is out of range.\n     */\n    public String getFieldName(int index) {\n        int fieldNameIndex = index * 2;\n        if (fieldNameIndex < 0 || fieldNameIndex >= namesAndValues.size()) {\n            return null;\n        }\n        return namesAndValues.get(fieldNameIndex);\n    }\n\n    /**\n     * Returns the value at {@code index} or null if that is out of range.\n     */\n    public String getValue(int index) {\n        int valueIndex = index * 2 + 1;\n        if (valueIndex < 0 || valueIndex >= namesAndValues.size()) {\n            return null;\n        }\n        return namesAndValues.get(valueIndex);\n    }\n\n    /**\n     * Returns the last value corresponding to the specified field, or null.\n     */\n    public String get(String fieldName) {\n        for (int i = namesAndValues.size() - 2; i >= 0; i -= 2) {\n            if (fieldName.equalsIgnoreCase(namesAndValues.get(i))) {\n                return namesAndValues.get(i + 1);\n            }\n        }\n        return null;\n    }\n\n    /**\n     * @param fieldNames a case-insensitive set of HTTP header field names.\n     */\n    public RawHeaders getAll(Set<String> fieldNames) {\n        RawHeaders result = new RawHeaders();\n        for (int i = 0; i < namesAndValues.size(); i += 2) {\n            String fieldName = namesAndValues.get(i);\n            if (fieldNames.contains(fieldName)) {\n                result.add(fieldName, namesAndValues.get(i + 1));\n            }\n        }\n        return result;\n    }\n\n    public String toHeaderString() {\n        StringBuilder result = new StringBuilder(256);\n        result.append(statusLine).append(\"\\r\\n\");\n        for (int i = 0; i < namesAndValues.size(); i += 2) {\n            result.append(namesAndValues.get(i)).append(\": \")\n                    .append(namesAndValues.get(i + 1)).append(\"\\r\\n\");\n        }\n        result.append(\"\\r\\n\");\n        return result.toString();\n    }\n\n    /**\n     * Returns an immutable map containing each field to its list of values. The\n     * status line is mapped to null.\n     */\n    public Map<String, List<String>> toMultimap() {\n        Map<String, List<String>> result = new TreeMap<String, List<String>>(FIELD_NAME_COMPARATOR);\n        for (int i = 0; i < namesAndValues.size(); i += 2) {\n            String fieldName = namesAndValues.get(i);\n            String value = namesAndValues.get(i + 1);\n\n            List<String> allValues = new ArrayList<String>();\n            List<String> otherValues = result.get(fieldName);\n            if (otherValues != null) {\n                allValues.addAll(otherValues);\n            }\n            allValues.add(value);\n            result.put(fieldName, Collections.unmodifiableList(allValues));\n        }\n        if (statusLine != null) {\n            result.put(null, Collections.unmodifiableList(Collections.singletonList(statusLine)));\n        }\n        return Collections.unmodifiableMap(result);\n    }\n\n    /**\n     * Creates a new instance from the given map of fields to values. If\n     * present, the null field's last element will be used to set the status\n     * line.\n     */\n    public static RawHeaders fromMultimap(Map<String, List<String>> map) {\n        RawHeaders result = new RawHeaders();\n        for (Entry<String, List<String>> entry : map.entrySet()) {\n            String fieldName = entry.getKey();\n            List<String> values = entry.getValue();\n            if (fieldName != null) {\n                result.addAll(fieldName, values);\n            } else if (!values.isEmpty()) {\n                result.setStatusLine(values.get(values.size() - 1));\n            }\n        }\n        return result;\n    }\n\n    public static RawHeaders parse(String payload) {\n        String[] lines = payload.split(\"\\n\");\n\n        RawHeaders headers = new RawHeaders();\n        for (String line: lines) {\n            line = line.trim();\n            if (TextUtils.isEmpty(line))\n                continue;\n\n            if (headers.getStatusLine() == null)\n                headers.setStatusLine(line);\n            else\n                headers.addLine(line);\n        }\n        return headers;\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/cache/RequestHeaders.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.koushikdutta.async.http.cache;\n\nimport android.net.Uri;\n\nimport com.koushikdutta.async.http.HttpDate;\n\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Parsed HTTP request headers.\n */\nfinal class RequestHeaders {\n    private final Uri uri;\n    private final RawHeaders headers;\n\n    /** Don't use a cache to satisfy this request. */\n    private boolean noCache;\n    private int maxAgeSeconds = -1;\n    private int maxStaleSeconds = -1;\n    private int minFreshSeconds = -1;\n\n    /**\n     * This field's name \"only-if-cached\" is misleading. It actually means \"do\n     * not use the network\". It is set by a client who only wants to make a\n     * request if it can be fully satisfied by the cache. Cached responses that\n     * would require validation (ie. conditional gets) are not permitted if this\n     * header is set.\n     */\n    private boolean onlyIfCached;\n\n    /**\n     * True if the request contains an authorization field. Although this isn't\n     * necessarily a shared cache, it follows the spec's strict requirements for\n     * shared caches.\n     */\n    private boolean hasAuthorization;\n\n    private int contentLength = -1;\n    private String transferEncoding;\n    private String userAgent;\n    private String host;\n    private String connection;\n    private String acceptEncoding;\n    private String contentType;\n    private String ifModifiedSince;\n    private String ifNoneMatch;\n    private String proxyAuthorization;\n\n    public RequestHeaders(Uri uri, RawHeaders headers) {\n        this.uri = uri;\n        this.headers = headers;\n\n        HeaderParser.CacheControlHandler handler = new HeaderParser.CacheControlHandler() {\n            @Override public void handle(String directive, String parameter) {\n                if (directive.equalsIgnoreCase(\"no-cache\")) {\n                    noCache = true;\n                } else if (directive.equalsIgnoreCase(\"max-age\")) {\n                    maxAgeSeconds = HeaderParser.parseSeconds(parameter);\n                } else if (directive.equalsIgnoreCase(\"max-stale\")) {\n                    maxStaleSeconds = HeaderParser.parseSeconds(parameter);\n                } else if (directive.equalsIgnoreCase(\"min-fresh\")) {\n                    minFreshSeconds = HeaderParser.parseSeconds(parameter);\n                } else if (directive.equalsIgnoreCase(\"only-if-cached\")) {\n                    onlyIfCached = true;\n                }\n            }\n        };\n\n        for (int i = 0; i < headers.length(); i++) {\n            String fieldName = headers.getFieldName(i);\n            String value = headers.getValue(i);\n            if (\"Cache-Control\".equalsIgnoreCase(fieldName)) {\n                HeaderParser.parseCacheControl(value, handler);\n            } else if (\"Pragma\".equalsIgnoreCase(fieldName)) {\n                if (value.equalsIgnoreCase(\"no-cache\")) {\n                    noCache = true;\n                }\n            } else if (\"If-None-Match\".equalsIgnoreCase(fieldName)) {\n                ifNoneMatch = value;\n            } else if (\"If-Modified-Since\".equalsIgnoreCase(fieldName)) {\n                ifModifiedSince = value;\n            } else if (\"Authorization\".equalsIgnoreCase(fieldName)) {\n                hasAuthorization = true;\n            } else if (\"Content-Length\".equalsIgnoreCase(fieldName)) {\n                try {\n                    contentLength = Integer.parseInt(value);\n                } catch (NumberFormatException ignored) {\n                }\n            } else if (\"Transfer-Encoding\".equalsIgnoreCase(fieldName)) {\n                transferEncoding = value;\n            } else if (\"User-Agent\".equalsIgnoreCase(fieldName)) {\n                userAgent = value;\n            } else if (\"Host\".equalsIgnoreCase(fieldName)) {\n                host = value;\n            } else if (\"Connection\".equalsIgnoreCase(fieldName)) {\n                connection = value;\n            } else if (\"Accept-Encoding\".equalsIgnoreCase(fieldName)) {\n                acceptEncoding = value;\n            } else if (\"Content-Type\".equalsIgnoreCase(fieldName)) {\n                contentType = value;\n            } else if (\"Proxy-Authorization\".equalsIgnoreCase(fieldName)) {\n                proxyAuthorization = value;\n            }\n        }\n    }\n\n    public boolean isChunked() {\n        return \"chunked\".equalsIgnoreCase(transferEncoding);\n    }\n\n    public boolean hasConnectionClose() {\n        return \"close\".equalsIgnoreCase(connection);\n    }\n\n    public Uri getUri() {\n        return uri;\n    }\n\n    public RawHeaders getHeaders() {\n        return headers;\n    }\n\n    public boolean isNoCache() {\n        return noCache;\n    }\n\n    public int getMaxAgeSeconds() {\n        return maxAgeSeconds;\n    }\n\n    public int getMaxStaleSeconds() {\n        return maxStaleSeconds;\n    }\n\n    public int getMinFreshSeconds() {\n        return minFreshSeconds;\n    }\n\n    public boolean isOnlyIfCached() {\n        return onlyIfCached;\n    }\n\n    public boolean hasAuthorization() {\n        return hasAuthorization;\n    }\n\n    public int getContentLength() {\n        return contentLength;\n    }\n\n    public String getTransferEncoding() {\n        return transferEncoding;\n    }\n\n    public String getUserAgent() {\n        return userAgent;\n    }\n\n    public String getHost() {\n        return host;\n    }\n\n    public String getConnection() {\n        return connection;\n    }\n\n    public String getAcceptEncoding() {\n        return acceptEncoding;\n    }\n\n    public String getContentType() {\n        return contentType;\n    }\n\n    public String getIfModifiedSince() {\n        return ifModifiedSince;\n    }\n\n    public String getIfNoneMatch() {\n        return ifNoneMatch;\n    }\n\n    public String getProxyAuthorization() {\n        return proxyAuthorization;\n    }\n\n    public void setChunked() {\n        if (this.transferEncoding != null) {\n            headers.removeAll(\"Transfer-Encoding\");\n        }\n        headers.add(\"Transfer-Encoding\", \"chunked\");\n        this.transferEncoding = \"chunked\";\n    }\n\n    public void setContentLength(int contentLength) {\n        if (this.contentLength != -1) {\n            headers.removeAll(\"Content-Length\");\n        }\n        if (contentLength != -1) {\n            headers.add(\"Content-Length\", Integer.toString(contentLength));\n        }\n        this.contentLength = contentLength;\n    }\n\n    public void setUserAgent(String userAgent) {\n        if (this.userAgent != null) {\n            headers.removeAll(\"User-Agent\");\n        }\n        headers.add(\"User-Agent\", userAgent);\n        this.userAgent = userAgent;\n    }\n\n    public void setHost(String host) {\n        if (this.host != null) {\n            headers.removeAll(\"Host\");\n        }\n        headers.add(\"Host\", host);\n        this.host = host;\n    }\n\n    public void setConnection(String connection) {\n        if (this.connection != null) {\n            headers.removeAll(\"Connection\");\n        }\n        headers.add(\"Connection\", connection);\n        this.connection = connection;\n    }\n\n    public void setAcceptEncoding(String acceptEncoding) {\n        if (this.acceptEncoding != null) {\n            headers.removeAll(\"Accept-Encoding\");\n        }\n        headers.add(\"Accept-Encoding\", acceptEncoding);\n        this.acceptEncoding = acceptEncoding;\n    }\n\n    public void setContentType(String contentType) {\n        if (this.contentType != null) {\n            headers.removeAll(\"Content-Type\");\n        }\n        headers.add(\"Content-Type\", contentType);\n        this.contentType = contentType;\n    }\n\n    public void setIfModifiedSince(Date date) {\n        if (ifModifiedSince != null) {\n            headers.removeAll(\"If-Modified-Since\");\n        }\n        String formattedDate = HttpDate.format(date);\n        headers.add(\"If-Modified-Since\", formattedDate);\n        ifModifiedSince = formattedDate;\n    }\n\n    public void setIfNoneMatch(String ifNoneMatch) {\n        if (this.ifNoneMatch != null) {\n            headers.removeAll(\"If-None-Match\");\n        }\n        headers.add(\"If-None-Match\", ifNoneMatch);\n        this.ifNoneMatch = ifNoneMatch;\n    }\n\n    /**\n     * Returns true if the request contains conditions that save the server from\n     * sending a response that the client has locally. When the caller adds\n     * conditions, this cache won't participate in the request.\n     */\n    public boolean hasConditions() {\n        return ifModifiedSince != null || ifNoneMatch != null;\n    }\n\n    public void addCookies(Map<String, List<String>> allCookieHeaders) {\n        for (Map.Entry<String, List<String>> entry : allCookieHeaders.entrySet()) {\n            String key = entry.getKey();\n            if (\"Cookie\".equalsIgnoreCase(key) || \"Cookie2\".equalsIgnoreCase(key)) {\n                headers.addAll(key, entry.getValue());\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/cache/ResponseCacheMiddleware.java",
    "content": "package com.koushikdutta.async.http.cache;\n\nimport android.net.Uri;\nimport android.util.Base64;\n\nimport com.koushikdutta.async.AsyncSSLSocket;\nimport com.koushikdutta.async.AsyncServer;\nimport com.koushikdutta.async.AsyncSocket;\nimport com.koushikdutta.async.ByteBufferList;\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.FilteredDataEmitter;\nimport com.koushikdutta.async.Util;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.callback.WritableCallback;\nimport com.koushikdutta.async.future.Cancellable;\nimport com.koushikdutta.async.future.SimpleCancellable;\nimport com.koushikdutta.async.http.AsyncHttpClient;\nimport com.koushikdutta.async.http.AsyncHttpClientMiddleware;\nimport com.koushikdutta.async.http.AsyncHttpGet;\nimport com.koushikdutta.async.http.AsyncHttpRequest;\nimport com.koushikdutta.async.http.Headers;\nimport com.koushikdutta.async.http.SimpleMiddleware;\nimport com.koushikdutta.async.util.Allocator;\nimport com.koushikdutta.async.util.Charsets;\nimport com.koushikdutta.async.util.FileCache;\nimport com.koushikdutta.async.util.StreamUtility;\n\nimport java.io.BufferedWriter;\nimport java.io.ByteArrayInputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.io.OutputStreamWriter;\nimport java.io.Writer;\nimport java.net.CacheResponse;\nimport java.nio.ByteBuffer;\nimport java.security.cert.Certificate;\nimport java.security.cert.CertificateEncodingException;\nimport java.security.cert.CertificateException;\nimport java.security.cert.CertificateFactory;\nimport java.security.cert.X509Certificate;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\n\nimport javax.net.ssl.SSLEngine;\n\npublic class ResponseCacheMiddleware extends SimpleMiddleware {\n    public static final int ENTRY_METADATA = 0;\n    public static final int ENTRY_BODY = 1;\n    public static final int ENTRY_COUNT = 2;\n    public static final String SERVED_FROM = \"X-Served-From\";\n    public static final String CONDITIONAL_CACHE = \"conditional-cache\";\n    public static final String CACHE = \"cache\";\n    private static final String LOGTAG = \"AsyncHttpCache\";\n    private boolean caching = true;\n    private int writeSuccessCount;\n    private int writeAbortCount;\n    private FileCache cache;\n    private AsyncServer server;\n    private int conditionalCacheHitCount;\n    private int cacheHitCount;\n    private int networkCount;\n    private int cacheStoreCount;\n\n    private ResponseCacheMiddleware() {\n    }\n\n    public static ResponseCacheMiddleware addCache(AsyncHttpClient client, File cacheDir, long size) throws IOException {\n        for (AsyncHttpClientMiddleware middleware: client.getMiddleware()) {\n            if (middleware instanceof ResponseCacheMiddleware)\n                throw new IOException(\"Response cache already added to http client\");\n        }\n        ResponseCacheMiddleware ret = new ResponseCacheMiddleware();\n        ret.server = client.getServer();\n        ret.cache = new FileCache(cacheDir, size, false);\n        client.insertMiddleware(ret);\n        return ret;\n    }\n\n    public FileCache getFileCache() {\n        return cache;\n    }\n    \n    public boolean getCaching() {\n        return caching;\n    }\n    \n    public void setCaching(boolean caching) {\n        this.caching = caching;\n    }\n\n    public void removeFromCache(Uri uri) {\n        String key = FileCache.toKeyString(uri);\n        getFileCache().remove(key);\n    }\n\n    // step 1) see if we can serve request from the cache directly.\n    // also see if this can be turned into a conditional cache request.\n    @Override\n    public Cancellable getSocket(final GetSocketData data) {\n        RequestHeaders requestHeaders = new RequestHeaders(data.request.getUri(), RawHeaders.fromMultimap(data.request.getHeaders().getMultiMap()));\n        data.state.put(\"request-headers\", requestHeaders);\n\n        if (cache == null || !caching || requestHeaders.isNoCache()) {\n            networkCount++;\n            return null;\n        }\n\n        String key = FileCache.toKeyString(data.request.getUri());\n        FileInputStream[] snapshot = null;\n        long contentLength;\n        Entry entry;\n        try {\n            snapshot = cache.get(key, ENTRY_COUNT);\n            if (snapshot == null) {\n                networkCount++;\n                return null;\n            }\n            contentLength = snapshot[ENTRY_BODY].available();\n            entry = new Entry(snapshot[ENTRY_METADATA]);\n        }\n        catch (IOException e) {\n            // Give up because the cache cannot be read.\n            networkCount++;\n            StreamUtility.closeQuietly(snapshot);\n            return null;\n        }\n\n        // verify the entry matches\n        if (!entry.matches(data.request.getUri(), data.request.getMethod(), data.request.getHeaders().getMultiMap())) {\n            networkCount++;\n            StreamUtility.closeQuietly(snapshot);\n            return null;\n        }\n\n        EntryCacheResponse candidate = new EntryCacheResponse(entry, snapshot[ENTRY_BODY]);\n\n        Map<String, List<String>> responseHeadersMap;\n        FileInputStream cachedResponseBody;\n        try {\n            responseHeadersMap = candidate.getHeaders();\n            cachedResponseBody = candidate.getBody();\n        }\n        catch (Exception e) {\n            networkCount++;\n            StreamUtility.closeQuietly(snapshot);\n            return null;\n        }\n        if (responseHeadersMap == null || cachedResponseBody == null) {\n            networkCount++;\n            StreamUtility.closeQuietly(snapshot);\n            return null;\n        }\n\n        RawHeaders rawResponseHeaders = RawHeaders.fromMultimap(responseHeadersMap);\n        ResponseHeaders cachedResponseHeaders = new ResponseHeaders(data.request.getUri(), rawResponseHeaders);\n        rawResponseHeaders.set(\"Content-Length\", String.valueOf(contentLength));\n        rawResponseHeaders.removeAll(\"Content-Encoding\");\n        rawResponseHeaders.removeAll(\"Transfer-Encoding\");\n        cachedResponseHeaders.setLocalTimestamps(System.currentTimeMillis(), System.currentTimeMillis());\n\n        long now = System.currentTimeMillis();\n        ResponseSource responseSource = cachedResponseHeaders.chooseResponseSource(now, requestHeaders);\n\n        if (responseSource == ResponseSource.CACHE) {\n            data.request.logi(\"Response retrieved from cache\");\n            final CachedSocket socket = entry.isHttps() ? new CachedSSLSocket(candidate, contentLength) : new CachedSocket(candidate, contentLength);\n            socket.pending.add(ByteBuffer.wrap(rawResponseHeaders.toHeaderString().getBytes()));\n\n            server.post(new Runnable() {\n                @Override\n                public void run() {\n                    data.connectCallback.onConnectCompleted(null, socket);\n                    socket.sendCachedDataOnNetworkThread();\n                }\n            });\n            cacheHitCount++;\n            data.state.put(\"socket-owner\", this);\n            SimpleCancellable ret = new SimpleCancellable();\n            ret.setComplete();\n            return ret;\n        }\n        else if (responseSource == ResponseSource.CONDITIONAL_CACHE) {\n            data.request.logi(\"Response may be served from conditional cache\");\n            CacheData cacheData = new CacheData();\n            cacheData.snapshot = snapshot;\n            cacheData.contentLength = contentLength;\n            cacheData.cachedResponseHeaders = cachedResponseHeaders;\n            cacheData.candidate = candidate;\n            data.state.put(\"cache-data\", cacheData);\n            return null;\n        }\n        else {\n            data.request.logd(\"Response can not be served from cache\");\n            // NETWORK or other\n            networkCount++;\n            StreamUtility.closeQuietly(snapshot);\n            return null;\n        }\n    }\n\n    public int getConditionalCacheHitCount() {\n        return conditionalCacheHitCount;\n    }\n\n    public int getCacheHitCount() {\n        return cacheHitCount;\n    }\n    \n    public int getNetworkCount() {\n        return networkCount;\n    }\n\n    public int getCacheStoreCount() {\n        return cacheStoreCount;\n    }\n\n    // step 2) if this is a conditional cache request, serve it from the cache if necessary\n    // otherwise, see if it is cacheable\n    @Override\n    public void onBodyDecoder(OnBodyDecoderData data) {\n        CachedSocket cached = com.koushikdutta.async.Util.getWrappedSocket(data.socket, CachedSocket.class);\n        if (cached != null) {\n            data.response.headers().set(SERVED_FROM, CACHE);\n            return;\n        }\n\n        CacheData cacheData = data.state.get(\"cache-data\");\n        RawHeaders rh = RawHeaders.fromMultimap(data.response.headers().getMultiMap());\n        rh.removeAll(\"Content-Length\");\n        rh.setStatusLine(String.format(Locale.ENGLISH, \"%s %s %s\", data.response.protocol(), data.response.code(), data.response.message()));\n        ResponseHeaders networkResponse = new ResponseHeaders(data.request.getUri(), rh);\n        data.state.put(\"response-headers\", networkResponse);\n        if (cacheData != null) {\n            if (cacheData.cachedResponseHeaders.validate(networkResponse)) {\n                data.request.logi(\"Serving response from conditional cache\");\n                ResponseHeaders combined = cacheData.cachedResponseHeaders.combine(networkResponse);\n                data.response.headers(new Headers(combined.getHeaders().toMultimap()));\n                data.response.code(combined.getHeaders().getResponseCode());\n                data.response.message(combined.getHeaders().getResponseMessage());\n\n                data.response.headers().set(SERVED_FROM, CONDITIONAL_CACHE);\n                conditionalCacheHitCount++;\n\n                CachedBodyEmitter bodySpewer = new CachedBodyEmitter(cacheData.candidate, cacheData.contentLength);\n                bodySpewer.setDataEmitter(data.bodyEmitter);\n                data.bodyEmitter = bodySpewer;\n                bodySpewer.sendCachedData();\n                return;\n            }\n\n            // did not validate, so fall through and cache the response\n            data.state.remove(\"cache-data\");\n            StreamUtility.closeQuietly(cacheData.snapshot);\n        }\n\n        if (!caching)\n            return;\n\n        RequestHeaders requestHeaders = data.state.get(\"request-headers\");\n        if (requestHeaders == null || !networkResponse.isCacheable(requestHeaders) || !data.request.getMethod().equals(AsyncHttpGet.METHOD)) {\n            /*\n             * Don't cache non-GET responses. We're technically allowed to cache\n             * HEAD requests and some POST requests, but the complexity of doing\n             * so is high and the benefit is low.\n             */\n            networkCount++;\n            data.request.logd(\"Response is not cacheable\");\n            return;\n        }\n\n        String key = FileCache.toKeyString(data.request.getUri());\n        RawHeaders varyHeaders = requestHeaders.getHeaders().getAll(networkResponse.getVaryFields());\n        Entry entry = new Entry(data.request.getUri(), varyHeaders, data.request, networkResponse.getHeaders());\n        BodyCacher cacher = new BodyCacher();\n        EntryEditor editor = new EntryEditor(key);\n        try {\n            entry.writeTo(editor);\n            // create the file\n            editor.newOutputStream(ENTRY_BODY);\n        }\n        catch (Exception e) {\n            // Log.e(LOGTAG, \"error\", e);\n            editor.abort();\n            networkCount++;\n            return;\n        }\n        cacher.editor = editor;\n\n        cacher.setDataEmitter(data.bodyEmitter);\n        data.bodyEmitter = cacher;\n\n        data.state.put(\"body-cacher\", cacher);\n        data.request.logd(\"Caching response\");\n        cacheStoreCount++;\n    }\n\n    // step 3: close up shop\n    @Override\n    public void onResponseComplete(OnResponseCompleteData data) {\n        CacheData cacheData = data.state.get(\"cache-data\");\n        if (cacheData != null && cacheData.snapshot != null)\n            StreamUtility.closeQuietly(cacheData.snapshot);\n\n        CachedSocket cachedSocket = Util.getWrappedSocket(data.socket, CachedSocket.class);\n        if (cachedSocket != null)\n            StreamUtility.closeQuietly((cachedSocket.cacheResponse).getBody());\n\n        BodyCacher cacher = data.state.get(\"body-cacher\");\n        if (cacher != null) {\n            if (data.exception != null)\n                cacher.abort();\n            else\n                cacher.commit();\n        }\n    }\n    \n    public void clear() {\n        if (cache != null) {\n            cache.clear();\n        }\n    }\n\n    public static class CacheData {\n        FileInputStream[] snapshot;\n        EntryCacheResponse candidate;\n        long contentLength;\n        ResponseHeaders cachedResponseHeaders;\n    }\n    \n    private static class BodyCacher extends FilteredDataEmitter {\n        EntryEditor editor;\n        ByteBufferList cached;\n\n        @Override\n        protected void report(Exception e) {\n            super.report(e);\n            if (e != null)\n                abort();\n        }\n\n        @Override\n        public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n            if (cached != null) {\n                super.onDataAvailable(emitter, cached);\n                // couldn't emit it all, so just wait for another day...\n                if (cached.remaining() > 0)\n                    return;\n                cached = null;\n            }\n\n            // write to cache... any data not consumed needs to be retained for the next callback\n            ByteBufferList copy = new ByteBufferList();\n            try {\n                if (editor != null) {\n                    OutputStream outputStream = editor.newOutputStream(ENTRY_BODY);\n                    if (outputStream != null) {\n                        while (!bb.isEmpty()) {\n                            ByteBuffer b = bb.remove();\n                            try {\n                                ByteBufferList.writeOutputStream(outputStream, b);\n                            }\n                            finally {\n                                copy.add(b);\n                            }\n                        }\n                    }\n                    else {\n                        abort();\n                    }\n                }\n            }\n            catch (Exception e) {\n                abort();\n            }\n            finally {\n                bb.get(copy);\n                copy.get(bb);\n            }\n\n            super.onDataAvailable(emitter, bb);\n\n            if (editor != null && bb.remaining() > 0) {\n                cached = new ByteBufferList();\n                bb.get(cached);\n            }\n        }\n\n        @Override\n        public void close() {\n            abort();\n            super.close();\n        }\n\n        public void abort() {\n            if (editor != null) {\n                editor.abort();\n                editor = null;\n            }\n        }\n\n        public void commit() {\n            if (editor != null) {\n                editor.commit();\n                editor = null;\n            }\n        }\n    }\n\n    private static class CachedBodyEmitter extends FilteredDataEmitter {\n        EntryCacheResponse cacheResponse;\n        ByteBufferList pending = new ByteBufferList();\n        private boolean paused;\n        private Allocator allocator = new Allocator();\n        boolean allowEnd;\n        public CachedBodyEmitter(EntryCacheResponse cacheResponse, long contentLength) {\n            this.cacheResponse = cacheResponse;\n            allocator.setCurrentAlloc((int)contentLength);\n        }\n\n        Runnable sendCachedDataRunnable = new Runnable() {\n            @Override\n            public void run() {\n                sendCachedDataOnNetworkThread();\n            }\n        };\n\n        void sendCachedDataOnNetworkThread() {\n            if (pending.remaining() > 0) {\n                super.onDataAvailable(CachedBodyEmitter.this, pending);\n                if (pending.remaining() > 0)\n                    return;\n            }\n\n            // fill pending\n            try {\n                ByteBuffer buffer = allocator.allocate();\n                FileInputStream din = cacheResponse.getBody();\n                int read = din.read(buffer.array(), buffer.arrayOffset(), buffer.capacity());\n                if (read == -1) {\n                    ByteBufferList.reclaim(buffer);\n                    allowEnd = true;\n                    report(null);\n                    return;\n                }\n                allocator.track(read);\n                buffer.limit(read);\n                pending.add(buffer);\n            }\n            catch (IOException e) {\n                allowEnd = true;\n                report(e);\n                return;\n            }\n            super.onDataAvailable(this, pending);\n            if (pending.remaining() > 0)\n                return;\n            // this limits max throughput to 256k (aka max alloc) * 100 per second...\n            // roughly 25MB/s\n            getServer().postDelayed(sendCachedDataRunnable, 10);\n        }\n\n        void sendCachedData() {\n            getServer().post(sendCachedDataRunnable);\n        }\n\n        @Override\n        public void resume() {\n            paused = false;\n            sendCachedData();\n        }\n\n        @Override\n        public boolean isPaused() {\n            return paused;\n        }\n\n        @Override\n        public void close() {\n            if (getServer().getAffinity() != Thread.currentThread()) {\n                getServer().post(new Runnable() {\n                    @Override\n                    public void run() {\n                        close();\n                    }\n                });\n                return;\n            }\n\n            pending.recycle();\n            StreamUtility.closeQuietly(cacheResponse.getBody());\n            super.close();\n        }\n\n        @Override\n        protected void report(Exception e) {\n            // a 304 response will immediate call report/end since there is no body.\n            // prevent this from happening by waiting for the actual body to be spit out.\n            if (!allowEnd)\n                return;\n            StreamUtility.closeQuietly(cacheResponse.getBody());\n            super.report(e);\n        }\n    }\n    \n    private static final class Entry {\n        private final String uri;\n        private final RawHeaders varyHeaders;\n        private final String requestMethod;\n        private final RawHeaders responseHeaders;\n        private final String cipherSuite;\n        private final Certificate[] peerCertificates;\n        private final Certificate[] localCertificates;\n\n        /*\n         * Reads an entry from an input stream. A typical entry looks like this:\n         *   http://google.com/foo\n         *   GET\n         *   2\n         *   Accept-Language: fr-CA\n         *   Accept-Charset: UTF-8\n         *   HTTP/1.1 200 OK\n         *   3\n         *   Content-Type: image/png\n         *   Content-Length: 100\n         *   Cache-Control: max-age=600\n         *\n         * A typical HTTPS file looks like this:\n         *   https://google.com/foo\n         *   GET\n         *   2\n         *   Accept-Language: fr-CA\n         *   Accept-Charset: UTF-8\n         *   HTTP/1.1 200 OK\n         *   3\n         *   Content-Type: image/png\n         *   Content-Length: 100\n         *   Cache-Control: max-age=600\n         *\n         *   AES_256_WITH_MD5\n         *   2\n         *   base64-encoded peerCertificate[0]\n         *   base64-encoded peerCertificate[1]\n         *   -1\n         *\n         * The file is newline separated. The first two lines are the URL and\n         * the request method. Next is the number of HTTP Vary request header\n         * lines, followed by those lines.\n         *\n         * Next is the response status line, followed by the number of HTTP\n         * response header lines, followed by those lines.\n         *\n         * HTTPS responses also contain SSL session information. This begins\n         * with a blank line, and then a line containing the cipher suite. Next\n         * is the length of the peer certificate chain. These certificates are\n         * base64-encoded and appear each on their own line. The next line\n         * contains the length of the local certificate chain. These\n         * certificates are also base64-encoded and appear each on their own\n         * line. A length of -1 is used to encode a null array.\n         */\n        public Entry(InputStream in) throws IOException {\n            StrictLineReader reader = null;\n            try {\n                reader = new StrictLineReader(in, Charsets.US_ASCII);\n                uri = reader.readLine();\n                requestMethod = reader.readLine();\n                varyHeaders = new RawHeaders();\n                int varyRequestHeaderLineCount = reader.readInt();\n                for (int i = 0; i < varyRequestHeaderLineCount; i++) {\n                    varyHeaders.addLine(reader.readLine());\n                }\n\n                responseHeaders = new RawHeaders();\n                responseHeaders.setStatusLine(reader.readLine());\n                int responseHeaderLineCount = reader.readInt();\n                for (int i = 0; i < responseHeaderLineCount; i++) {\n                    responseHeaders.addLine(reader.readLine());\n                }\n\n//                if (isHttps()) {\n//                    String blank = reader.readLine();\n//                    if (blank.length() != 0) {\n//                        throw new IOException(\"expected \\\"\\\" but was \\\"\" + blank + \"\\\"\");\n//                    }\n//                    cipherSuite = reader.readLine();\n//                    peerCertificates = readCertArray(reader);\n//                    localCertificates = readCertArray(reader);\n//                } else {\n                    cipherSuite = null;\n                    peerCertificates = null;\n                    localCertificates = null;\n//                }\n            } finally {\n                StreamUtility.closeQuietly(reader, in);\n            }\n        }\n\n        public Entry(Uri uri, RawHeaders varyHeaders, AsyncHttpRequest request, RawHeaders responseHeaders) {\n            this.uri = uri.toString();\n            this.varyHeaders = varyHeaders;\n            this.requestMethod = request.getMethod();\n            this.responseHeaders = responseHeaders;\n\n//            if (isHttps()) {\n//                HttpsURLConnection httpsConnection = (HttpsURLConnection) httpConnection;\n//                cipherSuite = httpsConnection.getCipherSuite();\n//                Certificate[] peerCertificatesNonFinal = null;\n//                try {\n//                    peerCertificatesNonFinal = httpsConnection.getServerCertificates();\n//                } catch (SSLPeerUnverifiedException ignored) {\n//                }\n//                peerCertificates = peerCertificatesNonFinal;\n//                localCertificates = httpsConnection.getLocalCertificates();\n//            } else {\n                cipherSuite = null;\n                peerCertificates = null;\n                localCertificates = null;\n//            }\n        }\n\n        public void writeTo(EntryEditor editor) throws IOException {\n            OutputStream out = editor.newOutputStream(ENTRY_METADATA);\n            Writer writer = new BufferedWriter(new OutputStreamWriter(out, Charsets.UTF_8));\n\n            writer.write(uri + '\\n');\n            writer.write(requestMethod + '\\n');\n            writer.write(Integer.toString(varyHeaders.length()) + '\\n');\n            for (int i = 0; i < varyHeaders.length(); i++) {\n                writer.write(varyHeaders.getFieldName(i) + \": \"\n                        + varyHeaders.getValue(i) + '\\n');\n            }\n\n            writer.write(responseHeaders.getStatusLine() + '\\n');\n            writer.write(Integer.toString(responseHeaders.length()) + '\\n');\n            for (int i = 0; i < responseHeaders.length(); i++) {\n                writer.write(responseHeaders.getFieldName(i) + \": \"\n                        + responseHeaders.getValue(i) + '\\n');\n            }\n\n            if (isHttps()) {\n                writer.write('\\n');\n                writer.write(cipherSuite + '\\n');\n                writeCertArray(writer, peerCertificates);\n                writeCertArray(writer, localCertificates);\n            }\n            writer.close();\n        }\n\n        private boolean isHttps() {\n            return uri.startsWith(\"https://\");\n        }\n\n        private Certificate[] readCertArray(StrictLineReader reader) throws IOException {\n            int length = reader.readInt();\n            if (length == -1) {\n                return null;\n            }\n            try {\n                CertificateFactory certificateFactory = CertificateFactory.getInstance(\"X.509\");\n                Certificate[] result = new Certificate[length];\n                for (int i = 0; i < result.length; i++) {\n                    String line = reader.readLine();\n                    byte[] bytes = Base64.decode(line, Base64.DEFAULT);\n                    result[i] = certificateFactory.generateCertificate(\n                            new ByteArrayInputStream(bytes));\n                }\n                return result;\n            } catch (CertificateException e) {\n                throw new IOException(e.getMessage());\n            }\n        }\n\n        private void writeCertArray(Writer writer, Certificate[] certificates) throws IOException {\n            if (certificates == null) {\n                writer.write(\"-1\\n\");\n                return;\n            }\n            try {\n                writer.write(Integer.toString(certificates.length) + '\\n');\n                for (Certificate certificate : certificates) {\n                    byte[] bytes = certificate.getEncoded();\n                    String line = Base64.encodeToString(bytes, Base64.DEFAULT);\n                    writer.write(line + '\\n');\n                }\n            } catch (CertificateEncodingException e) {\n                throw new IOException(e.getMessage());\n            }\n        }\n\n        public boolean matches(Uri uri, String requestMethod,\n                Map<String, List<String>> requestHeaders) {\n            return this.uri.equals(uri.toString())\n                    && this.requestMethod.equals(requestMethod)\n                    && new ResponseHeaders(uri, responseHeaders)\n                            .varyMatches(varyHeaders.toMultimap(), requestHeaders);\n        }\n    }\n\n    static class EntryCacheResponse extends CacheResponse {\n        private final Entry entry;\n        private final FileInputStream snapshot;\n\n        public EntryCacheResponse(Entry entry, FileInputStream snapshot) {\n            this.entry = entry;\n            this.snapshot = snapshot;\n        }\n\n        @Override public Map<String, List<String>> getHeaders() {\n            return entry.responseHeaders.toMultimap();\n        }\n\n        @Override public FileInputStream getBody() {\n            return snapshot;\n        }\n    }\n\n    private class CachedSSLSocket extends CachedSocket implements AsyncSSLSocket {\n        public CachedSSLSocket(EntryCacheResponse cacheResponse, long contentLength) {\n            super(cacheResponse, contentLength);\n        }\n\n        @Override\n        public SSLEngine getSSLEngine() {\n            return null;\n        }\n\n        @Override\n        public X509Certificate[] getPeerCertificates() {\n            return null;\n        }\n    }\n\n    private class CachedSocket extends CachedBodyEmitter implements AsyncSocket {\n        boolean closed;\n        boolean open;\n        CompletedCallback closedCallback;\n        public CachedSocket(EntryCacheResponse cacheResponse, long contentLength) {\n            super(cacheResponse, contentLength);\n            allowEnd = true;\n        }\n\n        @Override\n        public void end() {\n        }\n\n        @Override\n        protected void report(Exception e) {\n            super.report(e);\n            if (closed)\n                return;\n            closed = true;\n            if (closedCallback != null)\n                closedCallback.onCompleted(e);\n        }\n\n        @Override\n        public void write(ByteBufferList bb) {\n            // it's gonna write headers and stuff... whatever\n            bb.recycle();\n        }\n\n        @Override\n        public WritableCallback getWriteableCallback() {\n            return null;\n        }\n\n        @Override\n        public void setWriteableCallback(WritableCallback handler) {\n        }\n\n        @Override\n        public boolean isOpen() {\n            return open;\n        }\n\n        @Override\n        public void close() {\n            open = false;\n        }\n\n        @Override\n        public CompletedCallback getClosedCallback() {\n            return closedCallback;\n        }\n\n        @Override\n        public void setClosedCallback(CompletedCallback handler) {\n            closedCallback = handler;\n        }\n\n        @Override\n        public AsyncServer getServer() {\n            return server;\n        }\n    }\n\n    class EntryEditor {\n        String key;\n        File[] temps;\n        FileOutputStream[] outs;\n        boolean done;\n        public EntryEditor(String key) {\n            this.key = key;\n            temps = cache.getTempFiles(ENTRY_COUNT);\n            outs = new FileOutputStream[ENTRY_COUNT];\n        }\n\n        void commit() {\n            StreamUtility.closeQuietly(outs);\n            if (done)\n                return;\n            cache.commitTempFiles(key, temps);\n            writeSuccessCount++;\n            done = true;\n        }\n\n        FileOutputStream newOutputStream(int index) throws IOException {\n            if (outs[index] == null)\n                outs[index] = new FileOutputStream(temps[index]);\n            return outs[index];\n        }\n\n        void abort() {\n            StreamUtility.closeQuietly(outs);\n            FileCache.removeFiles(temps);\n            if (done)\n                return;\n            writeAbortCount++;\n            done = true;\n        }\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/cache/ResponseHeaders.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.koushikdutta.async.http.cache;\n\nimport android.net.Uri;\n\nimport com.koushikdutta.async.http.HttpDate;\n\nimport java.net.HttpURLConnection;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.TreeSet;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * Parsed HTTP response headers.\n */\nfinal class ResponseHeaders {\n\n    /** HTTP header name for the local time when the request was sent. */\n    private static final String SENT_MILLIS = \"X-Android-Sent-Millis\";\n\n    /** HTTP header name for the local time when the response was received. */\n    private static final String RECEIVED_MILLIS = \"X-Android-Received-Millis\";\n\n    private final Uri uri;\n    private final RawHeaders headers;\n\n    /** The server's time when this response was served, if known. */\n    private Date servedDate;\n\n    /** The last modified date of the response, if known. */\n    private Date lastModified;\n\n    /**\n     * The expiration date of the response, if known. If both this field and the\n     * max age are set, the max age is preferred.\n     */\n    private Date expires;\n\n    /**\n     * Extension header set by HttpURLConnectionImpl specifying the timestamp\n     * when the HTTP request was first initiated.\n     */\n    private long sentRequestMillis;\n\n    /**\n     * Extension header set by HttpURLConnectionImpl specifying the timestamp\n     * when the HTTP response was first received.\n     */\n    private long receivedResponseMillis;\n\n    /**\n     * In the response, this field's name \"no-cache\" is misleading. It doesn't\n     * prevent us from caching the response; it only means we have to validate\n     * the response with the origin server before returning it. We can do this\n     * with a conditional get.\n     */\n    private boolean noCache;\n\n    /** If true, this response should not be cached. */\n    private boolean noStore;\n\n    /**\n     * The duration past the response's served date that it can be served\n     * without validation.\n     */\n    private int maxAgeSeconds = -1;\n\n    /**\n     * The \"s-maxage\" directive is the max age for shared caches. Not to be\n     * confused with \"max-age\" for non-shared caches, As in Firefox and Chrome,\n     * this directive is not honored by this cache.\n     */\n    private int sMaxAgeSeconds = -1;\n\n    /**\n     * This request header field's name \"only-if-cached\" is misleading. It\n     * actually means \"do not use the network\". It is set by a client who only\n     * wants to make a request if it can be fully satisfied by the cache.\n     * Cached responses that would require validation (ie. conditional gets) are\n     * not permitted if this header is set.\n     */\n    private boolean isPublic;\n    private boolean mustRevalidate;\n    private String etag;\n    private int ageSeconds = -1;\n\n    /** Case-insensitive set of field names. */\n    private Set<String> varyFields = Collections.emptySet();\n\n    private String contentEncoding;\n    private String transferEncoding;\n    private long contentLength = -1;\n    private String connection;\n    private String proxyAuthenticate;\n    private String wwwAuthenticate;\n\n    public ResponseHeaders(Uri uri, RawHeaders headers) {\n        this.uri = uri;\n        this.headers = headers;\n\n        HeaderParser.CacheControlHandler handler = new HeaderParser.CacheControlHandler() {\n            @Override public void handle(String directive, String parameter) {\n                if (directive.equalsIgnoreCase(\"no-cache\")) {\n                    noCache = true;\n                } else if (directive.equalsIgnoreCase(\"no-store\")) {\n                    noStore = true;\n                } else if (directive.equalsIgnoreCase(\"max-age\")) {\n                    maxAgeSeconds = HeaderParser.parseSeconds(parameter);\n                } else if (directive.equalsIgnoreCase(\"s-maxage\")) {\n                    sMaxAgeSeconds = HeaderParser.parseSeconds(parameter);\n                } else if (directive.equalsIgnoreCase(\"public\")) {\n                    isPublic = true;\n                } else if (directive.equalsIgnoreCase(\"must-revalidate\")) {\n                    mustRevalidate = true;\n                }\n            }\n        };\n\n        for (int i = 0; i < headers.length(); i++) {\n            String fieldName = headers.getFieldName(i);\n            String value = headers.getValue(i);\n            if (\"Cache-Control\".equalsIgnoreCase(fieldName)) {\n                HeaderParser.parseCacheControl(value, handler);\n            } else if (\"Date\".equalsIgnoreCase(fieldName)) {\n                servedDate = HttpDate.parse(value);\n            } else if (\"Expires\".equalsIgnoreCase(fieldName)) {\n                expires = HttpDate.parse(value);\n            } else if (\"Last-Modified\".equalsIgnoreCase(fieldName)) {\n                lastModified = HttpDate.parse(value);\n            } else if (\"ETag\".equalsIgnoreCase(fieldName)) {\n                etag = value;\n            } else if (\"Pragma\".equalsIgnoreCase(fieldName)) {\n                if (value.equalsIgnoreCase(\"no-cache\")) {\n                    noCache = true;\n                }\n            } else if (\"Age\".equalsIgnoreCase(fieldName)) {\n                ageSeconds = HeaderParser.parseSeconds(value);\n            } else if (\"Vary\".equalsIgnoreCase(fieldName)) {\n                // Replace the immutable empty set with something we can mutate.\n                if (varyFields.isEmpty()) {\n                    varyFields = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);\n                }\n                for (String varyField : value.split(\",\")) {\n                    varyFields.add(varyField.trim().toLowerCase(Locale.US));\n                }\n            } else if (\"Content-Encoding\".equalsIgnoreCase(fieldName)) {\n                contentEncoding = value;\n            } else if (\"Transfer-Encoding\".equalsIgnoreCase(fieldName)) {\n                transferEncoding = value;\n            } else if (\"Content-Length\".equalsIgnoreCase(fieldName)) {\n                try {\n                    contentLength = Long.parseLong(value);\n                } catch (NumberFormatException ignored) {\n                }\n            } else if (\"Connection\".equalsIgnoreCase(fieldName)) {\n                connection = value;\n            } else if (\"Proxy-Authenticate\".equalsIgnoreCase(fieldName)) {\n                proxyAuthenticate = value;\n            } else if (\"WWW-Authenticate\".equalsIgnoreCase(fieldName)) {\n                wwwAuthenticate = value;\n            } else if (SENT_MILLIS.equalsIgnoreCase(fieldName)) {\n                sentRequestMillis = Long.parseLong(value);\n            } else if (RECEIVED_MILLIS.equalsIgnoreCase(fieldName)) {\n                receivedResponseMillis = Long.parseLong(value);\n            }\n        }\n    }\n\n    public boolean isContentEncodingGzip() {\n        return \"gzip\".equalsIgnoreCase(contentEncoding);\n    }\n\n    public void stripContentEncoding() {\n        contentEncoding = null;\n        headers.removeAll(\"Content-Encoding\");\n    }\n\n    public boolean isChunked() {\n        return \"chunked\".equalsIgnoreCase(transferEncoding);\n    }\n\n    public boolean hasConnectionClose() {\n        return \"close\".equalsIgnoreCase(connection);\n    }\n\n    public Uri getUri() {\n        return uri;\n    }\n\n    public RawHeaders getHeaders() {\n        return headers;\n    }\n\n    public Date getServedDate() {\n        return servedDate;\n    }\n\n    public Date getLastModified() {\n        return lastModified;\n    }\n\n    public Date getExpires() {\n        return expires;\n    }\n\n    public boolean isNoCache() {\n        return noCache;\n    }\n\n    public boolean isNoStore() {\n        return noStore;\n    }\n\n    public int getMaxAgeSeconds() {\n        return maxAgeSeconds;\n    }\n\n    public int getSMaxAgeSeconds() {\n        return sMaxAgeSeconds;\n    }\n\n    public boolean isPublic() {\n        return isPublic;\n    }\n\n    public boolean isMustRevalidate() {\n        return mustRevalidate;\n    }\n\n    public String getEtag() {\n        return etag;\n    }\n\n    public Set<String> getVaryFields() {\n        return varyFields;\n    }\n\n    public String getContentEncoding() {\n        return contentEncoding;\n    }\n\n    public long getContentLength() {\n        return contentLength;\n    }\n\n    public String getConnection() {\n        return connection;\n    }\n\n    public String getProxyAuthenticate() {\n        return proxyAuthenticate;\n    }\n\n    public String getWwwAuthenticate() {\n        return wwwAuthenticate;\n    }\n\n    public void setLocalTimestamps(long sentRequestMillis, long receivedResponseMillis) {\n        this.sentRequestMillis = sentRequestMillis;\n        headers.add(SENT_MILLIS, Long.toString(sentRequestMillis));\n        this.receivedResponseMillis = receivedResponseMillis;\n        headers.add(RECEIVED_MILLIS, Long.toString(receivedResponseMillis));\n    }\n\n    /**\n     * Returns the current age of the response, in milliseconds. The calculation\n     * is specified by RFC 2616, 13.2.3 Age Calculations.\n     */\n    private long computeAge(long nowMillis) {\n        long apparentReceivedAge = servedDate != null\n                ? Math.max(0, receivedResponseMillis - servedDate.getTime())\n                : 0;\n        long receivedAge = ageSeconds != -1\n                ? Math.max(apparentReceivedAge, TimeUnit.SECONDS.toMillis(ageSeconds))\n                : apparentReceivedAge;\n        long responseDuration = receivedResponseMillis - sentRequestMillis;\n        long residentDuration = nowMillis - receivedResponseMillis;\n        return receivedAge + responseDuration + residentDuration;\n    }\n\n    /**\n     * Returns the number of milliseconds that the response was fresh for,\n     * starting from the served date.\n     */\n    private long computeFreshnessLifetime() {\n        if (maxAgeSeconds != -1) {\n            return TimeUnit.SECONDS.toMillis(maxAgeSeconds);\n        } else if (expires != null) {\n            long servedMillis = servedDate != null ? servedDate.getTime() : receivedResponseMillis;\n            long delta = expires.getTime() - servedMillis;\n            return delta > 0 ? delta : 0;\n        } else if (lastModified != null && uri.getEncodedQuery() == null) {\n            /*\n             * As recommended by the HTTP RFC and implemented in Firefox, the\n             * max age of a document should be defaulted to 10% of the\n             * document's age at the time it was served. Default expiration\n             * dates aren't used for URIs containing a query.\n             */\n            long servedMillis = servedDate != null ? servedDate.getTime() : sentRequestMillis;\n            long delta = servedMillis - lastModified.getTime();\n            return delta > 0 ? (delta / 10) : 0;\n        }\n        return 0;\n    }\n\n    /**\n     * Returns true if computeFreshnessLifetime used a heuristic. If we used a\n     * heuristic to serve a cached response older than 24 hours, we are required\n     * to attach a warning.\n     */\n    private boolean isFreshnessLifetimeHeuristic() {\n        return maxAgeSeconds == -1 && expires == null;\n    }\n\n    /**\n     * Returns true if this response can be stored to later serve another\n     * request.\n     */\n    public boolean isCacheable(RequestHeaders request) {\n        /*\n         * Always go to network for uncacheable response codes (RFC 2616, 13.4),\n         * This implementation doesn't support caching partial content.\n         */\n        int responseCode = headers.getResponseCode();\n        if (responseCode != HttpURLConnection.HTTP_OK\n                && responseCode != HttpURLConnection.HTTP_NOT_AUTHORITATIVE\n                && responseCode != HttpURLConnection.HTTP_MULT_CHOICE\n                && responseCode != HttpURLConnection.HTTP_MOVED_PERM\n                && responseCode != HttpURLConnection.HTTP_GONE) {\n            return false;\n        }\n\n        /*\n         * Responses to authorized requests aren't cacheable unless they include\n         * a 'public', 'must-revalidate' or 's-maxage' directive.\n         */\n        if (request.hasAuthorization()\n                && !isPublic\n                && !mustRevalidate\n                && sMaxAgeSeconds == -1) {\n            return false;\n        }\n\n        if (noStore) {\n            return false;\n        }\n\n        return true;\n    }\n\n    /**\n     * Returns true if a Vary header contains an asterisk. Such responses cannot\n     * be cached.\n     */\n    public boolean hasVaryAll() {\n        return varyFields.contains(\"*\");\n    }\n\n    /**\n     * Returns true if none of the Vary headers on this response have changed\n     * between {@code cachedRequest} and {@code newRequest}.\n     */\n    public boolean varyMatches(Map<String, List<String>> cachedRequest,\n            Map<String, List<String>> newRequest) {\n        for (String field : varyFields) {\n            if (!Objects.equal(cachedRequest.get(field), newRequest.get(field))) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    /**\n     * Returns the source to satisfy {@code request} given this cached response.\n     */\n    public ResponseSource chooseResponseSource(long nowMillis, RequestHeaders request) {\n        /*\n         * If this response shouldn't have been stored, it should never be used\n         * as a response source. This check should be redundant as long as the\n         * persistence store is well-behaved and the rules are constant.\n         */\n        if (!isCacheable(request)) {\n            return ResponseSource.NETWORK;\n        }\n\n        if (request.isNoCache() || request.hasConditions()) {\n            return ResponseSource.NETWORK;\n        }\n\n        long ageMillis = computeAge(nowMillis);\n        long freshMillis = computeFreshnessLifetime();\n\n        if (request.getMaxAgeSeconds() != -1) {\n            freshMillis = Math.min(freshMillis,\n                    TimeUnit.SECONDS.toMillis(request.getMaxAgeSeconds()));\n        }\n\n        long minFreshMillis = 0;\n        if (request.getMinFreshSeconds() != -1) {\n            minFreshMillis = TimeUnit.SECONDS.toMillis(request.getMinFreshSeconds());\n        }\n\n        long maxStaleMillis = 0;\n        if (!mustRevalidate && request.getMaxStaleSeconds() != -1) {\n            maxStaleMillis = TimeUnit.SECONDS.toMillis(request.getMaxStaleSeconds());\n        }\n\n        if (!noCache && ageMillis + minFreshMillis < freshMillis + maxStaleMillis) {\n            if (ageMillis + minFreshMillis >= freshMillis) {\n                headers.add(\"Warning\", \"110 HttpURLConnection \\\"Response is stale\\\"\");\n            }\n            /*\n             * not available in API 8\n            if (ageMillis > TimeUnit.HOURS.toMillis(24) && isFreshnessLifetimeHeuristic()) {\n            */\n            if (ageMillis > 24L * 60L * 60L * 1000L && isFreshnessLifetimeHeuristic()) {\n                headers.add(\"Warning\", \"113 HttpURLConnection \\\"Heuristic expiration\\\"\");\n            }\n            return ResponseSource.CACHE;\n        }\n\n        if (etag != null) {\n            request.setIfNoneMatch(etag);\n        }\n        else if (lastModified != null) {\n            request.setIfModifiedSince(lastModified);\n        } else if (servedDate != null) {\n            request.setIfModifiedSince(servedDate);\n        }\n\n\n        return request.hasConditions()\n                ? ResponseSource.CONDITIONAL_CACHE\n                : ResponseSource.NETWORK;\n    }\n\n    /**\n     * Returns true if this cached response should be used; false if the\n     * network response should be used.\n     */\n    public boolean validate(ResponseHeaders networkResponse) {\n        if (networkResponse.headers.getResponseCode() == HttpURLConnection.HTTP_NOT_MODIFIED) {\n            return true;\n        }\n\n        /*\n         * The HTTP spec says that if the network's response is older than our\n         * cached response, we may return the cache's response. Like Chrome (but\n         * unlike Firefox), this client prefers to return the newer response.\n         */\n        if (lastModified != null\n                && networkResponse.lastModified != null\n                && networkResponse.lastModified.getTime() < lastModified.getTime()) {\n            return true;\n        }\n\n        return false;\n    }\n\n    /**\n     * Combines this cached header with a network header as defined by RFC 2616,\n     * 13.5.3.\n     */\n    public ResponseHeaders combine(ResponseHeaders network) {\n        RawHeaders result = new RawHeaders();\n\n        for (int i = 0; i < headers.length(); i++) {\n            String fieldName = headers.getFieldName(i);\n            String value = headers.getValue(i);\n            if (fieldName.equals(\"Warning\") && value.startsWith(\"1\")) {\n                continue; // drop 100-level freshness warnings\n            }\n            if (!isEndToEnd(fieldName) || network.headers.get(fieldName) == null) {\n                result.add(fieldName, value);\n            }\n        }\n\n        for (int i = 0; i < network.headers.length(); i++) {\n            String fieldName = network.headers.getFieldName(i);\n            if (isEndToEnd(fieldName)) {\n                result.add(fieldName, network.headers.getValue(i));\n            }\n        }\n\n        return new ResponseHeaders(uri, result);\n    }\n\n    /**\n     * Returns true if {@code fieldName} is an end-to-end HTTP header, as\n     * defined by RFC 2616, 13.5.1.\n     */\n    private static boolean isEndToEnd(String fieldName) {\n        return !fieldName.equalsIgnoreCase(\"Connection\")\n                && !fieldName.equalsIgnoreCase(\"Keep-Alive\")\n                && !fieldName.equalsIgnoreCase(\"Proxy-Authenticate\")\n                && !fieldName.equalsIgnoreCase(\"Proxy-Authorization\")\n                && !fieldName.equalsIgnoreCase(\"TE\")\n                && !fieldName.equalsIgnoreCase(\"Trailers\")\n                && !fieldName.equalsIgnoreCase(\"Transfer-Encoding\")\n                && !fieldName.equalsIgnoreCase(\"Upgrade\");\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/cache/ResponseSource.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.koushikdutta.async.http.cache;\n\nenum ResponseSource {\n\n    /**\n     * Return the response from the cache immediately.\n     */\n    CACHE,\n\n    /**\n     * Make a conditional request to the host, returning the cache response if\n     * the cache is valid and the network response otherwise.\n     */\n    CONDITIONAL_CACHE,\n\n    /**\n     * Return the response from the network.\n     */\n    NETWORK;\n\n    public boolean requiresConnection() {\n        return this == CONDITIONAL_CACHE || this == NETWORK;\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/cache/StrictLineReader.java",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.koushikdutta.async.http.cache;\n\nimport com.koushikdutta.async.util.Charsets;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.Closeable;\nimport java.io.EOFException;\nimport java.io.InputStream;\nimport java.io.IOException;\nimport java.nio.charset.Charset;\n\n/**\n * Buffers input from an {@link InputStream} for reading lines.\n *\n * This class is used for buffered reading of lines. For purposes of this class, a line ends with\n * \"\\n\" or \"\\r\\n\". End of input is reported by throwing {@code EOFException}. Unterminated line at\n * end of input is invalid and will be ignored, the caller may use {@code hasUnterminatedLine()}\n * to detect it after catching the {@code EOFException}.\n *\n * This class is intended for reading input that strictly consists of lines, such as line-based\n * cache entries or cache journal. Unlike the {@link BufferedReader} which in conjunction with\n * {@link InputStreamReader} provides similar functionality, this class uses different\n * end-of-input reporting and a more restrictive definition of a line.\n *\n * This class supports only charsets that encode '\\r' and '\\n' as a single byte with value 13\n * and 10, respectively, and the representation of no other character contains these values.\n * We currently check in constructor that the charset is one of US-ASCII, UTF-8 and ISO-8859-1.\n * The default charset is US_ASCII.\n */\nclass StrictLineReader implements Closeable {\n    private static final byte CR = (byte)'\\r';\n    private static final byte LF = (byte)'\\n';\n\n    private final InputStream in;\n\n    /*\n     * Buffered data is stored in {@code buf}. As long as no exception occurs, 0 <= pos <= end\n     * and the data in the range [pos, end) is buffered for reading. At end of input, if there is\n     * an unterminated line, we set end == -1, otherwise end == pos. If the underlying\n     * {@code InputStream} throws an {@code IOException}, end may remain as either pos or -1.\n     */\n    private byte[] buf;\n    private int pos;\n    private int end;\n\n    /**\n     * Constructs a new {@code StrictLineReader} with the default capacity and charset.\n     *\n     * @param in the {@code InputStream} to read data from.\n     * @throws NullPointerException if {@code in} is null.\n     */\n    public StrictLineReader(InputStream in) {\n        this(in, 8192);\n    }\n\n    /**\n     * Constructs a new {@code LineReader} with the specified capacity and the default charset.\n     *\n     * @param in the {@code InputStream} to read data from.\n     * @param capacity the capacity of the buffer.\n     * @throws NullPointerException if {@code in} is null.\n     * @throws IllegalArgumentException for negative or zero {@code capacity}.\n     */\n    public StrictLineReader(InputStream in, int capacity) {\n        this(in, capacity, Charsets.US_ASCII);\n    }\n\n    /**\n     * Constructs a new {@code LineReader} with the specified charset and the default capacity.\n     *\n     * @param in the {@code InputStream} to read data from.\n     * @param charset the charset used to decode data.\n     *         Only US-ASCII, UTF-8 and ISO-8859-1 is supported.\n     * @throws NullPointerException if {@code in} or {@code charset} is null.\n     * @throws IllegalArgumentException if the specified charset is not supported.\n     */\n    public StrictLineReader(InputStream in, Charset charset) {\n        this(in, 8192, charset);\n    }\n\n    /**\n     * Constructs a new {@code LineReader} with the specified capacity and charset.\n     *\n     * @param in the {@code InputStream} to read data from.\n     * @param capacity the capacity of the buffer.\n     * @param charset the charset used to decode data.\n     *         Only US-ASCII, UTF-8 and ISO-8859-1 is supported.\n     * @throws NullPointerException if {@code in} or {@code charset} is null.\n     * @throws IllegalArgumentException if {@code capacity} is negative or zero\n     *         or the specified charset is not supported.\n     */\n    public StrictLineReader(InputStream in, int capacity, Charset charset) {\n        if (in == null) {\n            throw new NullPointerException(\"in == null\");\n        } else if (charset == null) {\n            throw new NullPointerException(\"charset == null\");\n        }\n        if (capacity < 0) {\n            throw new IllegalArgumentException(\"capacity <= 0\");\n        }\n        if (!(charset.equals(Charsets.US_ASCII) || charset.equals(Charsets.UTF_8))) {\n            throw new IllegalArgumentException(\"Unsupported encoding\");\n        }\n\n        this.in = in;\n        buf = new byte[capacity];\n    }\n\n    /**\n     * Closes the reader by closing the underlying {@code InputStream} and\n     * marking this reader as closed.\n     *\n     * @throws IOException for errors when closing the underlying {@code InputStream}.\n     */\n    @Override\n    public void close() throws IOException {\n        synchronized (in) {\n            if (buf != null) {\n                buf = null;\n                in.close();\n            }\n        }\n    }\n\n    /**\n     * Reads the next line. A line ends with {@code \"\\n\"} or {@code \"\\r\\n\"},\n     * this end of line marker is not included in the result.\n     *\n     * @return the next line from the input.\n     * @throws IOException for underlying {@code InputStream} errors.\n     * @throws EOFException for the end of source stream.\n     */\n    public String readLine() throws IOException {\n        synchronized (in) {\n            if (buf == null) {\n                throw new IOException(\"LineReader is closed\");\n            }\n\n            // Read more data if we are at the end of the buffered data.\n            // Though it's an error to read after an exception, we will let {@code fillBuf()}\n            // throw again if that happens; thus we need to handle end == -1 as well as end == pos.\n            if (pos >= end) {\n                fillBuf();\n            }\n            // Try to find LF in the buffered data and return the line if successful.\n            for (int i = pos; i != end; ++i) {\n                if (buf[i] == LF) {\n                    int lineEnd = (i != pos && buf[i - 1] == CR) ? i - 1 : i;\n                    String res = new String(buf, pos, lineEnd - pos);\n                    pos = i + 1;\n                    return res;\n                }\n            }\n\n            // Let's anticipate up to 80 characters on top of those already read.\n            ByteArrayOutputStream out = new ByteArrayOutputStream(end - pos + 80) {\n                @Override\n                public String toString() {\n                    int length = (count > 0 && buf[count - 1] == CR) ? count - 1 : count;\n                    return new String(buf, 0, length);\n                }\n            };\n\n            while (true) {\n                out.write(buf, pos, end - pos);\n                // Mark unterminated line in case fillBuf throws EOFException or IOException.\n                end = -1;\n                fillBuf();\n                // Try to find LF in the buffered data and return the line if successful.\n                for (int i = pos; i != end; ++i) {\n                    if (buf[i] == LF) {\n                        if (i != pos) {\n                            out.write(buf, pos, i - pos);\n                        }\n                        pos = i + 1;\n                        return out.toString();\n                    }\n                }\n            }\n        }\n    }\n\n    /**\n     * Read an {@code int} from a line containing its decimal representation.\n     *\n     * @return the value of the {@code int} from the next line.\n     * @throws IOException for underlying {@code InputStream} errors or conversion error.\n     * @throws EOFException for the end of source stream.\n     */\n    public int readInt() throws IOException {\n        String intString = readLine();\n        try {\n            return Integer.parseInt(intString);\n        } catch (NumberFormatException e) {\n            throw new IOException(\"expected an int but was \\\"\" + intString + \"\\\"\");\n        }\n    }\n\n    /**\n     * Check whether there was an unterminated line at end of input after the line reader reported\n     * end-of-input with EOFException. The value is meaningless in any other situation.\n     *\n     * @return true if there was an unterminated line at end of input.\n     */\n    public boolean hasUnterminatedLine() {\n        return end == -1;\n    }\n\n    /**\n     * Reads new input data into the buffer. Call only with pos == end or end == -1,\n     * depending on the desired outcome if the function throws.\n     *\n     * @throws IOException for underlying {@code InputStream} errors.\n     * @throws EOFException for the end of source stream.\n     */\n    private void fillBuf() throws IOException {\n        int result = in.read(buf, 0, buf.length);\n        if (result == -1) {\n            throw new EOFException();\n        }\n        pos = 0;\n        end = result;\n    }\n}\n\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/callback/HttpConnectCallback.java",
    "content": "package com.koushikdutta.async.http.callback;\n\n\nimport com.koushikdutta.async.http.AsyncHttpResponse;\n\npublic interface HttpConnectCallback {\n    public void onConnectCompleted(Exception ex, AsyncHttpResponse response);\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/callback/RequestCallback.java",
    "content": "package com.koushikdutta.async.http.callback;\n\nimport com.koushikdutta.async.callback.ResultCallback;\nimport com.koushikdutta.async.http.AsyncHttpResponse;\n\npublic interface RequestCallback<T> extends ResultCallback<AsyncHttpResponse, T> {\n    public void onConnect(AsyncHttpResponse response);\n    public void onProgress(AsyncHttpResponse response, long downloaded, long total);\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/filter/ChunkedDataException.java",
    "content": "package com.koushikdutta.async.http.filter;\n\npublic class ChunkedDataException extends Exception {\n    public ChunkedDataException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/filter/ChunkedInputFilter.java",
    "content": "package com.koushikdutta.async.http.filter;\n\nimport com.koushikdutta.async.ByteBufferList;\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.FilteredDataEmitter;\nimport com.koushikdutta.async.Util;\n\npublic class ChunkedInputFilter extends FilteredDataEmitter {\n    private int mChunkLength = 0;\n    private int mChunkLengthRemaining = 0;\n    private State mState = State.CHUNK_LEN;\n    \n    private enum State {\n        CHUNK_LEN,\n        CHUNK_LEN_CR,\n        CHUNK_LEN_CRLF,\n        CHUNK,\n        CHUNK_CR,\n        CHUNK_CRLF,\n        COMPLETE,\n        ERROR,\n    }\n    \n    private boolean checkByte(char b, char value) {\n        if (b != value) {\n            mState = State.ERROR;\n            report(new ChunkedDataException(value + \" was expected, got \" + (char)b));\n            return false;\n        }\n        return true;\n    }\n\n    private boolean checkLF(char b) {\n        return checkByte(b, '\\n');\n    }\n\n    private boolean checkCR(char b) {\n        return checkByte(b, '\\r');\n    }\n\n    @Override\n    protected void report(Exception e) {\n        if (e == null && mState != State.COMPLETE)\n            e = new ChunkedDataException(\"chunked input ended before final chunk\");\n        super.report(e);\n    }\n\n    ByteBufferList pending = new ByteBufferList();\n    @Override\n    public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n        if (mState == State.ERROR) {\n            bb.recycle();\n            return;\n        }\n\n        try {\n            while (bb.remaining() > 0) {\n                switch (mState) {\n                case CHUNK_LEN:\n                    char c = bb.getByteChar();\n                    if (c == '\\r') {\n                        mState = State.CHUNK_LEN_CR;\n                    }\n                    else {\n                        mChunkLength *= 16;\n                        if (c >= 'a' && c <= 'f')\n                            mChunkLength += (c - 'a' + 10);\n                        else if (c >= '0' && c <= '9')\n                            mChunkLength += c - '0';\n                        else if (c >= 'A' && c <= 'F')\n                            mChunkLength += (c - 'A' + 10);\n                        else {\n                            report(new ChunkedDataException(\"invalid chunk length: \" + c));\n                            return;\n                        }\n                    }\n                    mChunkLengthRemaining = mChunkLength;\n                    break;\n                case CHUNK_LEN_CR:\n                    if (!checkLF(bb.getByteChar()))\n                        return;\n                    mState = State.CHUNK;\n                    break;\n                case CHUNK:\n                    int remaining = bb.remaining();\n                    int reading = Math.min(mChunkLengthRemaining, remaining);\n                    mChunkLengthRemaining -= reading;\n                    if (mChunkLengthRemaining == 0) {\n                        mState = State.CHUNK_CR;\n                    }\n                    if (reading == 0)\n                        break;\n                    bb.get(pending, reading);\n                    Util.emitAllData(this, pending);\n                    break;\n                case CHUNK_CR:\n                    if (!checkCR(bb.getByteChar()))\n                        return;\n                    mState = State.CHUNK_CRLF;\n                    break;\n                case CHUNK_CRLF:\n                    if (!checkLF(bb.getByteChar()))\n                        return;\n                    if (mChunkLength > 0) {\n                        mState = State.CHUNK_LEN;\n                        \n                    }\n                    else {\n                        mState = State.COMPLETE;\n                        report(null);\n                    }\n                    mChunkLength = 0;\n                    break;\n                case COMPLETE:\n//                    Exception fail = new Exception(\"Continued receiving data after chunk complete\");\n//                    report(fail);\n                    return;\n                }\n            }\n        }\n        catch (Exception ex) {\n            report(ex);\n        }\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/filter/ChunkedOutputFilter.java",
    "content": "package com.koushikdutta.async.http.filter;\n\nimport java.nio.ByteBuffer;\n\nimport com.koushikdutta.async.ByteBufferList;\nimport com.koushikdutta.async.DataSink;\nimport com.koushikdutta.async.FilteredDataSink;\n\npublic class ChunkedOutputFilter extends FilteredDataSink {\n    public ChunkedOutputFilter(DataSink sink) {\n        super(sink);\n    }\n\n    @Override\n    public ByteBufferList filter(ByteBufferList bb) {\n        String chunkLen = Integer.toString(bb.remaining(), 16) + \"\\r\\n\";\n        bb.addFirst(ByteBuffer.wrap(chunkLen.getBytes()));\n        bb.add(ByteBuffer.wrap(\"\\r\\n\".getBytes()));\n        return bb;\n    }\n\n    @Override\n    public void end() {\n        setMaxBuffer(Integer.MAX_VALUE);\n        ByteBufferList fin = new ByteBufferList();\n        write(fin);\n        setMaxBuffer(0);\n        // do NOT call through to super.end, as chunking is a framing protocol.\n        // we don't want to close the underlying transport.\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/filter/ContentLengthFilter.java",
    "content": "package com.koushikdutta.async.http.filter;\n\nimport com.koushikdutta.async.ByteBufferList;\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.FilteredDataEmitter;\n\npublic class ContentLengthFilter extends FilteredDataEmitter {\n    public ContentLengthFilter(long contentLength) {\n        this.contentLength = contentLength;\n    }\n\n    @Override\n    protected void report(Exception e) {\n        if (e == null && totalRead != contentLength)\n            e = new PrematureDataEndException(\"End of data reached before content length was read: \" + totalRead + \"/\" + contentLength + \" Paused: \" + isPaused());\n        super.report(e);\n    }\n\n    long contentLength;\n    long totalRead;\n    ByteBufferList transformed = new ByteBufferList();\n    @Override\n    public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n        int remaining = bb.remaining();\n        long toRead = Math.min(contentLength - totalRead, remaining);\n\n        bb.get(transformed, (int)toRead);\n\n        int beforeRead = transformed.remaining();\n\n        super.onDataAvailable(emitter, transformed);\n\n        totalRead += (beforeRead - transformed.remaining());\n        transformed.get(bb);\n\n        if (totalRead == contentLength)\n            report(null);\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/filter/DataRemainingException.java",
    "content": "package com.koushikdutta.async.http.filter;\n\npublic class DataRemainingException extends Exception {\n    public DataRemainingException(String message, Exception cause) {\n        super(message, cause);\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/filter/GZIPInputFilter.java",
    "content": "package com.koushikdutta.async.http.filter;\n\nimport com.koushikdutta.async.ByteBufferList;\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.PushParser;\nimport com.koushikdutta.async.PushParser.ParseCallback;\nimport com.koushikdutta.async.callback.DataCallback;\n\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.util.Locale;\nimport java.util.zip.CRC32;\nimport java.util.zip.GZIPInputStream;\nimport java.util.zip.Inflater;\n\npublic class GZIPInputFilter extends InflaterInputFilter {\n    static short peekShort(byte[] src, int offset, ByteOrder order) {\n        if (order == ByteOrder.BIG_ENDIAN) {\n            return (short) ((src[offset] << 8) | (src[offset + 1] & 0xff));\n        } else {\n            return (short) ((src[offset + 1] << 8) | (src[offset] & 0xff));\n        }\n    }\n\n    private static final int FCOMMENT = 16;\n\n    private static final int FEXTRA = 4;\n\n    private static final int FHCRC = 2;\n\n    private static final int FNAME = 8;\n\n\n    \n    public GZIPInputFilter() {\n        super(new Inflater(true));\n    }\n    \n    boolean mNeedsHeader = true;\n    protected CRC32 crc = new CRC32();\n\n    public static int unsignedToBytes(byte b) {\n        return b & 0xFF;\n    }\n    \n    @Override\n    @SuppressWarnings(\"unused\")\n    public void onDataAvailable(final DataEmitter emitter, ByteBufferList bb) {\n        if (mNeedsHeader) {\n            final PushParser parser = new PushParser(emitter);\n            parser.readByteArray(10, new ParseCallback<byte[]>() {\n                int flags;\n                boolean hcrc;\n\n                public void parsed(byte[] header) {\n                    short magic = peekShort(header, 0, ByteOrder.LITTLE_ENDIAN);\n                    if (magic != (short) GZIPInputStream.GZIP_MAGIC) {\n                        report(new IOException(String.format(Locale.ENGLISH, \"unknown format (magic number %x)\", magic)));\n                        emitter.setDataCallback(new NullDataCallback());\n                        return;\n                    }\n                    flags = header[3];\n                    hcrc = (flags & FHCRC) != 0;\n                    if (hcrc) {\n                        crc.update(header, 0, header.length);\n                    }\n                    if ((flags & FEXTRA) != 0) {\n                        parser.readByteArray(2, new ParseCallback<byte[]>() {\n                            public void parsed(byte[] header) {\n                                if (hcrc) {\n                                    crc.update(header, 0, 2);\n                                }\n                                int length = peekShort(header, 0, ByteOrder.LITTLE_ENDIAN) & 0xffff;\n                                parser.readByteArray(length, new ParseCallback<byte[]>() {\n                                    public void parsed(byte[] buf) {\n                                        if (hcrc) {\n                                            crc.update(buf, 0, buf.length);\n                                        }\n                                        next();\n                                    }\n                                });\n                            }\n                        });\n                    } else {\n                        next();\n                    }\n                }\n\n                private void next() {\n                    PushParser parser = new PushParser(emitter);\n                    DataCallback summer = new DataCallback() {\n                        @Override\n                        public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n                            if (hcrc) {\n                                while (bb.size() > 0) {\n                                    ByteBuffer b = bb.remove();\n                                    crc.update(b.array(), b.arrayOffset() + b.position(), b.remaining());\n                                    ByteBufferList.reclaim(b);\n                                }\n                            }\n                            bb.recycle();\n                            done();\n                        }\n                    };\n                    if ((flags & FNAME) != 0) {\n                        parser.until((byte) 0, summer);\n                        return;\n                    }\n                    if ((flags & FCOMMENT) != 0) {\n                        parser.until((byte) 0, summer);\n                        return;\n                    }\n\n                    done();\n                }\n\n                private void done() {\n                    if (hcrc) {\n                        parser.readByteArray(2, new ParseCallback<byte[]>() {\n                            public void parsed(byte[] header) {\n                                short crc16 = peekShort(header, 0, ByteOrder.LITTLE_ENDIAN);\n                                if ((short) crc.getValue() != crc16) {\n                                    report(new IOException(\"CRC mismatch\"));\n                                    return;\n                                }\n                                crc.reset();\n                                mNeedsHeader = false;\n                                setDataEmitter(emitter);\n//                            emitter.setDataCallback(GZIPInputFilter.this);\n                            }\n                        });\n                    } else {\n                        mNeedsHeader = false;\n                        setDataEmitter(emitter);\n                    }\n                }\n            });\n        }\n        else {\n            super.onDataAvailable(emitter, bb);\n        }\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/filter/InflaterInputFilter.java",
    "content": "package com.koushikdutta.async.http.filter;\n\nimport com.koushikdutta.async.ByteBufferList;\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.FilteredDataEmitter;\nimport com.koushikdutta.async.Util;\n\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\nimport java.util.zip.Inflater;\n\npublic class InflaterInputFilter extends FilteredDataEmitter {\n    private Inflater mInflater;\n\n    @Override\n    protected void report(Exception e) {\n        mInflater.end();\n        if (e != null && mInflater.getRemaining() > 0) {\n            e = new DataRemainingException(\"data still remaining in inflater\", e);\n        }\n        super.report(e);\n    }\n\n    ByteBufferList transformed = new ByteBufferList();\n    @Override\n    public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n        try {\n            ByteBuffer output = ByteBufferList.obtain(bb.remaining() * 2);\n            int totalRead = 0;\n            while (bb.size() > 0) {\n                ByteBuffer b = bb.remove();\n                if (b.hasRemaining()) {\n                    totalRead =+ b.remaining();\n                    mInflater.setInput(b.array(), b.arrayOffset() + b.position(), b.remaining());\n                    do {\n                        int inflated = mInflater.inflate(output.array(), output.arrayOffset() + output.position(), output.remaining());\n                        output.position(output.position() + inflated);\n                        if (!output.hasRemaining()) {\n                            output.flip();\n                            transformed.add(output);\n                            int newSize = output.capacity() * 2;\n                            output = ByteBufferList.obtain(newSize);\n                        }\n                    }\n                    while (!mInflater.needsInput() && !mInflater.finished());\n                }\n                ByteBufferList.reclaim(b);\n            }\n            output.flip();\n            transformed.add(output);\n\n            Util.emitAllData(this, transformed);\n        }\n        catch (Exception ex) {\n            report(ex);\n        }\n    }\n\n    public InflaterInputFilter() {\n        this(new Inflater());\n    }\n\n    public InflaterInputFilter(Inflater inflater) {\n        mInflater = inflater;\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/filter/PrematureDataEndException.java",
    "content": "package com.koushikdutta.async.http.filter;\n\npublic class PrematureDataEndException extends Exception {\n    public PrematureDataEndException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/server/AsyncHttpRequestBodyProvider.java",
    "content": "package com.koushikdutta.async.http.server;\n\nimport com.koushikdutta.async.http.Headers;\nimport com.koushikdutta.async.http.body.AsyncHttpRequestBody;\n\npublic interface AsyncHttpRequestBodyProvider {\n    AsyncHttpRequestBody getBody(Headers headers);\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/server/AsyncHttpServer.java",
    "content": "package com.koushikdutta.async.http.server;\n\nimport android.annotation.TargetApi;\nimport android.os.Build;\nimport android.util.Log;\n\nimport com.koushikdutta.async.AsyncSSLSocket;\nimport com.koushikdutta.async.AsyncSSLSocketWrapper;\nimport com.koushikdutta.async.AsyncServer;\nimport com.koushikdutta.async.AsyncServerSocket;\nimport com.koushikdutta.async.AsyncSocket;\nimport com.koushikdutta.async.ByteBufferList;\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.Util;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.callback.ListenCallback;\nimport com.koushikdutta.async.callback.ValueCallback;\nimport com.koushikdutta.async.http.Headers;\nimport com.koushikdutta.async.http.HttpUtil;\nimport com.koushikdutta.async.http.Multimap;\nimport com.koushikdutta.async.http.WebSocket;\nimport com.koushikdutta.async.http.body.AsyncHttpRequestBody;\n\nimport java.net.URLDecoder;\nimport java.util.ArrayList;\nimport java.util.Hashtable;\n\nimport javax.net.ssl.SSLContext;\n\n@TargetApi(Build.VERSION_CODES.ECLAIR)\npublic class AsyncHttpServer extends AsyncHttpServerRouter {\n    ArrayList<AsyncServerSocket> mListeners = new ArrayList<AsyncServerSocket>();\n    public void stop() {\n        if (mListeners != null) {\n            for (AsyncServerSocket listener: mListeners) {\n                listener.stop();\n            }\n        }\n    }\n    \n    protected boolean onRequest(AsyncHttpServerRequest request, AsyncHttpServerResponse response) {\n        return false;\n    }\n\n    protected void onResponseCompleted(AsyncHttpServerRequest request, AsyncHttpServerResponse response) {\n\n    }\n\n    protected void onRequest(HttpServerRequestCallback callback, AsyncHttpServerRequest request, AsyncHttpServerResponse response) {\n        if (callback != null) {\n            try {\n                callback.onRequest(request, response);\n            }\n            catch (Exception e) {\n                Log.e(\"AsyncHttpServer\", \"request callback raised uncaught exception. Catching versus crashing process\", e);\n                response.code(500);\n                response.end();\n            }\n        }\n    }\n\n    protected boolean isKeepAlive(AsyncHttpServerRequest request, AsyncHttpServerResponse response) {\n        return HttpUtil.isKeepAlive(response.getHttpVersion(), request.getHeaders());\n    }\n\n    protected AsyncHttpRequestBody onUnknownBody(Headers headers) {\n        return new UnknownRequestBody(headers.get(\"Content-Type\"));\n    }\n\n    protected boolean isSwitchingProtocols(AsyncHttpServerResponse res) {\n        return res.code() == 101;\n    }\n\n    ListenCallback mListenCallback = new ListenCallback() {\n        @Override\n        public void onAccepted(final AsyncSocket socket) {\n            final AsyncHttpServerRequestImpl req = new AsyncHttpServerRequestImpl() {\n                AsyncHttpServerRequestImpl self = this;\n                HttpServerRequestCallback requestCallback;\n                String fullPath;\n                String path;\n                boolean responseComplete;\n                boolean requestComplete;\n                AsyncHttpServerResponseImpl res;\n                boolean hasContinued;\n                boolean handled;\n\n                final Runnable onFinally = new Runnable() {\n                    @Override\n                    public void run() {\n                        Log.i(\"HTTP\", \"Done\");\n                    }\n                };\n\n                final ValueCallback<Exception> onException = new ValueCallback<Exception>() {\n                    @Override\n                    public void onResult(Exception value) {\n                        Log.e(\"HTTP\", \"exception\", value);\n                    }\n                };\n\n                void onRequest() {\n                    AsyncHttpServer.this.onRequest(requestCallback, this, res);\n                }\n\n                @Override\n                protected AsyncHttpRequestBody onBody(Headers headers) {\n                    String statusLine = getStatusLine();\n                    String[] parts = statusLine.split(\" \");\n                    fullPath = parts[1];\n                    path = URLDecoder.decode(fullPath.split(\"\\\\?\")[0]);\n                    method = parts[0];\n                    RouteMatch route = route(method, path);\n                    if (route == null)\n                        return null;\n\n                    matcher = route.matcher;\n                    requestCallback = route.callback;\n\n                    if (route.bodyCallback == null)\n                        return null;\n                    return route.bodyCallback.getBody(headers);\n                }\n\n                @Override\n                protected AsyncHttpRequestBody onUnknownBody(Headers headers) {\n                    return AsyncHttpServer.this.onUnknownBody(headers);\n                }\n\n                @Override\n                protected void onHeadersReceived() {\n                    Headers headers = getHeaders();\n\n                    // should the negotiation of 100 continue be here, or in the request impl?\n                    // probably here, so AsyncResponse can negotiate a 100 continue.\n                    if (!hasContinued && \"100-continue\".equals(headers.get(\"Expect\"))) {\n                        pause();\n//                        System.out.println(\"continuing...\");\n                        Util.writeAll(mSocket, \"HTTP/1.1 100 Continue\\r\\n\\r\\n\".getBytes(), new CompletedCallback() {\n                            @Override\n                            public void onCompleted(Exception ex) {\n                                resume();\n                                if (ex != null) {\n                                    report(ex);\n                                    return;\n                                }\n                                hasContinued = true;\n                                onHeadersReceived();\n                            }\n                        });\n                        return;\n                    }\n//                    System.out.println(headers.toHeaderString());\n                    \n                    res = new AsyncHttpServerResponseImpl(socket, this) {\n                        @Override\n                        protected void report(Exception e) {\n                            super.report(e);\n                            if (e != null) {\n                                socket.setDataCallback(new NullDataCallback());\n                                socket.setEndCallback(new NullCompletedCallback());\n                                socket.close();\n                            }\n                        }\n\n                        @Override\n                        protected void onEnd() {\n                            responseComplete = true;\n                            super.onEnd();\n                            mSocket.setEndCallback(null);\n\n                            onResponseCompleted(getRequest(), res);\n\n                            // reuse the socket for a subsequent request.\n                            handleOnCompleted();\n                        }\n                    };\n\n                    handled = AsyncHttpServer.this.onRequest(this, res);\n                    if (handled)\n                        return;\n\n                    if (requestCallback == null) {\n                        res.code(404);\n                        res.end();\n                        return;\n                    }\n\n                    if (!getBody().readFullyOnRequest() || requestComplete)\n                        onRequest();\n                }\n\n                @Override\n                public void onCompleted(Exception e) {\n                    if (isSwitchingProtocols(res))\n                        return;\n\n                    requestComplete = true;\n                    super.onCompleted(e);\n                    // no http pipelining, gc trashing if the socket dies\n                    // while the request is being sent and is paused or something\n                    mSocket.setDataCallback(new NullDataCallback() {\n                        @Override\n                        public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n                            super.onDataAvailable(emitter, bb);\n                            mSocket.close();\n                        }\n                    });\n\n                    if (e != null) {\n                        mSocket.close();\n                        return;\n                    }\n\n                    handleOnCompleted();\n\n                    if (getBody().readFullyOnRequest() && !handled) {\n                        onRequest();\n                    }\n                }\n                \n                private void handleOnCompleted() {\n                    // response may complete before request. the request may have a body, and\n                    // the response may be sent before it is fully sent.\n\n                    // if the protocol was switched off http, abandon the socket,\n                    // otherwise attempt to recycle it.\n                    if (requestComplete && responseComplete && !isSwitchingProtocols(res)) {\n                        if (isKeepAlive(self, res)) {\n                            onAccepted(socket);\n                        }\n                        else {\n                            socket.close();\n                        }\n                    }\n                }\n\n                @Override\n                public String getPath() {\n                    return path;\n                }\n\n                @Override\n                public Multimap getQuery() {\n                    String[] parts = fullPath.split(\"\\\\?\", 2);\n                    if (parts.length < 2)\n                        return new Multimap();\n                    return Multimap.parseQuery(parts[1]);\n                }\n\n                @Override\n                public String getUrl() {\n                    return fullPath;\n                }\n            };\n            req.setSocket(socket);\n            socket.resume();\n        }\n\n        @Override\n        public void onCompleted(Exception error) {\n            report(error);\n        }\n\n        @Override\n        public void onListening(AsyncServerSocket socket) {\n            mListeners.add(socket);\n        }\n    };\n\n    public AsyncServerSocket listen(AsyncServer server, int port) {\n        return server.listen(null, port, mListenCallback);\n    }\n\n    private void report(Exception ex) {\n        if (mCompletedCallback != null)\n            mCompletedCallback.onCompleted(ex);\n    }\n    \n    public AsyncServerSocket listen(int port) {\n        return listen(AsyncServer.getDefault(), port);\n    }\n\n    public void listenSecure(final int port, final SSLContext sslContext) {\n        AsyncServer.getDefault().listen(null, port, new ListenCallback() {\n            @Override\n            public void onAccepted(AsyncSocket socket) {\n                AsyncSSLSocketWrapper.handshake(socket, null, port, sslContext.createSSLEngine(), null, null, false,\n                new AsyncSSLSocketWrapper.HandshakeCallback() {\n                    @Override\n                    public void onHandshakeCompleted(Exception e, AsyncSSLSocket socket) {\n                        if (socket != null)\n                            mListenCallback.onAccepted(socket);\n                    }\n                });\n            }\n\n            @Override\n            public void onListening(AsyncServerSocket socket) {\n                mListenCallback.onListening(socket);\n            }\n\n            @Override\n            public void onCompleted(Exception ex) {\n                mListenCallback.onCompleted(ex);\n            }\n        });\n    }\n    \n    public ListenCallback getListenCallback() {\n        return mListenCallback;\n    }\n\n    CompletedCallback mCompletedCallback;\n    public void setErrorCallback(CompletedCallback callback) {\n        mCompletedCallback = callback;        \n    }\n\n    public CompletedCallback getErrorCallback() {\n        return mCompletedCallback;\n    }\n\n    private static Hashtable<Integer, String> mCodes = new Hashtable<Integer, String>();\n    static {\n        mCodes.put(200, \"OK\");\n        mCodes.put(202, \"Accepted\");\n        mCodes.put(206, \"Partial Content\");\n        mCodes.put(101, \"Switching Protocols\");\n        mCodes.put(301, \"Moved Permanently\");\n        mCodes.put(302, \"Found\");\n        mCodes.put(304, \"Not Modified\");\n        mCodes.put(400, \"Bad Request\");\n        mCodes.put(401, \"Unauthorized\");\n        mCodes.put(404, \"Not Found\");\n        mCodes.put(500, \"Internal Server Error\");\n    }\n    \n    public static String getResponseCodeDescription(int code) {\n        String d = mCodes.get(code);\n        if (d == null)\n            return \"Unknown\";\n        return d;\n    }\n\n    public static void addResponseCodeDescription( int code, String description ) {\n        mCodes.put(code, description);\n    }\n\n    public static interface WebSocketRequestCallback {\n        void onConnected(WebSocket webSocket, AsyncHttpServerRequest request);\n    }\n\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/server/AsyncHttpServerRequest.java",
    "content": "package com.koushikdutta.async.http.server;\n\nimport com.koushikdutta.async.AsyncSocket;\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.http.Headers;\nimport com.koushikdutta.async.http.Multimap;\nimport com.koushikdutta.async.http.body.AsyncHttpRequestBody;\n\nimport java.util.Map;\nimport java.util.regex.Matcher;\n\npublic interface AsyncHttpServerRequest extends DataEmitter {\n    Headers getHeaders();\n    Matcher getMatcher();\n    void setMatcher(Matcher matcher);\n    <T extends AsyncHttpRequestBody> T getBody();\n    AsyncSocket getSocket();\n    String getPath();\n    Multimap getQuery();\n    String getMethod();\n    String getUrl();\n\n    String get(String name);\n    Map<String, Object> getState();\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/server/AsyncHttpServerRequestImpl.java",
    "content": "package com.koushikdutta.async.http.server;\n\nimport com.koushikdutta.async.AsyncSocket;\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.FilteredDataEmitter;\nimport com.koushikdutta.async.LineEmitter;\nimport com.koushikdutta.async.LineEmitter.StringCallback;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.callback.DataCallback;\nimport com.koushikdutta.async.http.Headers;\nimport com.koushikdutta.async.http.HttpUtil;\nimport com.koushikdutta.async.http.Multimap;\nimport com.koushikdutta.async.http.Protocol;\nimport com.koushikdutta.async.http.body.AsyncHttpRequestBody;\n\nimport java.io.IOException;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.regex.Matcher;\n\npublic abstract class AsyncHttpServerRequestImpl extends FilteredDataEmitter implements AsyncHttpServerRequest, CompletedCallback {\n    private String statusLine;\n    private Headers mRawHeaders = new Headers();\n    AsyncSocket mSocket;\n    private HashMap<String, Object> state = new HashMap<>();\n\n    @Override\n    public HashMap<String, Object> getState() {\n        return state;\n    }\n\n    public String getStatusLine() {\n        return statusLine;\n    }\n\n    private CompletedCallback mReporter = new CompletedCallback() {\n        @Override\n        public void onCompleted(Exception error) {\n            AsyncHttpServerRequestImpl.this.onCompleted(error);\n        }\n    };\n\n    @Override\n    public void onCompleted(Exception e) {\n//        if (mBody != null)\n//            mBody.onCompleted(e);\n        report(e);\n    }\n\n    abstract protected void onHeadersReceived();\n    \n    protected void onNotHttp() {\n        System.out.println(\"not http!\");\n    }\n\n    protected AsyncHttpRequestBody onUnknownBody(Headers headers) {\n        return null;\n    }\n    protected AsyncHttpRequestBody onBody(Headers headers) {\n        return null;\n    }\n\n    \n    StringCallback mHeaderCallback = new StringCallback() {\n        @Override\n        public void onStringAvailable(String s) {\n            if (statusLine == null) {\n                statusLine = s;\n                if (!statusLine.contains(\"HTTP/\")) {\n                    onNotHttp();\n                    mSocket.setDataCallback(new NullDataCallback());\n                    report(new IOException(\"data/header received was not not http\"));\n                }\n\n                return;\n            }\n            if (!\"\\r\".equals(s)){\n                mRawHeaders.addLine(s);\n                return;\n            }\n\n            DataEmitter emitter = HttpUtil.getBodyDecoder(mSocket, Protocol.HTTP_1_1, mRawHeaders, true);\n            mBody = onBody(mRawHeaders);\n            if (mBody == null) {\n                mBody = HttpUtil.getBody(emitter, mReporter, mRawHeaders);\n                if (mBody == null) {\n                    mBody = onUnknownBody(mRawHeaders);\n                    if (mBody == null)\n                        mBody = new UnknownRequestBody(mRawHeaders.get(\"Content-Type\"));\n                }\n            }\n            mBody.parse(emitter, mReporter);\n            onHeadersReceived();\n        }\n    };\n\n    String method;\n    @Override\n    public String getMethod() {\n        return method;\n    }\n    \n    void setSocket(AsyncSocket socket) {\n        mSocket = socket;\n\n        LineEmitter liner = new LineEmitter();\n        mSocket.setDataCallback(liner);\n        liner.setLineCallback(mHeaderCallback);\n        mSocket.setEndCallback(new NullCompletedCallback());\n    }\n    \n    @Override\n    public AsyncSocket getSocket() {\n        return mSocket;\n    }\n\n    @Override\n    public Headers getHeaders() {\n        return mRawHeaders;\n    }\n\n    @Override\n    public void setDataCallback(DataCallback callback) {\n        mSocket.setDataCallback(callback);\n    }\n\n    @Override\n    public DataCallback getDataCallback() {\n        return mSocket.getDataCallback();\n    }\n\n    @Override\n    public boolean isChunked() {\n        return mSocket.isChunked();\n    }\n\n    AsyncHttpRequestBody mBody;\n    @Override\n    public AsyncHttpRequestBody getBody() {\n        return mBody;\n    }\n\n    @Override\n    public void pause() {\n        mSocket.pause();\n    }\n\n    @Override\n    public void resume() {\n        mSocket.resume();\n    }\n\n    @Override\n    public boolean isPaused() {\n        return mSocket.isPaused();\n    }\n\n    @Override\n    public String toString() {\n        if (mRawHeaders == null)\n            return super.toString();\n        return mRawHeaders.toPrefixString(statusLine);\n    }\n\n    @Override\n    public String get(String name) {\n        Multimap query = getQuery();\n        String ret = query.getString(name);\n        if (ret != null)\n            return ret;\n        AsyncHttpRequestBody body = getBody();\n        Object bodyObject = body.get();\n        if (bodyObject instanceof Multimap) {\n            Multimap map = (Multimap)bodyObject;\n            return map.getString(name);\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/server/AsyncHttpServerResponse.java",
    "content": "package com.koushikdutta.async.http.server;\n\nimport com.koushikdutta.async.AsyncSocket;\nimport com.koushikdutta.async.ByteBufferList;\nimport com.koushikdutta.async.DataSink;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.http.AsyncHttpResponse;\nimport com.koushikdutta.async.http.Headers;\nimport com.koushikdutta.async.http.body.AsyncHttpRequestBody;\nimport com.koushikdutta.async.parser.AsyncParser;\n\nimport org.json.JSONArray;\nimport org.json.JSONObject;\n\nimport java.io.File;\nimport java.io.InputStream;\nimport java.nio.ByteBuffer;\n\npublic interface AsyncHttpServerResponse extends DataSink, CompletedCallback {\n    void end();\n    void send(String contentType, byte[] bytes);\n    void send(String contentType, ByteBufferList bb);\n    void send(String contentType, ByteBuffer bb);\n    void send(String contentType, String string);\n    void send(String string);\n    void send(JSONObject json);\n    void send(JSONArray jsonArray);\n    void sendFile(File file);\n    void sendStream(InputStream inputStream, long totalLength);\n    <T> void sendBody(AsyncParser<T> body, T value);\n    AsyncHttpServerResponse code(int code);\n    int code();\n    Headers getHeaders();\n    void writeHead();\n    void setContentType(String contentType);\n    void redirect(String location);\n    AsyncHttpServerRequest getRequest();\n    String getHttpVersion();\n    void setHttpVersion(String httpVersion);\n\n    // NOT FINAL\n    void proxy(AsyncHttpResponse response);\n\n    /**\n     * Alias for end. Used with CompletedEmitters\n     */\n    void onCompleted(Exception ex);\n    AsyncSocket getSocket();\n    void setSocket(AsyncSocket socket);\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/server/AsyncHttpServerResponseImpl.java",
    "content": "package com.koushikdutta.async.http.server;\n\nimport android.text.TextUtils;\n\nimport com.koushikdutta.async.AsyncServer;\nimport com.koushikdutta.async.AsyncSocket;\nimport com.koushikdutta.async.ByteBufferList;\nimport com.koushikdutta.async.DataSink;\nimport com.koushikdutta.async.Util;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.callback.DataCallback;\nimport com.koushikdutta.async.callback.WritableCallback;\nimport com.koushikdutta.async.http.AsyncHttpHead;\nimport com.koushikdutta.async.http.AsyncHttpResponse;\nimport com.koushikdutta.async.http.Headers;\nimport com.koushikdutta.async.http.HttpUtil;\nimport com.koushikdutta.async.http.Protocol;\nimport com.koushikdutta.async.http.filter.ChunkedOutputFilter;\nimport com.koushikdutta.async.parser.AsyncParser;\nimport com.koushikdutta.async.util.StreamUtility;\n\nimport org.json.JSONArray;\nimport org.json.JSONObject;\n\nimport java.io.BufferedInputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.InputStream;\nimport java.io.UnsupportedEncodingException;\nimport java.nio.ByteBuffer;\nimport java.util.Locale;\n\npublic class AsyncHttpServerResponseImpl implements AsyncHttpServerResponse {\n    private Headers mRawHeaders = new Headers();\n    private long mContentLength = -1;\n\n    @Override\n    public Headers getHeaders() {\n        return mRawHeaders;\n    }\n    \n    public AsyncSocket getSocket() {\n        return mSocket;\n    }\n\n    @Override\n    public void setSocket(AsyncSocket socket) {\n        mSocket = socket;\n    }\n\n    AsyncSocket mSocket;\n    AsyncHttpServerRequestImpl mRequest;\n    AsyncHttpServerResponseImpl(AsyncSocket socket, AsyncHttpServerRequestImpl req) {\n        mSocket = socket;\n        mRequest = req;\n        if (HttpUtil.isKeepAlive(Protocol.HTTP_1_1, req.getHeaders()))\n            mRawHeaders.set(\"Connection\", \"Keep-Alive\");\n    }\n\n    @Override\n    public AsyncHttpServerRequest getRequest() {\n        return mRequest;\n    }\n\n    @Override\n    public void write(ByteBufferList bb) {\n        // order is important here...\n        // do the header write... this will call onWritable, which may be reentrant\n        if (!headWritten)\n            initFirstWrite();\n\n        // now check to see if the list is empty. reentrancy may cause it to empty itself.\n        if (bb.remaining() == 0)\n            return;\n\n        // null sink means that the header has not finished writing\n        if (mSink == null)\n            return;\n\n        // can successfully write!\n        mSink.write(bb);\n    }\n\n    boolean headWritten = false;\n    DataSink mSink;\n    void initFirstWrite() {\n        if (headWritten)\n            return;\n\n        headWritten = true;\n\n        final boolean isChunked;\n        String currentEncoding = mRawHeaders.get(\"Transfer-Encoding\");\n        if (\"\".equals(currentEncoding))\n            mRawHeaders.removeAll(\"Transfer-Encoding\");\n        boolean canUseChunked = (\"Chunked\".equalsIgnoreCase(currentEncoding) || currentEncoding == null)\n        && !\"close\".equalsIgnoreCase(mRawHeaders.get(\"Connection\"));\n        if (mContentLength < 0) {\n            String contentLength = mRawHeaders.get(\"Content-Length\");\n            if (!TextUtils.isEmpty(contentLength))\n                mContentLength = Long.valueOf(contentLength);\n        }\n        if (mContentLength < 0 && canUseChunked) {\n            mRawHeaders.set(\"Transfer-Encoding\", \"Chunked\");\n            isChunked = true;\n        }\n        else {\n            isChunked = false;\n        }\n\n        String statusLine = String.format(Locale.ENGLISH, \"%s %s %s\", httpVersion, code, AsyncHttpServer.getResponseCodeDescription(code));\n        String rh = mRawHeaders.toPrefixString(statusLine);\n\n        Util.writeAll(mSocket, rh.getBytes(), ex -> {\n            if (ex != null) {\n                report(ex);\n                return;\n            }\n            if (isChunked) {\n                ChunkedOutputFilter chunked = new ChunkedOutputFilter(mSocket);\n                chunked.setMaxBuffer(0);\n                mSink = chunked;\n            }\n            else {\n                mSink = mSocket;\n            }\n\n            mSink.setClosedCallback(closedCallback);\n            closedCallback = null;\n            mSink.setWriteableCallback(writable);\n            writable = null;\n            if (ended) {\n                // the response ended while headers were written\n                end();\n                return;\n            }\n            getServer().post(() -> {\n                WritableCallback wb = getWriteableCallback();\n                if (wb != null)\n                    wb.onWriteable();\n            });\n        });\n    }\n\n    WritableCallback writable;\n    @Override\n    public void setWriteableCallback(WritableCallback handler) {\n        if (mSink != null)\n            mSink.setWriteableCallback(handler);\n        else\n            writable = handler;\n    }\n\n    @Override\n    public WritableCallback getWriteableCallback() {\n        if (mSink != null)\n            return mSink.getWriteableCallback();\n        return writable;\n    }\n\n    boolean ended;\n    @Override\n    public void end() {\n        if (ended)\n            return;\n        ended = true;\n        if (headWritten && mSink == null) {\n            // header is in the process of being written... bail out.\n            // end will be called again after finished.\n            return;\n        }\n        if (!headWritten) {\n            // end was called, and no head or body was yet written,\n            // so strip the transfer encoding as that is superfluous.\n            mRawHeaders.remove(\"Transfer-Encoding\");\n        }\n        if (mSink instanceof ChunkedOutputFilter) {\n            // this filter won't close the socket underneath.\n            mSink.end();\n        }\n        else if (!headWritten) {\n            if (!mRequest.getMethod().equalsIgnoreCase(AsyncHttpHead.METHOD))\n                send(\"text/html\", \"\");\n            else {\n                writeHead();\n                onEnd();\n            }\n        }\n        else {\n            onEnd();\n        }\n    }\n\n    @Override\n    public void writeHead() {\n        initFirstWrite();\n    }\n\n    @Override\n    public void setContentType(String contentType) {\n        mRawHeaders.set(\"Content-Type\", contentType);\n    }\n\n    @Override\n    public void send(final String contentType, final byte[] bytes) {\n        send(contentType, new ByteBufferList(bytes));\n    }\n\n    @Override\n    public <T> void sendBody(AsyncParser<T> body, T value) {\n        mRawHeaders.set(\"Content-Type\", body.getMime());\n        body.write(this, value, ex -> end());\n    }\n\n    @Override\n    public void send(String contentType, ByteBuffer bb) {\n        send(contentType, new ByteBufferList(bb));\n    }\n\n    @Override\n    public void send(String contentType, ByteBufferList bb) {\n        getServer().post(() -> {\n            mContentLength = bb.remaining();\n            mRawHeaders.set(\"Content-Length\", Long.toString(mContentLength));\n            if (contentType != null)\n                mRawHeaders.set(\"Content-Type\", contentType);\n\n            Util.writeAll(AsyncHttpServerResponseImpl.this, bb, ex -> onEnd());\n        });\n    }\n\n    @Override\n    public void send(String contentType, final String string) {\n        try {\n            send(contentType, string.getBytes(\"UTF-8\"));\n        }\n        catch (UnsupportedEncodingException e) {\n            throw new AssertionError(e);\n        }\n    }\n    \n    boolean mEnded;\n    protected void onEnd() {\n        mEnded = true;\n    }\n    \n    protected void report(Exception e) {\n    }\n\n\n    @Override\n    public void send(String string) {\n        String contentType = mRawHeaders.get(\"Content-Type\");\n        if (contentType == null)\n            contentType = \"text/html; charset=utf-8\";\n        send(contentType, string);\n    }\n\n    @Override\n    public void send(JSONObject json) {\n        send(\"application/json; charset=utf-8\", json.toString());\n    }\n\n    @Override\n    public void send(JSONArray jsonArray) {\n        send(\"application/json; charset=utf-8\", jsonArray.toString());\n    }\n\n    @Override\n    public void sendStream(final InputStream inputStream, long totalLength) {\n        long start = 0;\n        long end = totalLength - 1;\n\n        String range = mRequest.getHeaders().get(\"Range\");\n        if (range != null) {\n            String[] parts = range.split(\"=\");\n            if (parts.length != 2 || !\"bytes\".equals(parts[0])) {\n                // Requested range not satisfiable\n                code(416);\n                end();\n                return;\n            }\n\n            parts = parts[1].split(\"-\");\n            try {\n                if (parts.length > 2)\n                    throw new MalformedRangeException();\n                if (!TextUtils.isEmpty(parts[0]))\n                    start = Long.parseLong(parts[0]);\n                if (parts.length == 2 && !TextUtils.isEmpty(parts[1]))\n                    end = Long.parseLong(parts[1]);\n                else\n                    end = totalLength - 1;\n\n                code(206);\n                getHeaders().set(\"Content-Range\", String.format(Locale.ENGLISH, \"bytes %d-%d/%d\", start, end, totalLength));\n            }\n            catch (Exception e) {\n                code(416);\n                end();\n                return;\n            }\n        }\n        try {\n            if (start != inputStream.skip(start))\n                throw new StreamSkipException(\"skip failed to skip requested amount\");\n            mContentLength = end - start + 1;\n            mRawHeaders.set(\"Content-Length\", String.valueOf(mContentLength));\n            mRawHeaders.set(\"Accept-Ranges\", \"bytes\");\n            if (mRequest.getMethod().equals(AsyncHttpHead.METHOD)) {\n                writeHead();\n                onEnd();\n                return;\n            }\n            if (mContentLength == 0) {\n                writeHead();\n                StreamUtility.closeQuietly(inputStream);\n                onEnd();\n                return;\n            }\n            getServer().post(() ->\n                    Util.pump(inputStream, mContentLength, AsyncHttpServerResponseImpl.this,\n                            ex -> {\n                                StreamUtility.closeQuietly(inputStream);\n                                onEnd();\n                            }));\n        }\n        catch (Exception e) {\n            code(500);\n            end();\n        }\n    }\n\n    @Override\n    public void sendFile(File file) {\n        try {\n            if (mRawHeaders.get(\"Content-Type\") == null)\n                mRawHeaders.set(\"Content-Type\", AsyncHttpServer.getContentType(file.getAbsolutePath()));\n            FileInputStream fin = new FileInputStream(file);\n            sendStream(new BufferedInputStream(fin, 64000), file.length());\n        }\n        catch (FileNotFoundException e) {\n            code(404);\n            end();\n        }\n    }\n\n    @Override\n    public void proxy(final AsyncHttpResponse remoteResponse) {\n        code(remoteResponse.code());\n        remoteResponse.headers().removeAll(\"Transfer-Encoding\");\n        remoteResponse.headers().removeAll(\"Content-Encoding\");\n        remoteResponse.headers().removeAll(\"Connection\");\n        getHeaders().addAll(remoteResponse.headers());\n        // TODO: remove?\n        remoteResponse.headers().set(\"Connection\", \"close\");\n        Util.pump(remoteResponse, this, ex -> {\n            remoteResponse.setEndCallback(new NullCompletedCallback());\n            remoteResponse.setDataCallback(new DataCallback.NullDataCallback());\n            end();\n        });\n    }\n\n    int code = 200;\n    @Override\n    public AsyncHttpServerResponse code(int code) {\n        this.code = code;\n        return this;\n    }\n\n    @Override\n    public int code() {\n        return code;\n    }\n\n    @Override\n    public void redirect(String location) {\n        code(302);\n        mRawHeaders.set(\"Location\", location);\n        end();\n    }\n\n    String httpVersion = \"HTTP/1.1\";\n    @Override\n    public String getHttpVersion() {\n        return httpVersion;\n    }\n\n    @Override\n    public void setHttpVersion(String httpVersion) {\n        this.httpVersion = httpVersion;\n    }\n\n    @Override\n    public void onCompleted(Exception ex) {\n        end();\n    }\n\n    @Override\n    public boolean isOpen() {\n        if (mSink != null)\n            return mSink.isOpen();\n        return mSocket.isOpen();\n    }\n\n    CompletedCallback closedCallback;\n    @Override\n    public void setClosedCallback(CompletedCallback handler) {\n        if (mSink != null)\n            mSink.setClosedCallback(handler);\n        else\n            closedCallback = handler;\n    }\n\n    @Override\n    public CompletedCallback getClosedCallback() {\n        if (mSink != null)\n            return mSink.getClosedCallback();\n        return closedCallback;\n    }\n\n    @Override\n    public AsyncServer getServer() {\n        return mSocket.getServer();\n    }\n\n    @Override\n    public String toString() {\n        if (mRawHeaders == null)\n            return super.toString();\n        String statusLine = String.format(Locale.ENGLISH, \"%s %s %s\", httpVersion, code, AsyncHttpServer.getResponseCodeDescription(code));\n        return mRawHeaders.toPrefixString(statusLine);\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/server/AsyncHttpServerRouter.java",
    "content": "package com.koushikdutta.async.http.server;\n\nimport android.content.Context;\nimport android.content.res.AssetManager;\nimport android.text.TextUtils;\nimport android.util.Log;\n\nimport com.koushikdutta.async.Util;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.future.Future;\nimport com.koushikdutta.async.future.SimpleFuture;\nimport com.koushikdutta.async.http.AsyncHttpGet;\nimport com.koushikdutta.async.http.AsyncHttpHead;\nimport com.koushikdutta.async.http.AsyncHttpPost;\nimport com.koushikdutta.async.http.WebSocket;\nimport com.koushikdutta.async.http.WebSocketImpl;\nimport com.koushikdutta.async.util.StreamUtility;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.Hashtable;\nimport java.util.jar.Manifest;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipFile;\n\npublic class AsyncHttpServerRouter implements RouteMatcher {\n\n    private static class RouteInfo {\n        String method;\n        Pattern regex;\n        HttpServerRequestCallback callback;\n        AsyncHttpRequestBodyProvider bodyCallback;\n    }\n\n    final ArrayList<RouteInfo> routes = new ArrayList<>();\n\n    public void removeAction(String action, String regex) {\n        for (int i = 0; i < routes.size(); i++) {\n            RouteInfo p = routes.get(i);\n            if (TextUtils.equals(p.method, action) && regex.equals(p.regex.toString())) {\n                routes.remove(i);\n                return;\n            }\n        }\n    }\n\n    public void addAction(String action, String regex, HttpServerRequestCallback callback, AsyncHttpRequestBodyProvider bodyCallback) {\n        RouteInfo p = new RouteInfo();\n        p.regex = Pattern.compile(\"^\" + regex);\n        p.callback = callback;\n        p.method = action;\n        p.bodyCallback = bodyCallback;\n\n        synchronized (routes) {\n            routes.add(p);\n        }\n    }\n\n    public void addAction(String action, String regex, HttpServerRequestCallback callback) {\n        addAction(action, regex, callback, null);\n    }\n\n    public void websocket(String regex, final AsyncHttpServer.WebSocketRequestCallback callback) {\n        websocket(regex, null, callback);\n    }\n\n    static public WebSocket checkWebSocketUpgrade(final String protocol, AsyncHttpServerRequest request, final AsyncHttpServerResponse response) {\n        boolean hasUpgrade = false;\n        String connection = request.getHeaders().get(\"Connection\");\n        if (connection != null) {\n            String[] connections = connection.split(\",\");\n            for (String c: connections) {\n                if (\"Upgrade\".equalsIgnoreCase(c.trim())) {\n                    hasUpgrade = true;\n                    break;\n                }\n            }\n        }\n        if (!\"websocket\".equalsIgnoreCase(request.getHeaders().get(\"Upgrade\")) || !hasUpgrade) {\n            return null;\n        }\n        String peerProtocol = request.getHeaders().get(\"Sec-WebSocket-Protocol\");\n        if (!TextUtils.equals(protocol, peerProtocol)) {\n            return null;\n        }\n        return new WebSocketImpl(request, response);\n    }\n\n    public void websocket(String regex, final String protocol, final AsyncHttpServer.WebSocketRequestCallback callback) {\n        get(regex, (request, response) -> {\n            WebSocket webSocket = checkWebSocketUpgrade(protocol, request, response);\n            if (webSocket == null) {\n                response.code(404);\n                response.end();\n                return;\n            }\n\n            callback.onConnected(webSocket, request);\n        });\n    }\n\n    public void get(String regex, HttpServerRequestCallback callback) {\n        addAction(AsyncHttpGet.METHOD, regex, callback);\n    }\n\n    public void post(String regex, HttpServerRequestCallback callback) {\n        addAction(AsyncHttpPost.METHOD, regex, callback);\n    }\n\n    public static class Asset {\n        public Asset(int available, InputStream inputStream, String path) {\n            this.available = available;\n            this.inputStream = inputStream;\n            this.path = path;\n        }\n\n        public int available;\n        public InputStream inputStream;\n        public String path;\n    }\n\n    public static Asset getAssetStream(final Context context, String asset) {\n        return getAssetStream(context.getAssets(), asset);\n    }\n\n    public static Asset getAssetStream(AssetManager am, String asset) {\n        try {\n            InputStream is = am.open(asset);\n            return new Asset(is.available(), is, asset);\n        }\n        catch (IOException e) {\n            final String[] extensions = new String[] { \"/index.htm\", \"/index.html\", \"index.htm\", \"index.html\", \".htm\", \".html\" };\n            for (String ext: extensions) {\n                try {\n                    InputStream is = am.open(asset + ext);\n                    return new Asset(is.available(), is, asset + ext);\n                }\n                catch (IOException ex) {\n                }\n            }\n            return null;\n        }\n    }\n\n    static Hashtable<String, String> mContentTypes = new Hashtable<>();\n\n    static\n    {\n        mContentTypes.put(\"js\", \"application/javascript\");\n        mContentTypes.put(\"json\", \"application/json\");\n        mContentTypes.put(\"png\", \"image/png\");\n        mContentTypes.put(\"jpg\", \"image/jpeg\");\n        mContentTypes.put(\"jpeg\", \"image/jpeg\");\n        mContentTypes.put(\"html\", \"text/html\");\n        mContentTypes.put(\"css\", \"text/css\");\n        mContentTypes.put(\"mp4\", \"video/mp4\");\n        mContentTypes.put(\"mov\", \"video/quicktime\");\n        mContentTypes.put(\"wmv\", \"video/x-ms-wmv\");\n        mContentTypes.put(\"txt\", \"text/plain\");\n    }\n\n    public static String getContentType(String path) {\n        int index = path.lastIndexOf(\".\");\n        if (index != -1) {\n            String e = path.substring(index + 1);\n            String ct = mContentTypes.get(e);\n            if (ct != null)\n                return ct;\n        }\n        return null;\n    }\n\n    static Hashtable<String, Future<Manifest>> AppManifests = new Hashtable<>();\n    static synchronized Manifest ensureManifest(Context context) {\n        Future<Manifest> future = AppManifests.get(context.getPackageName());\n        if (future != null)\n            return future.tryGet();\n\n        ZipFile zip = null;\n        SimpleFuture<Manifest> result = new SimpleFuture<>();\n        try {\n            zip = new ZipFile(context.getPackageResourcePath());\n            ZipEntry entry = zip.getEntry(\"META-INF/MANIFEST.MF\");\n            Manifest manifest = new Manifest(zip.getInputStream(entry));\n            result.setComplete(manifest);\n            return manifest;\n        }\n        catch (Exception e) {\n            result.setComplete(e);\n            return null;\n        }\n        finally {\n            StreamUtility.closeQuietly(zip);\n            AppManifests.put(context.getPackageName(), result);\n        }\n    }\n\n    static boolean isClientCached(Context context, AsyncHttpServerRequest request, AsyncHttpServerResponse response, String assetFileName) {\n        Manifest manifest = ensureManifest(context);\n        if (manifest == null)\n            return false;\n\n        try {\n            String digest = manifest.getEntries().get(\"assets/\" + assetFileName).getValue(\"SHA-256-Digest\");\n            if (TextUtils.isEmpty(digest))\n                return false;\n\n            String etag = String.format(\"\\\"%s\\\"\", digest);\n            response.getHeaders().set(\"ETag\", etag);\n            String ifNoneMatch = request.getHeaders().get(\"If-None-Match\");\n            return TextUtils.equals(ifNoneMatch, etag);\n        }\n        catch (Exception e) {\n            Log.w(AsyncHttpServerRouter.class.getSimpleName(), \"Error getting ETag for apk asset\", e);\n            return false;\n        }\n    }\n\n    public void directory(Context context, String regex, final String assetPath) {\n        AssetManager am = context.getAssets();\n        addAction(AsyncHttpGet.METHOD, regex, (request, response) -> {\n            String path = request.getMatcher().replaceAll(\"\");\n            Asset pair = getAssetStream(am, assetPath + path);\n            if (pair == null || pair.inputStream == null) {\n                response.code(404);\n                response.end();\n                return;\n            }\n\n            if (isClientCached(context, request, response, pair.path)) {\n                StreamUtility.closeQuietly(pair.inputStream);\n                response.code(304);\n                response.end();\n                return;\n            }\n\n            response.getHeaders().set(\"Content-Length\", String.valueOf(pair.available));\n            response.getHeaders().add(\"Content-Type\", getContentType(pair.path));\n\n            response.code(200);\n            Util.pump(pair.inputStream, pair.available, response, ex -> {\n                response.end();\n                StreamUtility.closeQuietly(pair.inputStream);\n            });\n        });\n        addAction(AsyncHttpHead.METHOD, regex, (request, response) -> {\n            String path = request.getMatcher().replaceAll(\"\");\n            Asset pair = getAssetStream(am, assetPath + path);\n            if (pair == null || pair.inputStream == null) {\n                response.code(404);\n                response.end();\n                return;\n            }\n            StreamUtility.closeQuietly(pair.inputStream);\n\n            if (isClientCached(context, request, response, pair.path)) {\n                response.code(304);\n            }\n            else\n            {\n                response.getHeaders().set(\"Content-Length\", String.valueOf(pair.available));\n                response.getHeaders().add(\"Content-Type\", getContentType(pair.path));\n                response.code(200);\n            }\n\n            response.end();\n        });\n    }\n\n    public void directory(String regex, final File directory) {\n        directory(regex, directory, false);\n    }\n\n    public void directory(String regex, final File directory, final boolean list) {\n        addAction(AsyncHttpGet.METHOD, regex, new HttpServerRequestCallback() {\n            @Override\n            public void onRequest(AsyncHttpServerRequest request, final AsyncHttpServerResponse response) {\n                String path = request.getMatcher().replaceAll(\"\");\n                File file = new File(directory, path);\n\n                if (file.isDirectory() && list) {\n                    ArrayList<File> dirs = new ArrayList<File>();\n                    ArrayList<File> files = new ArrayList<File>();\n                    for (File f: file.listFiles()) {\n                        if (f.isDirectory())\n                            dirs.add(f);\n                        else\n                            files.add(f);\n                    }\n\n                    Comparator<File> c = new Comparator<File>() {\n                        @Override\n                        public int compare(File lhs, File rhs) {\n                            return lhs.getName().compareTo(rhs.getName());\n                        }\n                    };\n\n                    Collections.sort(dirs, c);\n                    Collections.sort(files, c);\n\n                    files.addAll(0, dirs);\n                    StringBuilder builder = new StringBuilder();\n                    for (File f: files) {\n                        String p = new File(request.getPath(), f.getName()).getAbsolutePath();\n                        builder.append(String.format(\"<div><a href='%s'>%s</a></div>\", p, f.getName()));\n                    }\n                    response.send(builder.toString());\n\n                    return;\n                }\n                if (!file.isFile()) {\n                    response.code(404);\n                    response.end();\n                    return;\n                }\n                try {\n                    FileInputStream is = new FileInputStream(file);\n                    response.code(200);\n                    Util.pump(is, is.available(), response, new CompletedCallback() {\n                        @Override\n                        public void onCompleted(Exception ex) {\n                            response.end();\n                        }\n                    });\n                }\n                catch (IOException ex) {\n                    response.code(404);\n                    response.end();\n                }\n            }\n        });\n    }\n\n    public static class RouteMatch {\n        public final String method;\n        public final String path;\n        public final Matcher matcher;\n        public final HttpServerRequestCallback callback;\n        public final AsyncHttpRequestBodyProvider bodyCallback;\n\n        private RouteMatch(String method, String path, Matcher matcher, HttpServerRequestCallback callback, AsyncHttpRequestBodyProvider bodyCallback) {\n            this.method = method;\n            this.path = path;\n            this.matcher = matcher;\n            this.callback = callback;\n            this.bodyCallback = bodyCallback;\n        }\n    }\n\n    abstract class AsyncHttpServerRequestImpl extends com.koushikdutta.async.http.server.AsyncHttpServerRequestImpl {\n        Matcher matcher;\n        @Override\n        public Matcher getMatcher() {\n            return matcher;\n        }\n\n        @Override\n        public void setMatcher(Matcher matcher) {\n            this.matcher = matcher;\n        }\n    }\n\n    class Callback implements HttpServerRequestCallback, RouteMatcher {\n        @Override\n        public void onRequest(AsyncHttpServerRequest request, AsyncHttpServerResponse response) {\n            RouteMatch match = route(request.getMethod(), request.getPath());\n            if (match == null) {\n                response.code(404);\n                response.end();\n                return;\n            }\n\n            match.callback.onRequest(request, response);\n        }\n\n        @Override\n        public RouteMatch route(String method, String path) {\n            return AsyncHttpServerRouter.this.route(method, path);\n        }\n    }\n\n    private Callback callback = new Callback();\n\n    public HttpServerRequestCallback getCallback() {\n        return callback;\n    }\n\n    @Override\n    public RouteMatch route(String method, String path) {\n        synchronized (routes) {\n            for (RouteInfo p: routes) {\n                // a null method is wildcard. used for nesting routers.\n                if (!TextUtils.equals(method, p.method) && p.method != null)\n                    continue;\n                Matcher m = p.regex.matcher(path);\n                if (m.matches()) {\n                    if (p.callback instanceof RouteMatcher) {\n                        String subPath = m.group(1);\n                        return ((RouteMatcher)p.callback).route(method, subPath);\n                    }\n                    return new RouteMatch(method, path, m, p.callback, p.bodyCallback);\n                }\n            }\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/server/AsyncProxyServer.java",
    "content": "package com.koushikdutta.async.http.server;\n\nimport android.net.Uri;\n\nimport com.koushikdutta.async.AsyncServer;\nimport com.koushikdutta.async.Util;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.http.AsyncHttpClient;\nimport com.koushikdutta.async.http.AsyncHttpRequest;\nimport com.koushikdutta.async.http.AsyncHttpResponse;\nimport com.koushikdutta.async.http.callback.HttpConnectCallback;\n\n/**\n * Created by koush on 7/22/14.\n */\npublic class AsyncProxyServer extends AsyncHttpServer {\n    AsyncHttpClient proxyClient;\n    public AsyncProxyServer(AsyncServer server) {\n        proxyClient = new AsyncHttpClient(server);\n    }\n\n    @Override\n    protected void onRequest(HttpServerRequestCallback callback, AsyncHttpServerRequest request, final AsyncHttpServerResponse response) {\n        super.onRequest(callback, request, response);\n\n        if (callback != null)\n            return;\n\n        try {\n            Uri uri;\n\n            try {\n                uri = Uri.parse(request.getPath());\n                if (uri.getScheme() == null)\n                    throw new Exception(\"no host or full uri provided\");\n            }\n            catch (Exception e) {\n                String host = request.getHeaders().get(\"Host\");\n                int port = 80;\n                if (host != null) {\n                    String[] splits = host.split(\":\", 2);\n                    if (splits.length == 2) {\n                        host = splits[0];\n                        port = Integer.parseInt(splits[1]);\n                    }\n                }\n                uri = Uri.parse(\"http://\" + host + \":\" + port + request.getPath());\n            }\n\n            proxyClient.execute(new AsyncHttpRequest(uri, request.getMethod(), request.getHeaders()), new HttpConnectCallback() {\n                @Override\n                public void onConnectCompleted(Exception ex, AsyncHttpResponse remoteResponse) {\n                    if (ex != null) {\n                        response.code(500);\n                        response.send(ex.getMessage());\n                        return;\n                    }\n                    response.proxy(remoteResponse);\n                }\n            });\n        }\n        catch (Exception e) {\n            response.code(500);\n            response.send(e.getMessage());\n        }\n    }\n\n    @Override\n    protected boolean onRequest(AsyncHttpServerRequest request, AsyncHttpServerResponse response) {\n        return true;\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/server/BoundaryEmitter.java",
    "content": "package com.koushikdutta.async.http.server;\n\nimport com.koushikdutta.async.ByteBufferList;\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.FilteredDataEmitter;\n\nimport java.nio.ByteBuffer;\n\npublic class BoundaryEmitter extends FilteredDataEmitter {\n    private byte[] boundary;\n    public void setBoundary(String boundary) {\n        this.boundary = (\"\\r\\n--\" + boundary).getBytes();\n    }\n    \n    public String getBoundary() {\n        if (boundary == null)\n            return null;\n        return new String(boundary, 4, boundary.length - 4);\n    }\n    \n    public String getBoundaryStart() {\n        return new String(boundary, 2, boundary.length - 2);\n    }\n    \n    public String getBoundaryEnd() {\n        return getBoundaryStart() + \"--\\r\\n\";\n    }\n    \n    protected void onBoundaryStart() {\n    }\n    \n    protected void onBoundaryEnd() {\n    }\n    \n    // >= 0 matching\n    // -1 matching - (start of boundary end) or \\r (boundary start)\n    // -2 matching - (end of boundary end)\n    // -3 matching \\r after boundary\n    // -4 matching \\n after boundary\n\n    // the state starts out having already matched \\r\\n\n\n    /*\n    Mac:work$ curl -F person=anonymous -F secret=@test.kt  http://localhost:5555\n\n    POST / HTTP/1.1\n    Host: localhost:5555\n    User-Agent: curl/7.54.0\n    Content-Length: 372\n    Expect: 100-continue\n    Content-Type: multipart/form-data; boundary=------------------------17903558439eb6ff\n\n--------------------------17903558439eb6ff              <--- note! two dashes before boundary\n    Content-Disposition: form-data; name=\"person\"\n\n    anonymous\n--------------------------17903558439eb6ff              <--- note! two dashes before boundary\n    Content-Disposition: form-data; name=\"secret\"; filename=\"test.kt\"\n    Content-Type: application/octet-stream\n\n    fun main(args: Array<String>) {\n        println(\"Hello JavaScript!\")\n    }\n\n--------------------------17903558439eb6ff--            <--- note! two dashes before AND after boundary\n            */\n    \n    \n    int state = 2;\n    @Override\n    public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n//        System.out.println(bb.getString());\n//        System.out.println(\"chunk: \" + bb.remaining());\n        \n//        System.out.println(\"state: \" + state);\n        \n        // if we were in the middle of a potential match, let's throw that\n        // at the beginning of the buffer and process it too.\n        if (state > 0) {\n            ByteBuffer b = ByteBufferList.obtain(boundary.length);\n            b.put(boundary, 0, state);\n            b.flip();\n            bb.addFirst(b);\n            state = 0;\n        }\n        \n        int last = 0;\n        byte[] buf = new byte[bb.remaining()];\n        bb.get(buf);\n        for (int i = 0; i < buf.length; i++) {\n            if (state >= 0) {\n                if (buf[i] == boundary[state]) {\n                    state++;\n                    if (state == boundary.length)\n                        state = -1;\n                }\n                else if (state > 0) {\n                    // let's try matching again one byte after the start\n                    // of last match occurrence\n                    i -= state;\n                    state = 0;\n                }\n            }\n            else if (state == -1) {\n                if (buf[i] == '\\r') {\n                    state = -4;\n                    int len = i - last - boundary.length;\n                    if (last != 0 || len != 0) {\n                        ByteBuffer b = ByteBufferList.obtain(len).put(buf, last, len);\n                        b.flip();\n                        ByteBufferList list = new ByteBufferList();\n                        list.add(b);\n                        super.onDataAvailable(this, list);\n                    }\n//                    System.out.println(\"bstart\");\n                    onBoundaryStart();\n                }\n                else if (buf[i] == '-') {\n                    state = -2;\n                }\n                else {\n                    report(new MimeEncodingException(\"Invalid multipart/form-data. Expected \\r or -\"));\n                    return;\n                }\n            }\n            else if (state == -2) {\n                if (buf[i] == '-') {\n                    state = -3;\n                }\n                else {\n                    report(new MimeEncodingException(\"Invalid multipart/form-data. Expected -\"));\n                    return;\n                }\n            }\n            else if (state == -3) {\n                if (buf[i] == '\\r') {\n                    state = -4;\n                    ByteBuffer b = ByteBufferList.obtain(i - last - boundary.length - 2).put(buf, last, i - last - boundary.length - 2);\n                    b.flip();\n                    ByteBufferList list = new ByteBufferList();\n                    list.add(b);\n                    super.onDataAvailable(this, list);\n//                    System.out.println(\"bend\");\n                    onBoundaryEnd();\n                }\n                else {\n                    report(new MimeEncodingException(\"Invalid multipart/form-data. Expected \\r\"));\n                    return;\n                }\n            }\n            else if (state == -4) {\n                if (buf[i] == '\\n') {\n                    last = i + 1;\n                    state = 0;\n                }\n                else {\n                    report(new MimeEncodingException(\"Invalid multipart/form-data. Expected \\n\"));\n                }\n            }\n            else {\n                report(new MimeEncodingException(\"Invalid multipart/form-data. Unknown state?\"));\n            }\n        }\n\n        if (last < buf.length) {\n//            System.out.println(\"amount left at boundary: \" + (buf.length - last));\n//            System.out.println(\"State: \" + state);\n//            System.out.println(state);\n            int keep = Math.max(state, 0);\n            ByteBuffer b = ByteBufferList.obtain(buf.length - last - keep).put(buf, last, buf.length - last - keep);\n            b.flip();\n            ByteBufferList list = new ByteBufferList();\n            list.add(b);\n            super.onDataAvailable(this, list);\n        }\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/server/HttpServerRequestCallback.java",
    "content": "package com.koushikdutta.async.http.server;\n\n\npublic interface HttpServerRequestCallback {\n    public void onRequest(AsyncHttpServerRequest request, AsyncHttpServerResponse response);\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/server/MalformedRangeException.java",
    "content": "package com.koushikdutta.async.http.server;\n\npublic class MalformedRangeException extends Exception {\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/server/MimeEncodingException.java",
    "content": "package com.koushikdutta.async.http.server;\n\npublic class MimeEncodingException extends Exception {\n    public MimeEncodingException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/server/RouteMatcher.java",
    "content": "package com.koushikdutta.async.http.server;\n\npublic interface RouteMatcher {\n    AsyncHttpServerRouter.RouteMatch route(String method, String path);\n}"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/server/StreamSkipException.java",
    "content": "package com.koushikdutta.async.http.server;\n\npublic class StreamSkipException extends Exception {\n    public StreamSkipException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/http/server/UnknownRequestBody.java",
    "content": "package com.koushikdutta.async.http.server;\n\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.DataSink;\nimport com.koushikdutta.async.Util;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.callback.DataCallback;\nimport com.koushikdutta.async.http.AsyncHttpRequest;\nimport com.koushikdutta.async.http.body.AsyncHttpRequestBody;\n\npublic class UnknownRequestBody implements AsyncHttpRequestBody<Void> {\n    public UnknownRequestBody(String contentType) {\n        mContentType = contentType;\n    }\n\n    int length = -1;\n    public UnknownRequestBody(DataEmitter emitter, String contentType, int length) {\n        mContentType = contentType;\n        this.emitter = emitter;\n        this.length = length;\n    }\n\n    @Override\n    public void write(final AsyncHttpRequest request, DataSink sink, final CompletedCallback completed) {\n        Util.pump(emitter, sink, completed);\n        if (emitter.isPaused())\n            emitter.resume();\n    }\n\n    private String mContentType;\n    @Override\n    public String getContentType() {\n        return mContentType;\n    }\n\n    @Override\n    public boolean readFullyOnRequest() {\n        return false;\n    }\n\n    @Override\n    public int length() {\n        return length;\n    }\n\n    @Override\n    public Void get() {\n        return null;\n    }\n\n    @Deprecated\n    public void setCallbacks(DataCallback callback, CompletedCallback endCallback) {\n        emitter.setEndCallback(endCallback);\n        emitter.setDataCallback(callback);\n    }\n\n    public DataEmitter getEmitter() {\n        return emitter;\n    }\n\n    DataEmitter emitter;\n    @Override\n    public void parse(DataEmitter emitter, CompletedCallback completed) {\n        this.emitter = emitter;\n        emitter.setEndCallback(completed);\n        emitter.setDataCallback(new DataCallback.NullDataCallback());\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/parser/AsyncParser.java",
    "content": "package com.koushikdutta.async.parser;\n\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.DataSink;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.future.Future;\n\nimport java.lang.reflect.Type;\n\n/**\n * Created by koush on 5/27/13.\n */\npublic interface AsyncParser<T> {\n    Future<T> parse(DataEmitter emitter);\n    void write(DataSink sink, T value, CompletedCallback completed);\n    Type getType();\n    String getMime();\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/parser/ByteBufferListParser.java",
    "content": "package com.koushikdutta.async.parser;\n\nimport com.koushikdutta.async.ByteBufferList;\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.DataSink;\nimport com.koushikdutta.async.Util;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.callback.DataCallback;\nimport com.koushikdutta.async.future.Future;\nimport com.koushikdutta.async.future.SimpleFuture;\n\nimport java.lang.reflect.Type;\n\n/**\n * Created by koush on 5/27/13.\n */\npublic class ByteBufferListParser implements AsyncParser<ByteBufferList> {\n    @Override\n    public Future<ByteBufferList> parse(final DataEmitter emitter) {\n        final ByteBufferList bb = new ByteBufferList();\n        final SimpleFuture<ByteBufferList> ret = new SimpleFuture<ByteBufferList>() {\n            @Override\n            protected void cancelCleanup() {\n                emitter.close();\n            }\n        };\n        emitter.setDataCallback(new DataCallback() {\n            @Override\n            public void onDataAvailable(DataEmitter emitter, ByteBufferList data) {\n                data.get(bb);\n            }\n        });\n\n        emitter.setEndCallback(new CompletedCallback() {\n            @Override\n            public void onCompleted(Exception ex) {\n                if (ex != null) {\n                    ret.setComplete(ex);\n                    return;\n                }\n\n                try {\n                    ret.setComplete(bb);\n                }\n                catch (Exception e) {\n                    ret.setComplete(e);\n                }\n            }\n        });\n\n        return ret;\n    }\n\n    @Override\n    public void write(DataSink sink, ByteBufferList value, CompletedCallback completed) {\n        Util.writeAll(sink, value, completed);\n    }\n\n    @Override\n    public Type getType() {\n        return ByteBufferList.class;\n    }\n\n    @Override\n    public String getMime() {\n        return null;\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/parser/DocumentParser.java",
    "content": "package com.koushikdutta.async.parser;\n\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.DataSink;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.future.Future;\nimport com.koushikdutta.async.http.body.DocumentBody;\nimport com.koushikdutta.async.stream.ByteBufferListInputStream;\n\nimport org.w3c.dom.Document;\n\nimport java.lang.reflect.Type;\n\nimport javax.xml.parsers.DocumentBuilderFactory;\n\n/**\n * Created by koush on 8/3/13.\n */\npublic class DocumentParser implements AsyncParser<Document> {\n    @Override\n    public Future<Document> parse(DataEmitter emitter) {\n        return new ByteBufferListParser().parse(emitter)\n        .thenConvert(from -> DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new ByteBufferListInputStream(from)));\n    }\n\n    @Override\n    public void write(DataSink sink, Document value, CompletedCallback completed) {\n        new DocumentBody(value).write(null, sink, completed);\n    }\n\n    @Override\n    public Type getType() {\n        return Document.class;\n    }\n\n    @Override\n    public String getMime() {\n        return \"text/xml\";\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/parser/JSONArrayParser.java",
    "content": "package com.koushikdutta.async.parser;\n\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.DataSink;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.future.Future;\n\nimport org.json.JSONArray;\n\nimport java.lang.reflect.Type;\n\n/**\n * Created by koush on 5/27/13.\n */\npublic class JSONArrayParser implements AsyncParser<JSONArray> {\n    @Override\n    public Future<JSONArray> parse(DataEmitter emitter) {\n        return new StringParser().parse(emitter)\n        .thenConvert(JSONArray::new);\n    }\n\n    @Override\n    public void write(DataSink sink, JSONArray value, CompletedCallback completed) {\n        new StringParser().write(sink, value.toString(), completed);\n    }\n\n    @Override\n    public Type getType() {\n        return JSONArray.class;\n    }\n\n    @Override\n    public String getMime() {\n        return \"application/json\";\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/parser/JSONObjectParser.java",
    "content": "package com.koushikdutta.async.parser;\n\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.DataSink;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.future.Future;\n\nimport org.json.JSONObject;\n\nimport java.lang.reflect.Type;\n\n/**\n * Created by koush on 5/27/13.\n */\npublic class JSONObjectParser implements AsyncParser<JSONObject> {\n    @Override\n    public Future<JSONObject> parse(DataEmitter emitter) {\n        return new StringParser().parse(emitter).thenConvert(JSONObject::new);\n    }\n\n    @Override\n    public void write(DataSink sink, JSONObject value, CompletedCallback completed) {\n        new StringParser().write(sink, value.toString(), completed);\n    }\n\n    @Override\n    public Type getType() {\n        return JSONObject.class;\n    }\n\n    @Override\n    public String getMime() {\n        return \"application/json\";\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/parser/StringParser.java",
    "content": "package com.koushikdutta.async.parser;\n\nimport com.koushikdutta.async.ByteBufferList;\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.DataSink;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.future.Future;\n\nimport java.lang.reflect.Type;\nimport java.nio.charset.Charset;\n\n/**\n * Created by koush on 5/27/13.\n */\npublic class StringParser implements AsyncParser<String> {\n    Charset forcedCharset;\n\n    public StringParser() {\n    }\n\n    public StringParser(Charset charset) {\n        this.forcedCharset = charset;\n    }\n\n    @Override\n    public Future<String> parse(DataEmitter emitter) {\n        final String charset = emitter.charset();\n        return new ByteBufferListParser().parse(emitter)\n        .thenConvert(from -> {\n            Charset charsetToUse = forcedCharset;\n            if (charsetToUse == null && charset != null)\n                charsetToUse = Charset.forName(charset);\n            return from.readString(charsetToUse);\n        });\n    }\n\n    @Override\n    public void write(DataSink sink, String value, CompletedCallback completed) {\n        new ByteBufferListParser().write(sink, new ByteBufferList(value.getBytes()), completed);\n    }\n\n    @Override\n    public Type getType() {\n        return String.class;\n    }\n\n    @Override\n    public String getMime() {\n        return null;\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/stream/ByteBufferListInputStream.java",
    "content": "package com.koushikdutta.async.stream;\n\nimport com.koushikdutta.async.ByteBufferList;\n\nimport java.io.IOException;\nimport java.io.InputStream;\n\n/**\n * Created by koush on 6/1/13.\n */\npublic class ByteBufferListInputStream extends InputStream {\n    ByteBufferList bb;\n    public ByteBufferListInputStream(ByteBufferList bb) {\n        this.bb = bb;\n    }\n\n    @Override\n    public int read() throws IOException {\n        if (bb.remaining() <= 0)\n            return -1;\n        return (int)bb.get() & 0x000000ff;\n    }\n\n    @Override\n    public int read(byte[] buffer) throws IOException {\n        return this.read(buffer, 0, buffer.length);\n    }\n\n    @Override\n    public int read(byte[] buffer, int offset, int length) throws IOException {\n        if (bb.remaining() <= 0)\n            return -1;\n        int toRead = Math.min(length, bb.remaining());\n        bb.get(buffer, offset, toRead);\n        return toRead;\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/stream/FileDataSink.java",
    "content": "package com.koushikdutta.async.stream;\n\nimport com.koushikdutta.async.AsyncServer;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.OutputStream;\n\n/**\n * Created by koush on 2/2/14.\n */\npublic class FileDataSink extends OutputStreamDataSink {\n    File file;\n    public FileDataSink(AsyncServer server, File file) {\n        super(server);\n        this.file = file;\n    }\n\n    @Override\n    public OutputStream getOutputStream() throws IOException {\n        OutputStream ret = super.getOutputStream();\n        if (ret == null) {\n            file.getParentFile().mkdirs();\n            ret = new FileOutputStream(file);\n            setOutputStream(ret);\n        }\n        return ret;\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/stream/InputStreamDataEmitter.java",
    "content": "package com.koushikdutta.async.stream;\n\nimport com.koushikdutta.async.AsyncServer;\nimport com.koushikdutta.async.ByteBufferList;\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.Util;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.callback.DataCallback;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.InputStream;\nimport java.nio.ByteBuffer;\nimport java.nio.channels.FileChannel;\n\n/**\n * Created by koush on 5/22/13.\n */\npublic class InputStreamDataEmitter implements DataEmitter {\n    AsyncServer server;\n    InputStream inputStream;\n    public InputStreamDataEmitter(AsyncServer server, InputStream inputStream) {\n        this.server = server;\n        this.inputStream = inputStream;\n        doResume();\n    }\n\n    DataCallback callback;\n    @Override\n    public void setDataCallback(DataCallback callback) {\n        this.callback = callback;\n    }\n\n    @Override\n    public DataCallback getDataCallback() {\n        return callback;\n    }\n\n    @Override\n    public boolean isChunked() {\n        return false;\n    }\n\n    boolean paused;\n    @Override\n    public void pause() {\n        paused = true;\n    }\n\n    @Override\n    public void resume() {\n        paused = false;\n        doResume();\n    }\n\n    private void report(final Exception e) {\n        getServer().post(new Runnable() {\n            @Override\n            public void run() {\n                Exception ex = e;\n                try {\n                    inputStream.close();\n                }\n                catch (Exception e) {\n                    ex = e;\n                }\n                if (endCallback != null)\n                    endCallback.onCompleted(ex);\n            }\n        });\n    }\n\n    int mToAlloc = 0;\n    ByteBufferList pending = new ByteBufferList();\n    Runnable pumper = new Runnable() {\n        @Override\n        public void run() {\n            try {\n                if (!pending.isEmpty()) {\n                    getServer().run(new Runnable() {\n                        @Override\n                        public void run() {\n                            Util.emitAllData(InputStreamDataEmitter.this, pending);\n                        }\n                    });\n                    if (!pending.isEmpty())\n                        return;\n                }\n                ByteBuffer b;\n                do {\n                    b = ByteBufferList.obtain(Math.min(Math.max(mToAlloc, 2 << 11), 256 * 1024));\n                    int read;\n                    if (-1 == (read = inputStream.read(b.array()))) {\n                        report(null);\n                        return;\n                    }\n                    mToAlloc = read * 2;\n                    b.limit(read);\n                    pending.add(b);\n                    getServer().run(new Runnable() {\n                        @Override\n                        public void run() {\n                            Util.emitAllData(InputStreamDataEmitter.this, pending);\n                        }\n                    });\n                }\n                while (pending.remaining() == 0 && !isPaused());\n            }\n            catch (Exception e) {\n                report(e);\n            }\n        }\n    };\n\n    private void doResume() {\n        new Thread(pumper).start();\n    }\n\n    @Override\n    public boolean isPaused() {\n        return paused;\n    }\n\n    CompletedCallback endCallback;\n    @Override\n    public void setEndCallback(CompletedCallback callback) {\n        endCallback = callback;\n    }\n\n    @Override\n    public CompletedCallback getEndCallback() {\n        return endCallback;\n    }\n\n    @Override\n    public AsyncServer getServer() {\n        return server;\n    }\n\n    @Override\n    public void close() {\n        report(null);\n        try {\n            inputStream.close();\n        }\n        catch (Exception e) {\n        }\n    }\n\n    @Override\n    public String charset() {\n        return null;\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/stream/OutputStreamDataCallback.java",
    "content": "package com.koushikdutta.async.stream;\n\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.nio.ByteBuffer;\n\nimport com.koushikdutta.async.ByteBufferList;\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.callback.DataCallback;\n\npublic class OutputStreamDataCallback implements DataCallback, CompletedCallback {\n    private OutputStream mOutput;\n    public OutputStreamDataCallback(OutputStream os) {\n        mOutput = os;\n    }\n\n    public OutputStream getOutputStream() {\n        return mOutput;\n    }\n\n    @Override\n    public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n        try {\n            while (bb.size() > 0) {\n                ByteBuffer b = bb.remove();\n                mOutput.write(b.array(), b.arrayOffset() + b.position(), b.remaining());\n                ByteBufferList.reclaim(b);\n            }\n        }\n        catch (Exception ex) {\n            onCompleted(ex);\n        }\n        finally {\n            bb.recycle();\n        }\n    }\n    \n    public void close() {\n        try {\n            mOutput.close();\n        }\n        catch (IOException e) {\n            onCompleted(e);\n        }\n    }\n\n    @Override\n    public void onCompleted(Exception error) {\n        error.printStackTrace();       \n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/stream/OutputStreamDataSink.java",
    "content": "package com.koushikdutta.async.stream;\n\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.nio.ByteBuffer;\n\nimport com.koushikdutta.async.AsyncServer;\nimport com.koushikdutta.async.ByteBufferList;\nimport com.koushikdutta.async.DataSink;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.callback.WritableCallback;\n\npublic class OutputStreamDataSink implements DataSink {\n    public OutputStreamDataSink(AsyncServer server) {\n        this(server, null);\n    }\n\n    @Override\n    public void end() {\n        try {\n            if (mStream != null)\n                mStream.close();\n            reportClose(null);\n        }\n        catch (IOException e) {\n            reportClose(e);\n        }\n    }\n\n    AsyncServer server;\n    public OutputStreamDataSink(AsyncServer server, OutputStream stream) {\n        this.server = server;\n        setOutputStream(stream);\n    }\n\n    OutputStream mStream;\n    public void setOutputStream(OutputStream stream) {\n        mStream = stream;\n    }\n    \n    public OutputStream getOutputStream() throws IOException {\n        return mStream;\n    }\n\n    @Override\n    public void write(final ByteBufferList bb) {\n        try {\n            while (bb.size() > 0) {\n                ByteBuffer b = bb.remove();\n                getOutputStream().write(b.array(), b.arrayOffset() + b.position(), b.remaining());\n                ByteBufferList.reclaim(b);\n            }\n        }\n        catch (IOException e) {\n            reportClose(e);\n        }\n        finally {\n            bb.recycle();\n        }\n    }\n\n    WritableCallback mWritable;\n    @Override\n    public void setWriteableCallback(WritableCallback handler) {\n        mWritable = handler;        \n    }\n\n    @Override\n    public WritableCallback getWriteableCallback() {\n        return mWritable;\n    }\n\n    @Override\n    public boolean isOpen() {\n        return closeReported;\n    }\n\n    boolean closeReported;\n    Exception closeException;\n    public void reportClose(Exception ex) {\n        if (closeReported)\n            return;\n        closeReported = true;\n        closeException = ex;\n\n        if (mClosedCallback != null)\n            mClosedCallback.onCompleted(closeException);\n    }\n    \n    CompletedCallback mClosedCallback;\n    @Override\n    public void setClosedCallback(CompletedCallback handler) {\n        mClosedCallback = handler;        \n    }\n\n    @Override\n    public CompletedCallback getClosedCallback() {\n        return mClosedCallback;\n    }\n\n    @Override\n    public AsyncServer getServer() {\n        return server;\n    }\n\n    WritableCallback outputStreamCallback;\n    public void setOutputStreamWritableCallback(WritableCallback outputStreamCallback) {\n        this.outputStreamCallback = outputStreamCallback;\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/util/Allocator.java",
    "content": "package com.koushikdutta.async.util;\n\nimport com.koushikdutta.async.ByteBufferList;\n\nimport java.nio.ByteBuffer;\n\n/**\n * Created by koush on 6/28/14.\n */\npublic class Allocator {\n    final int maxAlloc;\n    int currentAlloc = 0;\n    int minAlloc = 2 << 11;\n\n    public Allocator(int maxAlloc) {\n        this.maxAlloc = maxAlloc;\n    }\n\n    public Allocator() {\n        maxAlloc = ByteBufferList.MAX_ITEM_SIZE;\n    }\n\n    public ByteBuffer allocate() {\n        return allocate(currentAlloc);\n    }\n\n    public ByteBuffer allocate(int currentAlloc) {\n        return ByteBufferList.obtain(Math.min(Math.max(currentAlloc, minAlloc), maxAlloc));\n    }\n\n    public void track(long read) {\n        currentAlloc = (int)read * 2;\n    }\n\n    public int getMaxAlloc() {\n        return maxAlloc;\n    }\n\n    public void setCurrentAlloc(int currentAlloc) {\n        this.currentAlloc = currentAlloc;\n    }\n\n    public int getMinAlloc() {\n        return minAlloc;\n    }\n\n    public Allocator setMinAlloc(int minAlloc ) {\n        this.minAlloc = Math.max(0, minAlloc);\n        return this;\n    }\n}\n\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/util/ArrayDeque.java",
    "content": "/*\n * Written by Josh Bloch of Google Inc. and released to the public domain,\n * as explained at http://creativecommons.org/publicdomain/zero/1.0/.\n */\n\npackage com.koushikdutta.async.util;\n\n// BEGIN android-note\n// removed link to collections framework docs\n// END android-note\n\nimport java.util.AbstractCollection;\nimport java.util.Collection;\nimport java.util.ConcurrentModificationException;\nimport java.util.Iterator;\nimport java.util.NoSuchElementException;\n\n/**\n * Resizable-array implementation of the {@link Deque} interface.  Array\n * deques have no capacity restrictions; they grow as necessary to support\n * usage.  They are not thread-safe; in the absence of external\n * synchronization, they do not support concurrent access by multiple threads.\n * Null elements are prohibited.  This class is likely to be faster than\n * {@link Stack} when used as a stack, and faster than {@link LinkedList}\n * when used as a queue.\n *\n * <p>Most <tt>ArrayDeque</tt> operations run in amortized constant time.\n * Exceptions include {@link #remove(Object) remove}, {@link\n * #removeFirstOccurrence removeFirstOccurrence}, {@link #removeLastOccurrence\n * removeLastOccurrence}, {@link #contains contains}, {@link #iterator\n * iterator.remove()}, and the bulk operations, all of which run in linear\n * time.\n *\n * <p>The iterators returned by this class's <tt>iterator</tt> method are\n * <i>fail-fast</i>: If the deque is modified at any time after the iterator\n * is created, in any way except through the iterator's own <tt>remove</tt>\n * method, the iterator will generally throw a {@link\n * ConcurrentModificationException}.  Thus, in the face of concurrent\n * modification, the iterator fails quickly and cleanly, rather than risking\n * arbitrary, non-deterministic behavior at an undetermined time in the\n * future.\n *\n * <p>Note that the fail-fast behavior of an iterator cannot be guaranteed\n * as it is, generally speaking, impossible to make any hard guarantees in the\n * presence of unsynchronized concurrent modification.  Fail-fast iterators\n * throw <tt>ConcurrentModificationException</tt> on a best-effort basis.\n * Therefore, it would be wrong to write a program that depended on this\n * exception for its correctness: <i>the fail-fast behavior of iterators\n * should be used only to detect bugs.</i>\n *\n * <p>This class and its iterator implement all of the\n * <em>optional</em> methods of the {@link Collection} and {@link\n * Iterator} interfaces.\n *\n * @author  Josh Bloch and Doug Lea\n * @since   1.6\n * @param <E> the type of elements held in this collection\n */\npublic class ArrayDeque<E> extends AbstractCollection<E>\n                           implements Deque<E>, Cloneable, java.io.Serializable\n{\n    /**\n     * The array in which the elements of the deque are stored.\n     * The capacity of the deque is the length of this array, which is\n     * always a power of two. The array is never allowed to become\n     * full, except transiently within an addX method where it is\n     * resized (see doubleCapacity) immediately upon becoming full,\n     * thus avoiding head and tail wrapping around to equal each\n     * other.  We also guarantee that all array cells not holding\n     * deque elements are always null.\n     */\n    private transient Object[] elements;\n\n    /**\n     * The index of the element at the head of the deque (which is the\n     * element that would be removed by remove() or pop()); or an\n     * arbitrary number equal to tail if the deque is empty.\n     */\n    private transient int head;\n\n    /**\n     * The index at which the next element would be added to the tail\n     * of the deque (via addLast(E), add(E), or push(E)).\n     */\n    private transient int tail;\n\n    /**\n     * The minimum capacity that we'll use for a newly created deque.\n     * Must be a power of 2.\n     */\n    private static final int MIN_INITIAL_CAPACITY = 8;\n\n    // ******  Array allocation and resizing utilities ******\n\n    /**\n     * Allocate empty array to hold the given number of elements.\n     *\n     * @param numElements  the number of elements to hold\n     */\n    private void allocateElements(int numElements) {\n        int initialCapacity = MIN_INITIAL_CAPACITY;\n        // Find the best power of two to hold elements.\n        // Tests \"<=\" because arrays aren't kept full.\n        if (numElements >= initialCapacity) {\n            initialCapacity = numElements;\n            initialCapacity |= (initialCapacity >>>  1);\n            initialCapacity |= (initialCapacity >>>  2);\n            initialCapacity |= (initialCapacity >>>  4);\n            initialCapacity |= (initialCapacity >>>  8);\n            initialCapacity |= (initialCapacity >>> 16);\n            initialCapacity++;\n\n            if (initialCapacity < 0)   // Too many elements, must back off\n                initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements\n        }\n        elements = new Object[initialCapacity];\n    }\n\n    /**\n     * Double the capacity of this deque.  Call only when full, i.e.,\n     * when head and tail have wrapped around to become equal.\n     */\n    private void doubleCapacity() {\n        int p = head;\n        int n = elements.length;\n        int r = n - p; // number of elements to the right of p\n        int newCapacity = n << 1;\n        if (newCapacity < 0)\n            throw new IllegalStateException(\"Sorry, deque too big\");\n        Object[] a = new Object[newCapacity];\n        System.arraycopy(elements, p, a, 0, r);\n        System.arraycopy(elements, 0, a, r, p);\n        elements = a;\n        head = 0;\n        tail = n;\n    }\n\n    /**\n     * Copies the elements from our element array into the specified array,\n     * in order (from first to last element in the deque).  It is assumed\n     * that the array is large enough to hold all elements in the deque.\n     *\n     * @return its argument\n     */\n    private <T> T[] copyElements(T[] a) {\n        if (head < tail) {\n            System.arraycopy(elements, head, a, 0, size());\n        } else if (head > tail) {\n            int headPortionLen = elements.length - head;\n            System.arraycopy(elements, head, a, 0, headPortionLen);\n            System.arraycopy(elements, 0, a, headPortionLen, tail);\n        }\n        return a;\n    }\n\n    /**\n     * Constructs an empty array deque with an initial capacity\n     * sufficient to hold 16 elements.\n     */\n    public ArrayDeque() {\n        elements = new Object[16];\n    }\n\n    /**\n     * Constructs an empty array deque with an initial capacity\n     * sufficient to hold the specified number of elements.\n     *\n     * @param numElements  lower bound on initial capacity of the deque\n     */\n    public ArrayDeque(int numElements) {\n        allocateElements(numElements);\n    }\n\n    /**\n     * Constructs a deque containing the elements of the specified\n     * collection, in the order they are returned by the collection's\n     * iterator.  (The first element returned by the collection's\n     * iterator becomes the first element, or <i>front</i> of the\n     * deque.)\n     *\n     * @param c the collection whose elements are to be placed into the deque\n     * @throws NullPointerException if the specified collection is null\n     */\n    public ArrayDeque(Collection<? extends E> c) {\n        allocateElements(c.size());\n        addAll(c);\n    }\n\n    // The main insertion and extraction methods are addFirst,\n    // addLast, pollFirst, pollLast. The other methods are defined in\n    // terms of these.\n\n    /**\n     * Inserts the specified element at the front of this deque.\n     *\n     * @param e the element to add\n     * @throws NullPointerException if the specified element is null\n     */\n    public void addFirst(E e) {\n        if (e == null)\n            throw new NullPointerException(\"e == null\");\n        elements[head = (head - 1) & (elements.length - 1)] = e;\n        if (head == tail)\n            doubleCapacity();\n    }\n\n    /**\n     * Inserts the specified element at the end of this deque.\n     *\n     * <p>This method is equivalent to {@link #add}.\n     *\n     * @param e the element to add\n     * @throws NullPointerException if the specified element is null\n     */\n    public void addLast(E e) {\n        if (e == null)\n            throw new NullPointerException(\"e == null\");\n        elements[tail] = e;\n        if ( (tail = (tail + 1) & (elements.length - 1)) == head)\n            doubleCapacity();\n    }\n\n    /**\n     * Inserts the specified element at the front of this deque.\n     *\n     * @param e the element to add\n     * @return <tt>true</tt> (as specified by {@link Deque#offerFirst})\n     * @throws NullPointerException if the specified element is null\n     */\n    public boolean offerFirst(E e) {\n        addFirst(e);\n        return true;\n    }\n\n    /**\n     * Inserts the specified element at the end of this deque.\n     *\n     * @param e the element to add\n     * @return <tt>true</tt> (as specified by {@link Deque#offerLast})\n     * @throws NullPointerException if the specified element is null\n     */\n    public boolean offerLast(E e) {\n        addLast(e);\n        return true;\n    }\n\n    /**\n     * @throws NoSuchElementException {@inheritDoc}\n     */\n    public E removeFirst() {\n        E x = pollFirst();\n        if (x == null)\n            throw new NoSuchElementException();\n        return x;\n    }\n\n    /**\n     * @throws NoSuchElementException {@inheritDoc}\n     */\n    public E removeLast() {\n        E x = pollLast();\n        if (x == null)\n            throw new NoSuchElementException();\n        return x;\n    }\n\n    public E pollFirst() {\n        int h = head;\n        @SuppressWarnings(\"unchecked\") E result = (E) elements[h];\n        // Element is null if deque empty\n        if (result == null)\n            return null;\n        elements[h] = null;     // Must null out slot\n        head = (h + 1) & (elements.length - 1);\n        return result;\n    }\n\n    public E pollLast() {\n        int t = (tail - 1) & (elements.length - 1);\n        @SuppressWarnings(\"unchecked\") E result = (E) elements[t];\n        if (result == null)\n            return null;\n        elements[t] = null;\n        tail = t;\n        return result;\n    }\n\n    /**\n     * @throws NoSuchElementException {@inheritDoc}\n     */\n    public E getFirst() {\n        @SuppressWarnings(\"unchecked\") E result = (E) elements[head];\n        if (result == null)\n            throw new NoSuchElementException();\n        return result;\n    }\n\n    /**\n     * @throws NoSuchElementException {@inheritDoc}\n     */\n    public E getLast() {\n        @SuppressWarnings(\"unchecked\")\n        E result = (E) elements[(tail - 1) & (elements.length - 1)];\n        if (result == null)\n            throw new NoSuchElementException();\n        return result;\n    }\n\n    public E peekFirst() {\n        @SuppressWarnings(\"unchecked\") E result = (E) elements[head];\n        // elements[head] is null if deque empty\n        return result;\n    }\n\n    public E peekLast() {\n        @SuppressWarnings(\"unchecked\")\n        E result = (E) elements[(tail - 1) & (elements.length - 1)];\n        return result;\n    }\n\n    /**\n     * Removes the first occurrence of the specified element in this\n     * deque (when traversing the deque from head to tail).\n     * If the deque does not contain the element, it is unchanged.\n     * More formally, removes the first element <tt>e</tt> such that\n     * <tt>o.equals(e)</tt> (if such an element exists).\n     * Returns <tt>true</tt> if this deque contained the specified element\n     * (or equivalently, if this deque changed as a result of the call).\n     *\n     * @param o element to be removed from this deque, if present\n     * @return <tt>true</tt> if the deque contained the specified element\n     */\n    public boolean removeFirstOccurrence(Object o) {\n        if (o == null)\n            return false;\n        int mask = elements.length - 1;\n        int i = head;\n        Object x;\n        while ( (x = elements[i]) != null) {\n            if (o.equals(x)) {\n                delete(i);\n                return true;\n            }\n            i = (i + 1) & mask;\n        }\n        return false;\n    }\n\n    /**\n     * Removes the last occurrence of the specified element in this\n     * deque (when traversing the deque from head to tail).\n     * If the deque does not contain the element, it is unchanged.\n     * More formally, removes the last element <tt>e</tt> such that\n     * <tt>o.equals(e)</tt> (if such an element exists).\n     * Returns <tt>true</tt> if this deque contained the specified element\n     * (or equivalently, if this deque changed as a result of the call).\n     *\n     * @param o element to be removed from this deque, if present\n     * @return <tt>true</tt> if the deque contained the specified element\n     */\n    public boolean removeLastOccurrence(Object o) {\n        if (o == null)\n            return false;\n        int mask = elements.length - 1;\n        int i = (tail - 1) & mask;\n        Object x;\n        while ( (x = elements[i]) != null) {\n            if (o.equals(x)) {\n                delete(i);\n                return true;\n            }\n            i = (i - 1) & mask;\n        }\n        return false;\n    }\n\n    // *** Queue methods ***\n\n    /**\n     * Inserts the specified element at the end of this deque.\n     *\n     * <p>This method is equivalent to {@link #addLast}.\n     *\n     * @param e the element to add\n     * @return <tt>true</tt> (as specified by {@link Collection#add})\n     * @throws NullPointerException if the specified element is null\n     */\n    public boolean add(E e) {\n        addLast(e);\n        return true;\n    }\n\n    /**\n     * Inserts the specified element at the end of this deque.\n     *\n     * <p>This method is equivalent to {@link #offerLast}.\n     *\n     * @param e the element to add\n     * @return <tt>true</tt> (as specified by {@link Queue#offer})\n     * @throws NullPointerException if the specified element is null\n     */\n    public boolean offer(E e) {\n        return offerLast(e);\n    }\n\n    /**\n     * Retrieves and removes the head of the queue represented by this deque.\n     *\n     * This method differs from {@link #poll poll} only in that it throws an\n     * exception if this deque is empty.\n     *\n     * <p>This method is equivalent to {@link #removeFirst}.\n     *\n     * @return the head of the queue represented by this deque\n     * @throws NoSuchElementException {@inheritDoc}\n     */\n    public E remove() {\n        return removeFirst();\n    }\n\n    /**\n     * Retrieves and removes the head of the queue represented by this deque\n     * (in other words, the first element of this deque), or returns\n     * <tt>null</tt> if this deque is empty.\n     *\n     * <p>This method is equivalent to {@link #pollFirst}.\n     *\n     * @return the head of the queue represented by this deque, or\n     *         <tt>null</tt> if this deque is empty\n     */\n    public E poll() {\n        return pollFirst();\n    }\n\n    /**\n     * Retrieves, but does not remove, the head of the queue represented by\n     * this deque.  This method differs from {@link #peek peek} only in\n     * that it throws an exception if this deque is empty.\n     *\n     * <p>This method is equivalent to {@link #getFirst}.\n     *\n     * @return the head of the queue represented by this deque\n     * @throws NoSuchElementException {@inheritDoc}\n     */\n    public E element() {\n        return getFirst();\n    }\n\n    /**\n     * Retrieves, but does not remove, the head of the queue represented by\n     * this deque, or returns <tt>null</tt> if this deque is empty.\n     *\n     * <p>This method is equivalent to {@link #peekFirst}.\n     *\n     * @return the head of the queue represented by this deque, or\n     *         <tt>null</tt> if this deque is empty\n     */\n    public E peek() {\n        return peekFirst();\n    }\n\n    // *** Stack methods ***\n\n    /**\n     * Pushes an element onto the stack represented by this deque.  In other\n     * words, inserts the element at the front of this deque.\n     *\n     * <p>This method is equivalent to {@link #addFirst}.\n     *\n     * @param e the element to push\n     * @throws NullPointerException if the specified element is null\n     */\n    public void push(E e) {\n        addFirst(e);\n    }\n\n    /**\n     * Pops an element from the stack represented by this deque.  In other\n     * words, removes and returns the first element of this deque.\n     *\n     * <p>This method is equivalent to {@link #removeFirst()}.\n     *\n     * @return the element at the front of this deque (which is the top\n     *         of the stack represented by this deque)\n     * @throws NoSuchElementException {@inheritDoc}\n     */\n    public E pop() {\n        return removeFirst();\n    }\n\n    private void checkInvariants() {\n    }\n\n    /**\n     * Removes the element at the specified position in the elements array,\n     * adjusting head and tail as necessary.  This can result in motion of\n     * elements backwards or forwards in the array.\n     *\n     * <p>This method is called delete rather than remove to emphasize\n     * that its semantics differ from those of {@link List#remove(int)}.\n     *\n     * @return true if elements moved backwards\n     */\n    private boolean delete(int i) {\n        checkInvariants();\n        final Object[] elements = this.elements;\n        final int mask = elements.length - 1;\n        final int h = head;\n        final int t = tail;\n        final int front = (i - h) & mask;\n        final int back  = (t - i) & mask;\n\n        // Invariant: head <= i < tail mod circularity\n        if (front >= ((t - h) & mask))\n            throw new ConcurrentModificationException();\n\n        // Optimize for least element motion\n        if (front < back) {\n            if (h <= i) {\n                System.arraycopy(elements, h, elements, h + 1, front);\n            } else { // Wrap around\n                System.arraycopy(elements, 0, elements, 1, i);\n                elements[0] = elements[mask];\n                System.arraycopy(elements, h, elements, h + 1, mask - h);\n            }\n            elements[h] = null;\n            head = (h + 1) & mask;\n            return false;\n        } else {\n            if (i < t) { // Copy the null tail as well\n                System.arraycopy(elements, i + 1, elements, i, back);\n                tail = t - 1;\n            } else { // Wrap around\n                System.arraycopy(elements, i + 1, elements, i, mask - i);\n                elements[mask] = elements[0];\n                System.arraycopy(elements, 1, elements, 0, t);\n                tail = (t - 1) & mask;\n            }\n            return true;\n        }\n    }\n\n    // *** Collection Methods ***\n\n    /**\n     * Returns the number of elements in this deque.\n     *\n     * @return the number of elements in this deque\n     */\n    public int size() {\n        return (tail - head) & (elements.length - 1);\n    }\n\n    /**\n     * Returns <tt>true</tt> if this deque contains no elements.\n     *\n     * @return <tt>true</tt> if this deque contains no elements\n     */\n    public boolean isEmpty() {\n        return head == tail;\n    }\n\n    /**\n     * Returns an iterator over the elements in this deque.  The elements\n     * will be ordered from first (head) to last (tail).  This is the same\n     * order that elements would be dequeued (via successive calls to\n     * {@link #remove} or popped (via successive calls to {@link #pop}).\n     *\n     * @return an iterator over the elements in this deque\n     */\n    public Iterator<E> iterator() {\n        return new DeqIterator();\n    }\n\n    public Iterator<E> descendingIterator() {\n        return new DescendingIterator();\n    }\n\n    private class DeqIterator implements Iterator<E> {\n        /**\n         * Index of element to be returned by subsequent call to next.\n         */\n        private int cursor = head;\n\n        /**\n         * Tail recorded at construction (also in remove), to stop\n         * iterator and also to check for comodification.\n         */\n        private int fence = tail;\n\n        /**\n         * Index of element returned by most recent call to next.\n         * Reset to -1 if element is deleted by a call to remove.\n         */\n        private int lastRet = -1;\n\n        public boolean hasNext() {\n            return cursor != fence;\n        }\n\n        public E next() {\n            if (cursor == fence)\n                throw new NoSuchElementException();\n            @SuppressWarnings(\"unchecked\") E result = (E) elements[cursor];\n            // This check doesn't catch all possible comodifications,\n            // but does catch the ones that corrupt traversal\n            if (tail != fence || result == null)\n                throw new ConcurrentModificationException();\n            lastRet = cursor;\n            cursor = (cursor + 1) & (elements.length - 1);\n            return result;\n        }\n\n        public void remove() {\n            if (lastRet < 0)\n                throw new IllegalStateException();\n            if (delete(lastRet)) { // if left-shifted, undo increment in next()\n                cursor = (cursor - 1) & (elements.length - 1);\n                fence = tail;\n            }\n            lastRet = -1;\n        }\n    }\n\n    private class DescendingIterator implements Iterator<E> {\n        /*\n         * This class is nearly a mirror-image of DeqIterator, using\n         * tail instead of head for initial cursor, and head instead of\n         * tail for fence.\n         */\n        private int cursor = tail;\n        private int fence = head;\n        private int lastRet = -1;\n\n        public boolean hasNext() {\n            return cursor != fence;\n        }\n\n        public E next() {\n            if (cursor == fence)\n                throw new NoSuchElementException();\n            cursor = (cursor - 1) & (elements.length - 1);\n            @SuppressWarnings(\"unchecked\") E result = (E) elements[cursor];\n            if (head != fence || result == null)\n                throw new ConcurrentModificationException();\n            lastRet = cursor;\n            return result;\n        }\n\n        public void remove() {\n            if (lastRet < 0)\n                throw new IllegalStateException();\n            if (!delete(lastRet)) {\n                cursor = (cursor + 1) & (elements.length - 1);\n                fence = head;\n            }\n            lastRet = -1;\n        }\n    }\n\n    /**\n     * Returns <tt>true</tt> if this deque contains the specified element.\n     * More formally, returns <tt>true</tt> if and only if this deque contains\n     * at least one element <tt>e</tt> such that <tt>o.equals(e)</tt>.\n     *\n     * @param o object to be checked for containment in this deque\n     * @return <tt>true</tt> if this deque contains the specified element\n     */\n    public boolean contains(Object o) {\n        if (o == null)\n            return false;\n        int mask = elements.length - 1;\n        int i = head;\n        Object x;\n        while ( (x = elements[i]) != null) {\n            if (o.equals(x))\n                return true;\n            i = (i + 1) & mask;\n        }\n        return false;\n    }\n\n    /**\n     * Removes a single instance of the specified element from this deque.\n     * If the deque does not contain the element, it is unchanged.\n     * More formally, removes the first element <tt>e</tt> such that\n     * <tt>o.equals(e)</tt> (if such an element exists).\n     * Returns <tt>true</tt> if this deque contained the specified element\n     * (or equivalently, if this deque changed as a result of the call).\n     *\n     * <p>This method is equivalent to {@link #removeFirstOccurrence}.\n     *\n     * @param o element to be removed from this deque, if present\n     * @return <tt>true</tt> if this deque contained the specified element\n     */\n    public boolean remove(Object o) {\n        return removeFirstOccurrence(o);\n    }\n\n    /**\n     * Removes all of the elements from this deque.\n     * The deque will be empty after this call returns.\n     */\n    public void clear() {\n        int h = head;\n        int t = tail;\n        if (h != t) { // clear all cells\n            head = tail = 0;\n            int i = h;\n            int mask = elements.length - 1;\n            do {\n                elements[i] = null;\n                i = (i + 1) & mask;\n            } while (i != t);\n        }\n    }\n\n    /**\n     * Returns an array containing all of the elements in this deque\n     * in proper sequence (from first to last element).\n     *\n     * <p>The returned array will be \"safe\" in that no references to it are\n     * maintained by this deque.  (In other words, this method must allocate\n     * a new array).  The caller is thus free to modify the returned array.\n     *\n     * <p>This method acts as bridge between array-based and collection-based\n     * APIs.\n     *\n     * @return an array containing all of the elements in this deque\n     */\n    public Object[] toArray() {\n        return copyElements(new Object[size()]);\n    }\n\n    /**\n     * Returns an array containing all of the elements in this deque in\n     * proper sequence (from first to last element); the runtime type of the\n     * returned array is that of the specified array.  If the deque fits in\n     * the specified array, it is returned therein.  Otherwise, a new array\n     * is allocated with the runtime type of the specified array and the\n     * size of this deque.\n     *\n     * <p>If this deque fits in the specified array with room to spare\n     * (i.e., the array has more elements than this deque), the element in\n     * the array immediately following the end of the deque is set to\n     * <tt>null</tt>.\n     *\n     * <p>Like the {@link #toArray()} method, this method acts as bridge between\n     * array-based and collection-based APIs.  Further, this method allows\n     * precise control over the runtime type of the output array, and may,\n     * under certain circumstances, be used to save allocation costs.\n     *\n     * <p>Suppose <tt>x</tt> is a deque known to contain only strings.\n     * The following code can be used to dump the deque into a newly\n     * allocated array of <tt>String</tt>:\n     *\n     *  <pre> {@code String[] y = x.toArray(new String[0]);}</pre>\n     *\n     * Note that <tt>toArray(new Object[0])</tt> is identical in function to\n     * <tt>toArray()</tt>.\n     *\n     * @param a the array into which the elements of the deque are to\n     *          be stored, if it is big enough; otherwise, a new array of the\n     *          same runtime type is allocated for this purpose\n     * @return an array containing all of the elements in this deque\n     * @throws ArrayStoreException if the runtime type of the specified array\n     *         is not a supertype of the runtime type of every element in\n     *         this deque\n     * @throws NullPointerException if the specified array is null\n     */\n    @SuppressWarnings(\"unchecked\")\n    public <T> T[] toArray(T[] a) {\n        int size = size();\n        if (a.length < size)\n            a = (T[])java.lang.reflect.Array.newInstance(\n                    a.getClass().getComponentType(), size);\n        copyElements(a);\n        if (a.length > size)\n            a[size] = null;\n        return a;\n    }\n\n    // *** Object methods ***\n\n    /**\n     * Returns a copy of this deque.\n     *\n     * @return a copy of this deque\n     */\n    public ArrayDeque<E> clone() {\n        try {\n            @SuppressWarnings(\"unchecked\")\n            ArrayDeque<E> result = (ArrayDeque<E>) super.clone();\n            System.arraycopy(elements, 0, result.elements, 0, elements.length);\n            return result;\n\n        } catch (CloneNotSupportedException e) {\n            throw new AssertionError();\n        }\n    }\n\n    /**\n     * Appease the serialization gods.\n     */\n    private static final long serialVersionUID = 2340985798034038923L;\n\n    /**\n     * Serialize this deque.\n     *\n     * @serialData The current size (<tt>int</tt>) of the deque,\n     * followed by all of its elements (each an object reference) in\n     * first-to-last order.\n     */\n    private void writeObject(java.io.ObjectOutputStream s)\n            throws java.io.IOException {\n        s.defaultWriteObject();\n\n        // Write out size\n        s.writeInt(size());\n\n        // Write out elements in order.\n        int mask = elements.length - 1;\n        for (int i = head; i != tail; i = (i + 1) & mask)\n            s.writeObject(elements[i]);\n    }\n\n    /**\n     * Deserialize this deque.\n     */\n    private void readObject(java.io.ObjectInputStream s)\n            throws java.io.IOException, ClassNotFoundException {\n        s.defaultReadObject();\n\n        // Read in size and allocate array\n        int size = s.readInt();\n        allocateElements(size);\n        head = 0;\n        tail = size;\n\n        // Read in all elements in the proper order.\n        for (int i = 0; i < size; i++)\n            elements[i] = s.readObject();\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/util/Charsets.java",
    "content": "package com.koushikdutta.async.util;\n\nimport java.nio.charset.Charset;\n\n/** From java.nio.charset.Charsets */\npublic class Charsets {\n  public static final Charset US_ASCII = Charset.forName(\"US-ASCII\");\n  public static final Charset UTF_8 = Charset.forName(\"UTF-8\");\n  public static final Charset ISO_8859_1 = Charset.forName(\"ISO-8859-1\");\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/util/Deque.java",
    "content": "/*\n * Written by Doug Lea and Josh Bloch with assistance from members of\n * JCP JSR-166 Expert Group and released to the public domain, as explained\n * at http://creativecommons.org/publicdomain/zero/1.0/\n */\n\npackage com.koushikdutta.async.util;\n\n// BEGIN android-note\n// removed link to collections framework docs\n// END android-note\n\nimport java.util.Iterator;\nimport java.util.Queue;\n\n/**\n * A linear collection that supports element insertion and removal at\n * both ends.  The name <i>deque</i> is short for \"double ended queue\"\n * and is usually pronounced \"deck\".  Most <tt>Deque</tt>\n * implementations place no fixed limits on the number of elements\n * they may contain, but this interface supports capacity-restricted\n * deques as well as those with no fixed size limit.\n *\n * <p>This interface defines methods to access the elements at both\n * ends of the deque.  Methods are provided to insert, remove, and\n * examine the element.  Each of these methods exists in two forms:\n * one throws an exception if the operation fails, the other returns a\n * special value (either <tt>null</tt> or <tt>false</tt>, depending on\n * the operation).  The latter form of the insert operation is\n * designed specifically for use with capacity-restricted\n * <tt>Deque</tt> implementations; in most implementations, insert\n * operations cannot fail.\n *\n * <p>The twelve methods described above are summarized in the\n * following table:\n *\n * <p>\n * <table BORDER CELLPADDING=3 CELLSPACING=1>\n *  <tr>\n *    <td></td>\n *    <td ALIGN=CENTER COLSPAN = 2> <b>First Element (Head)</b></td>\n *    <td ALIGN=CENTER COLSPAN = 2> <b>Last Element (Tail)</b></td>\n *  </tr>\n *  <tr>\n *    <td></td>\n *    <td ALIGN=CENTER><em>Throws exception</em></td>\n *    <td ALIGN=CENTER><em>Special value</em></td>\n *    <td ALIGN=CENTER><em>Throws exception</em></td>\n *    <td ALIGN=CENTER><em>Special value</em></td>\n *  </tr>\n *  <tr>\n *    <td><b>Insert</b></td>\n *    <td>{@link #addFirst addFirst(e)}</td>\n *    <td>{@link #offerFirst offerFirst(e)}</td>\n *    <td>{@link #addLast addLast(e)}</td>\n *    <td>{@link #offerLast offerLast(e)}</td>\n *  </tr>\n *  <tr>\n *    <td><b>Remove</b></td>\n *    <td>{@link #removeFirst removeFirst()}</td>\n *    <td>{@link #pollFirst pollFirst()}</td>\n *    <td>{@link #removeLast removeLast()}</td>\n *    <td>{@link #pollLast pollLast()}</td>\n *  </tr>\n *  <tr>\n *    <td><b>Examine</b></td>\n *    <td>{@link #getFirst getFirst()}</td>\n *    <td>{@link #peekFirst peekFirst()}</td>\n *    <td>{@link #getLast getLast()}</td>\n *    <td>{@link #peekLast peekLast()}</td>\n *  </tr>\n * </table>\n *\n * <p>This interface extends the {@link Queue} interface.  When a deque is\n * used as a queue, FIFO (First-In-First-Out) behavior results.  Elements are\n * added at the end of the deque and removed from the beginning.  The methods\n * inherited from the <tt>Queue</tt> interface are precisely equivalent to\n * <tt>Deque</tt> methods as indicated in the following table:\n *\n * <p>\n * <table BORDER CELLPADDING=3 CELLSPACING=1>\n *  <tr>\n *    <td ALIGN=CENTER> <b><tt>Queue</tt> Method</b></td>\n *    <td ALIGN=CENTER> <b>Equivalent <tt>Deque</tt> Method</b></td>\n *  </tr>\n *  <tr>\n *    <td>{@link java.util.Queue#add add(e)}</td>\n *    <td>{@link #addLast addLast(e)}</td>\n *  </tr>\n *  <tr>\n *    <td>{@link java.util.Queue#offer offer(e)}</td>\n *    <td>{@link #offerLast offerLast(e)}</td>\n *  </tr>\n *  <tr>\n *    <td>{@link java.util.Queue#remove remove()}</td>\n *    <td>{@link #removeFirst removeFirst()}</td>\n *  </tr>\n *  <tr>\n *    <td>{@link java.util.Queue#poll poll()}</td>\n *    <td>{@link #pollFirst pollFirst()}</td>\n *  </tr>\n *  <tr>\n *    <td>{@link java.util.Queue#element element()}</td>\n *    <td>{@link #getFirst getFirst()}</td>\n *  </tr>\n *  <tr>\n *    <td>{@link java.util.Queue#peek peek()}</td>\n *    <td>{@link #peek peekFirst()}</td>\n *  </tr>\n * </table>\n *\n * <p>Deques can also be used as LIFO (Last-In-First-Out) stacks.  This\n * interface should be used in preference to the legacy {@link Stack} class.\n * When a deque is used as a stack, elements are pushed and popped from the\n * beginning of the deque.  Stack methods are precisely equivalent to\n * <tt>Deque</tt> methods as indicated in the table below:\n *\n * <p>\n * <table BORDER CELLPADDING=3 CELLSPACING=1>\n *  <tr>\n *    <td ALIGN=CENTER> <b>Stack Method</b></td>\n *    <td ALIGN=CENTER> <b>Equivalent <tt>Deque</tt> Method</b></td>\n *  </tr>\n *  <tr>\n *    <td>{@link #push push(e)}</td>\n *    <td>{@link #addFirst addFirst(e)}</td>\n *  </tr>\n *  <tr>\n *    <td>{@link #pop pop()}</td>\n *    <td>{@link #removeFirst removeFirst()}</td>\n *  </tr>\n *  <tr>\n *    <td>{@link #peek peek()}</td>\n *    <td>{@link #peekFirst peekFirst()}</td>\n *  </tr>\n * </table>\n *\n * <p>Note that the {@link #peek peek} method works equally well when\n * a deque is used as a queue or a stack; in either case, elements are\n * drawn from the beginning of the deque.\n *\n * <p>This interface provides two methods to remove interior\n * elements, {@link #removeFirstOccurrence removeFirstOccurrence} and\n * {@link #removeLastOccurrence removeLastOccurrence}.\n *\n * <p>Unlike the {@link List} interface, this interface does not\n * provide support for indexed access to elements.\n *\n * <p>While <tt>Deque</tt> implementations are not strictly required\n * to prohibit the insertion of null elements, they are strongly\n * encouraged to do so.  Users of any <tt>Deque</tt> implementations\n * that do allow null elements are strongly encouraged <i>not</i> to\n * take advantage of the ability to insert nulls.  This is so because\n * <tt>null</tt> is used as a special return value by various methods\n * to indicated that the deque is empty.\n *\n * <p><tt>Deque</tt> implementations generally do not define\n * element-based versions of the <tt>equals</tt> and <tt>hashCode</tt>\n * methods, but instead inherit the identity-based versions from class\n * <tt>Object</tt>.\n *\n * @author Doug Lea\n * @author Josh Bloch\n * @since  1.6\n * @param <E> the type of elements held in this collection\n */\n\npublic interface Deque<E> extends Queue<E> {\n    /**\n     * Inserts the specified element at the front of this deque if it is\n     * possible to do so immediately without violating capacity restrictions.\n     * When using a capacity-restricted deque, it is generally preferable to\n     * use method {@link #offerFirst}.\n     *\n     * @param e the element to add\n     * @throws IllegalStateException if the element cannot be added at this\n     *         time due to capacity restrictions\n     * @throws ClassCastException if the class of the specified element\n     *         prevents it from being added to this deque\n     * @throws NullPointerException if the specified element is null and this\n     *         deque does not permit null elements\n     * @throws IllegalArgumentException if some property of the specified\n     *         element prevents it from being added to this deque\n     */\n    void addFirst(E e);\n\n    /**\n     * Inserts the specified element at the end of this deque if it is\n     * possible to do so immediately without violating capacity restrictions.\n     * When using a capacity-restricted deque, it is generally preferable to\n     * use method {@link #offerLast}.\n     *\n     * <p>This method is equivalent to {@link #add}.\n     *\n     * @param e the element to add\n     * @throws IllegalStateException if the element cannot be added at this\n     *         time due to capacity restrictions\n     * @throws ClassCastException if the class of the specified element\n     *         prevents it from being added to this deque\n     * @throws NullPointerException if the specified element is null and this\n     *         deque does not permit null elements\n     * @throws IllegalArgumentException if some property of the specified\n     *         element prevents it from being added to this deque\n     */\n    void addLast(E e);\n\n    /**\n     * Inserts the specified element at the front of this deque unless it would\n     * violate capacity restrictions.  When using a capacity-restricted deque,\n     * this method is generally preferable to the {@link #addFirst} method,\n     * which can fail to insert an element only by throwing an exception.\n     *\n     * @param e the element to add\n     * @return <tt>true</tt> if the element was added to this deque, else\n     *         <tt>false</tt>\n     * @throws ClassCastException if the class of the specified element\n     *         prevents it from being added to this deque\n     * @throws NullPointerException if the specified element is null and this\n     *         deque does not permit null elements\n     * @throws IllegalArgumentException if some property of the specified\n     *         element prevents it from being added to this deque\n     */\n    boolean offerFirst(E e);\n\n    /**\n     * Inserts the specified element at the end of this deque unless it would\n     * violate capacity restrictions.  When using a capacity-restricted deque,\n     * this method is generally preferable to the {@link #addLast} method,\n     * which can fail to insert an element only by throwing an exception.\n     *\n     * @param e the element to add\n     * @return <tt>true</tt> if the element was added to this deque, else\n     *         <tt>false</tt>\n     * @throws ClassCastException if the class of the specified element\n     *         prevents it from being added to this deque\n     * @throws NullPointerException if the specified element is null and this\n     *         deque does not permit null elements\n     * @throws IllegalArgumentException if some property of the specified\n     *         element prevents it from being added to this deque\n     */\n    boolean offerLast(E e);\n\n    /**\n     * Retrieves and removes the first element of this deque.  This method\n     * differs from {@link #pollFirst pollFirst} only in that it throws an\n     * exception if this deque is empty.\n     *\n     * @return the head of this deque\n     * @throws NoSuchElementException if this deque is empty\n     */\n    E removeFirst();\n\n    /**\n     * Retrieves and removes the last element of this deque.  This method\n     * differs from {@link #pollLast pollLast} only in that it throws an\n     * exception if this deque is empty.\n     *\n     * @return the tail of this deque\n     * @throws NoSuchElementException if this deque is empty\n     */\n    E removeLast();\n\n    /**\n     * Retrieves and removes the first element of this deque,\n     * or returns <tt>null</tt> if this deque is empty.\n     *\n     * @return the head of this deque, or <tt>null</tt> if this deque is empty\n     */\n    E pollFirst();\n\n    /**\n     * Retrieves and removes the last element of this deque,\n     * or returns <tt>null</tt> if this deque is empty.\n     *\n     * @return the tail of this deque, or <tt>null</tt> if this deque is empty\n     */\n    E pollLast();\n\n    /**\n     * Retrieves, but does not remove, the first element of this deque.\n     *\n     * This method differs from {@link #peekFirst peekFirst} only in that it\n     * throws an exception if this deque is empty.\n     *\n     * @return the head of this deque\n     * @throws NoSuchElementException if this deque is empty\n     */\n    E getFirst();\n\n    /**\n     * Retrieves, but does not remove, the last element of this deque.\n     * This method differs from {@link #peekLast peekLast} only in that it\n     * throws an exception if this deque is empty.\n     *\n     * @return the tail of this deque\n     * @throws NoSuchElementException if this deque is empty\n     */\n    E getLast();\n\n    /**\n     * Retrieves, but does not remove, the first element of this deque,\n     * or returns <tt>null</tt> if this deque is empty.\n     *\n     * @return the head of this deque, or <tt>null</tt> if this deque is empty\n     */\n    E peekFirst();\n\n    /**\n     * Retrieves, but does not remove, the last element of this deque,\n     * or returns <tt>null</tt> if this deque is empty.\n     *\n     * @return the tail of this deque, or <tt>null</tt> if this deque is empty\n     */\n    E peekLast();\n\n    /**\n     * Removes the first occurrence of the specified element from this deque.\n     * If the deque does not contain the element, it is unchanged.\n     * More formally, removes the first element <tt>e</tt> such that\n     * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>\n     * (if such an element exists).\n     * Returns <tt>true</tt> if this deque contained the specified element\n     * (or equivalently, if this deque changed as a result of the call).\n     *\n     * @param o element to be removed from this deque, if present\n     * @return <tt>true</tt> if an element was removed as a result of this call\n     * @throws ClassCastException if the class of the specified element\n     *         is incompatible with this deque (optional)\n     * @throws NullPointerException if the specified element is null and this\n     *         deque does not permit null elements (optional)\n     */\n    boolean removeFirstOccurrence(Object o);\n\n    /**\n     * Removes the last occurrence of the specified element from this deque.\n     * If the deque does not contain the element, it is unchanged.\n     * More formally, removes the last element <tt>e</tt> such that\n     * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>\n     * (if such an element exists).\n     * Returns <tt>true</tt> if this deque contained the specified element\n     * (or equivalently, if this deque changed as a result of the call).\n     *\n     * @param o element to be removed from this deque, if present\n     * @return <tt>true</tt> if an element was removed as a result of this call\n     * @throws ClassCastException if the class of the specified element\n     *         is incompatible with this deque (optional)\n     * @throws NullPointerException if the specified element is null and this\n     *         deque does not permit null elements (optional)\n     */\n    boolean removeLastOccurrence(Object o);\n\n    // *** Queue methods ***\n\n    /**\n     * Inserts the specified element into the queue represented by this deque\n     * (in other words, at the tail of this deque) if it is possible to do so\n     * immediately without violating capacity restrictions, returning\n     * <tt>true</tt> upon success and throwing an\n     * <tt>IllegalStateException</tt> if no space is currently available.\n     * When using a capacity-restricted deque, it is generally preferable to\n     * use {@link #offer(Object) offer}.\n     *\n     * <p>This method is equivalent to {@link #addLast}.\n     *\n     * @param e the element to add\n     * @return <tt>true</tt> (as specified by {@link Collection#add})\n     * @throws IllegalStateException if the element cannot be added at this\n     *         time due to capacity restrictions\n     * @throws ClassCastException if the class of the specified element\n     *         prevents it from being added to this deque\n     * @throws NullPointerException if the specified element is null and this\n     *         deque does not permit null elements\n     * @throws IllegalArgumentException if some property of the specified\n     *         element prevents it from being added to this deque\n     */\n    boolean add(E e);\n\n    /**\n     * Inserts the specified element into the queue represented by this deque\n     * (in other words, at the tail of this deque) if it is possible to do so\n     * immediately without violating capacity restrictions, returning\n     * <tt>true</tt> upon success and <tt>false</tt> if no space is currently\n     * available.  When using a capacity-restricted deque, this method is\n     * generally preferable to the {@link #add} method, which can fail to\n     * insert an element only by throwing an exception.\n     *\n     * <p>This method is equivalent to {@link #offerLast}.\n     *\n     * @param e the element to add\n     * @return <tt>true</tt> if the element was added to this deque, else\n     *         <tt>false</tt>\n     * @throws ClassCastException if the class of the specified element\n     *         prevents it from being added to this deque\n     * @throws NullPointerException if the specified element is null and this\n     *         deque does not permit null elements\n     * @throws IllegalArgumentException if some property of the specified\n     *         element prevents it from being added to this deque\n     */\n    boolean offer(E e);\n\n    /**\n     * Retrieves and removes the head of the queue represented by this deque\n     * (in other words, the first element of this deque).\n     * This method differs from {@link #poll poll} only in that it throws an\n     * exception if this deque is empty.\n     *\n     * <p>This method is equivalent to {@link #removeFirst()}.\n     *\n     * @return the head of the queue represented by this deque\n     * @throws NoSuchElementException if this deque is empty\n     */\n    E remove();\n\n    /**\n     * Retrieves and removes the head of the queue represented by this deque\n     * (in other words, the first element of this deque), or returns\n     * <tt>null</tt> if this deque is empty.\n     *\n     * <p>This method is equivalent to {@link #pollFirst()}.\n     *\n     * @return the first element of this deque, or <tt>null</tt> if\n     *         this deque is empty\n     */\n    E poll();\n\n    /**\n     * Retrieves, but does not remove, the head of the queue represented by\n     * this deque (in other words, the first element of this deque).\n     * This method differs from {@link #peek peek} only in that it throws an\n     * exception if this deque is empty.\n     *\n     * <p>This method is equivalent to {@link #getFirst()}.\n     *\n     * @return the head of the queue represented by this deque\n     * @throws NoSuchElementException if this deque is empty\n     */\n    E element();\n\n    /**\n     * Retrieves, but does not remove, the head of the queue represented by\n     * this deque (in other words, the first element of this deque), or\n     * returns <tt>null</tt> if this deque is empty.\n     *\n     * <p>This method is equivalent to {@link #peekFirst()}.\n     *\n     * @return the head of the queue represented by this deque, or\n     *         <tt>null</tt> if this deque is empty\n     */\n    E peek();\n\n\n    // *** Stack methods ***\n\n    /**\n     * Pushes an element onto the stack represented by this deque (in other\n     * words, at the head of this deque) if it is possible to do so\n     * immediately without violating capacity restrictions, returning\n     * <tt>true</tt> upon success and throwing an\n     * <tt>IllegalStateException</tt> if no space is currently available.\n     *\n     * <p>This method is equivalent to {@link #addFirst}.\n     *\n     * @param e the element to push\n     * @throws IllegalStateException if the element cannot be added at this\n     *         time due to capacity restrictions\n     * @throws ClassCastException if the class of the specified element\n     *         prevents it from being added to this deque\n     * @throws NullPointerException if the specified element is null and this\n     *         deque does not permit null elements\n     * @throws IllegalArgumentException if some property of the specified\n     *         element prevents it from being added to this deque\n     */\n    void push(E e);\n\n    /**\n     * Pops an element from the stack represented by this deque.  In other\n     * words, removes and returns the first element of this deque.\n     *\n     * <p>This method is equivalent to {@link #removeFirst()}.\n     *\n     * @return the element at the front of this deque (which is the top\n     *         of the stack represented by this deque)\n     * @throws NoSuchElementException if this deque is empty\n     */\n    E pop();\n\n\n    // *** Collection methods ***\n\n    /**\n     * Removes the first occurrence of the specified element from this deque.\n     * If the deque does not contain the element, it is unchanged.\n     * More formally, removes the first element <tt>e</tt> such that\n     * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>\n     * (if such an element exists).\n     * Returns <tt>true</tt> if this deque contained the specified element\n     * (or equivalently, if this deque changed as a result of the call).\n     *\n     * <p>This method is equivalent to {@link #removeFirstOccurrence}.\n     *\n     * @param o element to be removed from this deque, if present\n     * @return <tt>true</tt> if an element was removed as a result of this call\n     * @throws ClassCastException if the class of the specified element\n     *         is incompatible with this deque (optional)\n     * @throws NullPointerException if the specified element is null and this\n     *         deque does not permit null elements (optional)\n     */\n    boolean remove(Object o);\n\n    /**\n     * Returns <tt>true</tt> if this deque contains the specified element.\n     * More formally, returns <tt>true</tt> if and only if this deque contains\n     * at least one element <tt>e</tt> such that\n     * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>.\n     *\n     * @param o element whose presence in this deque is to be tested\n     * @return <tt>true</tt> if this deque contains the specified element\n     * @throws ClassCastException if the type of the specified element\n     *         is incompatible with this deque (optional)\n     * @throws NullPointerException if the specified element is null and this\n     *         deque does not permit null elements (optional)\n     */\n    boolean contains(Object o);\n\n    /**\n     * Returns the number of elements in this deque.\n     *\n     * @return the number of elements in this deque\n     */\n    public int size();\n\n    /**\n     * Returns an iterator over the elements in this deque in proper sequence.\n     * The elements will be returned in order from first (head) to last (tail).\n     *\n     * @return an iterator over the elements in this deque in proper sequence\n     */\n    Iterator<E> iterator();\n\n    /**\n     * Returns an iterator over the elements in this deque in reverse\n     * sequential order.  The elements will be returned in order from\n     * last (tail) to first (head).\n     *\n     * @return an iterator over the elements in this deque in reverse\n     * sequence\n     */\n    Iterator<E> descendingIterator();\n\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/util/FileCache.java",
    "content": "package com.koushikdutta.async.util;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.math.BigInteger;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.Provider;\nimport java.security.Security;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.HashSet;\nimport java.util.Random;\nimport java.util.Set;\n\n/**\n * Created by koush on 4/12/14.\n */\npublic class FileCache {\n    class CacheEntry {\n        final long size;\n        public CacheEntry(File file) {\n            size = file.length();\n        }\n    }\n\n    public static class Snapshot {\n        FileInputStream[] fins;\n        long[] lens;\n        Snapshot(FileInputStream[] fins, long[] lens) {\n            this.fins = fins;\n            this.lens = lens;\n        }\n\n        public long getLength(int index) {\n            return lens[index];\n        }\n\n        public void close() {\n            StreamUtility.closeQuietly(fins);\n        }\n    }\n\n    private static String hashAlgorithm = \"MD5\";\n\n    private static MessageDigest findAlternativeMessageDigest() {\n        if (\"MD5\".equals(hashAlgorithm)) {\n            for (Provider provider : Security.getProviders()) {\n                for (Provider.Service service : provider.getServices()) {\n                    hashAlgorithm = service.getAlgorithm();\n                    try {\n                        MessageDigest messageDigest = MessageDigest.getInstance(hashAlgorithm);\n                        if (messageDigest != null)\n                            return messageDigest;\n                    } catch (NoSuchAlgorithmException ignored) {\n                    }\n                }\n            }\n        }\n        return null;\n    }\n\n    static MessageDigest messageDigest;\n    static {\n        try {\n            messageDigest = MessageDigest.getInstance(hashAlgorithm);\n        } catch (NoSuchAlgorithmException e) {\n            messageDigest = findAlternativeMessageDigest();\n            if (null == messageDigest)\n                throw new RuntimeException(e);\n        }\n        try {\n            messageDigest = (MessageDigest)messageDigest.clone();\n        }\n        catch (CloneNotSupportedException e) {\n        }\n    }\n\n    public static synchronized String toKeyString(Object... parts) {\n        messageDigest.reset();\n        for (Object part : parts) {\n            messageDigest.update(part.toString().getBytes());\n        }\n        byte[] md5bytes = messageDigest.digest();\n        return new BigInteger(1, md5bytes).toString(16);\n    }\n\n    boolean loadAsync;\n    Random random = new Random();\n    public File getTempFile() {\n        File f;\n        while ((f = new File(directory, new BigInteger(128, random).toString(16))).exists());\n        return f;\n    }\n\n    public File[] getTempFiles(int count) {\n        File[] ret = new File[count];\n        for (int i = 0; i < count; i++) {\n            ret[i] = getTempFile();\n        }\n        return ret;\n    }\n\n    public static void removeFiles(File... files) {\n        if (files == null)\n            return;\n        for (File file: files) {\n            file.delete();\n        }\n    }\n\n    public void remove(String key) {\n        int i = 0;\n        while (cache.remove(getPartName(key, i)) != null) {\n            i++;\n        }\n        removePartFiles(key);\n    }\n\n    public boolean exists(String key, int part) {\n        return getPartFile(key, part).exists();\n    }\n\n    public boolean exists(String key) {\n        return getPartFile(key, 0).exists();\n    }\n\n    public File touch(File file) {\n        cache.get(file.getName());\n        file.setLastModified(System.currentTimeMillis());\n        return file;\n    }\n\n    public FileInputStream get(String key) throws IOException {\n        return new FileInputStream(touch(getPartFile(key, 0)));\n    }\n\n    public File getFile(String key) {\n        return touch(getPartFile(key, 0));\n    }\n\n    public FileInputStream[] get(String key, int count) throws IOException {\n        FileInputStream[] ret = new FileInputStream[count];\n        try {\n            for (int i = 0; i < count; i++) {\n                ret[i] = new FileInputStream(touch(getPartFile(key, i)));\n            }\n        }\n        catch (IOException e) {\n            // if we can't get all the parts, delete everything\n            for (FileInputStream fin: ret) {\n                StreamUtility.closeQuietly(fin);\n            }\n            remove(key);\n            throw e;\n        }\n\n        return ret;\n    }\n\n    String getPartName(String key, int part) {\n        return key + \".\" + part;\n    }\n\n    public void commitTempFiles(String key, File... tempFiles) {\n        removePartFiles(key);\n\n        // try to rename everything\n        for (int i = 0; i < tempFiles.length; i++) {\n            File tmp = tempFiles[i];\n            File partFile = getPartFile(key, i);\n            if (!tmp.renameTo(partFile)) {\n                // if any rename fails, delete everything\n                removeFiles(tempFiles);\n                remove(key);\n                return;\n            }\n            remove(tmp.getName());\n            cache.put(getPartName(key, i), new CacheEntry(partFile));\n        }\n    }\n\n    void removePartFiles(String key) {\n        int i = 0;\n        File f;\n        while ((f = getPartFile(key, i)).exists()) {\n            f.delete();\n            i++;\n        }\n    }\n\n    File getPartFile(String key, int part) {\n        return new File(directory, getPartName(key, part));\n    }\n\n    long blockSize = 4096;\n    public void setBlockSize(long blockSize) {\n        this.blockSize = blockSize;\n    }\n\n    class InternalCache extends LruCache<String, CacheEntry> {\n        public InternalCache() {\n            super(size);\n        }\n\n        @Override\n        protected long sizeOf(String key, CacheEntry value) {\n            return Math.max(blockSize, value.size);\n        }\n\n        @Override\n        protected void entryRemoved(boolean evicted, String key, CacheEntry oldValue, CacheEntry newValue) {\n            super.entryRemoved(evicted, key, oldValue, newValue);\n            if (newValue != null)\n                return;\n            if (loading)\n                return;\n            new File(directory, key).delete();\n        }\n    }\n\n    InternalCache cache;\n    File directory;\n    long size;\n\n    Comparator<File> dateCompare = new Comparator<File>() {\n        @Override\n        public int compare(File lhs, File rhs) {\n            long l = lhs.lastModified();\n            long r = rhs.lastModified();\n            if (l < r)\n                return -1;\n            if (r > l)\n                return 1;\n            return 0;\n        }\n    };\n\n    boolean loading;\n    void load() {\n        loading = true;\n        try {\n            File[] files = directory.listFiles();\n            if (files == null)\n                return;\n            ArrayList<File> list = new ArrayList<File>();\n            Collections.addAll(list, files);\n            Collections.sort(list, dateCompare);\n\n            for (File file: list) {\n                String name = file.getName();\n                CacheEntry entry = new CacheEntry(file);\n                cache.put(name, entry);\n                cache.get(name);\n            }\n        }\n        finally {\n            loading = false;\n        }\n    }\n\n    private void doLoad() {\n        if (loadAsync) {\n            new Thread() {\n                @Override\n                public void run() {\n                    load();\n                }\n            }.start();\n        }\n        else {\n            load();\n        }\n    }\n\n    public FileCache(File directory, long size, boolean loadAsync) {\n        this.directory = directory;\n        this.size = size;\n        this.loadAsync = loadAsync;\n        cache = new InternalCache();\n\n        directory.mkdirs();\n        doLoad();\n    }\n\n    public long size() {\n        return cache.size();\n    }\n\n    public void clear() {\n        removeFiles(directory.listFiles());\n        cache.evictAll();\n    }\n\n    public Set<String> keySet() {\n        HashSet<String> ret = new HashSet<String>();\n        File[] files = directory.listFiles();\n        if (files == null)\n            return ret;\n        for (File file: files) {\n            String name = file.getName();\n            int last = name.lastIndexOf('.');\n            if (last != -1)\n                ret.add(name.substring(0, last));\n        }\n        return ret;\n    }\n\n    public void setMaxSize(long maxSize) {\n        cache.setMaxSize(maxSize);\n        doLoad();\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/util/FileUtility.java",
    "content": "package com.koushikdutta.async.util;\n\nimport java.io.File;\n\n/**\n * Created by koush on 4/7/14.\n */\npublic class FileUtility {\n    static public boolean deleteDirectory(File path) {\n        if (path.exists()) {\n            File[] files = path.listFiles();\n            if (files != null) {\n                for (int i = 0; i < files.length; i++) {\n                    if (files[i].isDirectory()) {\n                        deleteDirectory(files[i]);\n                    }\n                    else {\n                        files[i].delete();\n                    }\n                }\n            }\n        }\n        return (path.delete());\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/util/HashList.java",
    "content": "package com.koushikdutta.async.util;\n\nimport java.util.ArrayList;\nimport java.util.Hashtable;\nimport java.util.Set;\n\n/**\n * Created by koush on 5/27/13.\n */\npublic class HashList<T> {\n    Hashtable<String, TaggedList<T>> internal = new Hashtable<String, TaggedList<T>>();\n\n    public HashList() {\n    }\n\n    public Set<String> keySet() {\n        return internal.keySet();\n    }\n\n    public synchronized <V> V tag(String key) {\n        TaggedList<T> list = internal.get(key);\n        if (list == null)\n            return null;\n        return list.tag();\n    }\n\n    public synchronized <V> void tag(String key, V tag) {\n        TaggedList<T> list = internal.get(key);\n        if (list == null) {\n            list = new TaggedList<T>();\n            internal.put(key, list);\n        }\n        list.tag(tag);\n    }\n\n    public synchronized ArrayList<T> remove(String key) {\n        return internal.remove(key);\n    }\n\n    public synchronized int size() {\n        return internal.size();\n    }\n\n    public synchronized ArrayList<T> get(String key) {\n        return internal.get(key);\n    }\n\n    synchronized public boolean contains(String key) {\n        ArrayList<T> check = get(key);\n        return check != null && check.size() > 0;\n    }\n\n    synchronized public void add(String key, T value) {\n        ArrayList<T> ret = get(key);\n        if (ret == null) {\n            TaggedList<T> put = new TaggedList<T>();\n            ret = put;\n            internal.put(key, put);\n        }\n        ret.add(value);\n    }\n\n    synchronized public T pop(String key) {\n        TaggedList<T> values = internal.get(key);\n        if (values == null)\n            return null;\n        if (values.size() == 0)\n            return null;\n        return values.remove(values.size() - 1);\n    }\n\n    synchronized public boolean removeItem(String key, T value) {\n        TaggedList<T> values = internal.get(key);\n        if (values == null)\n            return false;\n\n        values.remove(value);\n        return values.size() == 0;\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/util/IdleTimeout.java",
    "content": "package com.koushikdutta.async.util;\n\nimport android.os.Handler;\n\nimport com.koushikdutta.async.AsyncServer;\n\npublic class IdleTimeout extends TimeoutBase {\n    Runnable callback;\n\n    public IdleTimeout(AsyncServer server, long delay) {\n        super(server, delay);\n\n    }\n\n    public IdleTimeout(Handler handler, long delay) {\n        super(handler, delay);\n    }\n\n    public void setTimeout(Runnable callback) {\n        this.callback = callback;\n    }\n\n    Object cancellable;\n    public void reset() {\n        handlerish.removeAllCallbacks(cancellable);\n        cancellable = handlerish.postDelayed(callback, delay);\n    }\n\n    public void cancel() {\n        // must post this, so that when it runs it removes everything in the queue,\n        // preventing any rescheduling.\n        // posting gaurantees there is not a reschedule in progress.\n        handlerish.post(() -> handlerish.removeAllCallbacks(cancellable));\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/util/LruCache.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.koushikdutta.async.util;\n\nimport java.util.LinkedHashMap;\nimport java.util.Locale;\nimport java.util.Map;\n\n/**\n * Static library version of {@link android.util.LruCache}. Used to write apps\n * that run on API levels prior to 12. When running on API level 12 or above,\n * this implementation is still used; it does not try to switch to the\n * framework's implementation. See the framework SDK documentation for a class\n * overview.\n */\npublic class LruCache<K, V> {\n    private final LinkedHashMap<K, V> map;\n\n    /** Size of this cache in units. Not necessarily the number of elements. */\n    private long size;\n    private long maxSize;\n\n    private int putCount;\n    private int createCount;\n    private int evictionCount;\n    private int hitCount;\n    private int missCount;\n\n    /**\n     * @param maxSize for caches that do not override {@link #sizeOf}, this is\n     *     the maximum number of entries in the cache. For all other caches,\n     *     this is the maximum sum of the sizes of the entries in this cache.\n     */\n    public LruCache(long maxSize) {\n        if (maxSize <= 0) {\n            throw new IllegalArgumentException(\"maxSize <= 0\");\n        }\n        this.maxSize = maxSize;\n        this.map = new LinkedHashMap<K, V>(0, 0.75f, true);\n    }\n\n    /**\n     * Returns the value for {@code key} if it exists in the cache or can be\n     * created by {@code #create}. If a value was returned, it is moved to the\n     * head of the queue. This returns null if a value is not cached and cannot\n     * be created.\n     */\n    public final V get(K key) {\n        if (key == null) {\n            throw new NullPointerException(\"key == null\");\n        }\n\n        V mapValue;\n        synchronized (this) {\n            mapValue = map.get(key);\n            if (mapValue != null) {\n                hitCount++;\n                return mapValue;\n            }\n            missCount++;\n        }\n\n        /*\n         * Attempt to create a value. This may take a long time, and the map\n         * may be different when create() returns. If a conflicting value was\n         * added to the map while create() was working, we leave that value in\n         * the map and release the created value.\n         */\n\n        V createdValue = create(key);\n        if (createdValue == null) {\n            return null;\n        }\n\n        synchronized (this) {\n            createCount++;\n            mapValue = map.put(key, createdValue);\n\n            if (mapValue != null) {\n                // There was a conflict so undo that last put\n                map.put(key, mapValue);\n            } else {\n                size += safeSizeOf(key, createdValue);\n            }\n        }\n\n        if (mapValue != null) {\n            entryRemoved(false, key, createdValue, mapValue);\n            return mapValue;\n        } else {\n            trimToSize(maxSize);\n            return createdValue;\n        }\n    }\n\n    /**\n     * Caches {@code value} for {@code key}. The value is moved to the head of\n     * the queue.\n     *\n     * @return the previous value mapped by {@code key}.\n     */\n    public final V put(K key, V value) {\n        if (key == null || value == null) {\n            throw new NullPointerException(\"key == null || value == null\");\n        }\n\n        V previous;\n        synchronized (this) {\n            putCount++;\n            size += safeSizeOf(key, value);\n            previous = map.put(key, value);\n            if (previous != null) {\n                size -= safeSizeOf(key, previous);\n            }\n        }\n\n        if (previous != null) {\n            entryRemoved(false, key, previous, value);\n        }\n\n        trimToSize(maxSize);\n        return previous;\n    }\n\n    /**\n     * @param maxSize the maximum size of the cache before returning. May be -1\n     *     to evict even 0-sized elements.\n     */\n    private void trimToSize(long maxSize) {\n        while (true) {\n            K key;\n            V value;\n            synchronized (this) {\n                if (size < 0 || (map.isEmpty() && size != 0)) {\n                    throw new IllegalStateException(getClass().getName()\n                            + \".sizeOf() is reporting inconsistent results!\");\n                }\n\n                if (size <= maxSize || map.isEmpty()) {\n                    break;\n                }\n\n                Map.Entry<K, V> toEvict = map.entrySet().iterator().next();\n                key = toEvict.getKey();\n                value = toEvict.getValue();\n                map.remove(key);\n                size -= safeSizeOf(key, value);\n                evictionCount++;\n            }\n\n            entryRemoved(true, key, value, null);\n        }\n    }\n\n    /**\n     * Removes the entry for {@code key} if it exists.\n     *\n     * @return the previous value mapped by {@code key}.\n     */\n    public final V remove(K key) {\n        if (key == null) {\n            throw new NullPointerException(\"key == null\");\n        }\n\n        V previous;\n        synchronized (this) {\n            previous = map.remove(key);\n            if (previous != null) {\n                size -= safeSizeOf(key, previous);\n            }\n        }\n\n        if (previous != null) {\n            entryRemoved(false, key, previous, null);\n        }\n\n        return previous;\n    }\n\n    /**\n     * Called for entries that have been evicted or removed. This method is\n     * invoked when a value is evicted to make space, removed by a call to\n     * {@link #remove}, or replaced by a call to {@link #put}. The default\n     * implementation does nothing.\n     *\n     * <p>The method is called without synchronization: other threads may\n     * access the cache while this method is executing.\n     *\n     * @param evicted true if the entry is being removed to make space, false\n     *     if the removal was caused by a {@link #put} or {@link #remove}.\n     * @param newValue the new value for {@code key}, if it exists. If non-null,\n     *     this removal was caused by a {@link #put}. Otherwise it was caused by\n     *     an eviction or a {@link #remove}.\n     */\n    protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {}\n\n    /**\n     * Called after a cache miss to compute a value for the corresponding key.\n     * Returns the computed value or null if no value can be computed. The\n     * default implementation returns null.\n     *\n     * <p>The method is called without synchronization: other threads may\n     * access the cache while this method is executing.\n     *\n     * <p>If a value for {@code key} exists in the cache when this method\n     * returns, the created value will be released with {@link #entryRemoved}\n     * and discarded. This can occur when multiple threads request the same key\n     * at the same time (causing multiple values to be created), or when one\n     * thread calls {@link #put} while another is creating a value for the same\n     * key.\n     */\n    protected V create(K key) {\n        return null;\n    }\n\n    private long safeSizeOf(K key, V value) {\n        long result = sizeOf(key, value);\n        if (result < 0) {\n            throw new IllegalStateException(\"Negative size: \" + key + \"=\" + value);\n        }\n        return result;\n    }\n\n    /**\n     * Returns the size of the entry for {@code key} and {@code value} in\n     * user-defined units.  The default implementation returns 1 so that size\n     * is the number of entries and max size is the maximum number of entries.\n     *\n     * <p>An entry's size must not change while it is in the cache.\n     */\n    protected long sizeOf(K key, V value) {\n        return 1;\n    }\n\n    /**\n     * Clear the cache, calling {@link #entryRemoved} on each removed entry.\n     */\n    public final void evictAll() {\n        trimToSize(-1); // -1 will evict 0-sized elements\n    }\n\n    /**\n     * For caches that do not override {@link #sizeOf}, this returns the number\n     * of entries in the cache. For all other caches, this returns the sum of\n     * the sizes of the entries in this cache.\n     */\n    public synchronized final long size() {\n        return size;\n    }\n\n    public void setMaxSize(long maxSize) {\n        this.maxSize = maxSize;\n    }\n\n    /**\n     * For caches that do not override {@link #sizeOf}, this returns the maximum\n     * number of entries in the cache. For all other caches, this returns the\n     * maximum sum of the sizes of the entries in this cache.\n     */\n    public synchronized final long maxSize() {\n        return maxSize;\n    }\n\n    /**\n     * Returns the number of times {@link #get} returned a value.\n     */\n    public synchronized final int hitCount() {\n        return hitCount;\n    }\n\n    /**\n     * Returns the number of times {@link #get} returned null or required a new\n     * value to be created.\n     */\n    public synchronized final int missCount() {\n        return missCount;\n    }\n\n    /**\n     * Returns the number of times {@link #create(Object)} returned a value.\n     */\n    public synchronized final int createCount() {\n        return createCount;\n    }\n\n    /**\n     * Returns the number of times {@link #put} was called.\n     */\n    public synchronized final int putCount() {\n        return putCount;\n    }\n\n    /**\n     * Returns the number of values that have been evicted.\n     */\n    public synchronized final int evictionCount() {\n        return evictionCount;\n    }\n\n    /**\n     * Returns a copy of the current contents of the cache, ordered from least\n     * recently accessed to most recently accessed.\n     */\n    public synchronized final Map<K, V> snapshot() {\n        return new LinkedHashMap<K, V>(map);\n    }\n\n    @Override public synchronized final String toString() {\n        int accesses = hitCount + missCount;\n        int hitPercent = accesses != 0 ? (100 * hitCount / accesses) : 0;\n        return String.format(Locale.ENGLISH, \"LruCache[maxSize=%d,hits=%d,misses=%d,hitRate=%d%%]\",\n                maxSize, hitCount, missCount, hitPercent);\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/util/StreamUtility.java",
    "content": "package com.koushikdutta.async.util;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.Closeable;\nimport java.io.DataInputStream;\nimport java.io.DataOutputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.nio.ByteBuffer;\nimport java.nio.channels.Channels;\nimport java.nio.channels.ReadableByteChannel;\nimport java.nio.channels.WritableByteChannel;\n\npublic class StreamUtility {\n    public static void fastChannelCopy(final ReadableByteChannel src, final WritableByteChannel dest) throws IOException {\n        final ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024);\n        while (src.read(buffer) != -1) {\n            // prepare the buffer to be drained\n            buffer.flip();\n            // write to the channel, may block\n            dest.write(buffer);\n            // If partial transfer, shift remainder down\n            // If buffer is empty, same as doing recycle()\n            buffer.compact();\n        }\n        // EOF will leave buffer in fill state\n        buffer.flip();\n        // make sure the buffer is fully drained.\n        while (buffer.hasRemaining()) {\n            dest.write(buffer);\n        }\n    }\n\n\tpublic static void copyStream(InputStream input, OutputStream output) throws IOException\n\t{\n\t    final ReadableByteChannel inputChannel = Channels.newChannel(input);\n\t    final WritableByteChannel outputChannel = Channels.newChannel(output);\n\t    // copy the channels\n\t    fastChannelCopy(inputChannel, outputChannel);\n\t}\n\n    public static byte[] readToEndAsArray(InputStream input) throws IOException\n    {\n        DataInputStream dis = new DataInputStream(input);\n        byte[] stuff = new byte[1024];\n        ByteArrayOutputStream buff = new ByteArrayOutputStream();\n        int read = 0;\n        while ((read = dis.read(stuff)) != -1)\n        {\n            buff.write(stuff, 0, read);\n        }\n        dis.close();\n        return buff.toByteArray();\n    }\n    \n\tpublic static String readToEnd(InputStream input) throws IOException\n\t{\n\t    return new String(readToEndAsArray(input));\n\t}\n\n    static public String readFile(String filename) throws IOException {\n        return readFile(new File(filename));\n    }\n\n    static public String readFileSilent(String filename) {\n        try {\n            return readFile(new File(filename));\n        }\n        catch (IOException e) {\n            return null;\n        }\n    }\n\n    static public String readFile(File file) throws IOException {\n        byte[] buffer = new byte[(int) file.length()];\n        DataInputStream input = null;\n        try {\n            input = new DataInputStream(new FileInputStream(file));\n            input.readFully(buffer);\n        } finally {\n            closeQuietly(input);\n        }\n        return new String(buffer);\n    }\n    \n    public static void writeFile(File file, String string) throws IOException {\n        file.getParentFile().mkdirs();\n        DataOutputStream dout = new DataOutputStream(new FileOutputStream(file));\n        dout.write(string.getBytes());\n        dout.close();\n    }\n    \n    public static void writeFile(String file, String string) throws IOException {\n        writeFile(new File(file), string);\n    }\n    \n    public static void closeQuietly(Closeable... closeables) {\n        if (closeables == null)\n            return;\n        for (Closeable closeable : closeables) {\n            if (closeable != null) {\n                try {\n                    closeable.close();\n                } catch (Exception e) {\n                    // http://stackoverflow.com/a/156525/9636\n\n                    // also, catch all exceptions because some implementations throw random crap\n                    // like ArrayStoreException\n                }\n            }\n        }\n    }\n\n    public static void eat(InputStream input) throws IOException {\n        byte[] stuff = new byte[1024];\n        while (input.read(stuff) != -1);\n    }\n}\n\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/util/TaggedList.java",
    "content": "package com.koushikdutta.async.util;\n\nimport java.util.ArrayList;\n\npublic class TaggedList<T> extends ArrayList<T> {\n    private Object tag;\n\n    public synchronized <V> V tag() {\n        return (V)tag;\n    }\n\n    public synchronized <V> void tag(V tag) {\n        this.tag = tag;\n    }\n\n    public synchronized <V> void tagNull(V tag) {\n        if (this.tag == null)\n            this.tag = tag;\n    }\n}"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/util/ThrottleTimeout.java",
    "content": "package com.koushikdutta.async.util;\n\nimport android.os.Handler;\n\nimport com.koushikdutta.async.AsyncServer;\nimport com.koushikdutta.async.callback.ValueCallback;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Created by koush on 7/19/16.\n */\npublic class ThrottleTimeout<T> extends TimeoutBase {\n    ValueCallback<List<T>> callback;\n    ArrayList<T> values = new ArrayList<>();\n    ThrottleMode throttleMode = ThrottleMode.Collect;\n\n    public enum ThrottleMode {\n        /**\n         * The timeout will keep resetting until it expires, at which point all\n         * the collected values will be invoked on the callback.\n         */\n        Collect,\n        /**\n         * The callback will be invoked immediately with the first, but future values will be\n         * metered until it expires.\n         */\n        Meter,\n    }\n\n\n    public ThrottleTimeout(final AsyncServer server, long delay, ValueCallback<List<T>> callback) {\n        super(server, delay);\n        this.callback = callback;\n    }\n\n    public ThrottleTimeout(final Handler handler, long delay, ValueCallback<List<T>> callback) {\n        super(handler, delay);\n        this.callback = callback;\n    }\n\n    public void setCallback(ValueCallback<List<T>> callback) {\n        this.callback = callback;\n    }\n\n    private void runCallback() {\n        cancellable = null;\n        ArrayList<T> v = new ArrayList<>(values);\n        values.clear();\n        callback.onResult(v);\n    }\n\n    Object cancellable;\n    public synchronized void postThrottled(final T value) {\n        handlerish.post(() -> {\n            values.add(value);\n\n            if (throttleMode == ThrottleMode.Collect) {\n                // cancel the existing, schedule a new one, and wait.\n                handlerish.removeAllCallbacks(cancellable);\n                cancellable = handlerish.postDelayed(this::runCallback, delay);\n            }\n            else {\n                // nothing is pending, so this can be fired off immediately\n                if (cancellable == null) {\n                    runCallback();\n\n                    // meter future invocations\n                    cancellable = handlerish.postDelayed(this::runCallback, delay);\n                }\n            }\n        });\n    }\n\n    public void setThrottleMode(ThrottleMode throttleMode) {\n        this.throttleMode = throttleMode;\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/util/TimeoutBase.java",
    "content": "package com.koushikdutta.async.util;\n\nimport android.os.Handler;\n\nimport com.koushikdutta.async.AsyncServer;\nimport com.koushikdutta.async.future.Cancellable;\n\npublic class TimeoutBase {\n    protected Handlerish handlerish;\n    protected long delay;\n\n    interface Handlerish {\n        void post(Runnable r);\n        Object postDelayed(Runnable r, long delay);\n        void removeAllCallbacks(Object cancellable);\n    }\n\n    protected void onCallback() {\n\n    }\n\n    public TimeoutBase(final AsyncServer server, long delay) {\n        this.delay = delay;\n        this.handlerish = new Handlerish() {\n            @Override\n            public void post(Runnable r) {\n                server.post(r);\n            }\n\n            @Override\n            public Object postDelayed(Runnable r, long delay) {\n                return server.postDelayed(r, delay);\n            }\n\n            @Override\n            public void removeAllCallbacks(Object cancellable) {\n                if (cancellable == null)\n                    return;\n                ((Cancellable)cancellable).cancel();\n            }\n        };\n    }\n\n    public TimeoutBase(final Handler handler, long delay) {\n        this.delay = delay;\n        this.handlerish = new Handlerish() {\n            @Override\n            public void post(Runnable r) {\n                handler.post(r);\n            }\n\n            @Override\n            public Object postDelayed(Runnable r, long delay) {\n                handler.postDelayed(r, delay);\n                return r;\n            }\n\n            @Override\n            public void removeAllCallbacks(Object cancellable) {\n                if (cancellable == null)\n                    return;\n                handler.removeCallbacks((Runnable)cancellable);\n            }\n        };\n    }\n\n    public void setDelay(long delay) {\n        this.delay = delay;\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/util/UntypedHashtable.java",
    "content": "package com.koushikdutta.async.util;\n\nimport java.util.Hashtable;\n\npublic class UntypedHashtable {\n    private Hashtable<String, Object> hash = new Hashtable<String, Object>();\n\n    public void put(String key, Object value) {\n        hash.put(key, value);\n    }\n\n    public void remove(String key) {\n        hash.remove(key);\n    }\n\n    public <T> T get(String key, T defaultValue) {\n        T ret = get(key);\n        if (ret == null)\n            return defaultValue;\n        return ret;\n    }\n\n    public <T> T get(String key) {\n        return (T)hash.get(key);\n    }\n}"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/wrapper/AsyncSocketWrapper.java",
    "content": "package com.koushikdutta.async.wrapper;\n\nimport com.koushikdutta.async.AsyncSocket;\n\npublic interface AsyncSocketWrapper extends AsyncSocket, DataEmitterWrapper {\n    public AsyncSocket getSocket();\n}\n"
  },
  {
    "path": "AndroidAsync/src/com/koushikdutta/async/wrapper/DataEmitterWrapper.java",
    "content": "package com.koushikdutta.async.wrapper;\n\nimport com.koushikdutta.async.DataEmitter;\n\npublic interface DataEmitterWrapper extends DataEmitter {\n    public DataEmitter getDataEmitter();\n}\n"
  },
  {
    "path": "AndroidAsync/test/assets/hello.txt",
    "content": "hello world"
  },
  {
    "path": "AndroidAsync/test/assets/test.json",
    "content": "{\n  \"foo\": \"bar\"\n}"
  },
  {
    "path": "AndroidAsync/test/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <string name=\"app_name\">AndroidAsyncTestTest</string>\n\n</resources>"
  },
  {
    "path": "AndroidAsync/test/src/com/koushikdutta/async/test/BodyTests.java",
    "content": "package com.koushikdutta.async.test;\n\nimport androidx.test.runner.AndroidJUnit4;\n\nimport com.koushikdutta.async.http.Multimap;\nimport com.koushikdutta.async.http.body.UrlEncodedFormBody;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\n/**\n * Created by koush on 3/19/14.\n */\n@RunWith(AndroidJUnit4.class)\npublic class BodyTests {\n    @Test\n    public void testNullValue() throws Exception {\n        Multimap mm = new Multimap();\n        mm.add(\"hello\", null);\n        UrlEncodedFormBody body = new UrlEncodedFormBody(mm);\n\n        int length = body.length();\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/test/src/com/koushikdutta/async/test/ByteUtilTests.java",
    "content": "package com.koushikdutta.async.test;\n\nimport com.koushikdutta.async.ByteBufferList;\nimport com.koushikdutta.async.FilteredDataEmitter;\nimport com.koushikdutta.async.PushParser;\nimport com.koushikdutta.async.TapCallback;\nimport com.koushikdutta.async.Util;\nimport com.koushikdutta.async.callback.DataCallback;\n\nimport junit.framework.TestCase;\n\n/**\n * Created by koush on 5/17/13.\n */\npublic class ByteUtilTests extends TestCase {\n    int valRead;\n    public void testPushParserUntil() {\n        valRead = 0;\n        FilteredDataEmitter mock = new FilteredDataEmitter() {\n            @Override\n            public boolean isPaused() {\n                return false;\n            }\n        };\n        new PushParser(mock)\n            .until((byte)0, new DataCallback.NullDataCallback())\n            .readInt(new PushParser.ParseCallback<Integer>() {\n                public void parsed(Integer arg) {\n                    valRead = arg;\n                }\n            });\n        byte[] bytes = new byte[] { 5, 5, 5, 5, 0, 10, 5, 5, 5 };\n        Util.emitAllData(mock, new ByteBufferList(bytes));\n        assertEquals(valRead, 0x0A050505);\n    }\n\n    public void testPushParserTapUntil() {\n        valRead = 0;\n        FilteredDataEmitter mock = new FilteredDataEmitter() {\n            @Override\n            public boolean isPaused() {\n                return false;\n            }\n        };\n        new PushParser(mock)\n                .until((byte)0, new DataCallback.NullDataCallback())\n                .readInt()\n                .tap(new TapCallback() {\n                    public void parsed(int arg) {\n                        valRead = arg;\n                    }\n                });\n        byte[] bytes = new byte[] { 5, 5, 5, 5, 0, 10, 5, 5, 5 };\n        Util.emitAllData(mock, new ByteBufferList(bytes));\n        assertEquals(valRead, 0x0A050505);\n    }\n\n    int readInt;\n    byte readByte;\n    String readString;\n\n    public void testTapCallback() {\n        readInt = 0;\n        readByte = 0;\n        readString = \"\";\n\n        FilteredDataEmitter mock = new FilteredDataEmitter() {\n            @Override\n            public boolean isPaused() {\n                return false;\n            }\n        };\n        new PushParser(mock)\n                .readInt()\n                .readByte()\n                .readString()\n                .tap(new TapCallback() {\n                    void tap(int i, byte b, String s) {\n                        readInt = i;\n                        readByte = b;\n                        readString = s;\n                    }\n                });\n\n        byte[] bytes = new byte[] { 10, 5, 5, 5, 3, 0, 0, 0, 4, 116, 101, 115, 116 };\n        Util.emitAllData(mock, new ByteBufferList(bytes));\n        assertEquals(readInt, 0x0A050505);\n        assertEquals(readByte, (byte) 3);\n        assertEquals(readString, \"test\");\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/test/src/com/koushikdutta/async/test/CacheTests.java",
    "content": "package com.koushikdutta.async.test;\n\nimport android.content.res.AssetManager;\nimport androidx.test.InstrumentationRegistry;\nimport androidx.test.runner.AndroidJUnit4;\n\nimport com.koushikdutta.async.AsyncServer;\nimport com.koushikdutta.async.AsyncServerSocket;\nimport com.koushikdutta.async.ByteBufferList;\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.FilteredDataEmitter;\nimport com.koushikdutta.async.callback.DataCallback;\nimport com.koushikdutta.async.http.AsyncHttpClient;\nimport com.koushikdutta.async.http.AsyncHttpGet;\nimport com.koushikdutta.async.http.AsyncHttpResponse;\nimport com.koushikdutta.async.http.HttpDate;\nimport com.koushikdutta.async.http.cache.ResponseCacheMiddleware;\nimport com.koushikdutta.async.http.server.AsyncHttpServer;\nimport com.koushikdutta.async.http.server.AsyncHttpServerRequest;\nimport com.koushikdutta.async.http.server.AsyncHttpServerResponse;\nimport com.koushikdutta.async.http.server.HttpServerRequestCallback;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\nimport java.io.File;\nimport java.nio.ByteBuffer;\nimport java.util.Date;\nimport java.util.concurrent.Semaphore;\nimport java.util.concurrent.TimeUnit;\n\nimport static androidx.test.InstrumentationRegistry.getContext;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\nimport static org.junit.Assert.fail;\n\n/**\n * Created by koush on 6/13/13.\n */\n@RunWith(AndroidJUnit4.class)\npublic class CacheTests {\n    public void testMaxAgePrivate() throws Exception {\n        AsyncHttpClient client = new AsyncHttpClient(AsyncServer.getDefault());\n        ResponseCacheMiddleware cache = ResponseCacheMiddleware.addCache(client, new File(getContext().getFilesDir(), \"AndroidAsyncTest\"), 1024 * 1024 * 10);\n        AsyncHttpServer httpServer = new AsyncHttpServer();\n        try {\n            httpServer.get(\"/uname/(.*)\", new HttpServerRequestCallback() {\n                @Override\n                public void onRequest(AsyncHttpServerRequest request, AsyncHttpServerResponse response) {\n                    response.getHeaders().set(\"Date\", HttpDate.format(new Date()));\n                    response.getHeaders().set(\"Cache-Control\", \"private, max-age=10000\");\n                    response.send(request.getMatcher().group(1));\n                }\n            });\n\n            AsyncServerSocket socket = httpServer.listen(AsyncServer.getDefault(), 0);\n            int port = socket.getLocalPort();\n            // clear the old cache\n            cache.clear();\n\n            client.executeString(new AsyncHttpGet(\"http://localhost:\" + port + \"/uname/43434\"), null).get();\n\n            client.executeString(new AsyncHttpGet(\"http://localhost:\" + port + \"/uname/43434\"), null).get();\n\n\n            assertEquals(cache.getCacheHitCount(), 1);\n            assertEquals(cache.getNetworkCount(), 1);\n        }\n        finally {\n            AsyncServer.getDefault().stop();\n            client.getMiddleware().remove(cache);\n        }\n    }\n\n    final static String dataNameAndHash = \"6691924d7d24237d3b3679310157d640\";\n    @Test\n    public void test304() throws Exception {\n        try {\n            AsyncHttpServer httpServer = new AsyncHttpServer();\n            AsyncServerSocket socket = httpServer.listen(AsyncServer.getDefault(), 0);\n            int port = socket.getLocalPort();\n\n            AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();\n            httpServer.directory(InstrumentationRegistry.getTargetContext(), \"/.*?\", \"\");\n\n            AsyncHttpClient client = new AsyncHttpClient(AsyncServer.getDefault());\n            ByteBufferList bb = client.executeByteBufferList(new AsyncHttpGet(\"http://localhost:\" + port + \"/\" + dataNameAndHash), new AsyncHttpClient.DownloadCallback() {\n                @Override\n                public void onCompleted(Exception e, AsyncHttpResponse source, ByteBufferList result) {\n                    System.out.println(source.headers());\n                }\n            })\n                    .get();\n        }\n        finally {\n            AsyncServer.getDefault().stop();\n        }\n    }\n\n    private static final long TIMEOUT = 1000L;\n    public void testFilteredDataEmitter() throws Exception {\n        final Semaphore semaphore = new Semaphore(0);\n\n        FilteredDataEmitter f = new FilteredDataEmitter() {\n            @Override\n            public boolean isPaused() {\n                return false;\n            }\n        };\n\n        f.setDataCallback(new DataCallback() {\n            @Override\n            public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n                assertEquals(bb.readString(), \"hello\");\n                bb.recycle();\n                semaphore.release();\n            }\n        });\n\n        f.onDataAvailable(f, new ByteBufferList().add(ByteBuffer.wrap(\"hello\".getBytes())));\n        assertTrue(\"timeout\", semaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS));\n\n        f.setDataCallback(new DataCallback() {\n            @Override\n            public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n                fail();\n            }\n        });\n        f.close();\n\n        f.onDataAvailable(f, new ByteBufferList().add(ByteBuffer.wrap(\"hello\".getBytes())));\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/test/src/com/koushikdutta/async/test/CallbackTests.java",
    "content": "package com.koushikdutta.async.test;\n\nimport androidx.test.runner.AndroidJUnit4;\n\nimport com.koushikdutta.async.AsyncServer;\nimport com.koushikdutta.async.AsyncServerSocket;\nimport com.koushikdutta.async.AsyncSocket;\nimport com.koushikdutta.async.ByteBufferList;\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.Util;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.callback.ConnectCallback;\nimport com.koushikdutta.async.callback.DataCallback;\nimport com.koushikdutta.async.callback.ListenCallback;\nimport com.koushikdutta.async.future.FutureCallback;\nimport com.koushikdutta.async.http.AsyncHttpClient;\nimport com.koushikdutta.async.http.AsyncHttpGet;\nimport com.koushikdutta.async.http.server.AsyncHttpServer;\nimport com.koushikdutta.async.http.server.AsyncHttpServerRequest;\nimport com.koushikdutta.async.http.server.AsyncHttpServerResponse;\nimport com.koushikdutta.async.http.server.HttpServerRequestCallback;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\nimport static org.junit.Assert.fail;\n\n/**\n * <a href=\"http://d.android.com/tools/testing/testing_android.html\">Testing Fundamentals</a>\n */\n@RunWith(AndroidJUnit4.class)\npublic class CallbackTests {\n    @Test\n    public void testThrow() throws Exception {\n        int port = AsyncServer.getDefault().listen(null, 0, new ListenCallback() {\n            @Override\n            public void onAccepted(AsyncSocket socket) {\n                Util.writeAll(socket, \"poop\".getBytes(), new CompletedCallback() {\n                    @Override\n                    public void onCompleted(Exception ex) {\n\n                    }\n                });\n\n                socket.setDataCallback(new DataCallback.NullDataCallback());\n            }\n\n            @Override\n            public void onListening(AsyncServerSocket socket) {\n\n            }\n\n            @Override\n            public void onCompleted(Exception ex) {\n\n            }\n        }).getLocalPort();\n\n\n\n        AsyncServer.getDefault().connectSocket(\"localhost\", port, new ConnectCallback() {\n            @Override\n            public void onConnectCompleted(Exception ex, AsyncSocket socket) {\n                socket.setDataCallback(new DataCallback() {\n                    @Override\n                    public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n                        bb.recycle();\n                        throw new NullPointerException(\"this should crash?\");\n\n                    }\n                });\n            }\n        });\n\n        Thread.sleep(1000000);\n        fail();\n    }\n\n    @Test\n    public void testHttpServerThrow() throws Exception {\n        AsyncHttpServer server = new AsyncHttpServer();\n        int port = server.listen(0).getLocalPort();\n\n        server.get(\"/\", new HttpServerRequestCallback() {\n            @Override\n            public void onRequest(AsyncHttpServerRequest request, AsyncHttpServerResponse response) {\n                AsyncHttpClient.getDefaultInstance().executeString(new AsyncHttpGet(\"https://google.com\"), null)\n                .setCallback(new FutureCallback<String>() {\n                    @Override\n                    public void onCompleted(Exception e, String result) {\n                        throw new NullPointerException();\n                    }\n                });\n            }\n        });\n\n        String result = AsyncHttpClient.getDefaultInstance().executeString(new AsyncHttpGet(\"http://localhost:\" + port + \"/\"), null).get();\n\n        Thread.sleep(100000000);\n        fail();\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/test/src/com/koushikdutta/async/test/ConscryptTests.java",
    "content": "/*\n * Copyright 2013 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.koushikdutta.async.test;\n\n\nimport android.content.Context;\nimport androidx.test.runner.AndroidJUnit4;\n\nimport org.junit.runner.RunWith;\n\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.net.InetSocketAddress;\nimport java.net.Socket;\nimport java.nio.ByteBuffer;\nimport java.nio.charset.Charset;\n\nimport javax.net.ssl.SSLContext;\nimport javax.net.ssl.SSLEngine;\nimport javax.net.ssl.SSLEngineResult;\n\nimport static androidx.test.InstrumentationRegistry.getContext;\nimport static org.junit.Assert.assertEquals;\n\n/**\n * Created by koush on 7/15/14.\n */\n@RunWith(AndroidJUnit4.class)\npublic class ConscryptTests {\n    boolean initialized;\n    Field peerHost;\n    Field peerPort;\n    Field sslParameters;\n    Field npnProtocols;\n    Field alpnProtocols;\n    Field sslNativePointer;\n    Field useSni;\n    Method nativeGetNpnNegotiatedProtocol;\n    Method nativeGetAlpnNegotiatedProtocol;\n\n    private void configure(SSLEngine engine, String host, int port) throws Exception {\n        if (!initialized) {\n            initialized = true;\n            peerHost = engine.getClass().getSuperclass().getDeclaredField(\"peerHost\");\n            peerPort = engine.getClass().getSuperclass().getDeclaredField(\"peerPort\");\n            sslParameters = engine.getClass().getDeclaredField(\"sslParameters\");\n            npnProtocols = sslParameters.getType().getDeclaredField(\"npnProtocols\");\n            alpnProtocols = sslParameters.getType().getDeclaredField(\"alpnProtocols\");\n            useSni = sslParameters.getType().getDeclaredField(\"useSni\");\n            sslNativePointer = engine.getClass().getDeclaredField(\"sslNativePointer\");\n            String nativeCryptoName = sslParameters.getType().getPackage().getName() + \".NativeCrypto\";\n            nativeGetNpnNegotiatedProtocol = Class.forName(nativeCryptoName, true, sslParameters.getType().getClassLoader())\n            .getDeclaredMethod(\"SSL_get_npn_negotiated_protocol\", long.class);\n            nativeGetAlpnNegotiatedProtocol = Class.forName(nativeCryptoName, true, sslParameters.getType().getClassLoader())\n            .getDeclaredMethod(\"SSL_get0_alpn_selected\", long.class);\n\n            peerHost.setAccessible(true);\n            peerPort.setAccessible(true);\n            sslParameters.setAccessible(true);\n            npnProtocols.setAccessible(true);\n            alpnProtocols.setAccessible(true);\n            useSni.setAccessible(true);\n            sslNativePointer.setAccessible(true);\n            nativeGetNpnNegotiatedProtocol.setAccessible(true);\n            nativeGetAlpnNegotiatedProtocol.setAccessible(true);\n        }\n\n        byte[] protocols = concatLengthPrefixed(\n        \"http/1.1\",\n        \"spdy/3.1\"\n        );\n\n        peerHost.set(engine, host);\n        peerPort.set(engine, port);\n        Object sslp = sslParameters.get(engine);\n//        npnProtocols.set(sslp, protocols);\n        alpnProtocols.set(sslp, protocols);\n        useSni.set(sslp, true);\n    }\n\n    static byte[] concatLengthPrefixed(String... protocols) {\n        ByteBuffer result = ByteBuffer.allocate(8192);\n        for (String protocol: protocols) {\n            result.put((byte) protocol.toString().length());\n            result.put(protocol.toString().getBytes(Charset.forName(\"UTF-8\")));\n        }\n        result.flip();\n        byte[] ret = new byte[result.remaining()];\n        result.get(ret);\n        return ret;\n    }\n\n    public void testConscryptSSLEngineNPNHandshakeBug() throws Exception {\n//        Security.insertProviderAt(new OpenSSLProvider(\"MyNameBlah\"), 1);\n\n        Context gms = getContext().createPackageContext(\"com.google.android.gms\", Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);\n        gms\n        .getClassLoader()\n        .loadClass(\"com.google.android.gms.common.security.ProviderInstallerImpl\")\n        .getMethod(\"insertProvider\", Context.class)\n        .invoke(null, getContext());\n\n        SSLContext ctx = SSLContext.getInstance(\"TLS\");\n        ctx.init(null, null, null);\n\n        SSLEngine engine = ctx.createSSLEngine();\n        configure(engine, \"www.google.com\", 443);\n        engine.setUseClientMode(true);\n        engine.beginHandshake();\n\n        Socket socket = new Socket();\n        socket.connect(new InetSocketAddress(\"www.google.com\", 443));\n\n        InputStream is = socket.getInputStream();\n        OutputStream os = socket.getOutputStream();\n\n        byte[] buf = new byte[65536];\n        ByteBuffer unwrap = null;\n        ByteBuffer dummy = ByteBuffer.allocate(65536);\n\n        SSLEngineResult.HandshakeStatus handshakeStatus = engine.getHandshakeStatus();\n\n        while (handshakeStatus != SSLEngineResult.HandshakeStatus.FINISHED\n            && handshakeStatus != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {\n            if (handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP) {\n                System.out.println(\"waiting for read... \" + engine.getHandshakeStatus());\n                int read = is.read(buf);\n                System.out.println(\"read: \" + read);\n                if (read <= 0)\n                    throw new Exception(\"closed!\");\n\n                if (unwrap != null) {\n                    int bufLen = unwrap.remaining() + read;\n                    ByteBuffer b = ByteBuffer.allocate(bufLen);\n                    b.put(unwrap);\n                    b.put(buf, 0, read);\n                    b.flip();\n                    unwrap = b;\n                }\n                else {\n                    unwrap = ByteBuffer.wrap(buf, 0, read);\n                }\n\n                if (!unwrap.hasRemaining()) {\n                    unwrap = null;\n                }\n\n                dummy.clear();\n                SSLEngineResult res = engine.unwrap(unwrap, dummy);\n                System.out.println(\"data remaining after unwrap: \" + unwrap.remaining());\n                handshakeStatus = res.getHandshakeStatus();\n            }\n\n            if (handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP) {\n                dummy.clear();\n                SSLEngineResult res = engine.wrap(ByteBuffer.allocate(0), dummy);\n                handshakeStatus = res.getHandshakeStatus();\n                dummy.flip();\n                if (dummy.hasRemaining()) {\n                    os.write(dummy.array(), 0, dummy.remaining());\n                }\n            }\n            else if (handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_TASK) {\n                engine.getDelegatedTask().run();\n            }\n        }\n\n\n        System.out.println(\"Done handshaking! Thank you come again.\");\n        long ptr = (Long)sslNativePointer.get(engine);\n        byte[] proto = (byte[]) nativeGetAlpnNegotiatedProtocol.invoke(null, ptr);\n//        byte[] proto = (byte[]) nativeGetNpnNegotiatedProtocol.invoke(null, ptr);\n        String protoString = new String(proto);\n        System.out.println(\"negotiated protocol was: \" + protoString);\n        assertEquals(protoString, \"spdy/3.1\");\n\n        socket.close();\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/test/src/com/koushikdutta/async/test/ConvertTests.java",
    "content": "package com.koushikdutta.async.test;\n\nimport androidx.test.runner.AndroidJUnit4;\n\nimport com.koushikdutta.async.future.SimpleFuture;\n\nimport org.json.JSONObject;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\nimport java.nio.ByteBuffer;\n\nimport static com.koushikdutta.async.future.Converter.convert;\nimport static org.junit.Assert.assertEquals;\n\n@RunWith(AndroidJUnit4.class)\npublic class ConvertTests {\n    @Test\n    public void testConvert() throws Exception {\n        ByteBuffer buf = convert(new SimpleFuture<>(new JSONObject()))\n        .to(ByteBuffer.class)\n        .get();\n\n        assertEquals(buf.remaining(), 2);\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/test/src/com/koushikdutta/async/test/DnsTests.java",
    "content": "package com.koushikdutta.async.test;\n\nimport com.koushikdutta.async.AsyncDatagramSocket;\nimport com.koushikdutta.async.AsyncServer;\nimport com.koushikdutta.async.AsyncSocket;\nimport com.koushikdutta.async.ByteBufferList;\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.callback.ConnectCallback;\nimport com.koushikdutta.async.callback.DataCallback;\nimport com.koushikdutta.async.dns.Dns;\nimport com.koushikdutta.async.dns.DnsResponse;\nimport com.koushikdutta.async.future.FutureCallback;\n\nimport junit.framework.TestCase;\n\nimport java.net.DatagramPacket;\nimport java.net.DatagramSocket;\nimport java.net.Inet4Address;\nimport java.net.InetAddress;\nimport java.net.InetSocketAddress;\nimport java.net.MulticastSocket;\nimport java.net.UnknownHostException;\nimport java.nio.channels.DatagramChannel;\nimport java.util.concurrent.Semaphore;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * Created by koush on 10/20/13.\n */\npublic class DnsTests extends TestCase {\n    public void testLookup() throws Exception {\n//        final Semaphore semaphore = new Semaphore(0);\n//        Dns.lookup(\"google.com\")\n//        .setCallback(new FutureCallback<DnsResponse>() {\n//            @Override\n//            public void onCompleted(Exception e, DnsResponse result) {\n//                semaphore.release();\n//            }\n//        });\n//\n//        semaphore.tryAcquire(1000000, TimeUnit.MILLISECONDS);\n    }\n\n    public void testMulticastLookup() throws Exception {\n//        MulticastSocket socket = new MulticastSocket(5353);\n//        socket.joinGroup(InetAddress.getByName(\"224.0.0.251\"));\n//        DatagramChannel channel = socket.getChannel();\n//        assertNotNull(channel);\n\n//        while (true) {\n//            DatagramPacket packet = new DatagramPacket(new byte[2048], 2048);\n//            socket.receive(packet);\n//            System.out.println(new String(packet.getData()));\n//        }\n\n//        AsyncDatagramSocket dgram = AsyncServer.getDefault().openDatagram(new InetSocketAddress(5353), true);\n//        ((DatagramSocket)dgram.getSocket()).setReuseAddress(true);\n//        dgram.setDataCallback(new DataCallback() {\n//            @Override\n//            public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n//                System.out.println(bb.readString());\n//            }\n//        });\n//        ((DatagramSocket)dgram.getSocket()).setBroadcast(true);\n\n\n//        final Semaphore semaphore = new Semaphore(0);\n//        Dns.multicastLookup(\"_airplay._tcp.local\", new FutureCallback<DnsResponse>() {\n//            @Override\n//            public void onCompleted(Exception e, DnsResponse result) {\n////                semaphore.release();\n//            }\n//        });\n//\n//        semaphore.tryAcquire(1000000, TimeUnit.MILLISECONDS);\n    }\n\n    public void testNoDomain() throws Exception {\n        AsyncServer server = new AsyncServer();\n\n        try {\n            final Semaphore semaphore = new Semaphore(0);\n            server.connectSocket(\"www.clockworkmod-notfound.com\", 8080, new ConnectCallback() {\n                @Override\n                public void onConnectCompleted(Exception ex, AsyncSocket socket) {\n                    assertTrue(ex instanceof UnknownHostException);\n                    semaphore.release();\n                }\n            });\n            assertTrue(semaphore.tryAcquire(5000, TimeUnit.MILLISECONDS));\n        }\n        finally {\n            server.stop();\n        }\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/test/src/com/koushikdutta/async/test/FileCacheTests.java",
    "content": "package com.koushikdutta.async.test;\n\nimport androidx.test.runner.AndroidJUnit4;\n\nimport com.koushikdutta.async.util.FileCache;\nimport com.koushikdutta.async.util.StreamUtility;\n\nimport org.junit.runner.RunWith;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\n\nimport static androidx.test.InstrumentationRegistry.getContext;\nimport static junit.framework.TestCase.assertFalse;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\nimport static org.junit.Assert.fail;\n\n/**\n * Created by koush on 4/13/14.\n */\n@RunWith(AndroidJUnit4.class)\npublic class FileCacheTests {\n    protected void setUp() throws Exception {\n        File dir = new File(getContext().getCacheDir(), \"filecache\");\n        File[] files = dir.listFiles();\n        if (files == null)\n            return;\n        for (File f: files)\n            f.delete();\n    }\n\n    public void testSimple() throws Exception {\n        setUp();\n\n        FileCache cache = new FileCache(new File(getContext().getCacheDir(), \"filecache\"), 100000, false);\n        cache.setBlockSize(1);\n\n        File temp = cache.getTempFile();\n        StreamUtility.writeFile(temp, \"hello\");\n\n        cache.commitTempFiles(\"test\", temp);\n\n        String value = StreamUtility.readToEnd(cache.get(\"test\"));\n        assertEquals(value, \"hello\");\n    }\n\n    public void testEviction() throws Exception {\n        setUp();\n\n        FileCache cache = new FileCache(new File(getContext().getCacheDir(), \"filecache\"), 25, false);\n        cache.setBlockSize(1);\n\n        for (int i = 0; i < 10; i++) {\n            File temp = cache.getTempFile();\n            StreamUtility.writeFile(temp, \"hello\");\n            cache.commitTempFiles(\"test\" + i, temp);\n            String value = StreamUtility.readToEnd(cache.get(\"test\" + i));\n            assertEquals(value, \"hello\");\n        }\n\n        assertEquals(cache.size(), 25);\n        File dir = new File(getContext().getCacheDir(), \"filecache\");\n        File[] files = dir.listFiles();\n        assertEquals(files.length, 5);\n\n        for (int i = 5; i < 10; i++) {\n            assertTrue(cache.exists(\"test\" + i));\n        }\n    }\n\n    public void testMultipleParts() throws Exception {\n        setUp();\n\n        FileCache cache = new FileCache(new File(getContext().getCacheDir(), \"filecache\"), 100000, false);\n        cache.setBlockSize(1);\n        File[] temps = new File[10];\n        for (int i = 0; i < temps.length; i++) {\n            File temp = temps[i] = cache.getTempFile();\n            StreamUtility.writeFile(temp, \"hello\" + i);\n        }\n        cache.commitTempFiles(\"test\", temps);\n\n        assertEquals(cache.size(), temps.length * 6);\n        File dir = new File(getContext().getCacheDir(), \"filecache\");\n        File[] files = dir.listFiles();\n        assertEquals(files.length, temps.length);\n\n        for (int i = 5; i < 10; i++) {\n            assertTrue(cache.exists(\"test\", i));\n        }\n    }\n\n    public void testMultipartEviction() throws Exception {\n        setUp();\n\n        FileCache cache = new FileCache(new File(getContext().getCacheDir(), \"filecache\"), 12, false);\n        cache.setBlockSize(1);\n        File[] temps = new File[10];\n        for (int i = 0; i < temps.length; i++) {\n            File temp = temps[i] = cache.getTempFile();\n            StreamUtility.writeFile(temp, \"hello\" + i);\n        }\n        cache.commitTempFiles(\"test\", temps);\n\n        assertEquals(cache.size(), 12);\n        File dir = new File(getContext().getCacheDir(), \"filecache\");\n        File[] files = dir.listFiles();\n        assertEquals(files.length, 2);\n\n        for (int i = 8; i < 10; i++) {\n            assertTrue(cache.exists(\"test\", i));\n        }\n\n        try {\n            FileInputStream[] fins = cache.get(\"test\", temps.length);\n            fail();\n        }\n        catch (IOException e) {\n        }\n    }\n\n\n    public void testMultipartEvictionAgain() throws Exception {\n        setUp();\n\n        FileCache cache = new FileCache(new File(getContext().getCacheDir(), \"filecache\"), 72, false);\n        cache.setBlockSize(1);\n        File[] temps = new File[10];\n        for (int i = 0; i < temps.length; i++) {\n            File temp = temps[i] = cache.getTempFile();\n            StreamUtility.writeFile(temp, \"hello\" + i);\n        }\n        cache.commitTempFiles(\"test\", temps);\n\n        assertEquals(cache.size(), 60);\n        File dir = new File(getContext().getCacheDir(), \"filecache\");\n        File[] files = dir.listFiles();\n        assertEquals(files.length, 10);\n\n        for (int i = 0; i < temps.length; i++) {\n            assertTrue(cache.exists(\"test\", i));\n        }\n\n        FileInputStream[] fins = cache.get(\"test\", temps.length);\n        StreamUtility.closeQuietly(fins);\n\n        temps = new File[10];\n        for (int i = 0; i < temps.length; i++) {\n            File temp = temps[i] = cache.getTempFile();\n            StreamUtility.writeFile(temp, \"hello\" + i);\n        }\n        cache.commitTempFiles(\"test2\", temps);\n\n        assertEquals(cache.size(), 72);\n\n        fins = cache.get(\"test2\", temps.length);\n        StreamUtility.closeQuietly(fins);\n\n        try {\n            fins = cache.get(\"test\", temps.length);\n            fail();\n        }\n        catch (IOException e) {\n        }\n    }\n\n    public void testReinit() throws Exception {\n        setUp();\n\n        FileCache cache = new FileCache(new File(getContext().getCacheDir(), \"filecache\"), 10, false);\n        cache.setBlockSize(1);\n        File temp = cache.getTempFile();\n        StreamUtility.writeFile(temp, \"hello\");\n        cache.commitTempFiles(\"test\", temp);\n\n        temp = cache.getTempFile();\n        StreamUtility.writeFile(temp, \"hello\");\n        cache.commitTempFiles(\"test2\", temp);\n\n        assertEquals(cache.size(), 10);\n\n        cache = new FileCache(new File(getContext().getCacheDir(), \"filecache\"), 10, false);\n        cache.setBlockSize(1);\n\n        String value = StreamUtility.readToEnd(cache.get(\"test\"));\n        assertEquals(value, \"hello\");\n\n        value = StreamUtility.readToEnd(cache.get(\"test2\"));\n        assertEquals(value, \"hello\");\n    }\n\n    public void testCacheOrder() throws Exception {\n        setUp();\n\n        FileCache cache = new FileCache(new File(getContext().getCacheDir(), \"filecache\"), 10, false);\n        cache.setBlockSize(1);\n        File temp = cache.getTempFile();\n        StreamUtility.writeFile(temp, \"hello\");\n        cache.commitTempFiles(\"test\", temp);\n\n        temp = cache.getTempFile();\n        StreamUtility.writeFile(temp, \"hello\");\n        cache.commitTempFiles(\"test2\", temp);\n\n        assertEquals(cache.size(), 10);\n\n        String value = StreamUtility.readToEnd(cache.get(\"test\"));\n        assertEquals(value, \"hello\");\n\n        // should push test2 off\n        temp = cache.getTempFile();\n        StreamUtility.writeFile(temp, \"hello\");\n        cache.commitTempFiles(\"test3\", temp);\n\n        assertTrue(cache.exists(\"test\"));\n        assertFalse(cache.exists(\"test2\"));\n        assertTrue(cache.exists(\"test3\"));\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/test/src/com/koushikdutta/async/test/FileTests.java",
    "content": "package com.koushikdutta.async.test;\n\n\nimport androidx.test.runner.AndroidJUnit4;\n\nimport com.koushikdutta.async.AsyncServer;\nimport com.koushikdutta.async.FileDataEmitter;\nimport com.koushikdutta.async.future.Future;\nimport com.koushikdutta.async.future.FutureCallback;\nimport com.koushikdutta.async.parser.StringParser;\nimport com.koushikdutta.async.util.StreamUtility;\n\nimport org.junit.runner.RunWith;\n\nimport java.io.File;\nimport java.util.concurrent.Semaphore;\nimport java.util.concurrent.TimeUnit;\n\nimport static androidx.test.InstrumentationRegistry.getContext;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\n\n/**\n * Created by koush on 5/22/13.\n */\n@RunWith(AndroidJUnit4.class)\npublic class FileTests {\n    public static final long TIMEOUT = 1000L;\n    public void testFileDataEmitter() throws Exception {\n        final Semaphore semaphore = new Semaphore(0);\n        File f = getContext().getFileStreamPath(\"test.txt\");\n        StreamUtility.writeFile(f, \"hello world\");\n        FileDataEmitter fdm = new FileDataEmitter(AsyncServer.getDefault(), f);\n        final Md5 md5 = Md5.createInstance();\n        Future<String> stringBody = new StringParser().parse(fdm);\n        stringBody\n        .setCallback(new FutureCallback<String>() {\n            @Override\n            public void onCompleted(Exception e, String result) {\n                semaphore.release();\n            }\n        });\n        fdm.resume();\n\n        assertTrue(\"timeout\", semaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS));\n        assertEquals(\"hello world\", stringBody.get());\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/test/src/com/koushikdutta/async/test/FutureTests.java",
    "content": "package com.koushikdutta.async.test;\n\nimport android.os.Handler;\nimport android.os.Looper;\nimport androidx.test.runner.AndroidJUnit4;\n\nimport com.koushikdutta.async.AsyncServer;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.callback.ContinuationCallback;\nimport com.koushikdutta.async.future.Continuation;\nimport com.koushikdutta.async.future.FutureCallback;\nimport com.koushikdutta.async.future.MultiFuture;\nimport com.koushikdutta.async.future.SimpleFuture;\nimport com.koushikdutta.async.future.SuccessCallback;\nimport com.koushikdutta.async.future.ThenCallback;\n\nimport junit.framework.TestCase;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\nimport java.util.ArrayList;\nimport java.util.concurrent.CancellationException;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.Semaphore;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.TimeoutException;\n\n@RunWith(AndroidJUnit4.class)\npublic class FutureTests extends TestCase {\n    @Test\n    public void testChain() throws Exception {\n        SimpleFuture<Integer> foo = new SimpleFuture<>();\n\n        foo\n        .thenConvert(new ThenCallback<Integer, Integer>() {\n            @Override\n            public Integer then(Integer from) {\n                return null;\n            }\n        })\n        .thenConvert(new ThenCallback<Integer, Integer>() {\n            @Override\n            public Integer then(Integer from) {\n                return null;\n            }\n        })\n        .thenConvert(new ThenCallback<Integer, Integer>() {\n            @Override\n            public Integer then(Integer from) {\n                return null;\n            }\n        })\n        .thenConvert(new ThenCallback<Integer, Integer>() {\n            @Override\n            public Integer then(Integer from) {\n                return null;\n            }\n        });\n\n        foo.setComplete(3);\n    }\n\n    int sum = 0;\n    @Test\n    public void multifutureTest() throws Exception {\n        MultiFuture<Integer> foo = new MultiFuture<>();\n        foo.success(value -> sum += value + 10);\n        foo.success(value -> sum += value + 20);\n        foo.setComplete(1);\n        assertEquals(sum, 32);\n    }\n\n    private static class IntegerFuture extends SimpleFuture<Integer> {\n        private IntegerFuture() {\n        }\n\n        public static IntegerFuture create(final int value, final long timeout) {\n            final IntegerFuture ret = new IntegerFuture();\n            \n            new Thread() {\n                public void run() {\n                    try {\n                        Thread.sleep(timeout);\n                        ret.setComplete(value);\n                    }\n                    catch (Exception e) {\n                        ret.setComplete(e);\n                    }\n                };\n            }.start();\n            \n            return ret;\n        }\n    }\n\n\n\n    @Test\n    public void testFutureCallback() throws Exception {\n        final Semaphore semaphore = new Semaphore(0);\n        final IntegerFuture future = IntegerFuture.create(20, 1000);\n        final Thread mainThread = Thread.currentThread();\n        future.setCallback(new FutureCallback<Integer>() {\n            @Override\n            public void onCompleted(Exception e, Integer result) {\n                assertNotSame(Thread.currentThread(), mainThread);\n                semaphore.release();\n            }\n        });\n\n        assertTrue(semaphore.tryAcquire(3000, TimeUnit.MILLISECONDS));\n    }\n\n    @Test\n    public void testFutureFinishedCallback() throws Exception {\n        final Semaphore semaphore = new Semaphore(0);\n        final IntegerFuture future = IntegerFuture.create(20, 1);\n        Thread.sleep(1000);\n        final Thread mainThread = Thread.currentThread();\n        future.setCallback(new FutureCallback<Integer>() {\n            @Override\n            public void onCompleted(Exception e, Integer result) {\n                assertEquals(Thread.currentThread(), mainThread);\n                semaphore.release();\n            }\n        });\n\n        assertTrue(semaphore.tryAcquire(3000, TimeUnit.MILLISECONDS));\n    }\n\n    @Test\n    public void testFutureCancel() throws Exception {\n        // test a future being cancelled while waiting\n        final IntegerFuture future = IntegerFuture.create(20, 2000);\n        \n        new Thread() {\n            public void run() {\n                try {\n                    Thread.sleep(1000);\n                }\n                catch (InterruptedException e) {\n                }\n                future.cancel();\n            };\n        }\n        .start();\n\n        try {\n            future.get(3000, TimeUnit.MILLISECONDS);\n            // this should never reach here as it was cancelled\n            fail();\n        }\n        catch (TimeoutException e) {\n            // timeout should also fail, since it was cancelled\n            fail();\n        }\n        catch (ExecutionException e) {\n            // execution exception is correct, make sure inner exception is cancellation\n            assertTrue(e.getCause() instanceof CancellationException);\n        }\n    }\n\n    @Test\n    public void testIntegerFuture() throws Exception {\n        IntegerFuture i = IntegerFuture.create(10, 500L);\n        assertEquals((int)i.get(), 10);\n    }\n    \n    int someValue;\n    @Test\n    public void testContinuation() throws Exception {\n        final Semaphore semaphore = new Semaphore(0);\n        someValue = 0;\n        final Continuation c = new Continuation(new CompletedCallback() {\n            @Override\n            public void onCompleted(Exception ex) {\n                assertNull(ex);\n                semaphore.release();\n            }\n        });\n        \n        c.add(new ContinuationCallback() {\n            @Override\n            public void onContinue(Continuation continuation, final CompletedCallback next) throws Exception {\n                new Thread() {\n                    public void run() {\n                        someValue++;\n                        next.onCompleted(null);\n                    };\n                }.start();\n            }\n        });\n        \n        c.add(new ContinuationCallback() {\n            @Override\n            public void onContinue(Continuation continuation, final CompletedCallback next) throws Exception {\n                new Thread() {\n                    public void run() {\n                        someValue++;\n                        next.onCompleted(null);\n                    };\n                }.start();\n            }\n        });\n        \n        c.add(new ContinuationCallback() {\n            @Override\n            public void onContinue(Continuation continuation, final CompletedCallback next) throws Exception {\n                someValue++;\n                next.onCompleted(null);\n            }\n        });\n        \n        new Thread() {\n            public void run() {\n                c.start();\n            };\n        }.start();\n        \n        assertTrue(semaphore.tryAcquire(3000, TimeUnit.MILLISECONDS));\n        assertEquals(someValue, 3);\n    }\n\n    @Test\n    public void testFutureChain() throws Exception {\n        final Semaphore semaphore = new Semaphore(0);\n        final Continuation c = new Continuation(new CompletedCallback() {\n            @Override\n            public void onCompleted(Exception ex) {\n                semaphore.release();\n            }\n        });\n        \n        IntegerFuture i1;\n        c.add(i1 = IntegerFuture.create(2, 200));\n\n        IntegerFuture i2;\n        c.add(i2 = IntegerFuture.create(3, 200));\n        \n        new Thread() {\n            public void run() {\n                c.start();\n            };\n        }.start();\n\n        assertTrue(semaphore.tryAcquire(3000, TimeUnit.MILLISECONDS));\n        \n        assertEquals((int)i1.get(), 2);\n        assertEquals((int)i2.get(), 3);\n    }\n\n    @Test\n    public void testContinuationFail() throws Exception {\n        final Semaphore semaphore = new Semaphore(0);\n        final Continuation c = new Continuation(new CompletedCallback() {\n            @Override\n            public void onCompleted(Exception ex) {\n                assertNotNull(ex);\n                semaphore.release();\n            }\n        });\n        \n        c.add(new ContinuationCallback() {\n            @Override\n            public void onContinue(Continuation continuation, CompletedCallback next) throws Exception {\n                throw new Exception(\"fail\");\n            }\n        });\n        \n        new Thread() {\n            public void run() {\n                c.start();\n            };\n        }.start();\n        \n        assertTrue(semaphore.tryAcquire(3000, TimeUnit.MILLISECONDS));\n    }\n\n    @Test\n    public void testContinuationCancel() throws Exception {\n        final Semaphore semaphore = new Semaphore(0);\n        final Continuation c = new Continuation(new CompletedCallback() {\n            @Override\n            public void onCompleted(Exception ex) {\n                fail();\n                semaphore.release();\n            }\n        });\n        c.setCancelCallback(new Runnable() {\n            @Override\n            public void run() {\n                semaphore.release();\n            }\n        });\n        \n        c.add(new ContinuationCallback() {\n            @Override\n            public void onContinue(Continuation continuation, CompletedCallback next) throws Exception {\n                Thread.sleep(10000);\n            }\n        });\n        \n        new Thread() {\n            public void run() {\n                c.start();\n            };\n        }.start();\n        \n        new Thread() {\n            public void run() {\n                try {\n                    Thread.sleep(1000);\n                }\n                catch (Exception e) {\n                }\n                c.cancel();\n            };\n        }.start();\n        \n        assertTrue(semaphore.tryAcquire(3000, TimeUnit.MILLISECONDS));\n    }\n\n\n    @Test\n    public void testChildContinuationCancel() throws Exception {\n        final Semaphore semaphore = new Semaphore(0);\n        final Continuation c = new Continuation(new CompletedCallback() {\n            @Override\n            public void onCompleted(Exception ex) {\n                fail();\n                semaphore.release();\n            }\n        });\n        c.setCancelCallback(new Runnable() {\n            @Override\n            public void run() {\n                semaphore.release();\n            }\n        });\n        \n        c.add(new ContinuationCallback() {\n            @Override\n            public void onContinue(Continuation continuation, CompletedCallback next) throws Exception {\n                Thread.sleep(10000);\n            }\n        });\n        \n        final Continuation child = new Continuation();\n        child.add(new ContinuationCallback() {\n            @Override\n            public void onContinue(Continuation continuation, CompletedCallback next) throws Exception {\n                Thread.sleep(10000);\n            }\n        });\n\n        c.add(child);\n        \n        new Thread() {\n            public void run() {\n                c.start();\n            };\n        }.start();\n        \n        new Thread() {\n            public void run() {\n                try {\n                    Thread.sleep(1000);\n                }\n                catch (Exception e) {\n                }\n                child.cancel();\n            };\n        }.start();\n        \n        assertTrue(semaphore.tryAcquire(3000, TimeUnit.MILLISECONDS));\n    }\n    \n    public void testContinuationArray() throws Exception {\n        final ArrayList<Integer> results = new ArrayList<Integer>();\n        final Semaphore semaphore = new Semaphore(0);\n        final Continuation c = new Continuation(new CompletedCallback() {\n            @Override\n            public void onCompleted(Exception ex) {\n                semaphore.release();\n            }\n        });\n        \n        for (int i = 0; i < 10; i++) {\n            final int j = i;\n            c.add(new ContinuationCallback() {\n                @Override\n                public void onContinue(Continuation continuation, CompletedCallback next) throws Exception {\n                    results.add(j);\n                    next.onCompleted(null);\n                }\n            });\n        }\n        \n        new Thread() {\n            public void run() {\n                c.start();\n            };\n        }.start();\n        \n        assertTrue(semaphore.tryAcquire(3000, TimeUnit.MILLISECONDS));\n        \n        assertEquals(10, results.size());\n        for (int i = 0; i < 10; i++) {\n            assertEquals((int)results.get(i), i);\n        }\n    }\n\n    @Test\n    public void testReentrancy() throws Exception {\n        if (true) {\n            // disabled cause test framework no longer has a looper\n            return;\n        }\n\n        // verify reentrancy will work\n        \n        assertNotNull(Looper.myLooper());\n        \n        final Thread originalThread = Thread.currentThread();\n        final TriggerFuture trigger = new TriggerFuture();\n        final Handler handler = new Handler();\n        \n        AsyncServer.getDefault().post(new Runnable() {\n            @Override\n            public void run() {\n                AsyncServer.post(handler, new Runnable() {\n                    @Override\n                    public void run() {\n                        final TriggerFuture trigger2 = new TriggerFuture();\n\n                        AsyncServer.getDefault().post(new Runnable() {\n                            @Override\n                            public void run() {\n                                AsyncServer.post(handler, new Runnable() {\n                                    @Override\n                                    public void run() {\n                                        assertEquals(Thread.currentThread(), originalThread);\n                                        trigger2.trigger();\n                                    }\n                                });\n                            }\n                        });\n                        \n                        try {\n                            assertEquals((int)trigger2.get(5000, TimeUnit.MILLISECONDS), 2020);\n                        }\n                        catch (Exception e) {\n                            fail();\n                        }\n\n                        // callstack here should be on top of trigger.get below.\n                        // reentrant.\n                        assertEquals(Thread.currentThread(), originalThread);\n                        trigger.trigger();\n                    }\n                });\n            }\n        });\n        \n        // trigger.get will do a reentrant block.\n        assertEquals((int)trigger.get(5000, TimeUnit.MILLISECONDS), 2020);\n    }\n\n    @Test\n    public void testPostCancelCallback() throws Exception {\n        SimpleFuture<String> future = new SimpleFuture<String>();\n        final Semaphore semaphore = new Semaphore(0);\n        future.cancel();\n        future.setCallback(new FutureCallback<String>() {\n            @Override\n            public void onCompleted(Exception e, String result) {\n                assertTrue(e instanceof CancellationException);\n                semaphore.release();\n            }\n        });\n        assertTrue(semaphore.tryAcquire(1000, TimeUnit.MILLISECONDS));\n        assertNull(future.getCallback());\n    }\n\n    @Test\n    public void testPreCancelCallback() throws Exception {\n        final Semaphore semaphore = new Semaphore(0);\n        SimpleFuture<String> future = new SimpleFuture<String>();\n        future.setCallback(new FutureCallback<String>() {\n            @Override\n            public void onCompleted(Exception e, String result) {\n                assertTrue(e instanceof CancellationException);\n                semaphore.release();\n            }\n        });\n        assertNotNull(future.getCallback());\n        future.cancel();\n        assertTrue(semaphore.tryAcquire(1000, TimeUnit.MILLISECONDS));\n        assertNull(future.getCallback());\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/test/src/com/koushikdutta/async/test/HttpClientTests.java",
    "content": "package com.koushikdutta.async.test;\n\nimport android.net.Uri;\nimport androidx.test.runner.AndroidJUnit4;\nimport android.text.TextUtils;\nimport android.util.Log;\n\nimport com.koushikdutta.async.AsyncServer;\nimport com.koushikdutta.async.AsyncServerSocket;\nimport com.koushikdutta.async.ByteBufferList;\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.callback.DataCallback;\nimport com.koushikdutta.async.future.Future;\nimport com.koushikdutta.async.future.FutureCallback;\nimport com.koushikdutta.async.http.AsyncHttpClient;\nimport com.koushikdutta.async.http.AsyncHttpClient.StringCallback;\nimport com.koushikdutta.async.http.AsyncHttpGet;\nimport com.koushikdutta.async.http.AsyncHttpHead;\nimport com.koushikdutta.async.http.AsyncHttpPost;\nimport com.koushikdutta.async.http.AsyncHttpRequest;\nimport com.koushikdutta.async.http.AsyncHttpResponse;\nimport com.koushikdutta.async.http.body.JSONObjectBody;\nimport com.koushikdutta.async.http.cache.ResponseCacheMiddleware;\nimport com.koushikdutta.async.http.callback.HttpConnectCallback;\nimport com.koushikdutta.async.http.server.AsyncHttpServerRequest;\nimport com.koushikdutta.async.http.server.AsyncHttpServerResponse;\nimport com.koushikdutta.async.http.server.AsyncProxyServer;\n\nimport org.json.JSONObject;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\nimport java.io.File;\nimport java.util.concurrent.CancellationException;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.Semaphore;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.TimeoutException;\n\nimport static androidx.test.InstrumentationRegistry.getContext;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertNull;\nimport static org.junit.Assert.assertTrue;\nimport static org.junit.Assert.fail;\n\n@RunWith(AndroidJUnit4.class)\npublic class HttpClientTests {\n    AsyncServer server = new AsyncServer();\n    AsyncHttpClient client  = new AsyncHttpClient(server);\n\n    @Override\n    protected void finalize() throws Throwable {\n        super.finalize();\n        client.getSSLSocketMiddleware().setConnectAllAddresses(false);\n        client.getSocketMiddleware().setConnectAllAddresses(false);\n        client.getSocketMiddleware().disableProxy();\n        server.stop();\n    }\n\n    /*\n    public void testConnectAllAddresses() throws Exception {\n        assertEquals(client.getSSLSocketMiddleware().getConnectionPoolCount(), 0);\n        assertEquals(client.getSocketMiddleware().getConnectionPoolCount(), 0);\n\n        client.getSSLSocketMiddleware().setConnectAllAddresses(true);\n        client.getSocketMiddleware().setConnectAllAddresses(true);\n\n        final Semaphore semaphore = new Semaphore(0);\n        final Md5 md5 = Md5.createInstance();\n        AsyncHttpGet get = new AsyncHttpGet(\"http://www.clockworkmod.com\");\n        get.setLogging(\"ConnectionPool\", Log.VERBOSE);\n        client.execute(get, new HttpConnectCallback() {\n            @Override\n            public void onConnectCompleted(Exception ex, AsyncHttpResponse response) {\n                // make sure gzip decoding works, as that is generally what github sends.\n                Assert.assertEquals(\"gzip\", response.getHeaders().getContentEncoding());\n                response.setDataCallback(new DataCallback() {\n                    @Override\n                    public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n                        md5.update(bb);\n                    }\n                });\n\n                response.setEndCallback(new CompletedCallback() {\n                    @Override\n                    public void onCompleted(Exception ex) {\n                        semaphore.release();\n                    }\n                });\n            }\n        });\n\n        assertTrue(\"timeout\", semaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS));\n\n        long start = System.currentTimeMillis();\n        while (client.getSocketMiddleware().getConnectionPoolCount() != 2) {\n            Thread.sleep(50);\n            if (start + 5000L < System.currentTimeMillis())\n                fail();\n        }\n    }\n    */\n\n    private static final long TIMEOUT = 10000L;\n    @Test\n    public void testHomepage() throws Exception {\n        Future<String> ret = client.executeString(new AsyncHttpGet(\"http://google.com\"), null);\n        assertNotNull(ret.get(TIMEOUT, TimeUnit.MILLISECONDS));\n    }\n\n    @Test\n    public void testClockworkMod() throws Exception {\n        final Semaphore semaphore = new Semaphore(0);\n        final Md5 md5 = Md5.createInstance();\n        client.execute(\"http://www.clockworkmod.com\", new HttpConnectCallback() {\n            @Override\n            public void onConnectCompleted(Exception ex, AsyncHttpResponse response) {\n                // make sure gzip decoding works, as that is generally what github sends.\n                Assert.assertEquals(\"gzip\", response.headers().get(\"Content-Encoding\"));\n                response.setDataCallback(new DataCallback() {\n                    @Override\n                    public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n                        md5.update(bb);\n                    }\n                });\n\n                response.setEndCallback(new CompletedCallback() {\n                    @Override\n                    public void onCompleted(Exception ex) {\n                        semaphore.release();\n                    }\n                });\n            }\n        });\n\n        assertTrue(\"timeout\", semaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS));\n    }\n\n    // this testdata file was generated using /dev/random. filename is also the md5 of the file.\n    final static String dataNameAndHash = \"6691924d7d24237d3b3679310157d640\";\n    final static String githubPath = \"raw.githubusercontent.com/koush/AndroidAsync/master/AndroidAsync/test/assets/\";\n    final static String github = \"https://\" + githubPath + dataNameAndHash;\n    public void testGithubRandomData() throws Exception {\n        final Semaphore semaphore = new Semaphore(0);\n        final Md5 md5 = Md5.createInstance();\n        AsyncHttpGet get = new AsyncHttpGet(github);\n        get.setLogging(\"AsyncTest\", Log.VERBOSE);\n        client.execute(get, new HttpConnectCallback() {\n            @Override\n            public void onConnectCompleted(Exception ex, AsyncHttpResponse response) {\n                assertNull(ex);\n                // make sure gzip decoding works, as that is generally what github sends.\n                // this broke sometime in 03/2014\n//                Assert.assertEquals(\"gzip\", response.getHeaders().getContentEncoding());\n                response.setDataCallback(new DataCallback() {\n                    @Override\n                    public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n                        md5.update(bb);\n                    }\n                });\n                \n                response.setEndCallback(new CompletedCallback() {\n                    @Override\n                    public void onCompleted(Exception ex) {\n                        semaphore.release();\n                    }\n                });\n            }\n        });\n        \n        assertTrue(\"timeout\", semaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS));\n        assertEquals(md5.digest(), dataNameAndHash);\n    }\n    \n    public void testGithubRandomDataWithFuture() throws Exception {\n        final Md5 md5 = Md5.createInstance();\n        Future<ByteBufferList> bb = client.executeByteBufferList(new AsyncHttpGet(github), null);\n        md5.update(bb.get(TIMEOUT, TimeUnit.MILLISECONDS));\n        assertEquals(md5.digest(), dataNameAndHash);\n    }\n\n    public void testSni() throws Exception {\n//        ProviderInstaller.installIfNeeded(getContext());\n//        AsyncHttpClient.getDefaultInstance().getSSLSocketMiddleware().setSSLContext(SSLContext.getInstance(\"TLS\"));\n\n        // this server requires SNI as it serves multiple SSL certificates\n        // LOLLIPOP_MR1 and lower requires SSLEngineSNIConfigurator to set the appropriate fields via reflection.\n        // Higher than LOLLIPOP_MR1 can use createSSLEngine(host, port) as it is based off recent-ish versions of Conscrypt\n        // Conscrypt, if it is being used in GPS ProviderInstaller, can also use createSSLEngine(host, port)\n        Future<String> string = client.executeString(new AsyncHttpGet(\"https://koush.com/\"), null);\n        string.get(TIMEOUT, TimeUnit.MILLISECONDS);\n    }\n\n    public void testGithubHelloWithFuture() throws Exception {\n        Future<String> string = client.executeString(new AsyncHttpGet(\"https://\" + githubPath + \"hello.txt\"), null);\n        assertEquals(string.get(TIMEOUT, TimeUnit.MILLISECONDS), \"hello world\");\n    }\n\n    public void testGithubHelloWithFutureCallback() throws Exception {\n        final Semaphore semaphore = new Semaphore(0);\n        client.executeString(new AsyncHttpGet(\"https://\" + githubPath + \"hello.txt\"), null)\n        .setCallback(new FutureCallback<String>() {\n            @Override\n            public void onCompleted(Exception e, String result) {\n                assertEquals(result, \"hello world\");\n                semaphore.release();\n            }\n        });\n        assertTrue(\"timeout\", semaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS));\n    }\n\n    Future<String> future;\n    public void testCancel() throws Exception {\n        future = client.executeString(new AsyncHttpGet(\"http://yahoo.com\"), new StringCallback() {\n            @Override\n            public void onCompleted(Exception e, AsyncHttpResponse source, String result) {\n                fail();\n            }\n\n            @Override\n            public void onConnect(AsyncHttpResponse response) {\n                future.cancel();\n            }\n        });\n\n        try {\n            future.get(TIMEOUT, TimeUnit.MILLISECONDS);\n            // this should never reach here as it was cancelled\n            fail();\n        }\n        catch (TimeoutException e) {\n            // timeout should also fail, since it was cancelled\n            fail();\n        }\n        catch (ExecutionException e) {\n            // execution exception is correct, make sure inner exception is cancellation\n            assertTrue(e.getCause() instanceof CancellationException);\n        }\n    }\n\n    public void testCache() throws Exception {\n        ResponseCacheMiddleware cache = ResponseCacheMiddleware.addCache(client, new File(getContext().getFilesDir(), \"AndroidAsyncTest\"), 1024 * 1024 * 10);\n        try {\n            // clear the old cache\n            cache.clear();\n            // populate the cache\n            testGithubRandomData();\n            // this should result in a conditional cache hit\n            testGithubRandomData();\n            assertEquals(cache.getCacheHitCount(), 1);\n        }\n        finally {\n            client.getMiddleware().remove(cache);\n        }\n    }\n\n    Future<File> fileFuture;\n    public void testFileCancel() throws Exception {\n        final Semaphore semaphore = new Semaphore(0);\n        File f = getContext().getFileStreamPath(\"test.txt\");\n        fileFuture = client.executeFile(new AsyncHttpGet(github), f.getAbsolutePath(), new AsyncHttpClient.FileCallback() {\n            @Override\n            public void onCompleted(Exception e, AsyncHttpResponse source, File result) {\n                fail();\n            }\n\n            @Override\n            public void onProgress(AsyncHttpResponse response, long downloaded, long total) {\n                semaphore.release();\n            }\n        });\n        fileFuture.setCallback(new FutureCallback<File>() {\n            @Override\n            public void onCompleted(Exception e, File result) {\n                assertTrue(e instanceof CancellationException);\n            }\n        });\n\n        try {\n            assertTrue(\"timeout\", semaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS));\n            assertTrue(fileFuture.cancel());\n            fileFuture.get();\n            fail();\n        }\n        catch (ExecutionException ex) {\n            assertTrue(ex.getCause() instanceof CancellationException);\n        }\n//        Thread.sleep(1000);\n//        assertTrue(\"timeout\", semaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS));\n        assertFalse(f.exists());\n    }\n\n    boolean wasProxied;\n    public void testProxy() throws Exception {\n        wasProxied = false;\n        final AsyncServer proxyServer = new AsyncServer();\n        try {\n            AsyncProxyServer httpServer = new AsyncProxyServer(proxyServer) {\n                @Override\n                protected boolean onRequest(AsyncHttpServerRequest request, AsyncHttpServerResponse response) {\n                    wasProxied = true;\n                    return super.onRequest(request, response);\n                }\n            };\n\n            AsyncServerSocket socket = httpServer.listen(proxyServer, 0);\n\n//            client.getSocketMiddleware().enableProxy(\"localhost\", 5555);\n\n            AsyncHttpGet get = new AsyncHttpGet(\"http://www.clockworkmod.com\");\n            get.enableProxy(\"localhost\", socket.getLocalPort());\n\n            Future<String> ret = client.executeString(get, null);\n            String data;\n            assertNotNull(data = ret.get(TIMEOUT, TimeUnit.MILLISECONDS));\n            assertTrue(data.contains(\"ClockworkMod\"));\n            assertTrue(wasProxied);\n        }\n        finally {\n            proxyServer.stop();\n        }\n    }\n\n    public void testUriPathWithSpaces() throws Exception {\n        AsyncHttpRequest request = new AsyncHttpRequest(Uri.parse(\"http://jpkc.seiee.sjtu.edu.cn/ds/ds2/Course%20lecture/chapter%2010.pdf\"), AsyncHttpGet.METHOD);\n        String requestLine = request.getRequestLine().toString();\n        assertEquals(\"GET /ds/ds2/Course%20lecture/chapter%2010.pdf HTTP/1.1\", requestLine);\n    }\n\n    public void testHEAD() throws Exception {\n        AsyncHttpHead req = new AsyncHttpHead(Uri.parse(\"http://31.media.tumblr.com/9606dcaa33b6877b7c485040393b9392/tumblr_mrtnysMonE1r4vl1yo1_500.jpg\"));\n        Future<String> str = AsyncHttpClient.getDefaultInstance().executeString(req, null);\n        assertTrue(TextUtils.isEmpty(str.get(TIMEOUT, TimeUnit.MILLISECONDS)));\n    }\n\n    public void testPostJsonObject() throws Exception {\n        JSONObject post = new JSONObject();\n        post.put(\"ping\", \"pong\");\n        AsyncHttpPost p = new AsyncHttpPost(\"https://koush.clockworkmod.com/test/echo\");\n        p.setBody(new JSONObjectBody(post));\n        JSONObject ret = AsyncHttpClient.getDefaultInstance().executeJSONObject(p, null).get();\n        assertEquals(\"pong\", ret.getString(\"ping\"));\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/test/src/com/koushikdutta/async/test/HttpServerTests.java",
    "content": "package com.koushikdutta.async.test;\n\nimport com.koushikdutta.async.AsyncServer;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.http.AsyncHttpClient;\nimport com.koushikdutta.async.http.AsyncHttpPost;\nimport com.koushikdutta.async.http.NameValuePair;\nimport com.koushikdutta.async.http.body.JSONObjectBody;\nimport com.koushikdutta.async.http.body.MultipartFormDataBody;\nimport com.koushikdutta.async.http.body.StringBody;\nimport com.koushikdutta.async.http.body.UrlEncodedFormBody;\nimport com.koushikdutta.async.http.server.AsyncHttpServer;\nimport com.koushikdutta.async.http.server.AsyncHttpServerRequest;\nimport com.koushikdutta.async.http.server.AsyncHttpServerResponse;\nimport com.koushikdutta.async.http.server.HttpServerRequestCallback;\nimport com.koushikdutta.async.util.StreamUtility;\n\nimport junit.framework.TestCase;\n\nimport org.json.JSONObject;\n\nimport java.io.InputStream;\nimport java.net.URL;\nimport java.net.URLConnection;\n\npublic class HttpServerTests extends TestCase {\n    AsyncHttpServer httpServer;\n\n    @Override\n    protected void setUp() throws Exception {\n        super.setUp();\n\n        httpServer = new AsyncHttpServer();\n        httpServer.setErrorCallback(new CompletedCallback() {\n            @Override\n            public void onCompleted(Exception ex) {\n                fail();\n            }\n        });\n        httpServer.listen(AsyncServer.getDefault(), 5000);\n        \n        httpServer.get(\"/hello\", new HttpServerRequestCallback() {\n            @Override\n            public void onRequest(AsyncHttpServerRequest request, AsyncHttpServerResponse response) {\n                assertNotNull(request.getHeaders().get(\"Host\"));\n                response.send(\"hello\");\n            }\n        });\n\n        httpServer.post(\"/echo\", new HttpServerRequestCallback() {\n            @Override\n            public void onRequest(AsyncHttpServerRequest request, final AsyncHttpServerResponse response) {\n                try {\n                    assertNotNull(request.getHeaders().get(\"Host\"));\n                    JSONObject json = new JSONObject();\n                    if (request.getBody() instanceof UrlEncodedFormBody) {\n                        UrlEncodedFormBody body = (UrlEncodedFormBody)request.getBody();\n                        for (NameValuePair pair: body.get()) {\n                            json.put(pair.getName(), pair.getValue());\n                        }\n                    }\n                    else if (request.getBody() instanceof JSONObjectBody) {\n                        json = ((JSONObjectBody)request.getBody()).get();\n                    }\n                    else if (request.getBody() instanceof StringBody) {\n                        json.put(\"foo\", ((StringBody)request.getBody()).get());\n                    }\n                    else if (request.getBody() instanceof MultipartFormDataBody) {\n                        MultipartFormDataBody body = (MultipartFormDataBody)request.getBody();\n                        for (NameValuePair pair: body.get()) {\n                            json.put(pair.getName(), pair.getValue());\n                        }\n                    }\n\n                    response.send(json);\n                }\n                catch (Exception e) {\n                }\n            }\n        });\n    }\n\n    public void testJSONObject() throws Exception {\n        JSONObject json = new JSONObject();\n        json.put(\"foo\", \"bar\");\n        JSONObjectBody body = new JSONObjectBody(json);\n        AsyncHttpPost post = new AsyncHttpPost(\"http://localhost:5000/echo\");\n        post.setBody(body);\n        json = AsyncHttpClient.getDefaultInstance().executeJSONObject(post, null).get();\n        assertEquals(json.getString(\"foo\"), \"bar\");\n    }\n\n    public void testString() throws Exception {\n        StringBody body = new StringBody(\"bar\");\n        AsyncHttpPost post = new AsyncHttpPost(\"http://localhost:5000/echo\");\n        post.setBody(body);\n        JSONObject json = AsyncHttpClient.getDefaultInstance().executeJSONObject(post, null).get();\n        assertEquals(json.getString(\"foo\"), \"bar\");\n    }\n\n//    public void testUrlEncodedFormBody() throws Exception {\n//        List<NameValuePair> params = new ArrayList<NameValuePair>();\n//        params.add(new BasicNameValuePair(\"foo\", \"bar\"));\n//        HttpPost post = new HttpPost(\"http://localhost:5000/echo\");\n//        post.setEntity(new UrlEncodedFormEntity(params));\n//\n//        HttpResponse response = new DefaultHttpClient().execute(post);\n//        String contents = StreamUtility.readToEnd(response.getEntity().getContent());\n//        JSONObject json = new JSONObject(contents);\n//        assertEquals(json.getString(\"foo\"), \"bar\");\n//    }\n    \n    public void testServerHello() throws Exception {\n        URL url = new URL(\"http://localhost:5000/hello\");\n        URLConnection conn = url.openConnection();\n        \n        InputStream is = conn.getInputStream();\n        \n        String contents = StreamUtility.readToEnd(is);\n        is.close();\n        assertEquals(contents, \"hello\");\n    }\n    \n    public void testServerHelloAgain() throws Exception {\n        URL url = new URL(\"http://localhost:5000/hello\");\n        URLConnection conn = url.openConnection();\n        \n        InputStream is = conn.getInputStream();\n        \n        String contents = StreamUtility.readToEnd(is);\n        is.close();\n        assertEquals(contents, \"hello\");\n    }\n    \n    @Override\n    protected void tearDown() throws Exception {\n        super.tearDown();\n        \n        httpServer.stop();\n        AsyncServer.getDefault().stop();\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/test/src/com/koushikdutta/async/test/Issue59.java",
    "content": "package com.koushikdutta.async.test;\n\nimport android.util.Log;\n\nimport com.koushikdutta.async.AsyncServer;\nimport com.koushikdutta.async.ByteBufferList;\nimport com.koushikdutta.async.Util;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.http.AsyncHttpClient;\nimport com.koushikdutta.async.http.AsyncHttpGet;\nimport com.koushikdutta.async.http.server.AsyncHttpServer;\nimport com.koushikdutta.async.http.server.AsyncHttpServerRequest;\nimport com.koushikdutta.async.http.server.AsyncHttpServerResponse;\nimport com.koushikdutta.async.http.server.HttpServerRequestCallback;\n\nimport junit.framework.TestCase;\n\nimport java.util.concurrent.TimeUnit;\n\n/**\n * Created by koush on 8/31/13.\n */\npublic class Issue59 extends TestCase {\n    public void testIssue() throws Exception {\n        AsyncHttpServer httpServer = new AsyncHttpServer();\n        try {\n            httpServer.get(\"/\", new HttpServerRequestCallback() {\n                @Override\n                public void onRequest(AsyncHttpServerRequest request, final AsyncHttpServerResponse response) {\n                    // setting this to empty is a hacky way of telling the framework not to use\n                    // transfer-encoding. It will get removed.\n                    response.getHeaders().set(\"Transfer-Encoding\", \"\");\n                    response.code(200);\n                    Util.writeAll(response, \"foobarbeepboop\".getBytes(), new CompletedCallback() {\n                        @Override\n                        public void onCompleted(Exception ex) {\n                            response.end();\n                        }\n                    });\n                }\n            });\n\n            httpServer.listen(5959);\n\n            AsyncHttpGet get = new AsyncHttpGet(\"http://localhost:5959/\");\n            get.setLogging(\"issue59\", Log.VERBOSE);\n            get.getHeaders().removeAll(\"Connection\");\n            get.getHeaders().removeAll(\"Accept-Encoding\");\n\n            assertEquals(\"foobarbeepboop\", AsyncHttpClient.getDefaultInstance().executeString(get, null).get(1000, TimeUnit.MILLISECONDS));\n        }\n        finally {\n            httpServer.stop();\n            AsyncServer.getDefault().stop();\n        }\n    }\n\n    public void testIon428() throws Exception {\n        ByteBufferList bb = AsyncHttpClient.getDefaultInstance().executeByteBufferList(new AsyncHttpGet(\"https://cdn2.vox-cdn.com/thumbor/KxtZNw37jKNfxdA0hX5edHvbTBE=/0x0:2039x1359/800x536/cdn0.vox-cdn.com/uploads/chorus_image/image/44254028/lg-g-watch.0.0.jpg\"), null)\n        .get();\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/test/src/com/koushikdutta/async/test/IssueWithWebSocketFuturesTests.java",
    "content": "package com.koushikdutta.async.test;\n\nimport com.koushikdutta.async.future.Future;\nimport com.koushikdutta.async.http.AsyncHttpClient;\nimport com.koushikdutta.async.http.WebSocket;\nimport com.koushikdutta.async.http.server.AsyncHttpServer;\nimport com.koushikdutta.async.http.server.AsyncHttpServerRequest;\n\nimport junit.framework.TestCase;\n\nimport java.util.concurrent.CountDownLatch;\n\n\npublic class IssueWithWebSocketFuturesTests extends TestCase {\n\n    //testing that websocket callback gets called with the correct parameters.\n    public void testWebSocketFutureWithHandshakeFailureCallback() throws Exception {\n\n        //creating a faulty server!\n        AsyncHttpServer httpServer = new AsyncHttpServer();\n        httpServer.websocket(\".*\", new AsyncHttpServer.WebSocketRequestCallback() {\n            @Override\n            public void onConnected(WebSocket webSocket, AsyncHttpServerRequest request) {\n\n            }\n        });\n        httpServer.listen(6666);\n\n\n\n        final Exception[] callbackException = {null};\n        final WebSocket[] callbackWs = {null};\n        final CountDownLatch countDownLatch = new CountDownLatch(1);\n\n\n        //for some reason, it fails with a WebSocketHandshakeException.\n        //But in general, if the handshake fails, the callback must be called with an exception.\n        Future<WebSocket> wsFuture = AsyncHttpClient.getDefaultInstance().websocket(\"ws://127.0.0.1:6666\", \"ws\", new AsyncHttpClient.WebSocketConnectCallback() {\n            @Override\n            public void onCompleted(Exception ex, WebSocket webSocket) {\n                callbackException[0] = ex;\n                callbackWs[0] = webSocket;\n                countDownLatch.countDown();\n            }\n        });\n\n\n        //wait for the future to complete\n        countDownLatch.await();\n\n        //exactly one mut be null\n        assertTrue(callbackWs[0] == null ^ callbackException[0] == null);\n\n        //callback parameters must be the same as the future's result\n        assertEquals(wsFuture.tryGet(), callbackWs[0]);\n        assertEquals(wsFuture.tryGetException(), callbackException[0]);\n\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/test/src/com/koushikdutta/async/test/LineEmitterTests.java",
    "content": "package com.koushikdutta.async.test;\n\nimport com.koushikdutta.async.ByteBufferList;\nimport com.koushikdutta.async.LineEmitter;\nimport com.koushikdutta.async.util.Charsets;\n\nimport junit.framework.TestCase;\n\nimport java.nio.ByteBuffer;\nimport java.nio.charset.Charset;\nimport java.util.concurrent.Semaphore;\n\n/**\n * Created by koush on 6/9/16.\n */\npublic class LineEmitterTests extends TestCase {\n    public void testFunnyCharacter() {\n        final String stuff = \"é\\n\";\n        LineEmitter emitter = new LineEmitter(Charsets.UTF_8);\n        emitter.setLineCallback(new LineEmitter.StringCallback() {\n            @Override\n            public void onStringAvailable(String s) {\n                assertEquals(s + '\\n', stuff);\n            }\n        });\n\n\n        assertEquals(stuff.charAt(0), 233);\n        ByteBufferList bb = new ByteBufferList(ByteBuffer.wrap(stuff.getBytes(Charsets.UTF_8)));\n        emitter.onDataAvailable(null, bb);\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/test/src/com/koushikdutta/async/test/Md5.java",
    "content": "package com.koushikdutta.async.test;\n\nimport com.koushikdutta.async.ByteBufferList;\n\nimport java.math.BigInteger;\nimport java.nio.ByteBuffer;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\n\npublic class Md5 {\n    private MessageDigest digest;\n    public static Md5 createInstance() throws NoSuchAlgorithmException {\n        Md5 md5 = new Md5();\n        md5.digest = MessageDigest.getInstance(\"MD5\");\n        return md5;\n    }\n    \n    private Md5() {\n        \n    }\n    public void update(ByteBufferList bb) {\n        while (bb.size() > 0) {\n            ByteBuffer b = bb.remove();\n            digest.update(b);\n        }\n    }\n    \n    public String digest() {\n        String hash = new BigInteger(digest.digest()).toString(16);\n        return hash;\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/test/src/com/koushikdutta/async/test/MultipartTests.java",
    "content": "package com.koushikdutta.async.test;\n\nimport androidx.test.runner.AndroidJUnit4;\n\nimport com.koushikdutta.async.AsyncServer;\nimport com.koushikdutta.async.ByteBufferList;\nimport com.koushikdutta.async.DataEmitter;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.callback.DataCallback;\nimport com.koushikdutta.async.future.Future;\nimport com.koushikdutta.async.http.AsyncHttpClient;\nimport com.koushikdutta.async.http.AsyncHttpClient.StringCallback;\nimport com.koushikdutta.async.http.AsyncHttpPost;\nimport com.koushikdutta.async.http.AsyncHttpResponse;\nimport com.koushikdutta.async.http.body.MultipartFormDataBody;\nimport com.koushikdutta.async.http.body.MultipartFormDataBody.MultipartCallback;\nimport com.koushikdutta.async.http.body.Part;\nimport com.koushikdutta.async.http.server.AsyncHttpServer;\nimport com.koushikdutta.async.http.server.AsyncHttpServerRequest;\nimport com.koushikdutta.async.http.server.AsyncHttpServerResponse;\nimport com.koushikdutta.async.http.server.HttpServerRequestCallback;\n\nimport org.junit.runner.RunWith;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.util.concurrent.TimeUnit;\n\nimport static androidx.test.InstrumentationRegistry.getContext;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.fail;\n\n@RunWith(AndroidJUnit4.class)\npublic class MultipartTests {\n    AsyncHttpServer httpServer;\n\n    protected void setUp() throws Exception {\n\n        httpServer = new AsyncHttpServer();\n        httpServer.setErrorCallback(new CompletedCallback() {\n            @Override\n            public void onCompleted(Exception ex) {\n                fail();\n            }\n        });\n        httpServer.listen(AsyncServer.getDefault(), 5000);\n        \n        httpServer.post(\"/\", new HttpServerRequestCallback() {\n            int gotten = 0;\n            @Override\n            public void onRequest(final AsyncHttpServerRequest request, final AsyncHttpServerResponse response) {\n                final MultipartFormDataBody body = (MultipartFormDataBody)request.getBody();\n                body.setMultipartCallback(new MultipartCallback() {\n                    @Override\n                    public void onPart(Part part) {\n                        if (part.isFile()) {\n                            body.setDataCallback(new DataCallback() {\n                                @Override\n                                public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {\n                                    gotten += bb.remaining();\n                                    bb.recycle();\n                                }\n                            });\n                        }\n                    }\n                });\n\n                request.setEndCallback(new CompletedCallback() {\n                    @Override\n                    public void onCompleted(Exception ex) {\n                        response.send(body.getField(\"baz\") + gotten + body.getField(\"foo\"));\n                    }\n                });\n            }\n        });\n    }\n\n    protected void tearDown() throws Exception {\n\n        httpServer.stop();\n        AsyncServer.getDefault().stop();\n    }\n\n    public void testUpload() throws Exception {\n        setUp();\n\n        try {\n            File dummy = getContext().getFileStreamPath(\"dummy.txt\");\n            final String FIELD_VAL = \"bar\";\n            dummy.getParentFile().mkdirs();\n            FileOutputStream fout = new FileOutputStream(dummy);\n            byte[] zeroes = new byte[100000];\n            for (int i = 0; i < 10; i++) {\n                fout.write(zeroes);\n            }\n            fout.close();\n//        StreamUtility.writeFile(dummy, DUMMY_VAL);\n\n            AsyncHttpPost post = new AsyncHttpPost(\"http://localhost:5000\");\n            MultipartFormDataBody body = new MultipartFormDataBody();\n            body.addStringPart(\"foo\", FIELD_VAL);\n            body.addFilePart(\"my-file\", dummy);\n            body.addStringPart(\"baz\", FIELD_VAL);\n            post.setBody(body);\n\n            Future<String> ret = AsyncHttpClient.getDefaultInstance().executeString(post, new StringCallback() {\n                @Override\n                public void onCompleted(Exception e, AsyncHttpResponse source, String result) {\n                }\n            });\n\n            String data = ret.get(10000, TimeUnit.MILLISECONDS);\n            assertEquals(data, FIELD_VAL + (zeroes.length * 10) + FIELD_VAL);\n        }\n        finally {\n            tearDown();\n        }\n\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/test/src/com/koushikdutta/async/test/ParserTests.java",
    "content": "package com.koushikdutta.async.test;\n\nimport com.koushikdutta.async.ByteBufferList;\nimport com.koushikdutta.async.FilteredDataEmitter;\nimport com.koushikdutta.async.future.Future;\nimport com.koushikdutta.async.parser.DocumentParser;\nimport com.koushikdutta.async.parser.StringParser;\nimport com.koushikdutta.async.util.Charsets;\n\nimport junit.framework.TestCase;\n\nimport org.w3c.dom.Document;\n\nimport java.nio.ByteBuffer;\nimport java.nio.charset.Charset;\n\n/**\n * Created by koush on 7/10/14.\n */\npublic class ParserTests extends TestCase {\n    public void testString() throws Exception {\n        StringParser p = new StringParser();\n        FilteredDataEmitter f = new FilteredDataEmitter() {\n            @Override\n            public boolean isPaused() {\n                return false;\n            }\n        };\n        Future<String> ret = p.parse(f);\n        ByteBufferList l = new ByteBufferList();\n        l.add(ByteBuffer.wrap(\"foo\".getBytes(Charsets.US_ASCII.name())));\n        f.onDataAvailable(f, l);\n        f.getEndCallback().onCompleted(null);\n        String s = ret.get();\n        assertEquals(s, \"foo\");\n    }\n\n    public void testUtf8String() throws Exception {\n        StringParser p = new StringParser();\n        FilteredDataEmitter f = new FilteredDataEmitter() {\n            @Override\n            public String charset() {\n                return Charsets.UTF_8.name();\n            }\n\n            @Override\n            public boolean isPaused() {\n                return false;\n            }\n        };\n        Future<String> ret = p.parse(f);\n        ByteBufferList l = new ByteBufferList();\n        l.add(ByteBuffer.wrap(\"æææ\".getBytes(Charsets.UTF_8.name())));\n        f.onDataAvailable(f, l);\n        f.getEndCallback().onCompleted(null);\n        String s = ret.get();\n        assertEquals(s, \"æææ\");\n    }\n\n    public void testAsyncParserBase() throws Exception {\n        DocumentParser parser = new DocumentParser();\n        assertEquals(parser.getType(), Document.class);\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/test/src/com/koushikdutta/async/test/ProxyTests.java",
    "content": "package com.koushikdutta.async.test;\n\nimport android.util.Log;\n\nimport com.koushikdutta.async.http.AsyncHttpClient;\nimport com.koushikdutta.async.http.AsyncHttpGet;\n\nimport junit.framework.TestCase;\n\nimport java.util.concurrent.TimeUnit;\n\n/**\n * Created by koush on 4/20/14.\n */\npublic class ProxyTests extends TestCase {\n    private void disabledTestSSLProxy() throws Exception {\n        AsyncHttpGet get = new AsyncHttpGet(\"http://yahoo.com\");\n        get.enableProxy(\"192.168.2.21\", 8888);\n        get.setLogging(\"SSLProxy\", Log.VERBOSE);\n        String ret = AsyncHttpClient.getDefaultInstance().executeString(get, null).get(5000, TimeUnit.DAYS.MILLISECONDS);\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/test/src/com/koushikdutta/async/test/RedirectTests.java",
    "content": "package com.koushikdutta.async.test;\n\nimport com.koushikdutta.async.AsyncServer;\nimport com.koushikdutta.async.http.AsyncHttpClient;\nimport com.koushikdutta.async.http.AsyncHttpGet;\nimport com.koushikdutta.async.http.server.AsyncHttpServer;\nimport com.koushikdutta.async.http.server.AsyncHttpServerRequest;\nimport com.koushikdutta.async.http.server.AsyncHttpServerResponse;\nimport com.koushikdutta.async.http.server.HttpServerRequestCallback;\n\nimport junit.framework.TestCase;\n\n/**\n * Created by koush on 11/4/13.\n */\npublic class RedirectTests extends TestCase {\n    @Override\n    protected void setUp() throws Exception {\n        super.setUp();\n        AsyncHttpServer server = new AsyncHttpServer();\n        server.listen(6003);\n        server.get(\"/foo\", new HttpServerRequestCallback() {\n            @Override\n            public void onRequest(AsyncHttpServerRequest request, final AsyncHttpServerResponse response) {\n                response.redirect(\"/bar\");\n            }\n        });\n        server.get(\"/bar\", new HttpServerRequestCallback() {\n            @Override\n            public void onRequest(AsyncHttpServerRequest request, final AsyncHttpServerResponse response) {\n                response.send(\"BORAT!\");\n            }\n        });\n\n        server.get(\"/foo/poo\", new HttpServerRequestCallback() {\n            @Override\n            public void onRequest(AsyncHttpServerRequest request, final AsyncHttpServerResponse response) {\n                response.redirect(\"../poo\");\n            }\n        });\n        server.get(\"/poo\", new HttpServerRequestCallback() {\n            @Override\n            public void onRequest(AsyncHttpServerRequest request, final AsyncHttpServerResponse response) {\n                response.send(\"SWEET!\");\n            }\n        });\n        server.get(\"/foo/bar\", new HttpServerRequestCallback() {\n            @Override\n            public void onRequest(AsyncHttpServerRequest request, final AsyncHttpServerResponse response) {\n                response.redirect(\"baz\");\n            }\n        });\n        server.get(\"/foo/baz\", new HttpServerRequestCallback() {\n            @Override\n            public void onRequest(AsyncHttpServerRequest request, final AsyncHttpServerResponse response) {\n                response.send(\"SUCCESS!\");\n            }\n        });\n    }\n\n    @Override\n    protected void tearDown() throws Exception {\n        super.tearDown();\n        AsyncServer.getDefault().stop();\n    }\n\n    public void testRelativeRedirect() throws Exception {\n        String ret = AsyncHttpClient.getDefaultInstance()\n        .executeString(new AsyncHttpGet(\"http://localhost:6003/foo/bar\"), null)\n        .get();\n\n        assertEquals(ret, \"SUCCESS!\");\n\n        ret = AsyncHttpClient.getDefaultInstance()\n        .executeString(new AsyncHttpGet(\"http://localhost:6003/foo\"), null)\n        .get();\n\n        assertEquals(ret, \"BORAT!\");\n\n        ret = AsyncHttpClient.getDefaultInstance()\n        .executeString(new AsyncHttpGet(\"http://localhost:6003/foo/poo\"), null)\n        .get();\n\n        assertEquals(ret, \"SWEET!\");\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/test/src/com/koushikdutta/async/test/SSLTests.java",
    "content": "package com.koushikdutta.async.test;\n\nimport androidx.test.runner.AndroidJUnit4;\n\nimport com.koushikdutta.async.AsyncServer;\nimport com.koushikdutta.async.http.AsyncHttpClient;\nimport com.koushikdutta.async.http.AsyncHttpGet;\nimport com.koushikdutta.async.http.server.AsyncHttpServer;\nimport com.koushikdutta.async.http.server.AsyncHttpServerRequest;\nimport com.koushikdutta.async.http.server.AsyncHttpServerResponse;\nimport com.koushikdutta.async.http.server.HttpServerRequestCallback;\n\nimport org.json.JSONObject;\nimport org.junit.runner.RunWith;\n\nimport java.security.KeyStore;\n\nimport javax.net.ssl.KeyManagerFactory;\nimport javax.net.ssl.SSLContext;\nimport javax.net.ssl.TrustManagerFactory;\n\nimport static androidx.test.InstrumentationRegistry.getContext;\n\n/**\n * Created by koush on 6/4/13.\n */\n@RunWith(AndroidJUnit4.class)\npublic class SSLTests {\n    public void testKeys() throws Exception {\n        KeyManagerFactory kmf = KeyManagerFactory.getInstance(\"X509\");\n        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());\n\n        ks.load(getContext().getResources().openRawResource(R.raw.keystore), \"storepass\".toCharArray());\n        kmf.init(ks, \"storepass\".toCharArray());\n\n\n        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());\n        KeyStore ts = KeyStore.getInstance(KeyStore.getDefaultType());\n        ts.load(getContext().getResources().openRawResource(R.raw.keystore), \"storepass\".toCharArray());\n        tmf.init(ts);\n\n        SSLContext sslContext = SSLContext.getInstance(\"TLS\");\n        sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);\n\n        AsyncHttpServer httpServer = new AsyncHttpServer();\n        httpServer.listenSecure(8888, sslContext);\n        httpServer.get(\"/\", new HttpServerRequestCallback() {\n            @Override\n            public void onRequest(AsyncHttpServerRequest request, AsyncHttpServerResponse response) {\n                response.send(\"hello\");\n            }\n        });\n\n        Thread.sleep(1000);\n\n        AsyncHttpClient.getDefaultInstance().getSSLSocketMiddleware().setSSLContext(sslContext);\n        AsyncHttpClient.getDefaultInstance().getSSLSocketMiddleware().setTrustManagers(tmf.getTrustManagers());\n        AsyncHttpClient.getDefaultInstance().executeString(new AsyncHttpGet(\"https://localhost:8888/\"), null).get();\n    }\n\n    public void disabled__testClientCertificateIssue163() throws Exception {\n        // https://security.springthroughtest.com/hello.json\n\n        AsyncServer server = new AsyncServer();\n        try {\n            AsyncHttpClient client = new AsyncHttpClient(server);\n            JSONObject json = client.executeJSONObject(new AsyncHttpGet(\"https://security.springthroughtest.com/hello.json\"), null).get();\n\n        }\n        finally {\n            server.stop();\n        }\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/test/src/com/koushikdutta/async/test/SanityChecks.java",
    "content": "package com.koushikdutta.async.test;\n\nimport junit.framework.TestCase;\n\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\n\n/**\n * Created by koush on 5/15/13.\n */\npublic class SanityChecks extends TestCase {\n    public void testByteOrder() {\n        assertTrue(ByteBuffer.allocate(0).order().equals(ByteOrder.BIG_ENDIAN));\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/test/src/com/koushikdutta/async/test/TimeoutTests.java",
    "content": "package com.koushikdutta.async.test;\n\nimport android.net.Uri;\nimport android.util.Log;\n\nimport com.koushikdutta.async.AsyncServer;\nimport com.koushikdutta.async.DataSink;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.http.AsyncHttpClient;\nimport com.koushikdutta.async.http.AsyncHttpRequest;\nimport com.koushikdutta.async.http.body.StringBody;\nimport com.koushikdutta.async.http.server.AsyncHttpServer;\nimport com.koushikdutta.async.http.server.AsyncHttpServerRequest;\nimport com.koushikdutta.async.http.server.AsyncHttpServerResponse;\nimport com.koushikdutta.async.http.server.HttpServerRequestCallback;\n\nimport junit.framework.TestCase;\n\nimport java.net.URI;\nimport java.util.concurrent.TimeoutException;\n\n/**\n * Created by koush on 7/11/13.\n */\npublic class TimeoutTests extends TestCase {\n    public TimeoutTests() {\n        server.get(\"/3\", new HttpServerRequestCallback() {\n            @Override\n            public void onRequest(AsyncHttpServerRequest request, final AsyncHttpServerResponse response) {\n                // never respond\n                AsyncServer.getDefault().postDelayed(new Runnable() {\n                    @Override\n                    public void run() {\n                        response.send(\"3\");\n                    }\n                }, 1000);\n            }\n        });\n\n        server.post(\"/now\", new HttpServerRequestCallback() {\n            @Override\n            public void onRequest(AsyncHttpServerRequest request, AsyncHttpServerResponse response) {\n                StringBody body = (StringBody)request.getBody();\n                response.send(body.get());\n            }\n        });\n    }\n    AsyncHttpServer server = new AsyncHttpServer();\n\n    @Override\n    protected void setUp() throws Exception {\n        super.setUp();\n        server.listen(AsyncServer.getDefault(), 5000);\n    }\n\n    @Override\n    protected void tearDown() throws Exception {\n        super.tearDown();\n        server.stop();\n        AsyncServer.getDefault().stop();\n    }\n\n    public void testTimeout() throws Exception {\n        AsyncHttpRequest req = new AsyncHttpRequest(Uri.parse(\"http://localhost:5000/3\"), \"GET\");\n        req.setTimeout(1000);\n        try {\n            AsyncHttpClient.getDefaultInstance().executeString(req, null).get();\n            fail();\n        }\n        catch (Exception e) {\n            Log.d(\"timeout\", \"error\", e);\n            assertTrue(e.getCause() instanceof TimeoutException);\n        }\n\n        req = new AsyncHttpRequest(Uri.parse(\"http://localhost:5000/3\"), \"GET\");\n        assertEquals(\"3\", AsyncHttpClient.getDefaultInstance().executeString(req, null).get());\n    }\n\n    public void testSlowBody() throws Exception {\n        AsyncHttpRequest req = new AsyncHttpRequest(Uri.parse(\"http://localhost:5000/now\"), \"POST\");\n        req.setTimeout(1000);\n        req.setLogging(\"slowbody\", Log.VERBOSE);\n        req.setBody(new DelayedStringBody(\"foo\"));\n        assertEquals(\"foo\", AsyncHttpClient.getDefaultInstance().executeString(req, null).get());\n\n        req = new AsyncHttpRequest(Uri.parse(\"http://localhost:5000/3\"), \"GET\");\n        req.setLogging(\"slowbody\", Log.VERBOSE);\n        req.setTimeout(100);\n        req.setBody(new DelayedStringBody(\"foo\"));\n        try {\n            AsyncHttpClient.getDefaultInstance().executeString(req, null).get();\n            fail();\n        }\n        catch (Exception e) {\n            Log.d(\"timeout\", \"error\", e);\n            assertTrue(e.getCause() instanceof TimeoutException);\n        }\n    }\n\n    class DelayedStringBody extends StringBody {\n        public DelayedStringBody(String value) {\n            super(value);\n        }\n        @Override\n        public void write(final AsyncHttpRequest request, final DataSink sink, final CompletedCallback completed) {\n            AsyncServer.getDefault().postDelayed(new Runnable() {\n                @Override\n                public void run() {\n                    DelayedStringBody.super.write(request, sink, completed);\n                }\n            }, 1000);\n        }\n    }\n}\n"
  },
  {
    "path": "AndroidAsync/test/src/com/koushikdutta/async/test/TriggerFuture.java",
    "content": "package com.koushikdutta.async.test;\n\nimport com.koushikdutta.async.future.SimpleFuture;\n\nclass TriggerFuture extends SimpleFuture<Integer> {\n    public void trigger() {\n        setComplete(2020);\n    }\n}"
  },
  {
    "path": "AndroidAsync/test/src/com/koushikdutta/async/test/WebSocketTests.java",
    "content": "package com.koushikdutta.async.test;\n\nimport com.koushikdutta.async.AsyncServer;\nimport com.koushikdutta.async.callback.CompletedCallback;\nimport com.koushikdutta.async.http.AsyncHttpClient;\nimport com.koushikdutta.async.http.AsyncHttpClient.WebSocketConnectCallback;\nimport com.koushikdutta.async.http.Headers;\nimport com.koushikdutta.async.http.WebSocket;\nimport com.koushikdutta.async.http.WebSocket.StringCallback;\nimport com.koushikdutta.async.http.server.AsyncHttpServer;\nimport com.koushikdutta.async.http.server.AsyncHttpServer.WebSocketRequestCallback;\nimport com.koushikdutta.async.http.server.AsyncHttpServerRequest;\n\nimport junit.framework.TestCase;\n\nimport org.junit.Test;\n\nimport java.util.concurrent.Semaphore;\nimport java.util.concurrent.TimeUnit;\n\npublic class WebSocketTests extends TestCase {\n    AsyncHttpServer httpServer;\n\n    @Override\n    protected void setUp() throws Exception {\n        super.setUp();\n\n        httpServer = new AsyncHttpServer();\n        httpServer.setErrorCallback(new CompletedCallback() {\n            @Override\n            public void onCompleted(Exception ex) {\n                fail();\n            }\n        });\n        httpServer.listen(AsyncServer.getDefault(), 5000);\n        \n    \n        httpServer.websocket(\"/ws\", new WebSocketRequestCallback() {\n            @Override\n            public void onConnected(final WebSocket webSocket, AsyncHttpServerRequest request) {\n                webSocket.setStringCallback(new StringCallback() {\n                    @Override\n                    public void onStringAvailable(String s) {\n                        webSocket.send(s);\n                    }\n                });\n            }\n        });\n    }\n    \n    private static final long TIMEOUT = 60000L;\n    @Test\n    public void testWebSocket() throws Exception {\n        final Semaphore semaphore = new Semaphore(0);\n\n        AsyncHttpClient.getDefaultInstance().websocket(\"http://localhost:5000/ws\", (String)null, new WebSocketConnectCallback() {\n            @Override\n            public void onCompleted(Exception ex, WebSocket webSocket) {\n                webSocket.send(\"hello\");\n                webSocket.setStringCallback(new StringCallback() {\n                    @Override\n                    public void onStringAvailable(String s) {\n                        assertEquals(s, \"hello\");\n                        semaphore.release();\n                    }\n                });\n            }\n        });\n        \n        assertTrue(semaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS));\n    }\n\n//    public void testDisconnect() throws Exception {\n//        final Semaphore semaphore = new Semaphore(0);\n//\n//        AsyncHttpClient.getDefaultInstance().websocket(\"http://192.168.1.2:3005\", null, new WebSocketConnectCallback() {\n//            @Override\n//            public void onCompleted(Exception ex, WebSocket webSocket) {\n//                webSocket.setClosedCallback(new CompletedCallback() {\n//                    @Override\n//                    public void onCompleted(Exception ex) {\n//                        semaphore.release();\n//                    }\n//                });\n//            }\n//        });\n//\n//        assertTrue(semaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS));\n//    }\n    \n    @Override\n    protected void tearDown() throws Exception {\n        super.tearDown();\n        \n        httpServer.stop();\n    }\n}\n"
  },
  {
    "path": "AndroidAsync-Kotlin/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "AndroidAsync-Kotlin/README.md",
    "content": "# Support for Kotlin Coroutines in AndroidAsync and Ion\n\nAdds coroutines support to AndroidAsync and [Ion](https://github.com/koush/ion).\n\nMaven:\n```xml\n<dependency>\n    <groupId>com.koushikdutta.async</groupId>\n    <artifactId>androidasync-kotlin</artifactId>\n    <version>(insert latest version)</version>\n</dependency>\n```\n\nGradle:\n```groovy\ndependencies {\n    compile 'com.koushikdutta.async:androidasync-kotlin:<insert latest version>'\n}\n```\n\nSince AndroidAsync and Ion operations all returned futures, you can simply call await() on them within a Kotlin suspend function.\n\n```kotlin\nsuspend fun getTheRobotsTxt() {\n  val googleRobots = Ion.with(context)\n  .load(\"https://google.com/robots.txt\")\n  .asString()\n  .await()\n\n  val githubRobots = Ion.with(context)\n  .load(\"https://github.com/robots.txt\")\n  .asString()\n  .await()\n\n  return googleRobots + githubRobots\n}\n```\n\nThat's it!\n\nBut remember that the await() suspends, so if you want to fetch both robots.txt at the same time:\n\n```kotlin\nsuspend fun getTheRobotsTxt() {\n  val googleRobots = Ion.with(context)\n  .load(\"https://google.com/robots.txt\")\n  .asString()\n\n  val githubRobots = Ion.with(context)\n  .load(\"https://github.com/robots.txt\")\n  .asString()\n\n  return googleRobots.await() + githubRobots.await()\n}\n```\n\n"
  },
  {
    "path": "AndroidAsync-Kotlin/build.gradle",
    "content": "apply plugin: 'com.android.library'\napply plugin: 'kotlin-android'\napply plugin: 'kotlin-android-extensions'\n\nbuildscript {\n    ext.kotlin_version = '1.3.61'\n    repositories {\n        mavenCentral()\n    }\n    dependencies {\n        classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version\"\n    }\n}\n\nandroid {\n    compileSdkVersion 29\n    buildToolsVersion \"29.0.2\"\n\n    kotlinOptions {\n        apiVersion = \"1.3\"\n        languageVersion = \"1.3\"\n    }\n\n    compileOptions {\n        sourceCompatibility 1.8\n        targetCompatibility 1.8\n    }\n\n    defaultConfig {\n        minSdkVersion 14\n        targetSdkVersion 29\n\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n        consumerProguardFiles 'consumer-rules.pro'\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n        }\n    }\n\n}\n\ndependencies {\n    implementation fileTree(dir: 'libs', include: ['*.jar'])\n    api project(':AndroidAsync:AndroidAsync')\n    implementation \"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version\"\n\n\n    testImplementation 'junit:junit:4.12'\n    testImplementation 'org.jetbrains.kotlin:kotlin-test-junit:1.3.61'\n    androidTestImplementation 'androidx.test:runner:1.2.0'\n    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'\n}\n\n// upload to maven task\nif (false && System.getenv().I_AM_KOUSH == 'true') {\n    apply from: '/Users/koush/cfg/maven.gradle'\n}\n"
  },
  {
    "path": "AndroidAsync-Kotlin/consumer-rules.pro",
    "content": ""
  },
  {
    "path": "AndroidAsync-Kotlin/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "AndroidAsync-Kotlin/src/androidTest/java/com/koushikdutta/async/kotlin/ExampleInstrumentedTest.kt",
    "content": "package com.koushikdutta.async.kotlin\n\n\nimport androidx.test.platform.app.InstrumentationRegistry\nimport androidx.test.runner.AndroidJUnit4\nimport org.junit.Assert.assertEquals\nimport org.junit.Test\nimport org.junit.runner.RunWith\n\n/**\n * Instrumented test, which will execute on an Android device.\n *\n * See [testing documentation](http://d.android.com/tools/testing).\n */\n@RunWith(AndroidJUnit4::class)\nclass ExampleInstrumentedTest {\n    @Test\n    fun useAppContext() {\n        // Context of the app under test.\n        val appContext = InstrumentationRegistry.getInstrumentation().targetContext\n        assertEquals(\"com.koushikdutta.async.kotlin.test\", appContext.packageName)\n    }\n}\n"
  },
  {
    "path": "AndroidAsync-Kotlin/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:maven=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.koushikdutta.async.kotlin\"\n    maven:groupId=\"com.koushikdutta.async\"\n    android:versionCode=\"309\"\n    android:versionName=\"3.0.9\">\n\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n\n    <application>\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "AndroidAsync-Kotlin/src/main/java/com/koushikdutta/async/kotlin/FutureExtensions.kt",
    "content": "package com.koushikdutta.async.kotlin\n\nimport com.koushikdutta.async.future.Future\nimport kotlin.coroutines.resume\nimport kotlin.coroutines.resumeWithException\nimport kotlin.coroutines.suspendCoroutine\n\nsuspend fun <T> Future<T>.await(): T {\n    return suspendCoroutine {\n        this.setCallback { e, result ->\n            if (e != null)\n                it.resumeWithException(e)\n            else\n                it.resume(result)\n        }\n    }\n}"
  },
  {
    "path": "AndroidAsync-Kotlin/src/test/java/com/koushikdutta/async/kotlin/ExampleUnitTest.kt",
    "content": "package com.koushikdutta.async.kotlin\n\nimport org.junit.Test\n\nimport org.junit.Assert.*\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * See [testing documentation](http://d.android.com/tools/testing).\n */\nclass ExampleUnitTest {\n    @Test\n    fun addition_isCorrect() {\n        assertEquals(4, 2 + 2)\n    }\n}\n"
  },
  {
    "path": "AndroidAsyncSample/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.koushikdutta.async.sample\"\n    android:versionCode=\"1\"\n    android:versionName=\"1.0\" >\n\n    <uses-sdk\n        android:minSdkVersion=\"8\"\n        android:targetSdkVersion=\"17\" />\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n    <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>\n\n    <application\n        android:icon=\"@drawable/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:theme=\"@style/AppTheme\" >\n        <activity\n            android:name=\".MainActivity\"\n            android:label=\"@string/title_activity_main\" >\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n    </application>\n</manifest>\n"
  },
  {
    "path": "AndroidAsyncSample/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\ndependencies {\n    compile project(':AndroidAsync')\n}\n\nandroid {\n    sourceSets {\n        main {\n            manifest.srcFile 'AndroidManifest.xml'\n            res.srcDirs = ['res/']\n            java.srcDirs = ['src/']\n        }\n    }\n\n    defaultConfig {\n        targetSdkVersion 24\n        minSdkVersion 9\n    }\n\n    compileSdkVersion project.hasProperty('global_compileSdkVersion') ? global_compileSdkVersion : 25\n    buildToolsVersion project.hasProperty('global_buildToolsVersion') ? global_buildToolsVersion : '25.0.2'\n}\n\n"
  },
  {
    "path": "AndroidAsyncSample/proguard-project.txt",
    "content": "# To enable ProGuard in your project, edit project.properties\n# to define the proguard.config property as described in that file.\n#\n# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in ${sdk.dir}/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the ProGuard\n# include property in project.properties.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "AndroidAsyncSample/project.properties",
    "content": "# This file is automatically generated by Android Tools.\n# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\n#\n# This file must be checked in Version Control Systems.\n#\n# To customize properties used by the Ant build system edit\n# \"ant.properties\", and override values to adapt the script to your\n# project structure.\n#\n# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):\n#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt\n\n# Project target.\ntarget=android-17\nandroid.library.reference.1=../AndroidAsync\n"
  },
  {
    "path": "AndroidAsyncSample/res/layout/activity_main.xml",
    "content": "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\" android:layout_gravity=\"center\" android:gravity=\"center\">\n\n    <Button\n        android:layout_gravity=\"center\"\n        android:text=\"@string/download\"\n        android:id=\"@+id/go\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\" >\n    </Button>\n\n    <ImageView\n        android:id=\"@+id/rommanager\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\" />\n\n    <ImageView\n        android:id=\"@+id/desksms\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\" />\n\n    <ImageView\n        android:id=\"@+id/tether\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\" />\n\n    <ImageView\n        android:id=\"@+id/chart\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\" />\n\n</LinearLayout>"
  },
  {
    "path": "AndroidAsyncSample/res/menu/activity_main.xml",
    "content": "<menu xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:id=\"@+id/menu_settings\"\n        android:title=\"@string/menu_settings\"\n        android:orderInCategory=\"100\"\n        android:showAsAction=\"never\" />\n</menu>\n"
  },
  {
    "path": "AndroidAsyncSample/res/values/strings.xml",
    "content": "<resources>\n\n    <string name=\"app_name\">AndroidAsyncSample</string>\n    <string name=\"hello_world\">Hello world!</string>\n    <string name=\"menu_settings\">Settings</string>\n    <string name=\"title_activity_main\">AndroidAsync Sample</string>\n\t<string name=\"download\">Download Images</string>\n</resources>"
  },
  {
    "path": "AndroidAsyncSample/res/values/styles.xml",
    "content": "<resources>\n\n    <style name=\"AppTheme\" parent=\"android:Theme.Light\" />\n\n</resources>"
  },
  {
    "path": "AndroidAsyncSample/res/values-v11/styles.xml",
    "content": "<resources>\n\n    <style name=\"AppTheme\" parent=\"android:Theme.Holo.Light\" />\n\n</resources>"
  },
  {
    "path": "AndroidAsyncSample/res/values-v14/styles.xml",
    "content": "<resources>\n\n    <style name=\"AppTheme\" parent=\"android:Theme.Holo.Light.DarkActionBar\" />\n\n</resources>"
  },
  {
    "path": "AndroidAsyncSample/src/com/koushikdutta/async/sample/MainActivity.java",
    "content": "package com.koushikdutta.async.sample;\n\nimport android.annotation.SuppressLint;\nimport android.app.Activity;\nimport android.graphics.Bitmap;\nimport android.graphics.BitmapFactory;\nimport android.graphics.drawable.BitmapDrawable;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.view.Menu;\nimport android.view.MenuItem;\nimport android.view.MenuItem.OnMenuItemClickListener;\nimport android.view.View;\nimport android.view.View.OnClickListener;\nimport android.widget.Button;\nimport android.widget.ImageView;\nimport android.widget.Toast;\n\nimport com.koushikdutta.async.http.AsyncHttpClient;\nimport com.koushikdutta.async.http.AsyncHttpGet;\nimport com.koushikdutta.async.http.AsyncHttpPost;\nimport com.koushikdutta.async.http.AsyncHttpResponse;\nimport com.koushikdutta.async.http.BasicNameValuePair;\nimport com.koushikdutta.async.http.NameValuePair;\nimport com.koushikdutta.async.http.body.UrlEncodedFormBody;\nimport com.koushikdutta.async.http.cache.ResponseCacheMiddleware;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.ArrayList;\n\npublic class MainActivity extends Activity {\n    static ResponseCacheMiddleware cacher;\n    \n    ImageView rommanager;\n    ImageView tether;\n    ImageView desksms;\n    ImageView chart;\n\n    @SuppressLint(\"NewApi\")\n    @Override\n    public void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n\n        if (cacher == null) {\n            try {\n                cacher = ResponseCacheMiddleware.addCache(AsyncHttpClient.getDefaultInstance(), getFileStreamPath(\"asynccache\"), 1024 * 1024 * 10);\n                cacher.setCaching(false);\n            }\n            catch (IOException e) {\n                Toast.makeText(getApplicationContext(), \"unable to create cache\", Toast.LENGTH_SHORT).show();\n            }\n        }\n        setContentView(R.layout.activity_main);\n        \n        Button b = (Button)findViewById(R.id.go);\n        b.setOnClickListener(new OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                refresh();\n            }\n        });\n        \n        rommanager = (ImageView)findViewById(R.id.rommanager);\n        tether = (ImageView)findViewById(R.id.tether);\n        desksms = (ImageView)findViewById(R.id.desksms);\n        chart = (ImageView)findViewById(R.id.chart);\n        \n        showCacheToast();\n    }\n\n    void showCacheToast() {\n        boolean caching = cacher.getCaching();\n        Toast.makeText(getApplicationContext(), \"Caching: \" + caching, Toast.LENGTH_SHORT).show();\n    }\n    \n    @Override\n    public boolean onCreateOptionsMenu(Menu menu) {\n        menu.add(\"Toggle Caching\").setOnMenuItemClickListener(new OnMenuItemClickListener() {\n            @Override\n            public boolean onMenuItemClick(MenuItem item) {\n                cacher.setCaching(!cacher.getCaching());\n                showCacheToast();\n                return true;\n            }\n        });\n        return true;\n    }\n    \n    private void assignImageView(final ImageView iv, final BitmapDrawable bd) {\n        iv.getHandler().post(new Runnable() {\n          @Override\n          public void run() {\n              iv.setImageDrawable(bd);\n          }\n        });\n    }\n\n    private void getFile(final ImageView iv, String url, final String filename) {\n        AsyncHttpClient.getDefaultInstance().executeFile(new AsyncHttpGet(url), filename, new AsyncHttpClient.FileCallback() {\n            @Override\n            public void onCompleted(Exception e, AsyncHttpResponse response, File result) {\n                if (e != null) {\n                    e.printStackTrace();\n                    return;\n                }\n                Bitmap bitmap = BitmapFactory.decodeFile(filename);\n                result.delete();\n                if (bitmap == null)\n                    return;\n                BitmapDrawable bd = new BitmapDrawable(bitmap);\n                assignImageView(iv, bd);\n            }\n        });\n    }\n\n    private void getChartFile() {\n        final ImageView iv = chart;\n        final String filename = getFileStreamPath(randomFile()).getAbsolutePath();\n        ArrayList<NameValuePair> pairs = new ArrayList<NameValuePair>();\n        pairs.add(new BasicNameValuePair(\"cht\", \"lc\"));\n        pairs.add(new BasicNameValuePair(\"chtt\", \"This is a google chart\"));\n        pairs.add(new BasicNameValuePair(\"chs\", \"512x512\"));\n        pairs.add(new BasicNameValuePair(\"chxt\", \"x\"));\n        pairs.add(new BasicNameValuePair(\"chd\", \"t:40,20,50,20,100\"));\n        UrlEncodedFormBody writer = new UrlEncodedFormBody(pairs);\n        try {\n            AsyncHttpPost post = new AsyncHttpPost(\"http://chart.googleapis.com/chart\");\n            post.setBody(writer);\n            AsyncHttpClient.getDefaultInstance().executeFile(post, filename, new AsyncHttpClient.FileCallback() {\n                @Override\n                public void onCompleted(Exception e, AsyncHttpResponse response, File result) {\n                    if (e != null) {\n                        e.printStackTrace();\n                        return;\n                    }\n                    Bitmap bitmap = BitmapFactory.decodeFile(filename);\n                    result.delete();\n                    if (bitmap == null)\n                        return;\n                    BitmapDrawable bd = new BitmapDrawable(bitmap);\n                    assignImageView(iv, bd);\n                }\n            });\n        }\n        catch (Exception ex) {\n            ex.printStackTrace();\n        }\n    }\n    \n    private String randomFile() {\n        return ((Long)Math.round(Math.random() * 1000)).toString() + \".png\";\n    }\n    \n    private void refresh() {\n        rommanager.setImageBitmap(null);\n        tether.setImageBitmap(null);\n        desksms.setImageBitmap(null);\n        chart.setImageBitmap(null);\n        \n        getFile(rommanager, \"https://raw.github.com/koush/AndroidAsync/master/rommanager.png\", getFileStreamPath(randomFile()).getAbsolutePath());\n        getFile(tether, \"https://raw.github.com/koush/AndroidAsync/master/tether.png\", getFileStreamPath(randomFile()).getAbsolutePath());\n        getFile(desksms, \"https://raw.github.com/koush/AndroidAsync/master/desksms.png\", getFileStreamPath(randomFile()).getAbsolutePath());\n        getChartFile();\n        \n        Log.i(LOGTAG, \"cache hit: \" + cacher.getCacheHitCount());\n        Log.i(LOGTAG, \"cache store: \" + cacher.getCacheStoreCount());\n        Log.i(LOGTAG, \"conditional cache hit: \" + cacher.getConditionalCacheHitCount());\n        Log.i(LOGTAG, \"network: \" + cacher.getNetworkCount());\n    }\n    \n    private static final String LOGTAG = \"AsyncSample\";\n}\n"
  },
  {
    "path": "AndroidAsyncSample/src/com/koushikdutta/async/sample/middleware/BasicAuthMiddleware.java",
    "content": "package com.koushikdutta.async.sample.middleware;\n\nimport android.text.TextUtils;\nimport android.util.Base64;\n\nimport com.koushikdutta.async.http.AsyncHttpClient;\nimport com.koushikdutta.async.http.SimpleMiddleware;\n\nimport java.util.Hashtable;\n\n/**\n * Created by koush on 2/15/15.\n */\npublic class BasicAuthMiddleware extends SimpleMiddleware {\n    // insert this using\n    public static BasicAuthMiddleware add(AsyncHttpClient client) {\n        BasicAuthMiddleware ret = new BasicAuthMiddleware();\n        client.getMiddleware().add(ret);\n        return ret;\n    }\n\n    @Override\n    public void onRequest(OnRequestData data) {\n        super.onRequest(data);\n        // do more checking here, since uri may not necessarily be http or have a host, etc.\n        String auth = auths.get(data.request.getUri().getHost());\n        if (!TextUtils.isEmpty(auth))\n            data.request.setHeader(\"Authorization\", auth);\n    }\n\n    Hashtable<String, String> auths = new Hashtable<String, String>();\n    public void setAuthorizationForHost(String host, String username, String password) {\n        String auth = \"Basic \" + Base64.encodeToString(String.format(\"%s:%s\", username, password).getBytes(), Base64.NO_WRAP);\n        auths.put(host, auth);\n    }\n}\n"
  },
  {
    "path": "AndroidAsyncSample/src/com/koushikdutta/async/sample/middleware/CacheOverrideMiddleware.java",
    "content": "package com.koushikdutta.async.sample.middleware;\n\nimport android.text.TextUtils;\nimport android.util.Base64;\n\nimport com.koushikdutta.async.http.AsyncHttpClient;\nimport com.koushikdutta.async.http.AsyncHttpClientMiddleware;\nimport com.koushikdutta.async.http.SimpleMiddleware;\n\nimport java.util.Hashtable;\n\n/**\n * Created by koush on 2/15/15.\n */\npublic class CacheOverrideMiddleware extends SimpleMiddleware {\n    // insert this using\n    public static CacheOverrideMiddleware add(AsyncHttpClient client) {\n        CacheOverrideMiddleware ret = new CacheOverrideMiddleware();\n        // add this first so it gets called before everything else\n        client.getMiddleware().add(ret);\n        return ret;\n    }\n\n    @Override\n    public void onHeadersReceived(OnHeadersReceivedDataOnRequestSentData data) {\n        super.onHeadersReceived(data);\n\n        // do more checking here, since uri may not necessarily be http or have a host, etc.\n        String cache = cacheHeaders.get(data.request.getUri().getHost());\n        if (!TextUtils.isEmpty(cache))\n            data.response.headers().set(\"Cache-Control\", cache);\n    }\n\n    Hashtable<String, String> cacheHeaders = new Hashtable<String, String>();\n\n    /**\n     * Override cache-control directives\n     * @param host\n     * @param cacheControl a Cache-Control value, like \"max-age=300\" to cache for 5 minutes\n     */\n    public void setCacheControlForHost(String host, String cacheControl) {\n        cacheHeaders.put(host, cacheControl);\n    }\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright 2013 Koushik Dutta (2013)\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License."
  },
  {
    "path": "README.md",
    "content": "# AndroidAsync\r\n\r\nAndroidAsync is a low level network protocol library. If you are looking for an easy to use, higher level, Android aware,\r\nhttp request library, check out [Ion](https://github.com/koush/ion) (it is built on top of AndroidAsync). The typical Android\r\napp developer would probably be more interested in Ion.\r\n\r\nBut if you're looking for a raw Socket, HTTP(s) client/server, and WebSocket library for Android, AndroidAsync\r\nis it.\r\n\r\n#### Features\r\n * Based on NIO. Single threaded and callback driven.\r\n * All operations return a Future that can be cancelled\r\n * Socket client + socket server\r\n * HTTP client + server\r\n * WebSocket client + server\r\n\r\n### Download\r\n\r\nDownload [the latest JAR](https://search.maven.org/remote_content?g=com.koushikdutta.async&a=androidasync&v=LATEST\r\n) or grab via Maven:\r\n\r\n```xml\r\n<dependency>\r\n    <groupId>com.koushikdutta.async</groupId>\r\n    <artifactId>androidasync</artifactId>\r\n    <version>(insert latest version)</version>\r\n</dependency>\r\n```\r\n\r\nGradle: \r\n```groovy\r\ndependencies {\r\n    compile 'com.koushikdutta.async:androidasync:2.+'\r\n}\r\n```\r\n\r\n### Download a url to a String\r\n\r\n```java\r\n// url is the URL to download.\r\nAsyncHttpClient.getDefaultInstance().getString(url, new AsyncHttpClient.StringCallback() {\r\n    // Callback is invoked with any exceptions/errors, and the result, if available.\r\n    @Override\r\n    public void onCompleted(Exception e, AsyncHttpResponse response, String result) {\r\n        if (e != null) {\r\n            e.printStackTrace();\r\n            return;\r\n        }\r\n        System.out.println(\"I got a string: \" + result);\r\n    }\r\n});\r\n```\r\n\r\n\r\n### Download JSON from a url\r\n\r\n```java\r\n// url is the URL to download.\r\nAsyncHttpClient.getDefaultInstance().getJSONObject(url, new AsyncHttpClient.JSONObjectCallback() {\r\n    // Callback is invoked with any exceptions/errors, and the result, if available.\r\n    @Override\r\n    public void onCompleted(Exception e, AsyncHttpResponse response, JSONObject result) {\r\n        if (e != null) {\r\n            e.printStackTrace();\r\n            return;\r\n        }\r\n        System.out.println(\"I got a JSONObject: \" + result);\r\n    }\r\n});\r\n```\r\n\r\nOr for JSONArrays...\r\n\r\n```java\r\n// url is the URL to download.\r\nAsyncHttpClient.getDefaultInstance().getJSONArray(url, new AsyncHttpClient.JSONArrayCallback() {\r\n    // Callback is invoked with any exceptions/errors, and the result, if available.\r\n    @Override\r\n    public void onCompleted(Exception e, AsyncHttpResponse response, JSONArray result) {\r\n        if (e != null) {\r\n            e.printStackTrace();\r\n            return;\r\n        }\r\n        System.out.println(\"I got a JSONArray: \" + result);\r\n    }\r\n});\r\n```\r\n\r\n\r\n### Download a url to a file\r\n\r\n```java\r\nAsyncHttpClient.getDefaultInstance().getFile(url, filename, new AsyncHttpClient.FileCallback() {\r\n    @Override\r\n    public void onCompleted(Exception e, AsyncHttpResponse response, File result) {\r\n        if (e != null) {\r\n            e.printStackTrace();\r\n            return;\r\n        }\r\n        System.out.println(\"my file is available at: \" + result.getAbsolutePath());\r\n    }\r\n});\r\n```\r\n\r\n\r\n### Caching is supported too\r\n\r\n```java\r\n// arguments are the http client, the directory to store cache files,\r\n// and the size of the cache in bytes\r\nResponseCacheMiddleware.addCache(AsyncHttpClient.getDefaultInstance(),\r\n                                  getFileStreamPath(\"asynccache\"),\r\n                                  1024 * 1024 * 10);\r\n```\r\n\r\n\r\n### Need to do multipart/form-data uploads? That works too.\r\n\r\n```java\r\nAsyncHttpPost post = new AsyncHttpPost(\"http://myservercom/postform.html\");\r\nMultipartFormDataBody body = new MultipartFormDataBody();\r\nbody.addFilePart(\"my-file\", new File(\"/path/to/file.txt\");\r\nbody.addStringPart(\"foo\", \"bar\");\r\npost.setBody(body);\r\nAsyncHttpClient.getDefaultInstance().executeString(post, new AsyncHttpClient.StringCallback(){\r\n        @Override\r\n        public void onCompleted(Exception ex, AsyncHttpResponse source, String result) {\r\n            if (ex != null) {\r\n                ex.printStackTrace();\r\n                return;\r\n            }\r\n            System.out.println(\"Server says: \" + result);\r\n        }\r\n    });\r\n```\r\n\r\n\r\n### Can also create web sockets:\r\n\r\n```java\r\nAsyncHttpClient.getDefaultInstance().websocket(get, \"my-protocol\", new WebSocketConnectCallback() {\r\n    @Override\r\n    public void onCompleted(Exception ex, WebSocket webSocket) {\r\n        if (ex != null) {\r\n            ex.printStackTrace();\r\n            return;\r\n        }\r\n        webSocket.send(\"a string\");\r\n        webSocket.send(new byte[10]);\r\n        webSocket.setStringCallback(new StringCallback() {\r\n            public void onStringAvailable(String s) {\r\n                System.out.println(\"I got a string: \" + s);\r\n            }\r\n        });\r\n        webSocket.setDataCallback(new DataCallback() {\r\n            public void onDataAvailable(DataEmitter emitter, ByteBufferList byteBufferList) {\r\n                System.out.println(\"I got some bytes!\");\r\n                // note that this data has been read\r\n                byteBufferList.recycle();\r\n            }\r\n        });\r\n    }\r\n});\r\n```\r\n\r\n\r\n### AndroidAsync also let's you create simple HTTP servers:\r\n\r\n```java\r\nAsyncHttpServer server = new AsyncHttpServer();\r\n\r\nList<WebSocket> _sockets = new ArrayList<WebSocket>();\r\n\r\nserver.get(\"/\", new HttpServerRequestCallback() {\r\n    @Override\r\n    public void onRequest(AsyncHttpServerRequest request, AsyncHttpServerResponse response) {\r\n        response.send(\"Hello!!!\");\r\n    }\r\n});\r\n\r\n// listen on port 5000\r\nserver.listen(5000);\r\n// browsing http://localhost:5000 will return Hello!!!\r\n\r\n```\r\n\r\n### And WebSocket Servers:\r\n\r\n```java\r\nAsyncHttpServer httpServer = new AsyncHttpServer();\r\n\r\nhttpServer.listen(AsyncServer.getDefault(), port);\r\n\r\nhttpServer.websocket(\"/live\", new AsyncHttpServer.WebSocketRequestCallback() {\r\n    @Override\r\n    public void onConnected(final WebSocket webSocket, AsyncHttpServerRequest request) {\r\n        _sockets.add(webSocket);\r\n        \r\n        //Use this to clean up any references to your websocket\r\n        webSocket.setClosedCallback(new CompletedCallback() {\r\n            @Override\r\n            public void onCompleted(Exception ex) {\r\n                try {\r\n                    if (ex != null)\r\n                        Log.e(\"WebSocket\", \"An error occurred\", ex);\r\n                } finally {\r\n                    _sockets.remove(webSocket);\r\n                }\r\n            }\r\n        });\r\n        \r\n        webSocket.setStringCallback(new StringCallback() {\r\n            @Override\r\n            public void onStringAvailable(String s) {\r\n                if (\"Hello Server\".equals(s))\r\n                    webSocket.send(\"Welcome Client!\");\r\n            }\r\n        });\r\n    \r\n    }\r\n});\r\n\r\n//..Sometime later, broadcast!\r\nfor (WebSocket socket : _sockets)\r\n    socket.send(\"Fireball!\");\r\n```\r\n\r\n### Futures\r\n\r\nAll the API calls return [Futures](http://en.wikipedia.org/wiki/Futures_and_promises).\r\n\r\n```java\r\nFuture<String> string = client.getString(\"http://foo.com/hello.txt\");\r\n// this will block, and may also throw if there was an error!\r\nString value = string.get();\r\n```\r\n\r\nFutures can also have callbacks...\r\n\r\n```java\r\nFuture<String> string = client.getString(\"http://foo.com/hello.txt\");\r\nstring.setCallback(new FutureCallback<String>() {\r\n    @Override\r\n    public void onCompleted(Exception e, String result) {\r\n        System.out.println(result);\r\n    }\r\n});\r\n```\r\n\r\nFor brevity...\r\n\r\n```java\r\nclient.getString(\"http://foo.com/hello.txt\")\r\n.setCallback(new FutureCallback<String>() {\r\n    @Override\r\n    public void onCompleted(Exception e, String result) {\r\n        System.out.println(result);\r\n    }\r\n});\r\n```\r\n"
  },
  {
    "path": "build.gradle",
    "content": "buildscript {\n    repositories {\n        jcenter()\n        google()\n        mavenCentral()\n    }\n    dependencies {\n        classpath project.hasProperty('global_gradleAndroidPlugin') ? global_gradleAndroidPlugin : 'com.android.tools.build:gradle:3.2.0'\n    }\n}\n\nsubprojects {\n    repositories {\n        jcenter()\n        mavenCentral()\n        google()\n    }\n\n    ext.global_compileSdkVersion = project.hasProperty('global_compileSdkVersion') ? global_compileSdkVersion : 28\n    ext.global_buildToolsVersion = project.hasProperty('global_buildToolsVersion') ? global_buildToolsVersion : '28.0.3'\n    ext.global_gradleAndroidPlugin = project.hasProperty('global_gradleAndroidPlugin') ? global_gradleAndroidPlugin : 'com.android.tools.build:gradle:3.2.0'\n}\n"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn ( ) {\n    echo \"$*\"\n}\n\ndie ( ) {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\nesac\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules\nfunction splitJvmOpts() {\n    JVM_OPTS=(\"$@\")\n}\neval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\nJVM_OPTS[${#JVM_OPTS[*]}]=\"-Dorg.gradle.appname=$APP_BASE_NAME\"\n\nexec \"$JAVACMD\" \"${JVM_OPTS[@]}\" -classpath \"$CLASSPATH\" org.gradle.wrapper.GradleWrapperMain \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:init\r\n@rem Get command-line arguments, handling Windowz variants\r\n\r\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\r\nif \"%@eval[2+2]\" == \"4\" goto 4NT_args\r\n\r\n:win9xME_args\r\n@rem Slurp the command line arguments.\r\nset CMD_LINE_ARGS=\r\nset _SKIP=2\r\n\r\n:win9xME_args_slurp\r\nif \"x%~1\" == \"x\" goto execute\r\n\r\nset CMD_LINE_ARGS=%*\r\ngoto execute\r\n\r\n:4NT_args\r\n@rem Get arguments from the 4NT Shell from JP Software\r\nset CMD_LINE_ARGS=%$\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "settings.gradle",
    "content": "include 'AndroidAsync', 'AndroidAsyncSample', 'AndroidAsyncStetho'\n\n"
  }
]