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}