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
26package jdk.incubator.http;
27
28import jdk.incubator.http.internal.common.HttpHeadersImpl;
29import jdk.incubator.http.internal.websocket.WebSocketRequest;
30
31import java.io.IOException;
32import java.net.InetSocketAddress;
33import java.net.ProxySelector;
34import java.net.URI;
35import java.security.AccessControlContext;
36import java.time.Duration;
37import java.util.Locale;
38import java.util.Optional;
39
40import static jdk.incubator.http.internal.common.Utils.ALLOWED_HEADERS;
41
42class HttpRequestImpl extends HttpRequest implements WebSocketRequest {
43
44    private final HttpHeaders userHeaders;
45    private final HttpHeadersImpl systemHeaders;
46    private final URI uri;
47    private InetSocketAddress authority; // only used when URI not specified
48    private final String method;
49    final BodyProcessor requestProcessor;
50    final boolean secure;
51    final boolean expectContinue;
52    private boolean isWebSocket;
53    private AccessControlContext acc;
54    private final Duration duration;
55    private final Optional<HttpClient.Version> version;
56
57    /**
58     * Creates an HttpRequestImpl from the given builder.
59     */
60    public HttpRequestImpl(HttpRequestBuilderImpl builder) {
61        String method = builder.method();
62        this.method = method == null? "GET" : method;
63        this.userHeaders = ImmutableHeaders.of(builder.headers().map(), ALLOWED_HEADERS);
64        this.systemHeaders = new HttpHeadersImpl();
65        this.uri = builder.uri();
66        this.expectContinue = builder.expectContinue();
67        this.secure = uri.getScheme().toLowerCase(Locale.US).equals("https");
68        if (builder.body() == null) {
69            this.requestProcessor = HttpRequest.noBody();
70        } else {
71            this.requestProcessor = builder.body();
72        }
73        this.duration = builder.duration();
74        this.version = builder.version();
75    }
76
77    /**
78     * Creates an HttpRequestImpl from the given request.
79     */
80    public HttpRequestImpl(HttpRequest request) {
81        String method = request.method();
82        this.method = method == null? "GET" : method;
83        this.userHeaders = request.headers();
84        if (request instanceof HttpRequestImpl) {
85            this.systemHeaders = ((HttpRequestImpl) request).systemHeaders;
86            this.isWebSocket = ((HttpRequestImpl) request).isWebSocket;
87        } else {
88            this.systemHeaders = new HttpHeadersImpl();
89        }
90        this.uri = request.uri();
91        this.expectContinue = request.expectContinue();
92        this.secure = uri.getScheme().toLowerCase(Locale.US).equals("https");
93        if (!request.bodyProcessor().isPresent()) {
94            this.requestProcessor = HttpRequest.noBody();
95        } else {
96            this.requestProcessor = request.bodyProcessor().get();
97        }
98        this.duration = request.duration();
99        this.version = request.version();
100    }
101
102    /** Creates a HttpRequestImpl using fields of an existing request impl. */
103    public HttpRequestImpl(URI uri,
104                           String method,
105                           HttpRequestImpl other) {
106        this.method = method == null? "GET" : method;
107        this.userHeaders = other.userHeaders;
108        this.isWebSocket = other.isWebSocket;
109        this.systemHeaders = other.systemHeaders;
110        this.uri = uri;
111        this.expectContinue = other.expectContinue;
112        this.secure = uri.getScheme().toLowerCase(Locale.US).equals("https");
113        this.requestProcessor = other.requestProcessor;
114        this.acc = other.acc;
115        this.duration = other.duration;
116        this.version = other.version();
117    }
118
119    /* used for creating CONNECT requests  */
120    HttpRequestImpl(String method, HttpClientImpl client,
121                    InetSocketAddress authority) {
122        // TODO: isWebSocket flag is not specified, but the assumption is that
123        // such a request will never be made on a connection that will be returned
124        // to the connection pool (we might need to revisit this constructor later)
125        this.method = method;
126        this.systemHeaders = new HttpHeadersImpl();
127        this.userHeaders = ImmutableHeaders.empty();
128        this.uri = URI.create("socket://" + authority.getHostString() + ":" + Integer.toString(authority.getPort()) + "/");
129        this.requestProcessor = HttpRequest.noBody();
130        this.authority = authority;
131        this.secure = false;
132        this.expectContinue = false;
133        this.duration = null;
134        this.version = Optional.of(client.version());
135    }
136
137    /**
138     * Creates a HttpRequestImpl from the given set of Headers and the associated
139     * "parent" request. Fields not taken from the headers are taken from the
140     * parent.
141     */
142    static HttpRequestImpl createPushRequest(HttpRequestImpl parent,
143                                             HttpHeadersImpl headers)
144        throws IOException
145    {
146        return new HttpRequestImpl(parent, headers);
147    }
148
149    // only used for push requests
150    private HttpRequestImpl(HttpRequestImpl parent, HttpHeadersImpl headers)
151        throws IOException
152    {
153        this.method = headers.firstValue(":method")
154                .orElseThrow(() -> new IOException("No method in Push Promise"));
155        String path = headers.firstValue(":path")
156                .orElseThrow(() -> new IOException("No path in Push Promise"));
157        String scheme = headers.firstValue(":scheme")
158                .orElseThrow(() -> new IOException("No scheme in Push Promise"));
159        String authority = headers.firstValue(":authority")
160                .orElseThrow(() -> new IOException("No authority in Push Promise"));
161        StringBuilder sb = new StringBuilder();
162        sb.append(scheme).append("://").append(authority).append(path);
163        this.uri = URI.create(sb.toString());
164
165        this.userHeaders = ImmutableHeaders.of(headers.map(), ALLOWED_HEADERS);
166        this.systemHeaders = parent.systemHeaders;
167        this.expectContinue = parent.expectContinue;
168        this.secure = parent.secure;
169        this.requestProcessor = parent.requestProcessor;
170        this.acc = parent.acc;
171        this.duration = parent.duration;
172        this.version = parent.version;
173    }
174
175    @Override
176    public String toString() {
177        return (uri == null ? "" : uri.toString()) + " " + method;
178    }
179
180    @Override
181    public HttpHeaders headers() {
182        return userHeaders;
183    }
184
185    InetSocketAddress authority() { return authority; }
186
187    void setH2Upgrade(Http2ClientImpl h2client) {
188        systemHeaders.setHeader("Connection", "Upgrade, HTTP2-Settings");
189        systemHeaders.setHeader("Upgrade", "h2c");
190        systemHeaders.setHeader("HTTP2-Settings", h2client.getSettingsString());
191    }
192
193    @Override
194    public boolean expectContinue() { return expectContinue; }
195
196    InetSocketAddress proxy(HttpClientImpl client) {
197        ProxySelector ps = client.proxy().orElse(null);
198        if (ps == null) {
199            ps = client.proxy().orElse(null);
200        }
201        if (ps == null || method.equalsIgnoreCase("CONNECT")) {
202            return null;
203        }
204        return (InetSocketAddress)ps.select(uri).get(0).address();
205    }
206
207    boolean secure() { return secure; }
208
209    @Override
210    public void isWebSocket(boolean is) {
211        isWebSocket = is;
212    }
213
214    boolean isWebSocket() {
215        return isWebSocket;
216    }
217
218//    /** Returns the follow-redirects setting for this request. */
219//    @Override
220//    public jdk.incubator.http.HttpClient.Redirect followRedirects() {
221//        return followRedirects;
222//    }
223
224    @Override
225    public Optional<BodyProcessor> bodyProcessor() {
226        return Optional.of(requestProcessor);
227    }
228
229    /**
230     * Returns the request method for this request. If not set explicitly,
231     * the default method for any request is "GET".
232     */
233    @Override
234    public String method() { return method; }
235
236    @Override
237    public URI uri() { return uri; }
238
239    @Override
240    public Duration duration() {
241        return duration;
242    }
243
244//    HttpClientImpl client() {
245//        return client;
246//    }
247
248    HttpHeaders getUserHeaders() { return userHeaders; }
249
250    HttpHeadersImpl getSystemHeaders() { return systemHeaders; }
251
252    @Override
253    public Optional<HttpClient.Version> version() { return version; }
254
255    void addSystemHeader(String name, String value) {
256        systemHeaders.addHeader(name, value);
257    }
258
259    @Override
260    public void setSystemHeader(String name, String value) {
261        systemHeaders.setHeader(name, value);
262    }
263
264//    @Override
265//    public <T> HttpResponse<T>
266//    response(HttpResponse.BodyHandler<T> responseHandler)
267//        throws IOException, InterruptedException
268//    {
269//        if (!sent.compareAndSet(false, true)) {
270//            throw new IllegalStateException("request already sent");
271//        }
272//        MultiExchange<Void,T> mex = new MultiExchange<>(this, responseHandler);
273//        return mex.response();
274//    }
275//
276//    @Override
277//    public <T> CompletableFuture<HttpResponse<T>>
278//    responseAsync(HttpResponse.BodyHandler<T> responseHandler)
279//    {
280//        if (!sent.compareAndSet(false, true)) {
281//            throw new IllegalStateException("request already sent");
282//        }
283//        MultiExchange<Void,T> mex = new MultiExchange<>(this, responseHandler);
284//        return mex.responseAsync(null)
285//                  .thenApply((HttpResponseImpl<T> b) -> (HttpResponse<T>) b);
286//    }
287//
288//    @Override
289//    public <U, T> CompletableFuture<U>
290//    multiResponseAsync(HttpResponse.MultiProcessor<U, T> responseHandler)
291//    {
292//        if (!sent.compareAndSet(false, true)) {
293//            throw new IllegalStateException("request already sent");
294//        }
295//        MultiExchange<U,T> mex = new MultiExchange<>(this, responseHandler);
296//        return mex.multiResponseAsync();
297//    }
298
299    public InetSocketAddress getAddress(HttpClientImpl client) {
300        URI uri = uri();
301        if (uri == null) {
302            return authority();
303        }
304        int port = uri.getPort();
305        if (port == -1) {
306            if (uri.getScheme().equalsIgnoreCase("https")) {
307                port = 443;
308            } else {
309                port = 80;
310            }
311        }
312        String host = uri.getHost();
313        if (proxy(client) == null) {
314            return new InetSocketAddress(host, port);
315        } else {
316            return InetSocketAddress.createUnresolved(host, port);
317        }
318    }
319}
320