1 /* 2 * Copyright (c) 2015, 2020, 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.internal.net.http; 27 28 import java.io.IOException; 29 import java.net.URI; 30 import java.nio.ByteBuffer; 31 import java.util.Optional; 32 import java.util.concurrent.CompletableFuture; 33 import java.util.function.Supplier; 34 import javax.net.ssl.SSLSession; 35 import java.net.http.HttpClient; 36 import java.net.http.HttpHeaders; 37 import java.net.http.HttpRequest; 38 import java.net.http.HttpResponse; 39 import jdk.internal.net.http.websocket.RawChannel; 40 41 /** 42 * The implementation class for HttpResponse 43 */ 44 class HttpResponseImpl<T> implements HttpResponse<T>, RawChannel.Provider { 45 46 final int responseCode; 47 final HttpRequest initialRequest; 48 final Optional<HttpResponse<T>> previousResponse; 49 final HttpHeaders headers; 50 final Optional<SSLSession> sslSession; 51 final URI uri; 52 final HttpClient.Version version; 53 final RawChannelProvider rawChannelProvider; 54 final T body; 55 HttpResponseImpl(HttpRequest initialRequest, Response response, HttpResponse<T> previousResponse, T body, Exchange<T> exch)56 public HttpResponseImpl(HttpRequest initialRequest, 57 Response response, 58 HttpResponse<T> previousResponse, 59 T body, 60 Exchange<T> exch) { 61 this.responseCode = response.statusCode(); 62 this.initialRequest = initialRequest; 63 this.previousResponse = Optional.ofNullable(previousResponse); 64 this.headers = response.headers(); 65 //this.trailers = trailers; 66 this.sslSession = Optional.ofNullable(response.getSSLSession()); 67 this.uri = response.request().uri(); 68 this.version = response.version(); 69 this.rawChannelProvider = RawChannelProvider.create(response, exch); 70 this.body = body; 71 } 72 73 @Override statusCode()74 public int statusCode() { 75 return responseCode; 76 } 77 78 @Override request()79 public HttpRequest request() { 80 return initialRequest; 81 } 82 83 @Override previousResponse()84 public Optional<HttpResponse<T>> previousResponse() { 85 return previousResponse; 86 } 87 88 @Override headers()89 public HttpHeaders headers() { 90 return headers; 91 } 92 93 @Override body()94 public T body() { 95 return body; 96 } 97 98 @Override sslSession()99 public Optional<SSLSession> sslSession() { 100 return sslSession; 101 } 102 103 @Override uri()104 public URI uri() { 105 return uri; 106 } 107 108 @Override version()109 public HttpClient.Version version() { 110 return version; 111 } 112 // keepalive flag determines whether connection is closed or kept alive 113 // by reading/skipping data 114 115 /** 116 * Returns a RawChannel that may be used for WebSocket protocol. 117 * @implNote This implementation does not support RawChannel over 118 * HTTP/2 connections. 119 * @return a RawChannel that may be used for WebSocket protocol. 120 * @throws UnsupportedOperationException if getting a RawChannel over 121 * this connection is not supported. 122 * @throws IOException if an I/O exception occurs while retrieving 123 * the channel. 124 */ 125 @Override rawChannel()126 public synchronized RawChannel rawChannel() throws IOException { 127 if (rawChannelProvider == null) { 128 throw new UnsupportedOperationException( 129 "RawChannel is only supported for WebSocket creation"); 130 } 131 return rawChannelProvider.rawChannel(); 132 } 133 134 /** 135 * Closes the RawChannel that may have been used for WebSocket protocol. 136 * 137 * @apiNote This method should be called to close the connection 138 * if an exception occurs during the websocket handshake, in cases where 139 * {@link #rawChannel() rawChannel().close()} would have been called. 140 * An unsuccessful handshake may prevent the creation of the RawChannel: 141 * if a RawChannel has already been created, this method wil close it. 142 * Otherwise, it will close the connection. 143 * 144 * @throws UnsupportedOperationException if getting a RawChannel over 145 * this connection is not supported. 146 * @throws IOException if an I/O exception occurs while closing 147 * the channel. 148 */ 149 @Override closeRawChannel()150 public synchronized void closeRawChannel() throws IOException { 151 if (rawChannelProvider == null) { 152 throw new UnsupportedOperationException( 153 "RawChannel is only supported for WebSocket creation"); 154 } 155 rawChannelProvider.closeRawChannel(); 156 } 157 158 @Override toString()159 public String toString() { 160 StringBuilder sb = new StringBuilder(); 161 String method = request().method(); 162 URI uri = request().uri(); 163 String uristring = uri == null ? "" : uri.toString(); 164 sb.append('(') 165 .append(method) 166 .append(" ") 167 .append(uristring) 168 .append(") ") 169 .append(statusCode()); 170 return sb.toString(); 171 } 172 173 /** 174 * An auxiliary class used for RawChannel creation when creating a WebSocket. 175 * This avoids keeping around references to connection/exchange in the 176 * regular HttpResponse case. Only those responses corresponding to an 177 * initial WebSocket request have a RawChannelProvider. 178 */ 179 private static final class RawChannelProvider implements RawChannel.Provider { 180 private final HttpConnection connection; 181 private final Exchange<?> exchange; 182 private RawChannel rawchan; RawChannelProvider(HttpConnection conn, Exchange<?> exch)183 RawChannelProvider(HttpConnection conn, Exchange<?> exch) { 184 connection = conn; 185 exchange = exch; 186 } 187 create(Response resp, Exchange<?> exch)188 static RawChannelProvider create(Response resp, Exchange<?> exch) { 189 if (resp.request().isWebSocket()) { 190 return new RawChannelProvider(connection(resp, exch), exch); 191 } 192 return null; 193 } 194 195 @Override rawChannel()196 public synchronized RawChannel rawChannel() { 197 if (rawchan == null) { 198 ExchangeImpl<?> exchImpl = exchangeImpl(); 199 if (!(exchImpl instanceof Http1Exchange)) { 200 // RawChannel is only used for WebSocket - and WebSocket 201 // is not supported over HTTP/2 yet, so we should not come 202 // here. Getting a RawChannel over HTTP/2 might be supported 203 // in the future, but it would entail retrieving any left over 204 // bytes that might have been read but not consumed by the 205 // HTTP/2 connection. 206 throw new UnsupportedOperationException("RawChannel is not supported over HTTP/2"); 207 } 208 // Http1Exchange may have some remaining bytes in its 209 // internal buffer. 210 Supplier<ByteBuffer> initial = ((Http1Exchange<?>) exchImpl)::drainLeftOverBytes; 211 rawchan = new RawChannelTube(connection, initial); 212 } 213 return rawchan; 214 } 215 closeRawChannel()216 public synchronized void closeRawChannel() throws IOException { 217 // close the rawChannel, if created, or the 218 // connection, if not. 219 if (rawchan != null) rawchan.close(); 220 else connection.close(); 221 } 222 connection(Response resp, Exchange<?> exch)223 private static HttpConnection connection(Response resp, Exchange<?> exch) { 224 if (exch == null || exch.exchImpl == null) { 225 assert resp.statusCode == 407; 226 return null; // case of Proxy 407 227 } 228 return exch.exchImpl.connection(); 229 } 230 exchangeImpl()231 private ExchangeImpl<?> exchangeImpl() { 232 return exchange != null ? exchange.exchImpl : null; 233 } 234 235 } 236 } 237