001/* 002 * This file is part of the JDrupes non-blocking HTTP Codec 003 * Copyright (C) 2016, 2017 Michael N. Lipp 004 * 005 * This program is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU Lesser General Public License as published 007 * by the Free Software Foundation; either version 3 of the License, or 008 * (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, but 011 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 012 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 013 * License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public License along 016 * with this program; if not, see <http://www.gnu.org/licenses/>. 017 */ 018 019package org.jdrupes.httpcodec; 020 021import java.nio.Buffer; 022import java.nio.ByteBuffer; 023import java.util.Optional; 024 025/** 026 * The general interface of a decoder. 027 * 028 * @param <T> the type of message decoded by this decoder 029 * @param <R> the type of message that may be generated as response 030 * (see {@link Decoder.Result#response()}) 031 */ 032public interface Decoder<T extends MessageHeader, 033 R extends MessageHeader> extends Codec { 034 035 /** 036 * Sets the peer encoder. Some decoders need to know the state of 037 * the encoder or the last encoded message. 038 * 039 * @param encoder the encoder 040 * @return the decoder 041 */ 042 Decoder<T, R> setPeerEncoder(Encoder<R, T> encoder); 043 044 /** 045 * Returns the type of the messages decoded by this decoder. 046 * 047 * @return the value 048 */ 049 Class<T> decoding(); 050 051 /** 052 * Decodes the next chunk of data. This method will never leave 053 * remaining data in the `in` buffer unless a header has been 054 * decoded completely and/or the `out` buffer is full. In either case, 055 * decoding will therefore continue when the method is invoked again 056 * with the same `in` buffer and another (or emptied) `out` buffer. 057 * It is never necessary (though possible) to add data to an existing 058 * `in` buffer. 059 * 060 * @param in 061 * holds the data to be decoded 062 * @param out 063 * gets the body data (if any) written to it 064 * @param endOfInput 065 * {@code true} if there is no input left beyond the data 066 * currently in the {@code in} buffer (indicates end of body or 067 * no body at all) 068 * @return the result 069 * @throws ProtocolException 070 * if the message violates the HTTP 071 */ 072 public Result<R> decode(ByteBuffer in, Buffer out, boolean endOfInput) 073 throws ProtocolException; 074 075 /** 076 * Returns the last message (header) received. 077 * If a header is completed during a 078 * {@link #decode(ByteBuffer, Buffer, boolean)} invocation, 079 * the result's {@link Result#isHeaderCompleted()} is `true` 080 * and the header can be retrieved using this method. 081 * It remains available until `decode` is invoked for a new message 082 * (i.e. is invoked again after returning a result with 083 * {@link Codec.Result#isUnderflow()} being `false`. 084 * 085 * @return the result 086 */ 087 public Optional<T> header(); 088 089 /** 090 * The result from decoding. In addition to the common codec result, this 091 * includes the information wheteher a complete message header has been 092 * received and it can include a response that is to be sent back to the 093 * sender in order to fulfill the requirements of the protocol. As the 094 * decoder can (obviously) not sent back this response by itself, it is 095 * included in the result. 096 * 097 * The class is declared abstract to promote the usage of the factory 098 * method. 099 * 100 * @param <R> 101 * the type of the optionally generated response message 102 */ 103 public abstract static class Result<R extends MessageHeader> 104 extends Codec.Result { 105 106 private boolean headerCompleted; 107 private Optional<R> response; 108 private boolean responseOnly; 109 110 /** 111 * Creates a new result. 112 * @param overflow {@code true} if the data didn't fit in the out buffer 113 * @param underflow {@code true} if more data is expected 114 * @param closeConnection 115 * {@code true} if the connection should be closed 116 * @param headerCompleted {@code true} if the header has completely 117 * been decoded 118 * @param response a response to send due to an error 119 * @param responseOnly if the result includes a response 120 * this flag indicates that no further processing besides 121 * sending the response is required 122 */ 123 protected Result(boolean overflow, boolean underflow, 124 boolean closeConnection, boolean headerCompleted, 125 R response, boolean responseOnly) { 126 super(overflow, underflow, closeConnection); 127 this.headerCompleted = headerCompleted; 128 this.response = Optional.ofNullable(response); 129 this.responseOnly = responseOnly; 130 } 131 132 /** 133 * Returns {@code true} if the message header has been decoded 134 * completely during the decoder invocation that returned this 135 * result and is now available. Note that not only the header may 136 * heve been decoded, but that the output buffer may also 137 * already contain decoded data. 138 * 139 * @return the result 140 */ 141 public boolean isHeaderCompleted() { 142 return headerCompleted; 143 } 144 145 /** 146 * Returns the response if a response exists. A response in 147 * the decoder result indicates that some information 148 * must be signaled back to the sender. 149 * 150 * @return the response 151 */ 152 public Optional<R> response() { 153 return response; 154 } 155 156 /** 157 * If the result includes a response (see {@link #response()}) 158 * and this method returns {@code true} then no 159 * further processing of the received data is required. After sending 160 * the response data, the decode method should be invoked 161 * again with the same parameters. 162 * 163 * @return the result 164 */ 165 public boolean isResponseOnly() { 166 return responseOnly; 167 } 168 169 /* (non-Javadoc) 170 * @see java.lang.Object#hashCode() 171 */ 172 @Override 173 public int hashCode() { 174 final int prime = 31; 175 int result = super.hashCode(); 176 result = prime * result + (headerCompleted ? 1231 : 1237); 177 result = prime * result 178 + ((response == null) ? 0 : response.hashCode()); 179 result = prime * result + (responseOnly ? 1231 : 1237); 180 return result; 181 } 182 183 /* (non-Javadoc) 184 * @see java.lang.Object#equals(java.lang.Object) 185 */ 186 @Override 187 public boolean equals(Object obj) { 188 if (this == obj) { 189 return true; 190 } 191 if (!super.equals(obj)) { 192 return false; 193 } 194 if (!(obj instanceof Result)) { 195 return false; 196 } 197 @SuppressWarnings("rawtypes") 198 Result other = (Result) obj; 199 if (headerCompleted != other.headerCompleted) { 200 return false; 201 } 202 if (response == null) { 203 if (other.response != null) { 204 return false; 205 } 206 } else if (!response.equals(other.response)) { 207 return false; 208 } 209 if (responseOnly != other.responseOnly) { 210 return false; 211 } 212 return true; 213 } 214 215 /* (non-Javadoc) 216 * @see java.lang.Object#toString() 217 */ 218 @Override 219 public String toString() { 220 StringBuilder builder = new StringBuilder(); 221 builder.append("Decoder.Result [overflow="); 222 builder.append(isOverflow()); 223 builder.append(", underflow="); 224 builder.append(isUnderflow()); 225 builder.append(", closeConnection="); 226 builder.append(closeConnection()); 227 builder.append(", headerCompleted="); 228 builder.append(headerCompleted); 229 builder.append(", "); 230 if (response != null) { 231 builder.append("response="); 232 builder.append(response); 233 builder.append(", "); 234 } 235 builder.append("responseOnly="); 236 builder.append(responseOnly); 237 builder.append("]"); 238 return builder.toString(); 239 } 240 241 242 /** 243 * The Factory for (extended) results. 244 */ 245 protected abstract static class Factory<R extends MessageHeader> 246 extends Codec.Result.Factory { 247 } 248 } 249 250}