/** * Copyright (c) Istituto Nazionale di Fisica Nucleare (INFN). 2006-2016 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.italiangrid.voms.container.legacy; import java.io.IOException; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.util.EnumSet; import java.util.HashMap; import java.util.Map; import org.eclipse.jetty.http.HttpParser; import org.eclipse.jetty.io.Buffer; import org.eclipse.jetty.io.Buffers; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.EofException; import org.eclipse.jetty.io.nio.DirectNIOBuffer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class VOMSParser extends HttpParser { public static final Logger log = LoggerFactory.getLogger(VOMSParser.class); public enum ParserStatus { START, FOUND_ZERO, FOUND_HTTP_REQUEST, FOUND_LEGACY_VOMS_REQUEST, PARSED_VOMS_REQUEST, NO_MORE_INPUT; } private static final EnumSet<ParserStatus> finalStatuses = EnumSet.of( ParserStatus.FOUND_HTTP_REQUEST, ParserStatus.PARSED_VOMS_REQUEST, ParserStatus.NO_MORE_INPUT); private static final byte ZERO = '0'; private static final byte LEFT_BRACKET = '<'; private ParserStatus _vomsParserStatus = ParserStatus.START; private Buffer _vomsBuffer = null; private final EndPoint _vomsEndpoint; private final EventHandler _vomsHandler; private final JettyHTTPGetRequestBuffers _requestBuffers; private final VOMSXMLRequestTranslator _xmlRequestTranslator; public static final Charset charset = Charset.forName("UTF-8"); public static final CharsetDecoder decoder = charset.newDecoder(); private static final Map<Buffer, Buffer> legacyRequestHeaders; static { legacyRequestHeaders = new HashMap<Buffer, Buffer>(); for (LegacyHTTPHeader e : LegacyHTTPHeader.values()) { Buffer name = new DirectNIOBuffer(e.getHeaderName().length()); name.put(e.getHeaderName().getBytes()); Buffer value = new DirectNIOBuffer(e.getHeaderValue().length()); value.put(e.getHeaderValue().getBytes()); legacyRequestHeaders.put(name, value); } } public VOMSParser(Buffer buffer, EventHandler handler) { super(buffer, handler); _vomsEndpoint = null; _vomsHandler = handler; _requestBuffers = newRequestBuffers(); _xmlRequestTranslator = newXMLRequestTranslator(_requestBuffers); } public VOMSParser(Buffers buffers, EndPoint endp, EventHandler handler) { super(buffers, endp, handler); _vomsEndpoint = endp; _vomsHandler = handler; _requestBuffers = newRequestBuffers(); _xmlRequestTranslator = newXMLRequestTranslator(_requestBuffers); } protected JettyHTTPGetRequestBuffers newRequestBuffers() { return new HTTPRequestBuffers(); } protected VOMSXMLRequestTranslator newXMLRequestTranslator( JettyHTTPGetRequestBuffers buffers) { return new VOMSXMLRequestTranslatorImpl(); } protected void setParserStatus(ParserStatus s) { _vomsParserStatus = s; } protected boolean isDone() { return finalStatuses.contains(_vomsParserStatus); } protected int fillBuffer() throws IOException { int filled = -1; if (_vomsBuffer == null) _vomsBuffer = getHeaderBuffer(); if (_vomsEndpoint != null) { if (_vomsBuffer.space() == 0) { _vomsBuffer.clear(); throw new IllegalStateException("VOMS buffer full!"); } try { filled = _vomsEndpoint.fill(_vomsBuffer); } catch (EofException e) { log.debug("Caught eof exception: {}", e.getMessage(), e); throw e; } log.debug("From endpoint filled {}", filled); return filled; } return -1; } protected void notifyHTTPRequestComplete() throws IOException { _vomsHandler.startRequest(_requestBuffers.getMethodBuffer(), _requestBuffers.getURIBuffer(), _requestBuffers.getVersionBuffer()); for (Map.Entry<Buffer, Buffer> e : legacyRequestHeaders.entrySet()) { Buffer headerName = e.getKey(); Buffer headerValue = e.getValue(); _vomsHandler.parsedHeader(headerName, headerValue); } _vomsHandler.headerComplete(); } protected void parseXML() { // Save indexes in case something goes wrong int getIndex = _vomsBuffer.getIndex(); int putIndex = _vomsBuffer.putIndex(); boolean success = _xmlRequestTranslator.translateLegacyRequest(_vomsBuffer, _requestBuffers); if (success) { setParserStatus(ParserStatus.PARSED_VOMS_REQUEST); try { notifyHTTPRequestComplete(); } catch (IOException e) { log.error("Error completing legacy http request translation: {}", e.getMessage(), e); _requestBuffers.clearBuffers(); } } else { // reset buffer indexes _vomsBuffer.setGetIndex(getIndex); _vomsBuffer.setPutIndex(putIndex); } } protected int parseVOMSRequest() throws IOException { log.debug("parseVOMSRequest(): endpoint: {}", _vomsEndpoint); int progress = 0; if (isDone()) return 0; if (_vomsBuffer == null) _vomsBuffer = getHeaderBuffer(); if (_vomsBuffer.length() == 0) { int filled = -1; try { filled = fillBuffer(); log.debug("parseVOMSRequest(): _vomsBuffer: {}", _vomsBuffer); } catch (IOException e) { log.debug("Error filling buffer: {}", e.getMessage(), e); } if (filled > 0) progress++; else if (filled < 0) { setPersistent(false); log.debug("Error reading from channel, declaring parser done."); setParserStatus(ParserStatus.NO_MORE_INPUT); return -1; } } byte ch; while (!isDone() && (_vomsBuffer.length() > 0)) { if (_vomsParserStatus == ParserStatus.START || _vomsParserStatus == ParserStatus.FOUND_ZERO) { ch = _vomsBuffer.peek(); log.debug("parseVOMSRequest(): first req char: {}", (char) ch); if (ch != ZERO && ch != LEFT_BRACKET) { // No VOMS nonsense around here, we're done, fall back to // Jetty HTTP parser log.debug("parseVOMSRequest(): no voms nonsense found, " + "it's a plain http request"); setParserStatus(ParserStatus.FOUND_HTTP_REQUEST); return progress; } if (ch == ZERO) { _vomsBuffer.get(); progress++; log.debug("parseVOMSRequest(): found zero as first request char, " + "swallowing it."); setParserStatus(ParserStatus.FOUND_ZERO); } else { log .debug("parseVOMSRequest(): found voms legacy request. proceeding to" + "parse XML"); setParserStatus(ParserStatus.FOUND_LEGACY_VOMS_REQUEST); progress = progress + _vomsBuffer.length(); parseXML(); } continue; } } return progress; } @Override public boolean parseAvailable() throws IOException { if (isDone()) { log.debug("parseAvailable(): delegating up."); return super.parseAvailable(); } boolean progress = parseVOMSRequest() > 0; log.debug("parseAvailable(): progress: {}", progress); while (!isDone() && _vomsBuffer != null && _vomsBuffer.length() > 0) { progress |= (parseVOMSRequest() > 0); } return progress; } @Override public boolean isIdle() { return _vomsParserStatus.equals(ParserStatus.START); } @Override public boolean isComplete() { log.debug("isComplete(): vomsParserDone: {}", isDone()); if (isDone()) { boolean superComplete = super.isComplete(); log.debug("isComplete(): httpParserDone: {}", superComplete); return superComplete; } else return false; } @Override public void reset() { log.debug("reset()"); super.reset(); setParserStatus(ParserStatus.START); _vomsBuffer = null; _requestBuffers.clearBuffers(); } @Override public String toString() { return String.format("%s[status:%s];%s", getClass().getSimpleName(), _vomsParserStatus, super.toString()); } }