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 025import org.jdrupes.httpcodec.Codec.ProtocolSwitchResult; 026import org.jdrupes.httpcodec.protocols.http.client.HttpResponseDecoder; 027 028/** 029 * An engine that can be used as a client. It has an associated 030 * request encoder and a response decoder. Using a {@link ClientEngine} 031 * has two main advantages over using an encoder and decoder 032 * directly. It links encoder and decoder (see 033 * {@link Encoder#setPeerDecoder(Decoder)} and 034 * {@link Decoder#setPeerEncoder(Encoder)}) and it replaces the encoder 035 * and decoder if the decoded result indicates a switch. The change takes 036 * place upon the next `encode` or `decode` invocation. The "old" encoders 037 * and decoders are thus still available when the result of 038 * a decode invocation, that indicates a switch, is processed. 039 * 040 * @param <Q> the message header type handled be the encoder (the request) 041 * @param <R> the message header type handled by the decoder (the response) 042 */ 043public class ClientEngine<Q extends MessageHeader, 044 R extends MessageHeader> extends Engine { 045 046 private Encoder<?, ?> requestEncoder; 047 private Decoder<?, ?> responseDecoder; 048 private Encoder<?, ?> newRequestEncoder; 049 private Decoder<?, ?> newResponseDecoder; 050 051 /** 052 * Creates a new instance. 053 * 054 * @param requestEncoder the encoder for the request 055 * @param responseDecoder the decoder for the response 056 */ 057 public ClientEngine(Encoder<Q, R> requestEncoder, 058 Decoder<R, Q> responseDecoder) { 059 this.requestEncoder = requestEncoder; 060 this.responseDecoder = responseDecoder; 061 requestEncoder.setPeerDecoder(responseDecoder); 062 responseDecoder.setPeerEncoder(requestEncoder); 063 } 064 065 /** 066 * @return the requestEncoder 067 */ 068 @SuppressWarnings("unchecked") 069 public Encoder<Q, R> requestEncoder() { 070 return (Encoder<Q, R>)requestEncoder; 071 } 072 073 /** 074 * @return the responseDecoder 075 */ 076 @SuppressWarnings("unchecked") 077 public Decoder<R,Q> responseDecoder() { 078 return (Decoder<R,Q>)responseDecoder; 079 } 080 081 /** 082 * Convenience method to invoke the encoder's encode method. 083 * 084 * @param out the buffer to use for the result 085 * @return the result 086 */ 087 public Codec.Result encode(ByteBuffer out) { 088 return requestEncoder.encode(out); 089 } 090 091 /** 092 * Convenience method to invoke the encoder's encode method. 093 * 094 * @param in the buffer with the data to encode 095 * @param out the buffer to use for the result 096 * @param endOfInput {@code true} if end of input 097 * @return the result 098 */ 099 public Codec.Result encode(Buffer in, ByteBuffer out, boolean endOfInput) { 100 return requestEncoder.encode(in, out, endOfInput); 101 } 102 103 /** 104 * Convenience method to invoke the encoder's encode method. 105 * 106 * @param messageHeader the message header 107 */ 108 @SuppressWarnings("unchecked") 109 public void encode(Q messageHeader) { 110 if (newRequestEncoder != null) { 111 requestEncoder = newRequestEncoder; 112 newRequestEncoder = null; 113 } 114 ((Encoder<Q, R>)requestEncoder).encode(messageHeader); 115 } 116 117 /** 118 * Convenience method to invoke the decoder's decode method. 119 * 120 * @param in the buffer with the data to decode 121 * @param out the buffer to use for the result 122 * @param endOfInput {@code true} if end of input 123 * @return the result 124 * @throws ProtocolException if the input violates the protocol 125 * @see HttpResponseDecoder#decode(java.nio.ByteBuffer, java.nio.Buffer, boolean) 126 */ 127 @SuppressWarnings("unchecked") 128 public Decoder.Result<Q> decode( 129 ByteBuffer in, Buffer out, boolean endOfInput) 130 throws ProtocolException { 131 if (newResponseDecoder != null) { 132 responseDecoder = newResponseDecoder; 133 newResponseDecoder = null; 134 } 135 Decoder.Result<Q> result 136 = (Decoder.Result<Q>)responseDecoder.decode(in, out, endOfInput); 137 if (result instanceof ProtocolSwitchResult) { 138 ProtocolSwitchResult res = (ProtocolSwitchResult)result; 139 if (res.newProtocol() != null) { 140 setSwitchedTo(res.newProtocol()); 141 newResponseDecoder = res.newDecoder(); 142 newRequestEncoder = res.newEncoder(); 143 ((Decoder<MessageHeader, MessageHeader>)newResponseDecoder) 144 .setPeerEncoder((Encoder<MessageHeader, MessageHeader>) 145 newRequestEncoder); 146 ((Encoder<MessageHeader, MessageHeader>)newRequestEncoder) 147 .setPeerDecoder((Decoder<MessageHeader, MessageHeader>) 148 newResponseDecoder); 149 } 150 } 151 return result; 152 } 153 154 /** 155 * Returns the last encoded request. 156 * 157 * @return the request 158 */ 159 @SuppressWarnings("unchecked") 160 public Optional<Q> currentRequest() { 161 return (Optional<Q>)requestEncoder.header(); 162 } 163 164}