001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
002 *
003 * Copyright © 2019–2020 microBean™.
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * 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
014 * implied.  See the License for the specific language governing
015 * permissions and limitations under the License.
016 */
017package org.microbean.jersey.netty;
018
019import java.net.URI;
020
021import java.util.Iterator;
022import java.util.Map.Entry;
023import java.util.List; // for javadoc only
024
025import java.util.function.Supplier;
026
027import javax.ws.rs.core.Configuration;
028
029import io.netty.buffer.ByteBuf;
030import io.netty.buffer.ByteBufHolder;
031
032import io.netty.channel.ChannelHandlerContext; // for javadoc only
033
034import io.netty.handler.codec.http.FullHttpMessage;
035import io.netty.handler.codec.http.HttpContent;
036import io.netty.handler.codec.http.HttpHeaders;
037import io.netty.handler.codec.http.HttpMessage;
038import io.netty.handler.codec.http.HttpObject;
039import io.netty.handler.codec.http.HttpRequest;
040import io.netty.handler.codec.http.HttpUtil;
041import io.netty.handler.codec.http.LastHttpContent;
042
043import org.glassfish.jersey.server.ContainerRequest;
044
045/**
046 * An {@link AbstractContainerRequestDecoder} that {@linkplain
047 * #decode(ChannelHandlerContext, Object, List) decodes} {@link
048 * HttpObject}s into {@link ContainerRequest}s.
049 *
050 * @author <a href="https://about.me/lairdnelson"
051 * target="_parent">Laird Nelson</a>
052 *
053 * @see #decode(ChannelHandlerContext, Object, List)
054 */
055public final class HttpObjectToContainerRequestDecoder extends AbstractContainerRequestDecoder<HttpObject, HttpRequest, HttpContent> {
056
057
058  /*
059   * Constructors.
060   */
061
062
063  /**
064   * Creates a new {@link HttpObjectToContainerRequestDecoder}.
065   *
066   * @param baseUri a {@link URI} that will serve as the {@linkplain
067   * ContainerRequest#getBaseUri() base <code>URI</code>} in a new
068   * {@link ContainerRequest}; may be {@code null} in which case the
069   * return value of {@link URI#create(String) URI.create("/")} will
070   * be used instead
071   *
072   * @see #HttpObjectToContainerRequestDecoder(URI, Configuration)
073   *
074   * @deprecated Please use the {@link
075   * #HttpObjectToContainerRequestDecoder(URI, Supplier)}
076   * constructor instead.
077   */
078  @Deprecated
079  public HttpObjectToContainerRequestDecoder(final URI baseUri) {
080    this(baseUri, (Supplier<? extends Configuration>)null);
081  }
082
083  /**
084   * Creates a new {@link HttpObjectToContainerRequestDecoder}.
085   *
086   * @param baseUri a {@link URI} that will serve as the {@linkplain
087   * ContainerRequest#getBaseUri() base <code>URI</code>} in a new
088   * {@link ContainerRequest}; may be {@code null} in which case the
089   * return value of {@link URI#create(String) URI.create("/")} will
090   * be used instead
091   *
092   * @param configuration a {@link Configuration} describing how the
093   * container is configured; may be {@code null}
094   */
095  public HttpObjectToContainerRequestDecoder(final URI baseUri, final Configuration configuration) {
096    this(baseUri, configuration == null ? (Supplier<? extends Configuration>)null : new ImmutableSupplier<>(configuration));
097  }
098
099  /**
100   * Creates a new {@link HttpObjectToContainerRequestDecoder}.
101   *
102   * @param baseUri a {@link URI} that will serve as the {@linkplain
103   * ContainerRequest#getBaseUri() base <code>URI</code>} in a new
104   * {@link ContainerRequest}; may be {@code null} in which case the
105   * return value of {@link URI#create(String) URI.create("/")} will
106   * be used instead
107   *
108   * @param configurationSupplier a {@link Supplier} of {@link
109   * Configuration} instances describing how the container is
110   * configured; may be {@code null}
111   */
112  public HttpObjectToContainerRequestDecoder(final URI baseUri, final Supplier<? extends Configuration> configurationSupplier) {
113    super(baseUri, configurationSupplier, HttpRequest.class, HttpContent.class);
114  }
115
116
117  /*
118   * Instance methods.
119   */
120
121
122  /**
123   * Extracts and returns a {@link String} representing a request URI
124   * from the supplied message, which is guaranteed to be a
125   * {@linkplain #isHeaders(Object) "headers" message}.
126   *
127   * <p>This implementation calls {@link HttpRequest#uri()
128   * httpRequest.uri()} and returns the result.</p>
129   *
130   * @param httpRequest the message to interrogate; will not be {@code
131   * null}
132   *
133   * @return a {@link String} representing a request URI from the
134   * supplied message, or {@code null}
135   */
136  @Override
137  protected final String getRequestUriString(final HttpRequest httpRequest) {
138    return httpRequest.uri();
139  }
140
141  /**
142   * Extracts and returns the name of the request method from the
143   * supplied message, which is guaranteed to be a {@linkplain
144   * #isHeaders(Object) "headers" message}.
145   *
146   * <p>This implementation calls {@link HttpRequest#method()
147   * httpRequest.method().name()} and returns the result.</p>
148   *
149   * @param httpRequest the message to interrogate; will not be {@code
150   * null}
151   *
152   * @return a {@link String} representing the request method from the
153   * supplied message, or {@code null}
154   */
155  @Override
156  protected final String getMethod(final HttpRequest httpRequest) {
157    return httpRequest.method().name();
158  }
159
160  /**
161   * Overrides the {@link
162   * AbstractContainerRequestDecoder#installMessage(ChannelHandlerContext,
163   * Object, ContainerRequest)} method to
164   * <strong>additionally</strong> install incoming headers into the
165   * supplied {@link ContainerRequest}.
166   *
167   * @param channelHandlerContext the {@link ChannelHandlerContext} in
168   * effect; will not be {@code null}; supplied for convenience;
169   * overrides may (and often do) ignore this parameter
170   *
171   * @param message the message to install; will not be {@code null}
172   *
173   * @param containerRequest the just-constructed {@link
174   * ContainerRequest} into which to install the supplied {@code
175   * message}; will not be {@code null}
176   *
177   * @see HttpRequest#headers()
178   *
179   * @see
180   * org.glassfish.jersey.message.internal.InboundMessageContext#header(String,
181   * Object)
182   */
183  @Override
184  protected void installMessage(final ChannelHandlerContext channelHandlerContext,
185                                final HttpRequest message,
186                                final ContainerRequest containerRequest) {
187    super.installMessage(channelHandlerContext, message, containerRequest);
188    final HttpHeaders headers = message.headers();
189    if (headers != null && !headers.isEmpty()) {
190      final Iterator<? extends Entry<?, ?>> iterator = headers.iteratorCharSequence();
191      while (iterator.hasNext()) {
192        final Entry<?, ?> entry = iterator.next();
193        containerRequest.header(entry.getKey().toString(), entry.getValue());
194      }
195    }
196  }
197
198  /**
199   * Returns {@code true} if the supplied {@link HttpObject} is the
200   * last of a stream of messages.
201   *
202   * <p>This implementation returns {@code true} if either:</p>
203   *
204   * <ul>
205   *
206   * <li>{@code httpObject} is an instance of {@link LastHttpContent},
207   * or</li>
208   *
209   * <li>{@code httpObject} is an instance of {@link HttpRequest} and
210   * its {@linkplain HttpUtil#getContentLength(HttpMessage, long)
211   * content length} equals {@code 0L}</li>
212   *
213   * </ul>
214   *
215   * @param httpObject the message to interrogate; will not be {@code
216   * null}
217   *
218   * @return {@code true} if no further messages in the stream are
219   * forthcoming; {@code false} otherwise
220   */
221  @Override
222  protected final boolean isLast(final HttpObject httpObject) {
223    return httpObject instanceof LastHttpContent || (httpObject instanceof HttpRequest && HttpUtil.getContentLength((HttpRequest)httpObject, -1L) == 0L);
224  }
225
226}