001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.rest.client; 018 019import static org.apache.juneau.commons.utils.Shorts.*; 020 021import java.io.*; 022import java.util.*; 023 024/** 025 * A transport-layer HTTP response. 026 * 027 * <p> 028 * Produced by {@link HttpTransport#execute(TransportRequest)} and consumed by the higher-level 029 * {@code RestResponse}. 030 * 031 * <p> 032 * Callers <b>must</b> close this object (it implements {@link Closeable}) after consuming the response body; 033 * transport implementations use the close callback to release connection-pool resources. 034 * 035 * @since 9.2.1 036 */ 037@SuppressWarnings({ 038 "resource" // body and closeCallback are owned by TransportResponse and released in close() 039}) 040public final class TransportResponse implements Closeable { 041 042 private final int statusCode; 043 private final String reasonPhrase; 044 private final List<TransportHeader> headers; 045 private final InputStream body; 046 private final Closeable closeCallback; 047 048 private TransportResponse(Builder builder) { 049 this.statusCode = builder.statusCode; 050 this.reasonPhrase = builder.reasonPhrase; 051 this.headers = List.copyOf(builder.headers); 052 this.body = builder.body; 053 this.closeCallback = builder.closeCallback != null ? builder.closeCallback : () -> {}; 054 } 055 056 /** Returns a new {@link Builder}. */ 057 public static Builder builder() { 058 return new Builder(); 059 } 060 061 /** 062 * Returns the HTTP status code. 063 * 064 * @return The status code (e.g. {@code 200}). 065 */ 066 public int getStatusCode() { 067 return statusCode; 068 } 069 070 /** 071 * Returns the reason phrase (e.g. {@code "OK"}), or {@code null} if absent. 072 * 073 * @return The reason phrase, possibly <jk>null</jk>. 074 */ 075 public String getReasonPhrase() { 076 return reasonPhrase; 077 } 078 079 /** 080 * Returns all response headers. 081 * 082 * @return An unmodifiable list. Never <jk>null</jk>. 083 */ 084 public List<TransportHeader> getHeaders() { 085 return headers; 086 } 087 088 /** 089 * Returns the first header with the given name (case-insensitive), or {@code null} if absent. 090 * 091 * @param name The header name. Must not be <jk>null</jk>. 092 * @return The first matching header, or <jk>null</jk> if absent. 093 */ 094 public TransportHeader getFirstHeader(String name) { 095 return headers.stream() 096 .filter(h -> eqic(h.name(), name)) 097 .findFirst() 098 .orElse(null); 099 } 100 101 /** 102 * Returns the response body stream, or {@code null} if the response has no body. 103 * 104 * <p> 105 * Callers must not close this stream directly — close the {@link TransportResponse} instead. 106 * 107 * @return The body stream, possibly <jk>null</jk>. 108 */ 109 public InputStream getBody() { 110 return body; 111 } 112 113 @Override /* Closeable */ 114 public void close() throws IOException { 115 closeCallback.close(); 116 } 117 118 // ----------------------------------------------------------------------------------------------------------------- 119 // Builder 120 // ----------------------------------------------------------------------------------------------------------------- 121 122 /** 123 * Fluent builder for {@link TransportResponse}. 124 * 125 * <p> 126 * Primarily used by transport implementations and tests. 127 * 128 * @since 9.2.1 129 */ 130 public static final class Builder { 131 132 int statusCode; 133 String reasonPhrase; 134 final List<TransportHeader> headers = l(); 135 InputStream body; 136 Closeable closeCallback; 137 138 private Builder() {} 139 140 /** 141 * Sets the HTTP status code. 142 * 143 * @param value The status code. 144 * @return This object. 145 */ 146 public Builder statusCode(int value) { 147 statusCode = value; 148 return this; 149 } 150 151 /** 152 * Sets the reason phrase. 153 * 154 * @param value The reason phrase. May be <jk>null</jk>. 155 * @return This object. 156 */ 157 public Builder reasonPhrase(String value) { 158 reasonPhrase = value; 159 return this; 160 } 161 162 /** 163 * Appends a response header. 164 * 165 * @param name The header name. Must not be <jk>null</jk>. 166 * @param value The header value. Must not be <jk>null</jk>. 167 * @return This object. 168 */ 169 public Builder header(String name, String value) { 170 headers.add(TransportHeader.of(name, value)); 171 return this; 172 } 173 174 /** 175 * Sets the response body stream. 176 * 177 * @param value The stream. May be <jk>null</jk>. 178 * @return This object. 179 */ 180 public Builder body(InputStream value) { 181 body = value; 182 return this; 183 } 184 185 /** 186 * Sets a callback invoked when {@link TransportResponse#close()} is called. 187 * 188 * <p> 189 * Transport implementations use this to return connections to the pool. 190 * 191 * @param value The close callback. May be <jk>null</jk>. 192 * @return This object. 193 */ 194 public Builder closeCallback(Closeable value) { 195 closeCallback = value; 196 return this; 197 } 198 199 /** 200 * Builds and returns the {@link TransportResponse}. 201 * 202 * @return A new instance. Never <jk>null</jk>. 203 */ 204 public TransportResponse build() { 205 return new TransportResponse(this); 206 } 207 } 208}