package org.webpieces.frontend2.impl; import java.nio.ByteBuffer; import java.util.List; import org.webpieces.data.api.DataWrapper; import org.webpieces.data.api.DataWrapperGenerator; import org.webpieces.data.api.DataWrapperGeneratorFactory; import org.webpieces.frontend2.api.HttpRequestListener; import org.webpieces.frontend2.api.Protocol; import org.webpieces.frontend2.impl.translation.Http2Translations; import org.webpieces.httpparser.api.HttpParser; import org.webpieces.httpparser.api.Memento; import org.webpieces.httpparser.api.dto.HttpChunk; import org.webpieces.httpparser.api.dto.HttpMessageType; import org.webpieces.httpparser.api.dto.HttpPayload; import org.webpieces.httpparser.api.dto.HttpRequest; import org.webpieces.util.logging.Logger; import org.webpieces.util.logging.LoggerFactory; import com.webpieces.hpack.api.dto.Http2Headers; import com.webpieces.http2engine.api.StreamWriter; import com.webpieces.http2parser.api.dto.DataFrame; import com.webpieces.http2parser.api.dto.lib.Http2Header; import com.webpieces.http2parser.api.dto.lib.Http2HeaderName; import com.webpieces.http2parser.api.dto.lib.Http2Msg; public class Layer2Http1_1Handler { private static final Logger log = LoggerFactory.getLogger(Layer2Http1_1Handler.class); private static final DataWrapperGenerator dataGen = DataWrapperGeneratorFactory.createDataWrapperGenerator(); private HttpParser httpParser; private HttpRequestListener httpListener; private boolean isHttps; public Layer2Http1_1Handler(HttpParser httpParser, HttpRequestListener httpListener, boolean isHttps) { this.httpParser = httpParser; this.httpListener = httpListener; this.isHttps = isHttps; } public InitiationResult initialData(FrontendSocketImpl socket, ByteBuffer buf) { Memento state = parse(socket, buf); //IF we are receiving a preface, there will ONLY be ONE message AND leftover data InitiationResult result = checkForPreface(socket, state); if(result != null) return result; //TODO: check for EXACTLY ONE http request AND check if it is an h2c header with Http-Settings header!!!! //if so, return that initiation result and start using the http2 code //if we get this far, we now know we are http1.1 if(state.getParsedMessages().size() >= 0) { processHttp1Messages(socket, state); return new InitiationResult(InitiationStatus.HTTP1_1); } return null; // we don't know yet(not enough data) } private InitiationResult checkForPreface(FrontendSocketImpl socket, Memento state) { if(state.getParsedMessages().size() != 1) return null; if(state.getParsedMessages().get(0).getMessageType() != HttpMessageType.HTTP2_MARKER_MSG) return null; //release memory associated with 1.1 parser for this socket socket.setHttp1_1ParseState(null); return new InitiationResult(state.getLeftOverData(), InitiationStatus.PREFACE); } public void incomingData(FrontendSocketImpl socket, ByteBuffer buf) { Memento state = parse(socket, buf); processHttp1Messages(socket, state); } private Memento parse(FrontendSocketImpl socket, ByteBuffer buf) { DataWrapper moreData = dataGen.wrapByteBuffer(buf); Memento state = socket.getHttp1_1ParseState(); state = httpParser.parse(state, moreData); return state; } private void processHttp1Messages(FrontendSocketImpl socket, Memento state) { List<HttpPayload> parsed = state.getParsedMessages(); for(HttpPayload payload : parsed) { log.info("msg received="+payload); processCorrectly(socket, payload); } } private void processCorrectly(FrontendSocketImpl socket, HttpPayload payload) { Http2Msg msg = Http2Translations.translate(payload, isHttps); //TODO: close socket on violation of pipelining like previous request did not end if(payload instanceof HttpRequest) { processInitialPieceOfRequest(socket, payload, msg); } else if(payload instanceof HttpChunk) { processChunk(socket, (HttpChunk)payload, (DataFrame) msg); } else { throw new IllegalArgumentException("payload not supported="+payload); } } private void processChunk(FrontendSocketImpl socket, HttpChunk payload, DataFrame data) { StreamWriter writer = socket.getWriter(); writer.send(data); } private void processInitialPieceOfRequest(FrontendSocketImpl socket, HttpPayload payload, Http2Msg msg) { HttpRequest http1Req = (HttpRequest) payload; Http2Headers headers = (Http2Headers) msg; Http1_1StreamImpl stream = new Http1_1StreamImpl(socket, httpParser); socket.setAddStream(stream); Http2Header lengthHeader = headers.getHeaderLookupStruct().getHeader(Http2HeaderName.CONTENT_LENGTH); if(lengthHeader != null) { DataFrame frame = Http2Translations.translateBody(payload.getBody()); StreamWriter writer = httpListener.incomingRequest(stream, headers, Protocol.HTTP11); writer.send(frame); } else if(http1Req.isHasChunkedTransferHeader()) { StreamWriter writer = httpListener.incomingRequest(stream, headers, Protocol.HTTP11); socket.addWriter(writer); } else { httpListener.incomingRequest(stream, headers, Protocol.HTTP11); } } public void socketOpened(FrontendSocketImpl socket, boolean isReadyForWrites) { Memento parseState = httpParser.prepareToParse(); socket.setHttp1_1ParseState(parseState); //timeoutListener.connectionOpened(socket, isReadyForWrites); } public void farEndClosed(FrontendSocketImpl socket) { socket.farEndClosed(httpListener); } }