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.http.response; 018 019import static org.apache.juneau.assertions.Assertions.*; 020import static org.apache.juneau.commons.utils.AssertionUtils.*; 021import static org.apache.juneau.commons.utils.ThrowableUtils.*; 022import static org.apache.juneau.commons.utils.Utils.*; 023import static org.apache.juneau.http.HttpEntities.*; 024 025import java.net.*; 026import java.util.*; 027 028import org.apache.http.*; 029import org.apache.http.impl.*; 030import org.apache.http.params.*; 031import org.apache.juneau.annotation.*; 032import org.apache.juneau.http.*; 033import org.apache.juneau.http.header.*; 034 035/** 036 * Basic implementation of the {@link HttpResponse} interface. 037 * 038 * <p> 039 * Although this class implements the various setters defined on the {@link HttpResponse} interface, it's in general 040 * going to be more efficient to set the status/headers/content of this bean through the builder. 041 * 042 * <p> 043 * If the <c>unmodifiable</c> flag is set on this bean, calls to the setters will throw {@link UnsupportedOperationException} exceptions. 044 * 045 * <h5 class='section'>Notes:</h5><ul> 046 * <li class='warn'>Beans are not thread safe unless they're marked as unmodifiable. 047 * </ul> 048 * 049 * <h5 class='section'>See Also:</h5><ul> 050 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauRestCommonBasics">juneau-rest-common Basics</a> 051 * </ul> 052 */ 053@BeanIgnore /* Use toString() to serialize */ 054public class BasicHttpResponse implements HttpResponse { 055 BasicStatusLine statusLine = new BasicStatusLine(); 056 HeaderList headers = HeaderList.create(); 057 HttpEntity content; 058 boolean unmodifiable; 059 060 /** 061 * Copy constructor. 062 * 063 * @param copyFrom The bean to copy from. 064 */ 065 public BasicHttpResponse(BasicHttpResponse copyFrom) { 066 statusLine = copyFrom.statusLine.copy(); 067 headers = copyFrom.headers.copy(); 068 content = copyFrom.content; 069 } 070 071 /** 072 * Constructor. 073 * 074 * @param statusLine The HTTP status line. 075 */ 076 public BasicHttpResponse(BasicStatusLine statusLine) { 077 setStatusLine(statusLine.copy()); 078 } 079 080 /** 081 * Constructor. 082 * 083 * <p> 084 * This is the constructor used when parsing an HTTP response. 085 * 086 * @param response The HTTP response to copy from. Must not be <jk>null</jk>. 087 */ 088 public BasicHttpResponse(HttpResponse response) { 089 setHeaders(response.getAllHeaders()); 090 setContent(response.getEntity()); 091 setStatusLine(response.getStatusLine()); 092 } 093 094 @Override /* Overridden from HttpMessage */ 095 public void addHeader(Header value) { 096 headers.append(value); 097 } 098 099 @Override /* Overridden from HttpMessage */ 100 public void addHeader(String name, String value) { 101 headers.append(name, value); 102 } 103 104 @Override /* Overridden from HttpMessage */ 105 public boolean containsHeader(String name) { 106 return headers.contains(name); 107 } 108 109 @Override /* Overridden from HttpMessage */ 110 public Header[] getAllHeaders() { return headers.getAll(); } 111 112 @Override /* Overridden from HttpMessage */ 113 public HttpEntity getEntity() { 114 // Constructing a StringEntity is somewhat expensive, so don't create it unless it's needed. 115 if (content == null) 116 content = stringEntity(getStatusLine().getReasonPhrase()); 117 return content; 118 } 119 120 @Override /* Overridden from HttpMessage */ 121 public Header getFirstHeader(String name) { 122 return headers.getFirst(name).orElse(null); 123 } 124 125 /** 126 * Returns access to the underlying builder for the headers. 127 * 128 * @return The underlying builder for the headers. 129 */ 130 public HeaderList getHeaders() { return headers; } 131 132 @Override /* Overridden from HttpMessage */ 133 public Header[] getHeaders(String name) { 134 return headers.getAll(name); 135 } 136 137 @Override /* Overridden from HttpMessage */ 138 public Header getLastHeader(String name) { 139 return headers.getLast(name).orElse(null); 140 } 141 142 @Override /* Overridden from HttpMessage */ 143 public Locale getLocale() { return statusLine.getLocale(); } 144 145 @SuppressWarnings("deprecation") 146 @Override /* Overridden from HttpMessage */ 147 public HttpParams getParams() { return null; } 148 149 @Override /* Overridden from HttpMessage */ 150 public ProtocolVersion getProtocolVersion() { return statusLine.getProtocolVersion(); } 151 152 @Override /* Overridden from HttpMessage */ 153 public StatusLine getStatusLine() { return statusLine; } 154 155 @Override /* Overridden from HttpMessage */ 156 public HeaderIterator headerIterator() { 157 return headers.headerIterator(); 158 } 159 160 @Override /* Overridden from HttpMessage */ 161 public HeaderIterator headerIterator(String name) { 162 return headers.headerIterator(name); 163 } 164 165 /** 166 * Returns <jk>true</jk> if this bean is unmodifiable. 167 * 168 * @return <jk>true</jk> if this bean is unmodifiable. 169 */ 170 public boolean isUnmodifiable() { return unmodifiable; } 171 172 @Override /* Overridden from HttpMessage */ 173 public void removeHeader(Header value) { 174 headers.remove(value); 175 } 176 177 @Override /* Overridden from HttpMessage */ 178 public void removeHeaders(String name) { 179 headers.remove(name); 180 } 181 182 /** 183 * Sets the body on this response. 184 * 185 * @param value The body on this response. 186 * @return This object. 187 */ 188 public BasicHttpResponse setContent(HttpEntity value) { 189 assertModifiable(); 190 content = value; 191 return this; 192 } 193 194 /** 195 * Sets the body on this response. 196 * 197 * @param value The body on this response. 198 * @return This object. 199 */ 200 public BasicHttpResponse setContent(String value) { 201 return setContent(stringEntity(value)); 202 } 203 204 @Override /* Overridden from HttpMessage */ 205 public void setEntity(HttpEntity entity) { 206 assertModifiable(); 207 this.content = entity; 208 } 209 210 @Override /* Overridden from HttpMessage */ 211 public void setHeader(Header value) { 212 headers.set(value); 213 } 214 215 @Override /* Overridden from HttpMessage */ 216 public void setHeader(String name, String value) { 217 headers.set(name, value); 218 } 219 220 /** 221 * Sets the specified header to the end of the headers in this builder. 222 * 223 * @param value The header to add. <jk>null</jk> values are ignored. 224 * @return This object. 225 */ 226 public BasicHttpResponse setHeader2(Header value) { 227 headers.set(value); 228 return this; 229 } 230 231 /** 232 * Sets the specified header to the end of the headers in this builder. 233 * 234 * @param name The header name. 235 * @param value The header value. 236 * @return This object. 237 */ 238 public BasicHttpResponse setHeader2(String name, String value) { 239 headers.set(name, value); 240 return this; 241 } 242 243 @Override /* Overridden from HttpMessage */ 244 public void setHeaders(Header[] values) { 245 headers.removeAll().append(values); 246 } 247 248 /** 249 * Sets the specified headers on this response. 250 * 251 * @param value The new value. 252 * @return This object. 253 */ 254 public BasicHttpResponse setHeaders(HeaderList value) { 255 assertModifiable(); 256 headers = value.copy(); 257 return this; 258 } 259 260 /** 261 * Sets the specified headers to the end of the headers in this builder. 262 * 263 * @param values The headers to add. <jk>null</jk> values are ignored. 264 * @return This object. 265 */ 266 public BasicHttpResponse setHeaders(List<Header> values) { 267 headers.set(values); 268 return this; 269 } 270 271 /** 272 * Sets the specified headers to the end of the headers in this builder. 273 * 274 * @param values The headers to add. <jk>null</jk> values are ignored. 275 * @return This object. 276 */ 277 public BasicHttpResponse setHeaders2(Header...values) { 278 headers.set(values); 279 return this; 280 } 281 282 @Override /* Overridden from HttpMessage */ 283 public void setLocale(Locale loc) { 284 statusLine.setLocale(loc); 285 } 286 287 /** 288 * Sets the locale used to retrieve reason phrases. 289 * 290 * <p> 291 * If not specified, uses {@link Locale#getDefault()}. 292 * 293 * @param value The new value. 294 * @return This object. 295 */ 296 public BasicHttpResponse setLocale2(Locale value) { 297 statusLine.setLocale(value); 298 return this; 299 } 300 301 /** 302 * Specifies the value for the <c>Location</c> header. 303 * 304 * @param value The new header location. 305 * @return This object. 306 */ 307 public BasicHttpResponse setLocation(String value) { 308 headers.set(Location.of(value)); 309 return this; 310 } 311 312 /** 313 * Specifies the value for the <c>Location</c> header. 314 * 315 * @param value The new header location. 316 * @return This object. 317 */ 318 public BasicHttpResponse setLocation(URI value) { 319 headers.set(Location.of(value)); 320 return this; 321 } 322 323 @SuppressWarnings("deprecation") 324 @Override /* Overridden from HttpMessage */ 325 public void setParams(HttpParams params) {} 326 327 /** 328 * Sets the protocol version on the status line. 329 * 330 * <p> 331 * If not specified, <js>"HTTP/1.1"</js> will be used. 332 * 333 * @param value The new value. 334 * @return This object. 335 */ 336 public BasicHttpResponse setProtocolVersion(ProtocolVersion value) { 337 statusLine.setProtocolVersion(value); 338 return this; 339 } 340 341 @Override /* Overridden from HttpMessage */ 342 public void setReasonPhrase(String reason) throws IllegalStateException { 343 statusLine.setReasonPhrase(reason); 344 } 345 346 /** 347 * Sets the reason phrase on the status line. 348 * 349 * <p> 350 * If not specified, the reason phrase will be retrieved from the reason phrase catalog 351 * using the locale on this builder. 352 * 353 * @param value The new value. 354 * @return This object. 355 */ 356 public BasicHttpResponse setReasonPhrase2(String value) { 357 statusLine.setReasonPhrase(value); 358 return this; 359 } 360 361 /** 362 * Sets the reason phrase catalog used to retrieve reason phrases. 363 * 364 * <p> 365 * If not specified, uses {@link EnglishReasonPhraseCatalog}. 366 * 367 * @param value The new value. 368 * @return This object. 369 */ 370 public BasicHttpResponse setReasonPhraseCatalog(ReasonPhraseCatalog value) { 371 statusLine.setReasonPhraseCatalog(value); 372 return this; 373 } 374 375 @Override /* Overridden from HttpMessage */ 376 public void setStatusCode(int code) throws IllegalStateException { 377 statusLine.setStatusCode(code); 378 } 379 380 /** 381 * Sets the status code on the status line. 382 * 383 * <p> 384 * If not specified, <c>0</c> will be used. 385 * 386 * @param value The new value. 387 * @return This object. 388 */ 389 public BasicHttpResponse setStatusCode2(int value) { 390 statusLine.setStatusCode(value); 391 return this; 392 } 393 394 /** 395 * Sets the protocol version on the status line. 396 * 397 * <p> 398 * If not specified, <js>"HTTP/1.1"</js> will be used. 399 * 400 * @param value The new value. 401 * @return This object. 402 */ 403 public BasicHttpResponse setStatusLine(BasicStatusLine value) { 404 assertModifiable(); 405 statusLine = value.copy(); 406 return this; 407 } 408 409 @Override /* Overridden from HttpMessage */ 410 public void setStatusLine(ProtocolVersion ver, int code) { 411 statusLine.setProtocolVersion(ver).setStatusCode(code); 412 } 413 414 @Override /* Overridden from HttpMessage */ 415 public void setStatusLine(ProtocolVersion ver, int code, String reason) { 416 statusLine.setProtocolVersion(ver).setReasonPhrase(reason).setStatusCode(code); 417 } 418 419 @Override /* Overridden from HttpMessage */ 420 public void setStatusLine(StatusLine value) { 421 setStatusLine(value.getProtocolVersion(), value.getStatusCode(), value.getReasonPhrase()); 422 } 423 424 /** 425 * Specifies whether this bean should be unmodifiable. 426 * <p> 427 * When enabled, attempting to set any properties on this bean will cause an {@link UnsupportedOperationException}. 428 * 429 * @return This object. 430 */ 431 public BasicHttpResponse setUnmodifiable() { 432 unmodifiable = true; 433 return this; 434 } 435 436 @Override /* Overridden from Object */ 437 public String toString() { 438 var sb = new StringBuilder().append(statusLine).append(' ').append(headers); 439 if (nn(content)) 440 sb.append(' ').append(content); 441 return sb.toString(); 442 } 443 444 /** 445 * Throws an {@link UnsupportedOperationException} if the unmodifiable flag is set on this bean. 446 */ 447 protected final void assertModifiable() { 448 if (unmodifiable) 449 throw unsupportedOp("Bean is read-only"); 450 } 451 452 /** 453 * Asserts that the specified HTTP response has the same status code as the one on the status line of this bean. 454 * 455 * @param response The HTTP response to check. Must not be <jk>null</jk>. 456 * @throws AssertionError If status code is not what was expected. 457 */ 458 protected void assertStatusCode(HttpResponse response) throws AssertionError { 459 assertArgNotNull("response", response); 460 int expected = getStatusLine().getStatusCode(); 461 int actual = response.getStatusLine().getStatusCode(); 462 assertInteger(actual).setMsg("Unexpected status code. Expected:[{0}], Actual:[{1}]", expected, actual).is(expected); 463 } 464}