1 /*
   2  * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.incubator.http.internal.websocket;
  27 
  28 import jdk.incubator.http.HttpClient;
  29 import jdk.incubator.http.WebSocket;
  30 import jdk.incubator.http.WebSocket.Builder;
  31 import jdk.incubator.http.WebSocket.Listener;
  32 import jdk.incubator.http.internal.common.Pair;
  33 
  34 import java.net.ProxySelector;
  35 import java.net.URI;
  36 import java.time.Duration;
  37 import java.util.Collection;
  38 import java.util.LinkedList;
  39 import java.util.List;
  40 import java.util.Optional;
  41 import java.util.concurrent.CompletableFuture;
  42 
  43 import static java.util.Objects.requireNonNull;
  44 import static jdk.incubator.http.internal.common.Pair.pair;
  45 
  46 public final class BuilderImpl implements Builder {
  47 
  48     private final HttpClient client;
  49     private URI uri;
  50     private Listener listener;
  51     private final Optional<ProxySelector> proxySelector;
  52     private final Collection<Pair<String, String>> headers;
  53     private final Collection<String> subprotocols;
  54     private Duration timeout;
  55 
  56     public BuilderImpl(HttpClient client, ProxySelector proxySelector)
  57     {
  58         this(client, null, null, Optional.ofNullable(proxySelector),
  59              new LinkedList<>(), new LinkedList<>(), null);
  60     }
  61 
  62     private BuilderImpl(HttpClient client,
  63                         URI uri,
  64                         Listener listener,
  65                         Optional<ProxySelector> proxySelector,
  66                         Collection<Pair<String, String>> headers,
  67                         Collection<String> subprotocols,
  68                         Duration timeout) {
  69         this.client = client;
  70         this.uri = uri;
  71         this.listener = listener;
  72         this.proxySelector = proxySelector;
  73         // If a proxy selector was supplied by the user, it should be present
  74         // on the client and should be the same that what we got as an argument
  75         assert !client.proxy().isPresent()
  76                 || client.proxy().equals(proxySelector);
  77         this.headers = headers;
  78         this.subprotocols = subprotocols;
  79         this.timeout = timeout;
  80     }
  81 
  82     @Override
  83     public Builder header(String name, String value) {
  84         requireNonNull(name, "name");
  85         requireNonNull(value, "value");
  86         headers.add(pair(name, value));
  87         return this;
  88     }
  89 
  90     @Override
  91     public Builder subprotocols(String mostPreferred, String... lesserPreferred)
  92     {
  93         requireNonNull(mostPreferred, "mostPreferred");
  94         requireNonNull(lesserPreferred, "lesserPreferred");
  95         List<String> subprotocols = new LinkedList<>();
  96         subprotocols.add(mostPreferred);
  97         for (int i = 0; i < lesserPreferred.length; i++) {
  98             String p = lesserPreferred[i];
  99             requireNonNull(p, "lesserPreferred[" + i + "]");
 100             subprotocols.add(p);
 101         }
 102         this.subprotocols.clear();
 103         this.subprotocols.addAll(subprotocols);
 104         return this;
 105     }
 106 
 107     @Override
 108     public Builder connectTimeout(Duration timeout) {
 109         this.timeout = requireNonNull(timeout, "timeout");
 110         return this;
 111     }
 112 
 113     @Override
 114     public CompletableFuture<WebSocket> buildAsync(URI uri, Listener listener) {
 115         this.uri = requireNonNull(uri, "uri");
 116         this.listener = requireNonNull(listener, "listener");
 117         // A snapshot of builder inaccessible for further modification
 118         // from the outside
 119         BuilderImpl copy = immutableCopy();
 120         return WebSocketImpl.newInstanceAsync(copy);
 121     }
 122 
 123     HttpClient getClient() { return client; }
 124 
 125     URI getUri() { return uri; }
 126 
 127     Listener getListener() { return listener; }
 128 
 129     Collection<Pair<String, String>> getHeaders() { return headers; }
 130 
 131     Collection<String> getSubprotocols() { return subprotocols; }
 132 
 133     Duration getConnectTimeout() { return timeout; }
 134 
 135     Optional<ProxySelector> getProxySelector() { return proxySelector; }
 136 
 137     private BuilderImpl immutableCopy() {
 138         @SuppressWarnings({"unchecked", "rawtypes"})
 139         BuilderImpl copy = new BuilderImpl(
 140                 client,
 141                 uri,
 142                 listener,
 143                 proxySelector,
 144                 List.of(this.headers.toArray(new Pair[0])),
 145                 List.of(this.subprotocols.toArray(new String[0])),
 146                 timeout);
 147         return copy;
 148     }
 149 }