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}