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.classic.response;
018
019import static org.apache.juneau.commons.utils.Shorts.*;
020import static org.apache.juneau.http.classic.HttpEntities.*;
021
022import java.net.*;
023import java.util.*;
024
025import org.apache.http.*;
026import org.apache.http.Header;
027import org.apache.http.impl.*;
028import org.apache.http.params.*;
029import org.apache.juneau.http.*;
030import org.apache.juneau.http.classic.*;
031import org.apache.juneau.http.classic.header.*;
032import org.apache.juneau.marshall.*;
033
034/**
035 * Basic implementation of the {@link HttpResponse} interface.
036 *
037 * <p>
038 * Although this class implements the various setters defined on the {@link HttpResponse} interface, it's in general
039 * going to be more efficient to set the status/headers/content of this bean through the builder.
040 *
041 * <p>
042 * Immutability is expressed with the "funnel + nested {@code Unmodifiable} snapshot" paradigm: every mutator (fluent
043 * setter and {@code void} interface mutator) routes through the single protected {@link #modify(Runnable)} choke-point,
044 * and each concrete leaf gets a nested {@code X.Unmodifiable} whose only behavioral override is a throwing
045 * {@code modify(...)}.  A leaf's {@link #unmodifiable()} factory returns a point-in-time snapshot of type
046 * {@code X.Unmodifiable}; any attempt to set a property on it throws an {@link UnsupportedOperationException}.
047 *
048 * <p>
049 * This is a self-typed (CRTP) root: a leaf declares <c>class Ok <jk>extends</jk> BasicHttpResponse&lt;Ok&gt;</c> and
050 * inherits leaf-typed fluent setters with no covariant overrides.
051 *
052 * <h5 class='section'>Notes:</h5><ul>
053 *    <li class='warn'>Beans are not thread safe unless they're marked as unmodifiable.
054 * </ul>
055 *
056 * <h5 class='section'>See Also:</h5><ul>
057 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauRestCommon">juneau-rest-common</a>
058 * </ul>
059 *
060 * @param <SELF> The self type for fluent setters.
061 */
062@Marshalled(as=MarshalledAs.STRING)
063@SuppressWarnings({
064   "deprecation", // Uses deprecated HttpMessage API
065   "java:S119", // 'SELF' (CRTP self-type) is intentional and clearer than a single-letter name.
066   "java:S1192" // Duplicated literals (argument/property names) read more clearly inline than as constants
067})
068public abstract class BasicHttpResponse<SELF extends BasicHttpResponse<SELF>> implements HttpResponse {
069
070   BasicStatusLine statusLine = new BasicStatusLine();
071   HeaderList headers = HeaderList.create();
072   HttpEntity content;
073
074   /**
075    * Copy constructor.
076    *
077    * @param copyFrom The bean to copy from.  Must not be <jk>null</jk>.
078    */
079   protected BasicHttpResponse(BasicHttpResponse<?> copyFrom) {
080      statusLine = copyFrom.statusLine.copy();
081      headers = copyFrom.headers.copy();
082      content = copyFrom.content;
083   }
084
085   /**
086    * Constructor.
087    *
088    * @param statusLine The HTTP status line.  Must not be <jk>null</jk>.
089    */
090   protected BasicHttpResponse(BasicStatusLine statusLine) {
091      setStatusLine(statusLine.copy());
092   }
093
094   /**
095    * Constructor.
096    *
097    * <p>
098    * This is the constructor used when parsing an HTTP response.
099    *
100    * @param response The HTTP response to copy from.  Must not be <jk>null</jk>.
101    */
102   protected BasicHttpResponse(HttpResponse response) {
103      reqnn("response", response);
104      setHeaders(response.getAllHeaders());
105      setContent(response.getEntity());
106      setStatusLine(response.getStatusLine());
107   }
108
109   @Override /* Overridden from HttpMessage */
110   public void addHeader(Header value) {
111      modify(() -> headers.append(value));
112   }
113
114   @Override /* Overridden from HttpMessage */
115   public void addHeader(String name, String value) {
116      modify(() -> headers.append(name, value));
117   }
118
119   @Override /* Overridden from HttpMessage */
120   public boolean containsHeader(String name) {
121      return headers.contains(name);
122   }
123
124   @Override /* Overridden from HttpMessage */
125   public Header[] getAllHeaders() { return headers.getAll(); }
126
127   @Override /* Overridden from HttpMessage */
128   public HttpEntity getEntity() {
129      // Constructing a StringEntity is somewhat expensive, so don't create it unless it's needed.
130      if (content == null)
131         content = stringEntity(getStatusLine().getReasonPhrase());
132      return content;
133   }
134
135   @Override /* Overridden from HttpMessage */
136   public Header getFirstHeader(String name) {
137      return headers.getFirst(name).orElse(null);
138   }
139
140   /**
141    * Returns access to the underlying builder for the headers.
142    *
143    * @return The underlying builder for the headers.
144    */
145   public HeaderList getHeaders() { return headers; }
146
147   @Override /* Overridden from HttpMessage */
148   public Header[] getHeaders(String name) {
149      return headers.getAll(name);
150   }
151
152   @Override /* Overridden from HttpMessage */
153   public Header getLastHeader(String name) {
154      return headers.getLast(name).orElse(null);
155   }
156
157   @Override /* Overridden from HttpMessage */
158   public Locale getLocale() { return statusLine.getLocale(); }
159
160   @Override /* Overridden from HttpMessage */
161   public HttpParams getParams() { return null; }
162
163   @Override /* Overridden from HttpMessage */
164   public ProtocolVersion getProtocolVersion() { return statusLine.getProtocolVersion(); }
165
166   @Override /* Overridden from HttpMessage */
167   public StatusLine getStatusLine() { return statusLine; }
168
169   @Override /* Overridden from HttpMessage */
170   public HeaderIterator headerIterator() {
171      return headers.headerIterator();
172   }
173
174   @Override /* Overridden from HttpMessage */
175   public HeaderIterator headerIterator(String name) {
176      return headers.headerIterator(name);
177   }
178
179   /**
180    * Returns <jk>true</jk> if this bean is unmodifiable.
181    *
182    * @return <jk>true</jk> if this bean is an {@link UnmodifiableBean} snapshot.
183    */
184   public boolean isUnmodifiable() { return this instanceof UnmodifiableBean; }
185
186   @Override /* Overridden from HttpMessage */
187   public void removeHeader(Header value) {
188      modify(() -> headers.remove(value));
189   }
190
191   @Override /* Overridden from HttpMessage */
192   public void removeHeaders(String name) {
193      modify(() -> headers.remove(name));
194   }
195
196   /**
197    * Sets the body on this response.
198    *
199    * @param value The body on this response.  Can be <jk>null</jk> to unset the property.
200    * @return This object.
201    */
202   public SELF setContent(HttpEntity value) {
203      return modify(() -> content = value);
204   }
205
206   /**
207    * Sets the body on this response.
208    *
209    * @param value The body on this response.  Can be <jk>null</jk> (treated as an empty string).
210    * @return This object.
211    */
212   public SELF setContent(String value) {
213      return modify(() -> content = stringEntity(value));
214   }
215
216   @Override /* Overridden from HttpMessage */
217   public void setEntity(HttpEntity entity) {
218      modify(() -> content = entity);
219   }
220
221   @Override /* Overridden from HttpMessage */
222   public void setHeader(Header value) {
223      modify(() -> headers.set(value));
224   }
225
226   @Override /* Overridden from HttpMessage */
227   public void setHeader(String name, String value) {
228      modify(() -> headers.set(name, value));
229   }
230
231   /**
232    * Sets the specified header to the end of the headers in this builder.
233    *
234    * @param value The header to add.  <jk>null</jk> values are ignored.
235    * @return This object.
236    */
237   public SELF setHeader2(Header value) {
238      return modify(() -> headers.set(value));
239   }
240
241   /**
242    * Sets the specified header to the end of the headers in this builder.
243    *
244    * @param name The header name.
245    * @param value The header value.
246    * @return This object.
247    */
248   public SELF setHeader2(String name, String value) {
249      return modify(() -> headers.set(name, value));
250   }
251
252   @Override /* Overridden from HttpMessage */
253   public void setHeaders(Header[] values) {
254      modify(() -> headers.removeAll().append(values));
255   }
256
257   /**
258    * Sets the specified headers on this response.
259    *
260    * @param value The new value.  Must not be <jk>null</jk>.
261    * @return This object.
262    */
263   public SELF setHeaders(HeaderList value) {
264      return modify(() -> headers = value.copy());
265   }
266
267   /**
268    * Sets the specified headers to the end of the headers in this builder.
269    *
270    * @param values The headers to add.  <jk>null</jk> values are ignored.
271    * @return This object.
272    */
273   public SELF setHeaders(List<Header> values) {
274      return modify(() -> headers.set(values));
275   }
276
277   /**
278    * Sets the specified headers to the end of the headers in this builder.
279    *
280    * @param values The headers to add.  <jk>null</jk> values are ignored.
281    * @return This object.
282    */
283   public SELF setHeaders2(Header...values) {
284      return modify(() -> headers.set(values));
285   }
286
287   @Override /* Overridden from HttpMessage */
288   public void setLocale(Locale loc) {
289      modify(() -> statusLine.setLocale(loc));
290   }
291
292   /**
293    * Sets the locale used to retrieve reason phrases.
294    *
295    * <p>
296    * If not specified, uses {@link Locale#getDefault()}.
297    *
298    * @param value The new value.  Can be <jk>null</jk>.  The effect is implementation-dependent, depending on the configured {@link ReasonPhraseCatalog} (e.g. ignored by {@link EnglishReasonPhraseCatalog}).
299    * @return This object.
300    */
301   public SELF setLocale2(Locale value) {
302      return modify(() -> statusLine.setLocale(value));
303   }
304
305   /**
306    * Specifies the value for the <c>Location</c> header.
307    *
308    * @param value The new header location.  Can be <jk>null</jk> (no-op; the header is left unchanged).
309    * @return This object.
310    */
311   public SELF setLocation(String value) {
312      return modify(() -> headers.set(Location.of(value)));
313   }
314
315   /**
316    * Specifies the value for the <c>Location</c> header.
317    *
318    * @param value The new header location.  Can be <jk>null</jk> (no-op; the header is left unchanged).
319    * @return This object.
320    */
321   public SELF setLocation(URI value) {
322      return modify(() -> headers.set(Location.of(value)));
323   }
324
325   @Override /* Overridden from HttpMessage */
326   public void setParams(HttpParams params) {
327      // Deprecated optional interface method; routed through the funnel so it is frozen on unmodifiable snapshots.
328      modify(() -> { /* No-op */ });
329   }
330
331   /**
332    * Sets the protocol version on the status line.
333    *
334    * <p>
335    * If not specified, <js>"HTTP/1.1"</js> will be used.
336    *
337    * @param value The new value.  Can be <jk>null</jk>, in which case the default protocol version is cleared and {@link BasicStatusLine#getProtocolVersion()} returns <jk>null</jk>.
338    * @return This object.
339    */
340   public SELF setProtocolVersion(ProtocolVersion value) {
341      return modify(() -> statusLine.setProtocolVersion(value));
342   }
343
344   @Override /* Overridden from HttpMessage */
345   public void setReasonPhrase(String reason) throws IllegalStateException {
346      modify(() -> statusLine.setReasonPhrase(reason));
347   }
348
349   /**
350    * Sets the reason phrase on the status line.
351    *
352    * <p>
353    * If not specified, the reason phrase will be retrieved from the reason phrase catalog
354    * using the locale on this builder.
355    *
356    * @param value The new value.  Can be <jk>null</jk> to fall back to the reason phrase catalog.
357    * @return This object.
358    */
359   public SELF setReasonPhrase2(String value) {
360      return modify(() -> statusLine.setReasonPhrase(value));
361   }
362
363   /**
364    * Sets the reason phrase catalog used to retrieve reason phrases.
365    *
366    * <p>
367    * If not specified, uses {@link EnglishReasonPhraseCatalog}.
368    *
369    * @param value The new value.  Can be <jk>null</jk> to fall back to {@link EnglishReasonPhraseCatalog#INSTANCE}.
370    * @return This object.
371    */
372   public SELF setReasonPhraseCatalog(ReasonPhraseCatalog value) {
373      return modify(() -> statusLine.setReasonPhraseCatalog(value));
374   }
375
376   @Override /* Overridden from HttpMessage */
377   public void setStatusCode(int code) throws IllegalStateException {
378      modify(() -> statusLine.setStatusCode(code));
379   }
380
381   /**
382    * Sets the status code on the status line.
383    *
384    * <p>
385    * If not specified, <c>0</c> will be used.
386    *
387    * @param value The new value.
388    * @return This object.
389    */
390   public SELF setStatusCode2(int value) {
391      return modify(() -> statusLine.setStatusCode(value));
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.  Must not be <jk>null</jk>.
401    * @return This object.
402    */
403   public SELF setStatusLine(BasicStatusLine value) {
404      return modify(() -> statusLine = value.copy());
405   }
406
407   @Override /* Overridden from HttpMessage */
408   public void setStatusLine(ProtocolVersion ver, int code) {
409      modify(() -> statusLine.setProtocolVersion(ver).setStatusCode(code));
410   }
411
412   @Override /* Overridden from HttpMessage */
413   public void setStatusLine(ProtocolVersion ver, int code, String reason) {
414      modify(() -> statusLine.setProtocolVersion(ver).setReasonPhrase(reason).setStatusCode(code));
415   }
416
417   @Override /* Overridden from HttpMessage */
418   public void setStatusLine(StatusLine value) {
419      modify(() -> statusLine.setProtocolVersion(value.getProtocolVersion()).setReasonPhrase(value.getReasonPhrase()).setStatusCode(value.getStatusCode()));
420   }
421
422   /**
423    * Returns an unmodifiable snapshot of this bean.
424    *
425    * <p>
426    * The returned instance is a point-in-time copy of the leaf's nested {@code Unmodifiable} type; any attempt to set a
427    * property on it throws an {@link UnsupportedOperationException}.  This method is idempotent: if this bean is already
428    * unmodifiable it returns itself rather than taking another snapshot.  The receiver is left unchanged (and still
429    * modifiable unless it was already unmodifiable).
430    *
431    * @return An unmodifiable snapshot of this bean, or this bean if it is already unmodifiable.
432    */
433   public abstract SELF unmodifiable();
434
435   @Override /* Overridden from Object */
436   public String toString() {
437      var sb = new StringBuilder().append(statusLine).append(' ').append(headers);
438      if (nn(content))
439         sb.append(' ').append(content);
440      return sb.toString();
441   }
442
443   @Override /* Overridden from Object */
444   public boolean equals(Object o) {
445      return o instanceof BasicHttpResponse<?> other && eq(this, other, (x, y) ->
446         eq(x.statusLine, y.statusLine) && eq(x.headers, y.headers) && eq(x.content, y.content));
447   }
448
449   @Override /* Overridden from Object */
450   public int hashCode() {
451      return h(statusLine, headers, content);
452   }
453
454   /**
455    * Returns this object cast to the self type.
456    *
457    * @return This object.
458    */
459   @SuppressWarnings({
460      "unchecked" // CRTP self-type cast is safe: SELF is bound to the concrete leaf type.
461   })
462   protected final SELF self() { return (SELF) this; }
463
464   /**
465    * Single mutation funnel — the only choke-point through which all state changes on this bean flow.
466    *
467    * <p>
468    * Every mutator (fluent setter and {@code void} interface mutator) routes through this method.  The nested
469    * {@code Unmodifiable} snapshot overrides only this method to throw, which freezes the entire mutation surface with
470    * a single override.
471    *
472    * @param mutation The state change to apply.
473    * @return This object.
474    */
475   protected SELF modify(Runnable mutation) {
476      mutation.run();
477      return self();
478   }
479
480   /**
481    * Deep-freeze hook invoked from the nested {@code Unmodifiable} constructor after the snapshot copy completes.
482    *
483    * <p>
484    * The mutable sub-beans are frozen here by <b>direct field assignment</b> (never via a setter, which would route
485    * through the throwing {@link #modify(Runnable)} and blow up during construction).  A leaf that adds its own mutable
486    * sub-bean overrides this method (calling {@code super.freeze()} first).
487    */
488   protected void freeze() {
489      statusLine = statusLine.unmodifiable();   // DIRECT field write — must NOT use setStatusLine(...)
490      headers = headers.unmodifiable();         // DIRECT field write — snapshot the copied HeaderList.
491   }
492
493   /**
494    * Verifies that the specified HTTP response has the expected status code and returns it.
495    *
496    * @param response The HTTP response to check.  Must not be <jk>null</jk>.
497    * @param expected The status code the response is required to have.
498    * @return The same response.
499    * @throws IllegalArgumentException If the response status code is not what was expected.
500    */
501   static HttpResponse requireStatus(HttpResponse response, int expected) throws IllegalArgumentException {
502      return BasicHttpException.requireStatus(response, expected);
503   }
504
505   /**
506    * Asserts that the specified HTTP response has the same status code as the one on the status line of this bean.
507    *
508    * @param response The HTTP response to check.  Must not be <jk>null</jk>.
509    * @throws IllegalArgumentException If status code is not what was expected.
510    */
511   protected void assertStatusCode(HttpResponse response) throws IllegalArgumentException {
512      requireStatus(response, getStatusLine().getStatusCode());
513   }
514}