blob: 9b134986b0cd5f57577cdb9c3d83576626fae0ee [file] [log] [blame]
//
// ========================================================================
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.fcgi.parser;
import java.nio.ByteBuffer;
import org.eclipse.jetty.fcgi.FCGI;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/**
* <p>The FastCGI protocol exchanges <em>frames</em>.</p>
* <pre>
* struct frame {
* ubyte version;
* ubyte type;
* ushort requestId;
* ushort contentLength;
* ubyte paddingLength;
* ubyte reserved;
* ubyte[] content;
* ubyte[] padding;
* }
* </pre>
* <p>Depending on the {@code type}, the content may have a different format,
* so there are specialized content parsers.</p>
*
* @see HeaderParser
* @see ContentParser
*/
public abstract class Parser
{
private static final Logger LOG = Log.getLogger(Parser.class);
protected final HeaderParser headerParser = new HeaderParser();
private State state = State.HEADER;
private int padding;
/**
* @param buffer the bytes to parse
* @return true if the caller should stop parsing, false if the caller should continue parsing
*/
public boolean parse(ByteBuffer buffer)
{
while (true)
{
switch (state)
{
case HEADER:
{
if (!headerParser.parse(buffer))
return false;
state = State.CONTENT;
break;
}
case CONTENT:
{
ContentParser contentParser = findContentParser(headerParser.getFrameType());
if (headerParser.getContentLength() == 0)
{
contentParser.noContent();
}
else
{
ContentParser.Result result = contentParser.parse(buffer);
if (LOG.isDebugEnabled())
LOG.debug("Parsed request {} content {} result={}", headerParser.getRequest(), headerParser.getFrameType(), result);
if (result == ContentParser.Result.PENDING)
{
// Not enough data, signal to read/parse more.
return false;
}
if (result == ContentParser.Result.ASYNC)
{
// The content will be processed asynchronously, signal to stop
// parsing; the async operation will eventually resume parsing.
return true;
}
}
padding = headerParser.getPaddingLength();
state = State.PADDING;
break;
}
case PADDING:
{
if (buffer.remaining() >= padding)
{
buffer.position(buffer.position() + padding);
reset();
break;
}
else
{
padding -= buffer.remaining();
buffer.position(buffer.limit());
return false;
}
}
default:
{
throw new IllegalStateException();
}
}
}
}
protected abstract ContentParser findContentParser(FCGI.FrameType frameType);
private void reset()
{
headerParser.reset();
state = State.HEADER;
padding = 0;
}
public interface Listener
{
public void onHeader(int request, HttpField field);
public void onHeaders(int request);
/**
* @param request the request id
* @param stream the stream type
* @param buffer the content bytes
* @return true to signal to the parser to stop parsing, false to continue parsing
* @see Parser#parse(java.nio.ByteBuffer)
*/
public boolean onContent(int request, FCGI.StreamType stream, ByteBuffer buffer);
public void onEnd(int request);
public void onFailure(int request, Throwable failure);
public static class Adapter implements Listener
{
@Override
public void onHeader(int request, HttpField field)
{
}
@Override
public void onHeaders(int request)
{
}
@Override
public boolean onContent(int request, FCGI.StreamType stream, ByteBuffer buffer)
{
return false;
}
@Override
public void onEnd(int request)
{
}
@Override
public void onFailure(int request, Throwable failure)
{
}
}
}
private enum State
{
HEADER, CONTENT, PADDING
}
}