blob: b48f1f19d8a1791b579440d0ad6a217a89ff6e88 [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 java.nio.charset.Charset;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
public class ParamsContentParser extends ContentParser
{
private static final Logger LOG = Log.getLogger(ParamsContentParser.class);
private final ServerParser.Listener listener;
private State state = State.LENGTH;
private int cursor;
private int length;
private int nameLength;
private int valueLength;
private byte[] nameBytes;
private byte[] valueBytes;
public ParamsContentParser(HeaderParser headerParser, ServerParser.Listener listener)
{
super(headerParser);
this.listener = listener;
}
@Override
public Result parse(ByteBuffer buffer)
{
while (buffer.hasRemaining() || state == State.PARAM)
{
switch (state)
{
case LENGTH:
{
length = getContentLength();
state = State.NAME_LENGTH;
break;
}
case NAME_LENGTH:
{
if (isLargeLength(buffer))
{
if (buffer.remaining() >= 4)
{
nameLength = buffer.getInt() & 0x7F_FF;
state = State.VALUE_LENGTH;
length -= 4;
}
else
{
state = State.NAME_LENGTH_BYTES;
cursor = 0;
}
}
else
{
nameLength = buffer.get() & 0xFF;
state = State.VALUE_LENGTH;
--length;
}
break;
}
case NAME_LENGTH_BYTES:
{
int quarterInt = buffer.get() & 0xFF;
nameLength = (nameLength << 8) + quarterInt;
--length;
if (++cursor == 4)
{
nameLength &= 0x7F_FF;
state = State.VALUE_LENGTH;
}
break;
}
case VALUE_LENGTH:
{
if (isLargeLength(buffer))
{
if (buffer.remaining() >= 4)
{
valueLength = buffer.getInt() & 0x7F_FF;
state = State.NAME;
length -= 4;
}
else
{
state = State.VALUE_LENGTH_BYTES;
cursor = 0;
}
}
else
{
valueLength = buffer.get() & 0xFF;
state = State.NAME;
--length;
}
break;
}
case VALUE_LENGTH_BYTES:
{
int quarterInt = buffer.get() & 0xFF;
valueLength = (valueLength << 8) + quarterInt;
--length;
if (++cursor == 4)
{
valueLength &= 0x7F_FF;
state = State.NAME;
}
break;
}
case NAME:
{
nameBytes = new byte[nameLength];
if (buffer.remaining() >= nameLength)
{
buffer.get(nameBytes);
state = State.VALUE;
length -= nameLength;
}
else
{
state = State.NAME_BYTES;
cursor = 0;
}
break;
}
case NAME_BYTES:
{
nameBytes[cursor] = buffer.get();
--length;
if (++cursor == nameLength)
state = State.VALUE;
break;
}
case VALUE:
{
valueBytes = new byte[valueLength];
if (buffer.remaining() >= valueLength)
{
buffer.get(valueBytes);
state = State.PARAM;
length -= valueLength;
}
else
{
state = State.VALUE_BYTES;
cursor = 0;
}
break;
}
case VALUE_BYTES:
{
valueBytes[cursor] = buffer.get();
--length;
if (++cursor == valueLength)
state = State.PARAM;
break;
}
case PARAM:
{
Charset utf8 = Charset.forName("UTF-8");
onParam(new String(nameBytes, utf8), new String(valueBytes, utf8));
partialReset();
if (length == 0)
{
reset();
return Result.COMPLETE;
}
break;
}
default:
{
throw new IllegalStateException();
}
}
}
return Result.PENDING;
}
@Override
public void noContent()
{
onParams();
}
protected void onParam(String name, String value)
{
try
{
listener.onHeader(getRequest(), new HttpField(name, value));
}
catch (Throwable x)
{
if (LOG.isDebugEnabled())
LOG.debug("Exception while invoking listener " + listener, x);
}
}
protected void onParams()
{
try
{
listener.onHeaders(getRequest());
}
catch (Throwable x)
{
if (LOG.isDebugEnabled())
LOG.debug("Exception while invoking listener " + listener, x);
}
}
private boolean isLargeLength(ByteBuffer buffer)
{
return (buffer.get(buffer.position()) & 0x80) == 0x80;
}
private void partialReset()
{
state = State.NAME_LENGTH;
cursor = 0;
nameLength = 0;
valueLength = 0;
nameBytes = null;
valueBytes = null;
}
private void reset()
{
partialReset();
state = State.LENGTH;
length = 0;
}
private enum State
{
LENGTH, NAME_LENGTH, NAME_LENGTH_BYTES, VALUE_LENGTH, VALUE_LENGTH_BYTES, NAME, NAME_BYTES, VALUE, VALUE_BYTES, PARAM
}
}