// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

package com.microsoft.signalr;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import io.reactivex.Single;
import io.reactivex.subjects.SingleSubject;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Cookie;
import okhttp3.CookieJar;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;

final class DefaultHttpClient extends HttpClient {
    private final OkHttpClient client;

    public DefaultHttpClient() {
        this.client = new OkHttpClient.Builder().cookieJar(new CookieJar() {
            private List<Cookie> cookieList = new ArrayList<>();
            private Lock cookieLock = new ReentrantLock();

            @Override
            public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
                cookieLock.lock();
                try {
                    for (Cookie cookie : cookies) {
                        boolean replacedCookie = false;
                        for (int i = 0; i < cookieList.size(); i++) {
                            Cookie innerCookie = cookieList.get(i);
                            if (cookie.name().equals(innerCookie.name()) && innerCookie.matches(url)) {
                                // We have a new cookie that matches an older one so we replace the older one.
                                cookieList.set(i, innerCookie);
                                replacedCookie = true;
                                break;
                            }
                        }
                        if (!replacedCookie) {
                            cookieList.add(cookie);
                        }
                    }
                } finally {
                    cookieLock.unlock();
                }
            }

            @Override
            public List<Cookie> loadForRequest(HttpUrl url) {
                cookieLock.lock();
                try {
                    List<Cookie> matchedCookies = new ArrayList<>();
                    List<Cookie> expiredCookies = new ArrayList<>();
                    for (Cookie cookie : cookieList) {
                        if (cookie.expiresAt() < System.currentTimeMillis()) {
                            expiredCookies.add(cookie);
                        } else if (cookie.matches(url)) {
                            matchedCookies.add(cookie);
                        }
                    }

                    cookieList.removeAll(expiredCookies);
                    return matchedCookies;
                } finally {
                    cookieLock.unlock();
                }
            }
        }).build();
    }

    @Override
    public Single<HttpResponse> send(HttpRequest httpRequest) {
        Request.Builder requestBuilder = new Request.Builder().url(httpRequest.getUrl());

        switch (httpRequest.getMethod()) {
            case "GET":
                requestBuilder.get();
                break;
            case "POST":
                RequestBody body = RequestBody.create(null, new byte[]{});
                requestBuilder.post(body);
                break;
            case "DELETE":
                requestBuilder.delete();
                break;
        }

        if (httpRequest.getHeaders() != null) {
            httpRequest.getHeaders().forEach(requestBuilder::addHeader);
        }

        Request request = requestBuilder.build();

        SingleSubject<HttpResponse> responseSubject = SingleSubject.create();

        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                responseSubject.onError(e.getCause());
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                try (ResponseBody body = response.body()) {
                    HttpResponse httpResponse = new HttpResponse(response.code(), response.message(), body.string());
                    responseSubject.onSuccess(httpResponse);
                }
            }
        });

        return responseSubject;
    }

    @Override
    public WebSocketWrapper createWebSocket(String url, Map<String, String> headers) {
        return new OkHttpWebSocketWrapper(url, headers, client);
    }
}
@@ -245,24 +245,53 @@
 .ArrayList;%0A
+import java.util.Collection;%0A
 import java.
@@ -3637,41 +3637,123 @@
     
-httpRequest.getHeaders().forEach(
+Collection%3CString%3E keys = httpRequest.getHeaders().keySet();%0A            for (String key : keys) %7B%0A                
 requ
@@ -3766,10 +3766,9 @@
 lder
-::
+.
 addH
@@ -3772,18 +3772,71 @@
 ddHeader
-);
+(key, httpRequest.getHeaders().get(key));%0A            %7D
 %0A       
@@ -4784,8 +4784,9 @@
 %0A    %7D%0A%7D
+%0A

Remove hidden BiConsumer reference (#3215)