From b42017b942aefec505860c854406896b7a5c6fa8 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Sun, 8 Jul 2018 14:44:38 +0000 Subject: [PATCH 01/10] WIP Signed-off-by: Greg Wilkins --- jetty-home/src/main/resources/bin/jetty.sh | 618 ------------------ .../jetty/http2/hpack/HpackDecoder.java | 6 +- .../jetty/http2/hpack/MetaDataBuilder.java | 120 +++- .../src/main/webapp/META-INF/MANIFEST.MF | 21 - .../src/main/webapp/stylesheet.css~ | 7 - .../src/main/webapp/META-INF/MANIFEST.MF | 3 - 6 files changed, 104 insertions(+), 671 deletions(-) delete mode 100755 jetty-home/src/main/resources/bin/jetty.sh delete mode 100644 tests/test-webapps/test-proxy-webapp/src/main/webapp/META-INF/MANIFEST.MF delete mode 100644 tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/webapp/stylesheet.css~ delete mode 100644 tests/test-webapps/test-webapp-rfc2616/src/main/webapp/META-INF/MANIFEST.MF diff --git a/jetty-home/src/main/resources/bin/jetty.sh b/jetty-home/src/main/resources/bin/jetty.sh deleted file mode 100755 index b2b69a3d75c..00000000000 --- a/jetty-home/src/main/resources/bin/jetty.sh +++ /dev/null @@ -1,618 +0,0 @@ -#!/usr/bin/env bash - -# LSB Tags -### BEGIN INIT INFO -# Provides: jetty -# Required-Start: $local_fs $network -# Required-Stop: $local_fs $network -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: Jetty start script. -# Description: Start Jetty web server. -### END INIT INFO - -# Startup script for jetty under *nix systems (it works under NT/cygwin too). - -################################################## -# Set the name which is used by other variables. -# Defaults to the file name without extension. -################################################## -NAME=$(echo $(basename $0) | sed -e 's/^[SK][0-9]*//' -e 's/\.sh$//') - -# To get the service to restart correctly on reboot, uncomment below (3 lines): -# ======================== -# chkconfig: 3 99 99 -# description: Jetty 9 webserver -# processname: jetty -# ======================== - -# Configuration files -# -# /etc/default/$NAME -# If it exists, this is read at the start of script. It may perform any -# sequence of shell commands, like setting relevant environment variables. -# -# $HOME/.$NAMErc (e.g. $HOME/.jettyrc) -# If it exists, this is read at the start of script. It may perform any -# sequence of shell commands, like setting relevant environment variables. -# -# /etc/$NAME.conf -# If found, and no configurations were given on the command line, -# the file will be used as this script's configuration. -# Each line in the file may contain: -# - A comment denoted by the pound (#) sign as first non-blank character. -# - The path to a regular file, which will be passed to jetty as a -# config.xml file. -# - The path to a directory. Each *.xml file in the directory will be -# passed to jetty as a config.xml file. -# - All other lines will be passed, as-is to the start.jar -# -# The files will be checked for existence before being passed to jetty. -# -# Configuration variables -# -# JAVA -# Command to invoke Java. If not set, java (from the PATH) will be used. -# -# JAVA_OPTIONS -# Extra options to pass to the JVM -# -# JETTY_HOME -# Where Jetty is installed. If not set, the script will try go -# guess it by looking at the invocation path for the script -# The java system property "jetty.home" will be -# set to this value for use by configure.xml files, f.e.: -# -# /webapps/jetty.war -# -# JETTY_BASE -# Where your Jetty base directory is. If not set, the value from -# $JETTY_HOME will be used. -# -# JETTY_RUN -# Where the $NAME.pid file should be stored. It defaults to the -# first available of /var/run, /usr/var/run, JETTY_BASE and /tmp -# if not set. -# -# JETTY_PID -# The Jetty PID file, defaults to $JETTY_RUN/$NAME.pid -# -# JETTY_ARGS -# The default arguments to pass to jetty. -# For example -# JETTY_ARGS=jetty.http.port=8080 jetty.ssl.port=8443 -# -# JETTY_USER -# if set, then used as a username to run the server as -# -# JETTY_SHELL -# If set, then used as the shell by su when starting the server. Will have -# no effect if start-stop-daemon exists. Useful when JETTY_USER does not -# have shell access, e.g. /bin/false -# -# JETTY_START_TIMEOUT -# Time spent waiting to see if startup was successful/failed. Defaults to 60 seconds -# - -usage() -{ - echo "Usage: ${0##*/} [-d] {start|stop|run|restart|check|supervise} [ CONFIGS ... ] " - exit 1 -} - -[ $# -gt 0 ] || usage - - -################################################## -# Some utility functions -################################################## -findDirectory() -{ - local L OP=$1 - shift - for L in "$@"; do - [ "$OP" "$L" ] || continue - printf %s "$L" - break - done -} - -running() -{ - if [ -f "$1" ] - then - local PID=$(cat "$1" 2>/dev/null) || return 1 - kill -0 "$PID" 2>/dev/null - return - fi - rm -f "$1" - return 1 -} - -started() -{ - # wait for 60s to see "STARTED" in PID file, needs jetty-started.xml as argument - for ((T = 0; T < $(($3 / 4)); T++)) - do - sleep 4 - [ -z "$(grep STARTED $1 2>/dev/null)" ] || return 0 - [ -z "$(grep STOPPED $1 2>/dev/null)" ] || return 1 - [ -z "$(grep FAILED $1 2>/dev/null)" ] || return 1 - local PID=$(cat "$2" 2>/dev/null) || return 1 - kill -0 "$PID" 2>/dev/null || return 1 - echo -n ". " - done - - return 1; -} - - -readConfig() -{ - (( DEBUG )) && echo "Reading $1.." - source "$1" -} - -dumpEnv() -{ - echo "JAVA = $JAVA" - echo "JAVA_OPTIONS = ${JAVA_OPTIONS[*]}" - echo "JETTY_HOME = $JETTY_HOME" - echo "JETTY_BASE = $JETTY_BASE" - echo "START_D = $START_D" - echo "START_INI = $START_INI" - echo "JETTY_START = $JETTY_START" - echo "JETTY_CONF = $JETTY_CONF" - echo "JETTY_ARGS = ${JETTY_ARGS[*]}" - echo "JETTY_RUN = $JETTY_RUN" - echo "JETTY_PID = $JETTY_PID" - echo "JETTY_START_LOG = $JETTY_START_LOG" - echo "JETTY_STATE = $JETTY_STATE" - echo "JETTY_START_TIMEOUT = $JETTY_START_TIMEOUT" - echo "RUN_CMD = ${RUN_CMD[*]}" -} - - - -################################################## -# Get the action & configs -################################################## -CONFIGS=() -NO_START=0 -DEBUG=0 - -while [[ $1 = -* ]]; do - case $1 in - -d) DEBUG=1 ;; - esac - shift -done -ACTION=$1 -shift - -################################################## -# Read any configuration files -################################################## -ETC=/etc -if [ $UID != 0 ] -then - ETC=$HOME/etc -fi - -for CONFIG in {/etc,~/etc}/default/${NAME}{,9} $HOME/.${NAME}rc; do - if [ -f "$CONFIG" ] ; then - readConfig "$CONFIG" - fi -done - - -################################################## -# Set tmp if not already set. -################################################## -TMPDIR=${TMPDIR:-/tmp} - -################################################## -# Jetty's hallmark -################################################## -JETTY_INSTALL_TRACE_FILE="start.jar" - - -################################################## -# Try to determine JETTY_HOME if not set -################################################## -if [ -z "$JETTY_HOME" ] -then - JETTY_SH=$0 - case "$JETTY_SH" in - /*) JETTY_HOME=${JETTY_SH%/*/*} ;; - ./*/*) JETTY_HOME=${JETTY_SH%/*/*} ;; - ./*) JETTY_HOME=.. ;; - */*/*) JETTY_HOME=./${JETTY_SH%/*/*} ;; - */*) JETTY_HOME=. ;; - *) JETTY_HOME=.. ;; - esac - - if [ ! -f "$JETTY_HOME/$JETTY_INSTALL_TRACE_FILE" ] - then - JETTY_HOME= - fi -fi - - -################################################## -# No JETTY_HOME yet? We're out of luck! -################################################## -if [ -z "$JETTY_HOME" ]; then - echo "** ERROR: JETTY_HOME not set, you need to set it or install in a standard location" - exit 1 -fi - -cd "$JETTY_HOME" -JETTY_HOME=$PWD - - -################################################## -# Set JETTY_BASE -################################################## -if [ -z "$JETTY_BASE" ]; then - JETTY_BASE=$JETTY_HOME -fi - -cd "$JETTY_BASE" -JETTY_BASE=$PWD - - -##################################################### -# Check that jetty is where we think it is -##################################################### -if [ ! -r "$JETTY_HOME/$JETTY_INSTALL_TRACE_FILE" ] -then - echo "** ERROR: Oops! Jetty doesn't appear to be installed in $JETTY_HOME" - echo "** ERROR: $JETTY_HOME/$JETTY_INSTALL_TRACE_FILE is not readable!" - exit 1 -fi - -################################################## -# Try to find this script's configuration file, -# but only if no configurations were given on the -# command line. -################################################## -if [ -z "$JETTY_CONF" ] -then - if [ -f $ETC/${NAME}.conf ] - then - JETTY_CONF=$ETC/${NAME}.conf - elif [ -f "$JETTY_BASE/etc/jetty.conf" ] - then - JETTY_CONF=$JETTY_BASE/etc/jetty.conf - elif [ -f "$JETTY_HOME/etc/jetty.conf" ] - then - JETTY_CONF=$JETTY_HOME/etc/jetty.conf - fi -fi - -##################################################### -# Find a location for the pid file -##################################################### -if [ -z "$JETTY_RUN" ] -then - JETTY_RUN=$(findDirectory -w /var/run /usr/var/run $JETTY_BASE /tmp)/jetty - [ -d "$JETTY_RUN" ] || mkdir $JETTY_RUN -fi - -##################################################### -# define start log location -##################################################### -if [ -z "$JETTY_START_LOG" ] -then - JETTY_START_LOG="$JETTY_RUN/$NAME-start.log" -fi - -##################################################### -# Find a pid and state file -##################################################### -if [ -z "$JETTY_PID" ] -then - JETTY_PID="$JETTY_RUN/${NAME}.pid" -fi - -if [ -z "$JETTY_STATE" ] -then - JETTY_STATE=$JETTY_BASE/${NAME}.state -fi - -case "`uname`" in -CYGWIN*) JETTY_STATE="`cygpath -w $JETTY_STATE`";; -esac - - -JETTY_ARGS=(${JETTY_ARGS[*]} "jetty.state=$JETTY_STATE") - -################################################## -# Get the list of config.xml files from jetty.conf -################################################## -if [ -f "$JETTY_CONF" ] && [ -r "$JETTY_CONF" ] -then - while read -r CONF - do - if expr "$CONF" : '#' >/dev/null ; then - continue - fi - - if [ -d "$CONF" ] - then - # assume it's a directory with configure.xml files - # for example: /etc/jetty.d/ - # sort the files before adding them to the list of JETTY_ARGS - for XMLFILE in "$CONF/"*.xml - do - if [ -r "$XMLFILE" ] && [ -f "$XMLFILE" ] - then - JETTY_ARGS=(${JETTY_ARGS[*]} "$XMLFILE") - else - echo "** WARNING: Cannot read '$XMLFILE' specified in '$JETTY_CONF'" - fi - done - else - # assume it's a command line parameter (let start.jar deal with its validity) - JETTY_ARGS=(${JETTY_ARGS[*]} "$CONF") - fi - done < "$JETTY_CONF" -fi - -################################################## -# Setup JAVA if unset -################################################## -if [ -z "$JAVA" ] -then - JAVA=$(which java) -fi - -if [ -z "$JAVA" ] -then - echo "Cannot find a Java JDK. Please set either set JAVA or put java (>=1.5) in your PATH." >&2 - exit 1 -fi - -##################################################### -# See if Deprecated JETTY_LOGS is defined -##################################################### -if [ "$JETTY_LOGS" ] -then - echo "** WARNING: JETTY_LOGS is Deprecated. Please configure logging within the jetty base." >&2 -fi - -##################################################### -# Set STARTED timeout -##################################################### -if [ -z "$JETTY_START_TIMEOUT"] -then - JETTY_START_TIMEOUT=60 -fi - -##################################################### -# Are we running on Windows? Could be, with Cygwin/NT. -##################################################### -case "`uname`" in -CYGWIN*) PATH_SEPARATOR=";";; -*) PATH_SEPARATOR=":";; -esac - - -##################################################### -# Add jetty properties to Java VM options. -##################################################### - -case "`uname`" in -CYGWIN*) -JETTY_HOME="`cygpath -w $JETTY_HOME`" -JETTY_BASE="`cygpath -w $JETTY_BASE`" -TMPDIR="`cygpath -w $TMPDIR`" -;; -esac - -JAVA_OPTIONS=(${JAVA_OPTIONS[*]} "-Djetty.home=$JETTY_HOME" "-Djetty.base=$JETTY_BASE" "-Djava.io.tmpdir=$TMPDIR") - -##################################################### -# This is how the Jetty server will be started -##################################################### - -JETTY_START=$JETTY_HOME/start.jar -START_INI=$JETTY_BASE/start.ini -START_D=$JETTY_BASE/start.d -if [ ! -f "$START_INI" -a ! -d "$START_D" ] -then - echo "Cannot find a start.ini file or a start.d directory in your JETTY_BASE directory: $JETTY_BASE" >&2 - exit 1 -fi - -case "`uname`" in -CYGWIN*) JETTY_START="`cygpath -w $JETTY_START`";; -esac - -RUN_ARGS=(${JAVA_OPTIONS[@]} -jar "$JETTY_START" ${JETTY_ARGS[*]}) -RUN_CMD=("$JAVA" ${RUN_ARGS[@]}) - -##################################################### -# Comment these out after you're happy with what -# the script is doing. -##################################################### -if (( DEBUG )) -then - dumpEnv -fi - -################################################## -# Do the action -################################################## -case "$ACTION" in - start) - echo -n "Starting Jetty: " - - if (( NO_START )); then - echo "Not starting ${NAME} - NO_START=1"; - exit - fi - - if [ $UID -eq 0 ] && type start-stop-daemon > /dev/null 2>&1 - then - unset CH_USER - if [ -n "$JETTY_USER" ] - then - CH_USER="-c$JETTY_USER" - fi - - start-stop-daemon -S -p"$JETTY_PID" $CH_USER -d"$JETTY_BASE" -b -m -a "$JAVA" -- "${RUN_ARGS[@]}" start-log-file="$JETTY_START_LOG" - - else - - if running $JETTY_PID - then - echo "Already Running $(cat $JETTY_PID)!" - exit 1 - fi - - if [ -n "$JETTY_USER" ] && [ `whoami` != "$JETTY_USER" ] - then - unset SU_SHELL - if [ "$JETTY_SHELL" ] - then - SU_SHELL="-s $JETTY_SHELL" - fi - - touch "$JETTY_PID" - chown "$JETTY_USER" "$JETTY_PID" - # FIXME: Broken solution: wordsplitting, pathname expansion, arbitrary command execution, etc. - su - "$JETTY_USER" $SU_SHELL -c " - cd \"$JETTY_BASE\" - exec ${RUN_CMD[*]} start-log-file=\"$JETTY_START_LOG\" > /dev/null & - disown \$! - echo \$! > \"$JETTY_PID\"" - else - "${RUN_CMD[@]}" > /dev/null & - disown $! - echo $! > "$JETTY_PID" - fi - - fi - - if expr "${JETTY_ARGS[*]}" : '.*jetty-started.xml.*' >/dev/null - then - if started "$JETTY_STATE" "$JETTY_PID" "$JETTY_START_TIMEOUT" - then - echo "OK `date`" - else - echo "FAILED `date`" - exit 1 - fi - else - echo "ok `date`" - fi - - ;; - - stop) - echo -n "Stopping Jetty: " - if [ $UID -eq 0 ] && type start-stop-daemon > /dev/null 2>&1; then - start-stop-daemon -K -p"$JETTY_PID" -d"$JETTY_HOME" -a "$JAVA" -s HUP - - TIMEOUT=30 - while running "$JETTY_PID"; do - if (( TIMEOUT-- == 0 )); then - start-stop-daemon -K -p"$JETTY_PID" -d"$JETTY_HOME" -a "$JAVA" -s KILL - fi - - sleep 1 - done - else - if [ ! -f "$JETTY_PID" ] ; then - echo "ERROR: no pid found at $JETTY_PID" - exit 1 - fi - - PID=$(cat "$JETTY_PID" 2>/dev/null) - if [ -z "$PID" ] ; then - echo "ERROR: no pid id found in $JETTY_PID" - exit 1 - fi - kill "$PID" 2>/dev/null - - TIMEOUT=30 - while running $JETTY_PID; do - if (( TIMEOUT-- == 0 )); then - kill -KILL "$PID" 2>/dev/null - fi - - sleep 1 - done - fi - - rm -f "$JETTY_PID" - rm -f "$JETTY_STATE" - echo OK - - ;; - - restart) - JETTY_SH=$0 - > "$JETTY_STATE" - if [ ! -f $JETTY_SH ]; then - if [ ! -f $JETTY_HOME/bin/jetty.sh ]; then - echo "$JETTY_HOME/bin/jetty.sh does not exist." - exit 1 - fi - JETTY_SH=$JETTY_HOME/bin/jetty.sh - fi - - "$JETTY_SH" stop "$@" - "$JETTY_SH" start "$@" - - ;; - - supervise) - # - # Under control of daemontools supervise monitor which - # handles restarts and shutdowns via the svc program. - # - exec "${RUN_CMD[@]}" - - ;; - - run|demo) - echo "Running Jetty: " - - if running "$JETTY_PID" - then - echo Already Running $(cat "$JETTY_PID")! - exit 1 - fi - - exec "${RUN_CMD[@]}" - ;; - - check|status) - if running "$JETTY_PID" - then - echo "Jetty running pid=$(< "$JETTY_PID")" - else - echo "Jetty NOT running" - fi - echo - dumpEnv - echo - - if running "$JETTY_PID" - then - exit 0 - fi - exit 1 - - ;; - - *) - usage - - ;; -esac - -exit 0 diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackDecoder.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackDecoder.java index 28cb913c34f..e730718c643 100644 --- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackDecoder.java +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackDecoder.java @@ -27,6 +27,7 @@ import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.MetaData; import org.eclipse.jetty.http2.hpack.HpackContext.Entry; + import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; @@ -66,14 +67,15 @@ public class HpackDecoder _localMaxDynamicTableSize=localMaxdynamciTableSize; } - public MetaData decode(ByteBuffer buffer) + public MetaData decode(ByteBuffer buffer) throws HpackException { if (LOG.isDebugEnabled()) LOG.debug(String.format("CtxTbl[%x] decoding %d octets",_context.hashCode(),buffer.remaining())); // If the buffer is big, don't even think about decoding it if (buffer.remaining()>_builder.getMaxSize()) - throw new BadMessageException(HttpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE_431,"Header frame size "+buffer.remaining()+">"+_builder.getMaxSize()); + throw new HpackException.Session("431 Request Header Fields too large",6); + //throw new BadMessageException(HttpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE_431,"Header frame size "+buffer.remaining()+">"+_builder.getMaxSize()); while(buffer.hasRemaining()) { diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/MetaDataBuilder.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/MetaDataBuilder.java index b9e72e7352f..c0f5266473e 100644 --- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/MetaDataBuilder.java +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/MetaDataBuilder.java @@ -29,18 +29,22 @@ import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.http.MetaData; +import org.eclipse.jetty.http2.hpack.HpackException.Session; public class MetaDataBuilder { private final int _maxSize; private int _size; - private int _status; + private int _status=-1; private String _method; private HttpScheme _scheme; private HostPortHttpField _authority; private String _path; private long _contentLength=Long.MIN_VALUE; private HttpFields _fields = new HttpFields(10); + private HpackException.Stream _streamException; + private boolean _request; + private boolean _response; /** * @param maxHeadersSize The maximum size of the headers, expressed as total name and value characters. @@ -66,7 +70,7 @@ public class MetaDataBuilder return _size; } - public void emit(HttpField field) + public void emit(HttpField field) throws HpackException.Session { HttpHeader header = field.getHeader(); String name = field.getName(); @@ -74,7 +78,7 @@ public class MetaDataBuilder int field_size = name.length() + (value == null ? 0 : value.length()); _size+=field_size+32; if (_size>_maxSize) - throw new BadMessageException(HttpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE_431,"Header size "+_size+">"+_maxSize); + throw new HpackException.Session("Header Size %d > %d",_size,_maxSize); if (field instanceof StaticTableHttpField) { @@ -82,15 +86,21 @@ public class MetaDataBuilder switch(header) { case C_STATUS: - _status=(Integer)staticField.getStaticValue(); + if(checkHeader(header, _status)) + _status = (Integer)staticField.getStaticValue(); + _response = true; break; case C_METHOD: - _method=value; + if(checkPseudoHeader(header, _method)) + _method = value; + _request = true; break; case C_SCHEME: - _scheme = (HttpScheme)staticField.getStaticValue(); + if(checkPseudoHeader(header, _scheme)) + _scheme = (HttpScheme)staticField.getStaticValue(); + _request = true; break; default: @@ -102,23 +112,32 @@ public class MetaDataBuilder switch(header) { case C_STATUS: - _status=field.getIntValue(); + if(checkHeader(header, _status)) + _status = field.getIntValue(); + _response = true; break; case C_METHOD: - _method=value; + if(checkPseudoHeader(header, _method)) + _method = value; + _request = true; break; case C_SCHEME: - if (value != null) + if(checkPseudoHeader(header, _scheme) && value != null) _scheme = HttpScheme.CACHE.get(value); + _request = true; break; case C_AUTHORITY: - if (field instanceof HostPortHttpField) - _authority = (HostPortHttpField)field; - else if (value != null) - _authority = new AuthorityHttpField(value); + if(checkPseudoHeader(header, _authority)) + { + if (field instanceof HostPortHttpField) + _authority = (HostPortHttpField)field; + else if (value != null) + _authority = new AuthorityHttpField(value); + } + _request = true; break; case HOST: @@ -134,29 +153,89 @@ public class MetaDataBuilder break; case C_PATH: - _path = value; + if(checkPseudoHeader(header, _path)) + _path = value; + _request = true; break; case CONTENT_LENGTH: _contentLength = field.getLongValue(); _fields.add(field); break; + + case TE: + if ("trailors".equalsIgnoreCase(value)) + _fields.add(field); + else + streamException("unsupported TE value %s", value); + break; + + case CONNECTION: + // TODO should other connection specific fields be listed here? + streamException("Connection specific field %s", header); + break; - default: - if (name.charAt(0)!=':') + default: + if (name.charAt(0)==':') + streamException("Unknown psuodo header %s", name); + else _fields.add(field); break; } } else { - if (name.charAt(0)!=':') + if (name.charAt(0)==':') + streamException("Unknown psuedo header %s",name); + else _fields.add(field); } } - public MetaData build() + private void streamException(String messageFormat, Object... args) { + HpackException.Stream stream = new HpackException.Stream(messageFormat, args); + if (_streamException==null) + _streamException = stream; + else + _streamException.addSuppressed(stream); + } + + private boolean checkHeader(HttpHeader header, int value) + { + if (_fields.size()>0) + { + streamException("Psuedo header %s after fields", header.asString()); + return false; + } + if (value==-1) + return true; + streamException("Duplicate psuedo header %s", header.asString()); + return false; + } + + private boolean checkPseudoHeader(HttpHeader header, Object value) + { + if (_fields.size()>0) + { + streamException("Psuedo header %s after fields", header.asString()); + return false; + } + if (value==null) + return true; + streamException("Duplicate psuedo header %s", header.asString()); + return false; + } + + + public MetaData build() throws HpackException.Stream + { + if (_streamException!=null) + throw _streamException; + + if (_request && _response) + throw new HpackException.Stream("Request and Response headers"); + try { HttpFields fields = _fields; @@ -189,13 +268,14 @@ public class MetaDataBuilder * Check that the max size will not be exceeded. * @param length the length * @param huffman the huffman name + * @throws Session */ - public void checkSize(int length, boolean huffman) + public void checkSize(int length, boolean huffman) throws Session { // Apply a huffman fudge factor if (huffman) length=(length*4)/3; if ((_size+length)>_maxSize) - throw new BadMessageException(HttpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE_431,"Header size "+(_size+length)+">"+_maxSize); + throw new HpackException.Session("Header too large %d > %d", _size+length, _maxSize); } } diff --git a/tests/test-webapps/test-proxy-webapp/src/main/webapp/META-INF/MANIFEST.MF b/tests/test-webapps/test-proxy-webapp/src/main/webapp/META-INF/MANIFEST.MF deleted file mode 100644 index 886d243c096..00000000000 --- a/tests/test-webapps/test-proxy-webapp/src/main/webapp/META-INF/MANIFEST.MF +++ /dev/null @@ -1,21 +0,0 @@ -Manifest-Version: 1.0 -Bundle-ManifestVersion: 2 -Bundle-Name: TestIt -Bundle-SymbolicName: TestIt -Bundle-Version: 1.0.0.qualifier -Bundle-Activator: testit.Activator -Import-Package: javax.servlet;version="2.6", - javax.servlet.http;version="2.6", - javax.servlet.jsp, - javax.servlet.jsp.tagext -Require-Bundle: org.eclipse.jetty.client, - org.eclipse.jetty.proxy, - org.eclipse.jetty.http, - org.eclipse.jetty.io, - org.eclipse.jetty.util -Bundle-ClassPath: WEB-INF/classes -Bundle-RequiredExecutionEnvironment: JavaSE-1.7 -Bundle-ActivationPolicy: lazy -Web-ContextPath: / -Class-Path: - diff --git a/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/webapp/stylesheet.css~ b/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/webapp/stylesheet.css~ deleted file mode 100644 index def6847d14c..00000000000 --- a/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/webapp/stylesheet.css~ +++ /dev/null @@ -1,7 +0,0 @@ -body {color: #2E2E2E; font-family:sans-serif; font-size:90%;} -h1 {font-variant: small-caps; font-size:130%; letter-spacing: 0.1em;} -h2 {font-variant: small-caps; font-size:100%; letter-spacing: 0.1em; margin-top:2em} -h3 {font-size:100%; letter-spacing: 0.1em;} - -span.pass { color: green; } -span.fail { color:red; } diff --git a/tests/test-webapps/test-webapp-rfc2616/src/main/webapp/META-INF/MANIFEST.MF b/tests/test-webapps/test-webapp-rfc2616/src/main/webapp/META-INF/MANIFEST.MF deleted file mode 100644 index 5e9495128c0..00000000000 --- a/tests/test-webapps/test-webapp-rfc2616/src/main/webapp/META-INF/MANIFEST.MF +++ /dev/null @@ -1,3 +0,0 @@ -Manifest-Version: 1.0 -Class-Path: - From 10ec53319ab4c2b10471eb142365b6a02423f626 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Sun, 8 Jul 2018 14:56:42 +0000 Subject: [PATCH 02/10] WIP Signed-off-by: Greg Wilkins --- .../jetty/http2/hpack/MetaDataBuilder.java | 2 - .../jetty/http2/hpack/HpackDecoderTest.java | 46 +++++++++---------- .../eclipse/jetty/http2/hpack/HpackTest.java | 6 +-- 3 files changed, 24 insertions(+), 30 deletions(-) diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/MetaDataBuilder.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/MetaDataBuilder.java index c0f5266473e..7a5d7e158e3 100644 --- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/MetaDataBuilder.java +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/MetaDataBuilder.java @@ -20,13 +20,11 @@ package org.eclipse.jetty.http2.hpack; -import org.eclipse.jetty.http.BadMessageException; import org.eclipse.jetty.http.HostPortHttpField; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpScheme; -import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.http.MetaData; import org.eclipse.jetty.http2.hpack.HpackException.Session; diff --git a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackDecoderTest.java b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackDecoderTest.java index d6cb341aca9..1b3acc7338f 100644 --- a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackDecoderTest.java +++ b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackDecoderTest.java @@ -19,24 +19,6 @@ package org.eclipse.jetty.http2.hpack; -import java.nio.ByteBuffer; -import java.util.Iterator; - -import org.eclipse.jetty.http.BadMessageException; -import org.eclipse.jetty.http.HttpField; -import org.eclipse.jetty.http.HttpFields; -import org.eclipse.jetty.http.HttpHeader; -import org.eclipse.jetty.http.HttpScheme; -import org.eclipse.jetty.http.HttpStatus; -import org.eclipse.jetty.http.HttpVersion; -import org.eclipse.jetty.http.MetaData; -import org.eclipse.jetty.util.BufferUtil; -import org.eclipse.jetty.util.TypeUtil; -import org.eclipse.jetty.util.log.Log; -import org.hamcrest.Matchers; -import org.junit.Assert; -import org.junit.Test; - import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertEquals; @@ -44,10 +26,24 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; +import java.nio.ByteBuffer; +import java.util.Iterator; + +import org.eclipse.jetty.http.BadMessageException; +import org.eclipse.jetty.http.HttpField; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpScheme; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.http.MetaData; +import org.eclipse.jetty.util.TypeUtil; +import org.hamcrest.Matchers; +import org.junit.Assert; +import org.junit.Test; + public class HpackDecoderTest { @Test - public void testDecodeD_3() + public void testDecodeD_3() throws Exception { HpackDecoder decoder = new HpackDecoder(4096,8192); @@ -95,7 +91,7 @@ public class HpackDecoderTest } @Test - public void testDecodeD_4() + public void testDecodeD_4() throws Exception { HpackDecoder decoder = new HpackDecoder(4096,8192); @@ -128,7 +124,7 @@ public class HpackDecoderTest } @Test - public void testDecodeWithArrayOffset() + public void testDecodeWithArrayOffset() throws Exception { String value = "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="; @@ -152,7 +148,7 @@ public class HpackDecoderTest } @Test - public void testDecodeHuffmanWithArrayOffset() + public void testDecodeHuffmanWithArrayOffset() throws Exception { HpackDecoder decoder = new HpackDecoder(4096,8192); @@ -172,7 +168,7 @@ public class HpackDecoderTest } @Test - public void testNghttpx() + public void testNghttpx() throws Exception { // Response encoded by nghttpx String encoded="886196C361Be940b6a65B6850400B8A00571972e080a62D1Bf5f87497cA589D34d1f9a0f0d0234327690Aa69D29aFcA954D3A5358980Ae112e0f7c880aE152A9A74a6bF3"; @@ -204,7 +200,7 @@ public class HpackDecoderTest } @Test - public void testTooBigToIndex() + public void testTooBigToIndex() throws Exception { String encoded = "44FfEc02Df3990A190A0D4Ee5b3d2940Ec98Aa4a62D127D29e273a0aA20dEcAa190a503b262d8a2671D4A2672a927aA874988a2471D05510750c951139EdA2452a3a548cAa1aA90bE4B228342864A9E0D450A5474a92992a1aA513395448E3A0Aa17B96cFe3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f14E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F353F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F54f"; ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded)); @@ -217,7 +213,7 @@ public class HpackDecoderTest } @Test - public void testUnknownIndex() + public void testUnknownIndex() throws Exception { String encoded = "BE"; ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded)); diff --git a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackTest.java b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackTest.java index 31c1ee21d8f..9c7d2a24eb5 100644 --- a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackTest.java +++ b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackTest.java @@ -46,7 +46,7 @@ public class HpackTest final static HttpField Date = new PreEncodedHttpField(HttpHeader.DATE,DateGenerator.formatDate(TimeUnit.NANOSECONDS.toMillis(System.nanoTime()))); @Test - public void encodeDecodeResponseTest() + public void encodeDecodeResponseTest() throws Exception { HpackEncoder encoder = new HpackEncoder(); HpackDecoder decoder = new HpackDecoder(4096,8192); @@ -99,7 +99,7 @@ public class HpackTest } @Test - public void encodeDecodeTooLargeTest() + public void encodeDecodeTooLargeTest() throws Exception { HpackEncoder encoder = new HpackEncoder(); HpackDecoder decoder = new HpackDecoder(4096,164); @@ -138,7 +138,7 @@ public class HpackTest } @Test - public void evictReferencedFieldTest() + public void evictReferencedFieldTest() throws Exception { HpackEncoder encoder = new HpackEncoder(200,200); HpackDecoder decoder = new HpackDecoder(200,1024); From 9fd80e8524d62ec2ebf8f8235b50fbf49e571752 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Sun, 8 Jul 2018 15:17:39 +0000 Subject: [PATCH 03/10] WIP Signed-off-by: Greg Wilkins --- .../jetty/http2/hpack/HpackException.java | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackException.java diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackException.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackException.java new file mode 100644 index 00000000000..6eebf27c92b --- /dev/null +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackException.java @@ -0,0 +1,54 @@ +// +// ======================================================================== +// Copyright (c) 1995-2018 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.http2.hpack; + +import java.io.IOException; + +public abstract class HpackException extends IOException +{ + HpackException(String messageFormat, Object... args) + { + super(String.format(messageFormat, args)); + } + + public static class Stream extends HpackException + { + Stream(String messageFormat, Object... args) + { + super(messageFormat,args); + } + } + + + public static class Session extends HpackException + { + Session(String messageFormat, Object... args) + { + super(messageFormat,args); + } + } + + public static class Compression extends Session + { + public Compression(String messageFormat, Object... args) + { + super(messageFormat,args); + } + } +} From cb6c582333fbb7a3645033f45325f92a6add56e2 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Sun, 8 Jul 2018 15:24:55 +0000 Subject: [PATCH 04/10] WIP Signed-off-by: Greg Wilkins --- jetty-home/src/main/resources/bin/jetty.sh | 618 ++++++++++++++++++ .../src/main/webapp/META-INF/MANIFEST.MF | 3 + 2 files changed, 621 insertions(+) create mode 100755 jetty-home/src/main/resources/bin/jetty.sh create mode 100644 tests/test-webapps/test-webapp-rfc2616/src/main/webapp/META-INF/MANIFEST.MF diff --git a/jetty-home/src/main/resources/bin/jetty.sh b/jetty-home/src/main/resources/bin/jetty.sh new file mode 100755 index 00000000000..b2b69a3d75c --- /dev/null +++ b/jetty-home/src/main/resources/bin/jetty.sh @@ -0,0 +1,618 @@ +#!/usr/bin/env bash + +# LSB Tags +### BEGIN INIT INFO +# Provides: jetty +# Required-Start: $local_fs $network +# Required-Stop: $local_fs $network +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Jetty start script. +# Description: Start Jetty web server. +### END INIT INFO + +# Startup script for jetty under *nix systems (it works under NT/cygwin too). + +################################################## +# Set the name which is used by other variables. +# Defaults to the file name without extension. +################################################## +NAME=$(echo $(basename $0) | sed -e 's/^[SK][0-9]*//' -e 's/\.sh$//') + +# To get the service to restart correctly on reboot, uncomment below (3 lines): +# ======================== +# chkconfig: 3 99 99 +# description: Jetty 9 webserver +# processname: jetty +# ======================== + +# Configuration files +# +# /etc/default/$NAME +# If it exists, this is read at the start of script. It may perform any +# sequence of shell commands, like setting relevant environment variables. +# +# $HOME/.$NAMErc (e.g. $HOME/.jettyrc) +# If it exists, this is read at the start of script. It may perform any +# sequence of shell commands, like setting relevant environment variables. +# +# /etc/$NAME.conf +# If found, and no configurations were given on the command line, +# the file will be used as this script's configuration. +# Each line in the file may contain: +# - A comment denoted by the pound (#) sign as first non-blank character. +# - The path to a regular file, which will be passed to jetty as a +# config.xml file. +# - The path to a directory. Each *.xml file in the directory will be +# passed to jetty as a config.xml file. +# - All other lines will be passed, as-is to the start.jar +# +# The files will be checked for existence before being passed to jetty. +# +# Configuration variables +# +# JAVA +# Command to invoke Java. If not set, java (from the PATH) will be used. +# +# JAVA_OPTIONS +# Extra options to pass to the JVM +# +# JETTY_HOME +# Where Jetty is installed. If not set, the script will try go +# guess it by looking at the invocation path for the script +# The java system property "jetty.home" will be +# set to this value for use by configure.xml files, f.e.: +# +# /webapps/jetty.war +# +# JETTY_BASE +# Where your Jetty base directory is. If not set, the value from +# $JETTY_HOME will be used. +# +# JETTY_RUN +# Where the $NAME.pid file should be stored. It defaults to the +# first available of /var/run, /usr/var/run, JETTY_BASE and /tmp +# if not set. +# +# JETTY_PID +# The Jetty PID file, defaults to $JETTY_RUN/$NAME.pid +# +# JETTY_ARGS +# The default arguments to pass to jetty. +# For example +# JETTY_ARGS=jetty.http.port=8080 jetty.ssl.port=8443 +# +# JETTY_USER +# if set, then used as a username to run the server as +# +# JETTY_SHELL +# If set, then used as the shell by su when starting the server. Will have +# no effect if start-stop-daemon exists. Useful when JETTY_USER does not +# have shell access, e.g. /bin/false +# +# JETTY_START_TIMEOUT +# Time spent waiting to see if startup was successful/failed. Defaults to 60 seconds +# + +usage() +{ + echo "Usage: ${0##*/} [-d] {start|stop|run|restart|check|supervise} [ CONFIGS ... ] " + exit 1 +} + +[ $# -gt 0 ] || usage + + +################################################## +# Some utility functions +################################################## +findDirectory() +{ + local L OP=$1 + shift + for L in "$@"; do + [ "$OP" "$L" ] || continue + printf %s "$L" + break + done +} + +running() +{ + if [ -f "$1" ] + then + local PID=$(cat "$1" 2>/dev/null) || return 1 + kill -0 "$PID" 2>/dev/null + return + fi + rm -f "$1" + return 1 +} + +started() +{ + # wait for 60s to see "STARTED" in PID file, needs jetty-started.xml as argument + for ((T = 0; T < $(($3 / 4)); T++)) + do + sleep 4 + [ -z "$(grep STARTED $1 2>/dev/null)" ] || return 0 + [ -z "$(grep STOPPED $1 2>/dev/null)" ] || return 1 + [ -z "$(grep FAILED $1 2>/dev/null)" ] || return 1 + local PID=$(cat "$2" 2>/dev/null) || return 1 + kill -0 "$PID" 2>/dev/null || return 1 + echo -n ". " + done + + return 1; +} + + +readConfig() +{ + (( DEBUG )) && echo "Reading $1.." + source "$1" +} + +dumpEnv() +{ + echo "JAVA = $JAVA" + echo "JAVA_OPTIONS = ${JAVA_OPTIONS[*]}" + echo "JETTY_HOME = $JETTY_HOME" + echo "JETTY_BASE = $JETTY_BASE" + echo "START_D = $START_D" + echo "START_INI = $START_INI" + echo "JETTY_START = $JETTY_START" + echo "JETTY_CONF = $JETTY_CONF" + echo "JETTY_ARGS = ${JETTY_ARGS[*]}" + echo "JETTY_RUN = $JETTY_RUN" + echo "JETTY_PID = $JETTY_PID" + echo "JETTY_START_LOG = $JETTY_START_LOG" + echo "JETTY_STATE = $JETTY_STATE" + echo "JETTY_START_TIMEOUT = $JETTY_START_TIMEOUT" + echo "RUN_CMD = ${RUN_CMD[*]}" +} + + + +################################################## +# Get the action & configs +################################################## +CONFIGS=() +NO_START=0 +DEBUG=0 + +while [[ $1 = -* ]]; do + case $1 in + -d) DEBUG=1 ;; + esac + shift +done +ACTION=$1 +shift + +################################################## +# Read any configuration files +################################################## +ETC=/etc +if [ $UID != 0 ] +then + ETC=$HOME/etc +fi + +for CONFIG in {/etc,~/etc}/default/${NAME}{,9} $HOME/.${NAME}rc; do + if [ -f "$CONFIG" ] ; then + readConfig "$CONFIG" + fi +done + + +################################################## +# Set tmp if not already set. +################################################## +TMPDIR=${TMPDIR:-/tmp} + +################################################## +# Jetty's hallmark +################################################## +JETTY_INSTALL_TRACE_FILE="start.jar" + + +################################################## +# Try to determine JETTY_HOME if not set +################################################## +if [ -z "$JETTY_HOME" ] +then + JETTY_SH=$0 + case "$JETTY_SH" in + /*) JETTY_HOME=${JETTY_SH%/*/*} ;; + ./*/*) JETTY_HOME=${JETTY_SH%/*/*} ;; + ./*) JETTY_HOME=.. ;; + */*/*) JETTY_HOME=./${JETTY_SH%/*/*} ;; + */*) JETTY_HOME=. ;; + *) JETTY_HOME=.. ;; + esac + + if [ ! -f "$JETTY_HOME/$JETTY_INSTALL_TRACE_FILE" ] + then + JETTY_HOME= + fi +fi + + +################################################## +# No JETTY_HOME yet? We're out of luck! +################################################## +if [ -z "$JETTY_HOME" ]; then + echo "** ERROR: JETTY_HOME not set, you need to set it or install in a standard location" + exit 1 +fi + +cd "$JETTY_HOME" +JETTY_HOME=$PWD + + +################################################## +# Set JETTY_BASE +################################################## +if [ -z "$JETTY_BASE" ]; then + JETTY_BASE=$JETTY_HOME +fi + +cd "$JETTY_BASE" +JETTY_BASE=$PWD + + +##################################################### +# Check that jetty is where we think it is +##################################################### +if [ ! -r "$JETTY_HOME/$JETTY_INSTALL_TRACE_FILE" ] +then + echo "** ERROR: Oops! Jetty doesn't appear to be installed in $JETTY_HOME" + echo "** ERROR: $JETTY_HOME/$JETTY_INSTALL_TRACE_FILE is not readable!" + exit 1 +fi + +################################################## +# Try to find this script's configuration file, +# but only if no configurations were given on the +# command line. +################################################## +if [ -z "$JETTY_CONF" ] +then + if [ -f $ETC/${NAME}.conf ] + then + JETTY_CONF=$ETC/${NAME}.conf + elif [ -f "$JETTY_BASE/etc/jetty.conf" ] + then + JETTY_CONF=$JETTY_BASE/etc/jetty.conf + elif [ -f "$JETTY_HOME/etc/jetty.conf" ] + then + JETTY_CONF=$JETTY_HOME/etc/jetty.conf + fi +fi + +##################################################### +# Find a location for the pid file +##################################################### +if [ -z "$JETTY_RUN" ] +then + JETTY_RUN=$(findDirectory -w /var/run /usr/var/run $JETTY_BASE /tmp)/jetty + [ -d "$JETTY_RUN" ] || mkdir $JETTY_RUN +fi + +##################################################### +# define start log location +##################################################### +if [ -z "$JETTY_START_LOG" ] +then + JETTY_START_LOG="$JETTY_RUN/$NAME-start.log" +fi + +##################################################### +# Find a pid and state file +##################################################### +if [ -z "$JETTY_PID" ] +then + JETTY_PID="$JETTY_RUN/${NAME}.pid" +fi + +if [ -z "$JETTY_STATE" ] +then + JETTY_STATE=$JETTY_BASE/${NAME}.state +fi + +case "`uname`" in +CYGWIN*) JETTY_STATE="`cygpath -w $JETTY_STATE`";; +esac + + +JETTY_ARGS=(${JETTY_ARGS[*]} "jetty.state=$JETTY_STATE") + +################################################## +# Get the list of config.xml files from jetty.conf +################################################## +if [ -f "$JETTY_CONF" ] && [ -r "$JETTY_CONF" ] +then + while read -r CONF + do + if expr "$CONF" : '#' >/dev/null ; then + continue + fi + + if [ -d "$CONF" ] + then + # assume it's a directory with configure.xml files + # for example: /etc/jetty.d/ + # sort the files before adding them to the list of JETTY_ARGS + for XMLFILE in "$CONF/"*.xml + do + if [ -r "$XMLFILE" ] && [ -f "$XMLFILE" ] + then + JETTY_ARGS=(${JETTY_ARGS[*]} "$XMLFILE") + else + echo "** WARNING: Cannot read '$XMLFILE' specified in '$JETTY_CONF'" + fi + done + else + # assume it's a command line parameter (let start.jar deal with its validity) + JETTY_ARGS=(${JETTY_ARGS[*]} "$CONF") + fi + done < "$JETTY_CONF" +fi + +################################################## +# Setup JAVA if unset +################################################## +if [ -z "$JAVA" ] +then + JAVA=$(which java) +fi + +if [ -z "$JAVA" ] +then + echo "Cannot find a Java JDK. Please set either set JAVA or put java (>=1.5) in your PATH." >&2 + exit 1 +fi + +##################################################### +# See if Deprecated JETTY_LOGS is defined +##################################################### +if [ "$JETTY_LOGS" ] +then + echo "** WARNING: JETTY_LOGS is Deprecated. Please configure logging within the jetty base." >&2 +fi + +##################################################### +# Set STARTED timeout +##################################################### +if [ -z "$JETTY_START_TIMEOUT"] +then + JETTY_START_TIMEOUT=60 +fi + +##################################################### +# Are we running on Windows? Could be, with Cygwin/NT. +##################################################### +case "`uname`" in +CYGWIN*) PATH_SEPARATOR=";";; +*) PATH_SEPARATOR=":";; +esac + + +##################################################### +# Add jetty properties to Java VM options. +##################################################### + +case "`uname`" in +CYGWIN*) +JETTY_HOME="`cygpath -w $JETTY_HOME`" +JETTY_BASE="`cygpath -w $JETTY_BASE`" +TMPDIR="`cygpath -w $TMPDIR`" +;; +esac + +JAVA_OPTIONS=(${JAVA_OPTIONS[*]} "-Djetty.home=$JETTY_HOME" "-Djetty.base=$JETTY_BASE" "-Djava.io.tmpdir=$TMPDIR") + +##################################################### +# This is how the Jetty server will be started +##################################################### + +JETTY_START=$JETTY_HOME/start.jar +START_INI=$JETTY_BASE/start.ini +START_D=$JETTY_BASE/start.d +if [ ! -f "$START_INI" -a ! -d "$START_D" ] +then + echo "Cannot find a start.ini file or a start.d directory in your JETTY_BASE directory: $JETTY_BASE" >&2 + exit 1 +fi + +case "`uname`" in +CYGWIN*) JETTY_START="`cygpath -w $JETTY_START`";; +esac + +RUN_ARGS=(${JAVA_OPTIONS[@]} -jar "$JETTY_START" ${JETTY_ARGS[*]}) +RUN_CMD=("$JAVA" ${RUN_ARGS[@]}) + +##################################################### +# Comment these out after you're happy with what +# the script is doing. +##################################################### +if (( DEBUG )) +then + dumpEnv +fi + +################################################## +# Do the action +################################################## +case "$ACTION" in + start) + echo -n "Starting Jetty: " + + if (( NO_START )); then + echo "Not starting ${NAME} - NO_START=1"; + exit + fi + + if [ $UID -eq 0 ] && type start-stop-daemon > /dev/null 2>&1 + then + unset CH_USER + if [ -n "$JETTY_USER" ] + then + CH_USER="-c$JETTY_USER" + fi + + start-stop-daemon -S -p"$JETTY_PID" $CH_USER -d"$JETTY_BASE" -b -m -a "$JAVA" -- "${RUN_ARGS[@]}" start-log-file="$JETTY_START_LOG" + + else + + if running $JETTY_PID + then + echo "Already Running $(cat $JETTY_PID)!" + exit 1 + fi + + if [ -n "$JETTY_USER" ] && [ `whoami` != "$JETTY_USER" ] + then + unset SU_SHELL + if [ "$JETTY_SHELL" ] + then + SU_SHELL="-s $JETTY_SHELL" + fi + + touch "$JETTY_PID" + chown "$JETTY_USER" "$JETTY_PID" + # FIXME: Broken solution: wordsplitting, pathname expansion, arbitrary command execution, etc. + su - "$JETTY_USER" $SU_SHELL -c " + cd \"$JETTY_BASE\" + exec ${RUN_CMD[*]} start-log-file=\"$JETTY_START_LOG\" > /dev/null & + disown \$! + echo \$! > \"$JETTY_PID\"" + else + "${RUN_CMD[@]}" > /dev/null & + disown $! + echo $! > "$JETTY_PID" + fi + + fi + + if expr "${JETTY_ARGS[*]}" : '.*jetty-started.xml.*' >/dev/null + then + if started "$JETTY_STATE" "$JETTY_PID" "$JETTY_START_TIMEOUT" + then + echo "OK `date`" + else + echo "FAILED `date`" + exit 1 + fi + else + echo "ok `date`" + fi + + ;; + + stop) + echo -n "Stopping Jetty: " + if [ $UID -eq 0 ] && type start-stop-daemon > /dev/null 2>&1; then + start-stop-daemon -K -p"$JETTY_PID" -d"$JETTY_HOME" -a "$JAVA" -s HUP + + TIMEOUT=30 + while running "$JETTY_PID"; do + if (( TIMEOUT-- == 0 )); then + start-stop-daemon -K -p"$JETTY_PID" -d"$JETTY_HOME" -a "$JAVA" -s KILL + fi + + sleep 1 + done + else + if [ ! -f "$JETTY_PID" ] ; then + echo "ERROR: no pid found at $JETTY_PID" + exit 1 + fi + + PID=$(cat "$JETTY_PID" 2>/dev/null) + if [ -z "$PID" ] ; then + echo "ERROR: no pid id found in $JETTY_PID" + exit 1 + fi + kill "$PID" 2>/dev/null + + TIMEOUT=30 + while running $JETTY_PID; do + if (( TIMEOUT-- == 0 )); then + kill -KILL "$PID" 2>/dev/null + fi + + sleep 1 + done + fi + + rm -f "$JETTY_PID" + rm -f "$JETTY_STATE" + echo OK + + ;; + + restart) + JETTY_SH=$0 + > "$JETTY_STATE" + if [ ! -f $JETTY_SH ]; then + if [ ! -f $JETTY_HOME/bin/jetty.sh ]; then + echo "$JETTY_HOME/bin/jetty.sh does not exist." + exit 1 + fi + JETTY_SH=$JETTY_HOME/bin/jetty.sh + fi + + "$JETTY_SH" stop "$@" + "$JETTY_SH" start "$@" + + ;; + + supervise) + # + # Under control of daemontools supervise monitor which + # handles restarts and shutdowns via the svc program. + # + exec "${RUN_CMD[@]}" + + ;; + + run|demo) + echo "Running Jetty: " + + if running "$JETTY_PID" + then + echo Already Running $(cat "$JETTY_PID")! + exit 1 + fi + + exec "${RUN_CMD[@]}" + ;; + + check|status) + if running "$JETTY_PID" + then + echo "Jetty running pid=$(< "$JETTY_PID")" + else + echo "Jetty NOT running" + fi + echo + dumpEnv + echo + + if running "$JETTY_PID" + then + exit 0 + fi + exit 1 + + ;; + + *) + usage + + ;; +esac + +exit 0 diff --git a/tests/test-webapps/test-webapp-rfc2616/src/main/webapp/META-INF/MANIFEST.MF b/tests/test-webapps/test-webapp-rfc2616/src/main/webapp/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..5e9495128c0 --- /dev/null +++ b/tests/test-webapps/test-webapp-rfc2616/src/main/webapp/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Class-Path: + From 09eb303d2bf8ea3d81e84b86db6d633097a9f26a Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Sun, 8 Jul 2018 15:28:05 +0000 Subject: [PATCH 05/10] WIP Signed-off-by: Greg Wilkins --- .../src/main/webapp/META-INF/MANIFEST.MF | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 tests/test-webapps/test-proxy-webapp/src/main/webapp/META-INF/MANIFEST.MF diff --git a/tests/test-webapps/test-proxy-webapp/src/main/webapp/META-INF/MANIFEST.MF b/tests/test-webapps/test-proxy-webapp/src/main/webapp/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..886d243c096 --- /dev/null +++ b/tests/test-webapps/test-proxy-webapp/src/main/webapp/META-INF/MANIFEST.MF @@ -0,0 +1,21 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: TestIt +Bundle-SymbolicName: TestIt +Bundle-Version: 1.0.0.qualifier +Bundle-Activator: testit.Activator +Import-Package: javax.servlet;version="2.6", + javax.servlet.http;version="2.6", + javax.servlet.jsp, + javax.servlet.jsp.tagext +Require-Bundle: org.eclipse.jetty.client, + org.eclipse.jetty.proxy, + org.eclipse.jetty.http, + org.eclipse.jetty.io, + org.eclipse.jetty.util +Bundle-ClassPath: WEB-INF/classes +Bundle-RequiredExecutionEnvironment: JavaSE-1.7 +Bundle-ActivationPolicy: lazy +Web-ContextPath: / +Class-Path: + From 44801d8ff3130250e784126086c7f13bd177d9e9 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Sun, 8 Jul 2018 18:08:36 +0000 Subject: [PATCH 06/10] WIP Signed-off-by: Greg Wilkins --- .../org/eclipse/jetty/http2/hpack/HpackDecoder.java | 12 +++++------- .../eclipse/jetty/http2/hpack/HpackException.java | 2 +- .../eclipse/jetty/http2/hpack/MetaDataBuilder.java | 2 +- .../eclipse/jetty/http2/hpack/HpackDecoderTest.java | 5 ++--- 4 files changed, 9 insertions(+), 12 deletions(-) diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackDecoder.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackDecoder.java index e730718c643..0f6ff926361 100644 --- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackDecoder.java +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackDecoder.java @@ -21,7 +21,6 @@ package org.eclipse.jetty.http2.hpack; import java.nio.ByteBuffer; -import org.eclipse.jetty.http.BadMessageException; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpStatus; @@ -75,7 +74,6 @@ public class HpackDecoder // If the buffer is big, don't even think about decoding it if (buffer.remaining()>_builder.getMaxSize()) throw new HpackException.Session("431 Request Header Fields too large",6); - //throw new BadMessageException(HttpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE_431,"Header frame size "+buffer.remaining()+">"+_builder.getMaxSize()); while(buffer.hasRemaining()) { @@ -94,10 +92,9 @@ public class HpackDecoder int index = NBitInteger.decode(buffer,7); Entry entry=_context.get(index); if (entry==null) - { - throw new BadMessageException(HttpStatus.BAD_REQUEST_400, "Unknown index "+index); - } - else if (entry.isStatic()) + throw new HpackException.Session("Unknown index %d",index); + + if (entry.isStatic()) { if (LOG.isDebugEnabled()) LOG.debug("decode IdxStatic {}",entry); @@ -180,7 +177,8 @@ public class HpackDecoder char c=name.charAt(i); if (c>='A'&&c<='Z') { - throw new BadMessageException(400,"Uppercase header name"); + _builder.streamException("Uppercase header name %s",name); + break; } } header=HttpHeader.CACHE.get(name); diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackException.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackException.java index 6eebf27c92b..6e3655c2b70 100644 --- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackException.java +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackException.java @@ -20,7 +20,7 @@ package org.eclipse.jetty.http2.hpack; import java.io.IOException; -public abstract class HpackException extends IOException +public abstract class HpackException extends RuntimeException { HpackException(String messageFormat, Object... args) { diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/MetaDataBuilder.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/MetaDataBuilder.java index 7a5d7e158e3..dbfb698beba 100644 --- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/MetaDataBuilder.java +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/MetaDataBuilder.java @@ -190,7 +190,7 @@ public class MetaDataBuilder } } - private void streamException(String messageFormat, Object... args) + void streamException(String messageFormat, Object... args) { HpackException.Stream stream = new HpackException.Stream(messageFormat, args); if (_streamException==null) diff --git a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackDecoderTest.java b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackDecoderTest.java index 1b3acc7338f..1fb9d4ced9c 100644 --- a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackDecoderTest.java +++ b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackDecoderTest.java @@ -224,10 +224,9 @@ public class HpackDecoderTest decoder.decode(buffer); Assert.fail(); } - catch (BadMessageException e) + catch (HpackException.Session e) { - assertThat(e.getCode(),equalTo(HttpStatus.BAD_REQUEST_400)); - assertThat(e.getReason(),Matchers.startsWith("Unknown index")); + assertThat(e.getMessage(),Matchers.startsWith("Unknown index")); } } From a9819ebb01f89a8d62b425152a72e8210bf6e8ae Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Thu, 12 Jul 2018 11:50:53 +0200 Subject: [PATCH 07/10] review fixes Signed-off-by: Greg Wilkins --- .../jetty/http2/hpack/HpackDecoder.java | 4 +-- .../jetty/http2/hpack/HpackException.java | 17 ++++++----- .../jetty/http2/hpack/MetaDataBuilder.java | 28 +++++++++---------- .../jetty/http2/hpack/HpackDecoderTest.java | 2 +- 4 files changed, 25 insertions(+), 26 deletions(-) diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackDecoder.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackDecoder.java index 0f6ff926361..5526eb44772 100644 --- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackDecoder.java +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackDecoder.java @@ -73,7 +73,7 @@ public class HpackDecoder // If the buffer is big, don't even think about decoding it if (buffer.remaining()>_builder.getMaxSize()) - throw new HpackException.Session("431 Request Header Fields too large",6); + throw new HpackException.SessionException("431 Request Header Fields too large"); while(buffer.hasRemaining()) { @@ -92,7 +92,7 @@ public class HpackDecoder int index = NBitInteger.decode(buffer,7); Entry entry=_context.get(index); if (entry==null) - throw new HpackException.Session("Unknown index %d",index); + throw new HpackException.SessionException("Unknown index %d",index); if (entry.isStatic()) { diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackException.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackException.java index 6e3655c2b70..287e02f2e13 100644 --- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackException.java +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackException.java @@ -27,26 +27,25 @@ public abstract class HpackException extends RuntimeException super(String.format(messageFormat, args)); } - public static class Stream extends HpackException + public static class StreamException extends HpackException { - Stream(String messageFormat, Object... args) + StreamException(String messageFormat, Object... args) { super(messageFormat,args); } } - - - public static class Session extends HpackException + + public static class SessionException extends HpackException { - Session(String messageFormat, Object... args) + SessionException(String messageFormat, Object... args) { super(messageFormat,args); } } - - public static class Compression extends Session + + public static class CompressionException extends SessionException { - public Compression(String messageFormat, Object... args) + public CompressionException(String messageFormat, Object... args) { super(messageFormat,args); } diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/MetaDataBuilder.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/MetaDataBuilder.java index dbfb698beba..de15f936716 100644 --- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/MetaDataBuilder.java +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/MetaDataBuilder.java @@ -27,7 +27,7 @@ import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.http.MetaData; -import org.eclipse.jetty.http2.hpack.HpackException.Session; +import org.eclipse.jetty.http2.hpack.HpackException.SessionException; public class MetaDataBuilder { @@ -40,7 +40,7 @@ public class MetaDataBuilder private String _path; private long _contentLength=Long.MIN_VALUE; private HttpFields _fields = new HttpFields(10); - private HpackException.Stream _streamException; + private HpackException.StreamException _streamException; private boolean _request; private boolean _response; @@ -68,7 +68,7 @@ public class MetaDataBuilder return _size; } - public void emit(HttpField field) throws HpackException.Session + public void emit(HttpField field) throws HpackException.SessionException { HttpHeader header = field.getHeader(); String name = field.getName(); @@ -76,7 +76,7 @@ public class MetaDataBuilder int field_size = name.length() + (value == null ? 0 : value.length()); _size+=field_size+32; if (_size>_maxSize) - throw new HpackException.Session("Header Size %d > %d",_size,_maxSize); + throw new HpackException.SessionException("Header Size %d > %d",_size,_maxSize); if (field instanceof StaticTableHttpField) { @@ -162,10 +162,10 @@ public class MetaDataBuilder break; case TE: - if ("trailors".equalsIgnoreCase(value)) + if ("trailers".equalsIgnoreCase(value)) _fields.add(field); else - streamException("unsupported TE value %s", value); + streamException("Unsupported TE value %s", value); break; case CONNECTION: @@ -175,7 +175,7 @@ public class MetaDataBuilder default: if (name.charAt(0)==':') - streamException("Unknown psuodo header %s", name); + streamException("Unknown pseudo header %s", name); else _fields.add(field); break; @@ -184,7 +184,7 @@ public class MetaDataBuilder else { if (name.charAt(0)==':') - streamException("Unknown psuedo header %s",name); + streamException("Unknown pseudo header %s",name); else _fields.add(field); } @@ -192,7 +192,7 @@ public class MetaDataBuilder void streamException(String messageFormat, Object... args) { - HpackException.Stream stream = new HpackException.Stream(messageFormat, args); + HpackException.StreamException stream = new HpackException.StreamException(messageFormat, args); if (_streamException==null) _streamException = stream; else @@ -226,13 +226,13 @@ public class MetaDataBuilder } - public MetaData build() throws HpackException.Stream + public MetaData build() throws HpackException.StreamException { if (_streamException!=null) throw _streamException; if (_request && _response) - throw new HpackException.Stream("Request and Response headers"); + throw new HpackException.StreamException("Request and Response headers"); try { @@ -266,14 +266,14 @@ public class MetaDataBuilder * Check that the max size will not be exceeded. * @param length the length * @param huffman the huffman name - * @throws Session + * @throws SessionException */ - public void checkSize(int length, boolean huffman) throws Session + public void checkSize(int length, boolean huffman) throws SessionException { // Apply a huffman fudge factor if (huffman) length=(length*4)/3; if ((_size+length)>_maxSize) - throw new HpackException.Session("Header too large %d > %d", _size+length, _maxSize); + throw new HpackException.SessionException("Header too large %d > %d", _size+length, _maxSize); } } diff --git a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackDecoderTest.java b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackDecoderTest.java index 1fb9d4ced9c..6d98a68ed00 100644 --- a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackDecoderTest.java +++ b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackDecoderTest.java @@ -224,7 +224,7 @@ public class HpackDecoderTest decoder.decode(buffer); Assert.fail(); } - catch (HpackException.Session e) + catch (HpackException.SessionException e) { assertThat(e.getMessage(),Matchers.startsWith("Unknown index")); } From 0ad4b4483b986a8d1027eba599a633ea8667d889 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Sun, 15 Jul 2018 19:22:31 +0200 Subject: [PATCH 08/10] WIP Signed-off-by: Greg Wilkins --- .../java/org/eclipse/jetty/http/HttpURI.java | 3 +- .../java/org/eclipse/jetty/http/MetaData.java | 15 +- .../jetty/http2/hpack/MetaDataBuilder.java | 20 +- .../jetty/http2/hpack/HpackDecoderTest.java | 321 +++++++++++++++++- .../test/resources/jetty-logging.properties | 2 +- 5 files changed, 341 insertions(+), 20 deletions(-) diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java index b09accbb18e..af3e12e678e 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java @@ -164,7 +164,8 @@ public class HttpURI _host=host; _port=port; - parse(State.PATH,pathQuery,0,pathQuery.length()); + if (pathQuery!=null) + parse(State.PATH,pathQuery,0,pathQuery.length()); } diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/MetaData.java b/jetty-http/src/main/java/org/eclipse/jetty/http/MetaData.java index 75551f76ede..decab4d73bd 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/MetaData.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/MetaData.java @@ -163,17 +163,26 @@ public class MetaData implements Iterable public Request(String method, HttpScheme scheme, HostPortHttpField hostPort, String uri, HttpVersion version, HttpFields fields) { - this(method, new HttpURI(scheme == null ? null : scheme.asString(), hostPort.getHost(), hostPort.getPort(), uri), version, fields); + this(method, new HttpURI(scheme == null ? null : scheme.asString(), + hostPort==null?null:hostPort.getHost(), + hostPort==null?-1:hostPort.getPort(), + uri), version, fields); } public Request(String method, HttpScheme scheme, HostPortHttpField hostPort, String uri, HttpVersion version, HttpFields fields, long contentLength) { - this(method, new HttpURI(scheme == null ? null : scheme.asString(), hostPort.getHost(), hostPort.getPort(), uri), version, fields, contentLength); + this(method, new HttpURI(scheme==null?null:scheme.asString(), + hostPort==null?null:hostPort.getHost(), + hostPort==null?-1:hostPort.getPort(), + uri), version, fields, contentLength); } public Request(String method, String scheme, HostPortHttpField hostPort, String uri, HttpVersion version, HttpFields fields, long contentLength) { - this(method, new HttpURI(scheme, hostPort.getHost(), hostPort.getPort(), uri), version, fields, contentLength); + this(method, new HttpURI(scheme, + hostPort==null?null:hostPort.getHost(), + hostPort==null?-1:hostPort.getPort(), + uri), version, fields, contentLength); } public Request(Request request) diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/MetaDataBuilder.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/MetaDataBuilder.java index de15f936716..a53b70de97c 100644 --- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/MetaDataBuilder.java +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/MetaDataBuilder.java @@ -203,12 +203,12 @@ public class MetaDataBuilder { if (_fields.size()>0) { - streamException("Psuedo header %s after fields", header.asString()); + streamException("Pseudo header %s after fields", header.asString()); return false; } if (value==-1) return true; - streamException("Duplicate psuedo header %s", header.asString()); + streamException("Duplicate pseudo header %s", header.asString()); return false; } @@ -216,20 +216,22 @@ public class MetaDataBuilder { if (_fields.size()>0) { - streamException("Psuedo header %s after fields", header.asString()); + streamException("Pseudo header %s after fields", header.asString()); return false; } if (value==null) return true; - streamException("Duplicate psuedo header %s", header.asString()); + streamException("Duplicate pseudo header %s", header.asString()); return false; } - public MetaData build() throws HpackException.StreamException { if (_streamException!=null) + { + _streamException.addSuppressed(new Throwable()); throw _streamException; + } if (_request && _response) throw new HpackException.StreamException("Request and Response headers"); @@ -239,14 +241,10 @@ public class MetaDataBuilder HttpFields fields = _fields; _fields = new HttpFields(Math.max(10,fields.size()+5)); - if (_method!=null) + if (_method!=null || _path!=null || _authority!=null || _scheme!=null) return new MetaData.Request(_method,_scheme,_authority,_path,HttpVersion.HTTP_2,fields,_contentLength); - if (_status!=0) + if (_status>0) return new MetaData.Response(HttpVersion.HTTP_2,_status,fields,_contentLength); - if (_path!=null) - fields.put(HttpHeader.C_PATH,_path); - if (_authority!=null) - fields.put(HttpHeader.HOST,_authority.getValue()); return new MetaData(HttpVersion.HTTP_2,fields,_contentLength); } diff --git a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackDecoderTest.java b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackDecoderTest.java index 6d98a68ed00..dd861890d7e 100644 --- a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackDecoderTest.java +++ b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackDecoderTest.java @@ -19,7 +19,6 @@ package org.eclipse.jetty.http2.hpack; -import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -29,12 +28,11 @@ import static org.junit.Assert.assertTrue; import java.nio.ByteBuffer; import java.util.Iterator; -import org.eclipse.jetty.http.BadMessageException; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpScheme; -import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.MetaData; +import org.eclipse.jetty.http2.hpack.HpackException.StreamException; import org.eclipse.jetty.util.TypeUtil; import org.hamcrest.Matchers; import org.junit.Assert; @@ -209,7 +207,7 @@ public class HpackDecoderTest MetaData metaData = decoder.decode(buffer); assertThat(decoder.getHpackContext().getDynamicTableSize(),is(0)); - assertThat(metaData.getFields().get(HttpHeader.C_PATH),Matchers.startsWith("This is a very large field")); + assertThat(((MetaData.Request)metaData).getURI().toString(),Matchers.startsWith("This is a very large field")); } @Test @@ -230,4 +228,319 @@ public class HpackDecoderTest } } + + /* 8.1.2.1. Pseudo-Header Fields */ + @Test() + public void test8_1_2_1_PsuedoHeaderFields() + { + // 1:Sends a HEADERS frame that contains a unknown pseudo-header field + MetaDataBuilder mdb = new MetaDataBuilder(4096); + mdb.emit(new HttpField(":unknown","value")); + try + { + mdb.build(); + Assert.fail(); + } + catch(StreamException ex) + { + Assert.assertThat(ex.getMessage(),Matchers.containsString("Unknown pseudo header")); + } + + // 2: Sends a HEADERS frame that contains the pseudo-header field defined for response + mdb = new MetaDataBuilder(4096); + mdb.emit(new HttpField(HttpHeader.C_SCHEME,"http")); + mdb.emit(new HttpField(HttpHeader.C_METHOD,"GET")); + mdb.emit(new HttpField(HttpHeader.C_PATH,"/path")); + mdb.emit(new HttpField(HttpHeader.C_STATUS,"100")); + try + { + mdb.build(); + Assert.fail(); + } + catch(StreamException ex) + { + Assert.assertThat(ex.getMessage(),Matchers.containsString("Request and Response headers")); + } + + // 3: Sends a HEADERS frame that contains a pseudo-header field as trailers + + // 4: Sends a HEADERS frame that contains a pseudo-header field that appears in a header block after a regular header field + mdb = new MetaDataBuilder(4096); + mdb.emit(new HttpField(HttpHeader.C_SCHEME,"http")); + mdb.emit(new HttpField(HttpHeader.C_METHOD,"GET")); + mdb.emit(new HttpField(HttpHeader.C_PATH,"/path")); + mdb.emit(new HttpField("Accept","No Compromise")); + mdb.emit(new HttpField(HttpHeader.C_AUTHORITY,"localhost")); + try + { + mdb.build(); + Assert.fail(); + } + catch(StreamException ex) + { + Assert.assertThat(ex.getMessage(),Matchers.containsString("Pseudo header :authority after fields")); + } + } + + /* + * + -> The endpoint MUST respond with a stream error of type PROTOCOL_ERROR. + ✔ 3: Sends a HEADERS frame that contains a pseudo-header field as trailers + + × 4: Sends a HEADERS frame that contains a pseudo-header field that appears in a header block after a regular header field + -> The endpoint MUST respond with a stream error of type PROTOCOL_ERROR. + + */ + + + + /* + 8.1.2.2. Connection-Specific Header Fields + [send] SETTINGS Frame (length:6, flags:0x00, stream_id:0) + [recv] SETTINGS Frame (length:24, flags:0x00, stream_id:0) + [send] SETTINGS Frame (length:0, flags:0x01, stream_id:0) + [recv] WINDOW_UPDATE Frame (length:4, flags:0x00, stream_id:0) + [recv] SETTINGS Frame (length:0, flags:0x01, stream_id:0) + [send] HEADERS Frame (length:33, flags:0x05, stream_id:1) + [recv] HEADERS Frame (length:101, flags:0x04, stream_id:1) + [recv] DATA Frame (length:687, flags:0x01, stream_id:1) + [recv] Timeout + × 1: Sends a HEADERS frame that contains the connection-specific header field + -> The endpoint MUST respond with a stream error of type PROTOCOL_ERROR. + Expected: GOAWAY Frame (Error Code: PROTOCOL_ERROR) + RST_STREAM Frame (Error Code: PROTOCOL_ERROR) + Connection closed + Actual: DATA Frame (length:687, flags:0x01, stream_id:1) + [send] SETTINGS Frame (length:6, flags:0x00, stream_id:0) + [recv] SETTINGS Frame (length:24, flags:0x00, stream_id:0) + [send] SETTINGS Frame (length:0, flags:0x01, stream_id:0) + [recv] WINDOW_UPDATE Frame (length:4, flags:0x00, stream_id:0) + [recv] SETTINGS Frame (length:0, flags:0x01, stream_id:0) + [send] HEADERS Frame (length:44, flags:0x05, stream_id:1) + [recv] HEADERS Frame (length:101, flags:0x04, stream_id:1) + [recv] DATA Frame (length:687, flags:0x01, stream_id:1) + [recv] Timeout + × 2: Sends a HEADERS frame that contains the TE header field with any value other than "trailers" + -> The endpoint MUST respond with a stream error of type PROTOCOL_ERROR. + Expected: GOAWAY Frame (Error Code: PROTOCOL_ERROR) + RST_STREAM Frame (Error Code: PROTOCOL_ERROR) + Connection closed + Actual: DATA Frame (length:687, flags:0x01, stream_id:1) + + 8.1.2.3. Request Pseudo-Header Fields + [send] SETTINGS Frame (length:6, flags:0x00, stream_id:0) + [recv] SETTINGS Frame (length:24, flags:0x00, stream_id:0) + [send] SETTINGS Frame (length:0, flags:0x01, stream_id:0) + [recv] WINDOW_UPDATE Frame (length:4, flags:0x00, stream_id:0) + [recv] SETTINGS Frame (length:0, flags:0x01, stream_id:0) + [send] HEADERS Frame (length:16, flags:0x05, stream_id:1) + [recv] HEADERS Frame (length:23, flags:0x04, stream_id:1) + [recv] DATA Frame (length:50, flags:0x01, stream_id:1) + [recv] RST_STREAM Frame (length:4, flags:0x00, stream_id:1) + [recv] Timeout + × 1: Sends a HEADERS frame with empty ":path" pseudo-header field + -> The endpoint MUST respond with a stream error of type PROTOCOL_ERROR. + Expected: GOAWAY Frame (Error Code: PROTOCOL_ERROR) + RST_STREAM Frame (Error Code: PROTOCOL_ERROR) + Connection closed + Actual: DATA Frame (length:50, flags:0x01, stream_id:1) + [send] SETTINGS Frame (length:6, flags:0x00, stream_id:0) + [recv] SETTINGS Frame (length:24, flags:0x00, stream_id:0) + [send] SETTINGS Frame (length:0, flags:0x01, stream_id:0) + [recv] WINDOW_UPDATE Frame (length:4, flags:0x00, stream_id:0) + [recv] SETTINGS Frame (length:0, flags:0x01, stream_id:0) + [send] HEADERS Frame (length:13, flags:0x05, stream_id:1) + [recv] Timeout + × 2: Sends a HEADERS frame that omits ":method" pseudo-header field + -> The endpoint MUST respond with a stream error of type PROTOCOL_ERROR. + Expected: GOAWAY Frame (Error Code: PROTOCOL_ERROR) + RST_STREAM Frame (Error Code: PROTOCOL_ERROR) + Connection closed + Actual: Timeout + [send] SETTINGS Frame (length:6, flags:0x00, stream_id:0) + [recv] SETTINGS Frame (length:24, flags:0x00, stream_id:0) + [send] SETTINGS Frame (length:0, flags:0x01, stream_id:0) + [recv] WINDOW_UPDATE Frame (length:4, flags:0x00, stream_id:0) + [recv] SETTINGS Frame (length:0, flags:0x01, stream_id:0) + [send] HEADERS Frame (length:14, flags:0x05, stream_id:1) + [recv] HEADERS Frame (length:100, flags:0x04, stream_id:1) + [recv] DATA Frame (length:687, flags:0x01, stream_id:1) + [recv] Timeout + × 3: Sends a HEADERS frame that omits ":scheme" pseudo-header field + -> The endpoint MUST respond with a stream error of type PROTOCOL_ERROR. + Expected: GOAWAY Frame (Error Code: PROTOCOL_ERROR) + RST_STREAM Frame (Error Code: PROTOCOL_ERROR) + Connection closed + Actual: DATA Frame (length:687, flags:0x01, stream_id:1) + [send] SETTINGS Frame (length:6, flags:0x00, stream_id:0) + [recv] SETTINGS Frame (length:24, flags:0x00, stream_id:0) + [send] SETTINGS Frame (length:0, flags:0x01, stream_id:0) + [recv] WINDOW_UPDATE Frame (length:4, flags:0x00, stream_id:0) + [recv] SETTINGS Frame (length:0, flags:0x01, stream_id:0) + [send] HEADERS Frame (length:14, flags:0x05, stream_id:1) + [recv] GOAWAY Frame (length:20, flags:0x00, stream_id:0) + ✔ 4: Sends a HEADERS frame that omits ":path" pseudo-header field + [send] SETTINGS Frame (length:6, flags:0x00, stream_id:0) + [recv] SETTINGS Frame (length:24, flags:0x00, stream_id:0) + [send] SETTINGS Frame (length:0, flags:0x01, stream_id:0) + [recv] WINDOW_UPDATE Frame (length:4, flags:0x00, stream_id:0) + [recv] SETTINGS Frame (length:0, flags:0x01, stream_id:0) + [send] HEADERS Frame (length:16, flags:0x05, stream_id:1) + [recv] HEADERS Frame (length:101, flags:0x04, stream_id:1) + [recv] DATA Frame (length:687, flags:0x01, stream_id:1) + [recv] Timeout + × 5: Sends a HEADERS frame with duplicated ":method" pseudo-header field + -> The endpoint MUST respond with a stream error of type PROTOCOL_ERROR. + Expected: GOAWAY Frame (Error Code: PROTOCOL_ERROR) + RST_STREAM Frame (Error Code: PROTOCOL_ERROR) + Connection closed + Actual: DATA Frame (length:687, flags:0x01, stream_id:1) + [send] SETTINGS Frame (length:6, flags:0x00, stream_id:0) + [recv] SETTINGS Frame (length:24, flags:0x00, stream_id:0) + [send] SETTINGS Frame (length:0, flags:0x01, stream_id:0) + [recv] WINDOW_UPDATE Frame (length:4, flags:0x00, stream_id:0) + [recv] SETTINGS Frame (length:0, flags:0x01, stream_id:0) + [send] HEADERS Frame (length:16, flags:0x05, stream_id:1) + [recv] HEADERS Frame (length:101, flags:0x04, stream_id:1) + [recv] DATA Frame (length:687, flags:0x01, stream_id:1) + [recv] Timeout + × 6: Sends a HEADERS frame with duplicated ":scheme" pseudo-header field + -> The endpoint MUST respond with a stream error of type PROTOCOL_ERROR. + Expected: GOAWAY Frame (Error Code: PROTOCOL_ERROR) + RST_STREAM Frame (Error Code: PROTOCOL_ERROR) + Connection closed + Actual: DATA Frame (length:687, flags:0x01, stream_id:1) + [send] SETTINGS Frame (length:6, flags:0x00, stream_id:0) + [recv] SETTINGS Frame (length:24, flags:0x00, stream_id:0) + [send] SETTINGS Frame (length:0, flags:0x01, stream_id:0) + [recv] WINDOW_UPDATE Frame (length:4, flags:0x00, stream_id:0) + [recv] SETTINGS Frame (length:0, flags:0x01, stream_id:0) + [send] HEADERS Frame (length:18, flags:0x05, stream_id:1) + [recv] HEADERS Frame (length:79, flags:0x05, stream_id:1) + [recv] Timeout + × 7: Sends a HEADERS frame with duplicated ":method" pseudo-header field + -> The endpoint MUST respond with a stream error of type PROTOCOL_ERROR. + Expected: GOAWAY Frame (Error Code: PROTOCOL_ERROR) + RST_STREAM Frame (Error Code: PROTOCOL_ERROR) + Connection closed + Actual: HEADERS Frame (length:79, flags:0x05, stream_id:1) + + 8.1.2.6. Malformed Requests and Responses + [send] SETTINGS Frame (length:6, flags:0x00, stream_id:0) + [recv] SETTINGS Frame (length:24, flags:0x00, stream_id:0) + [send] SETTINGS Frame (length:0, flags:0x01, stream_id:0) + [recv] WINDOW_UPDATE Frame (length:4, flags:0x00, stream_id:0) + [recv] SETTINGS Frame (length:0, flags:0x01, stream_id:0) + [send] HEADERS Frame (length:18, flags:0x04, stream_id:1) + [send] DATA Frame (length:4, flags:0x01, stream_id:1) + [recv] HEADERS Frame (length:100, flags:0x04, stream_id:1) + [recv] DATA Frame (length:687, flags:0x01, stream_id:1) + [recv] Timeout + × 1: Sends a HEADERS frame with the "content-length" header field which does not equal the DATA frame payload length + -> The endpoint MUST treat this as a stream error of type PROTOCOL_ERROR. + Expected: GOAWAY Frame (Error Code: PROTOCOL_ERROR) + RST_STREAM Frame (Error Code: PROTOCOL_ERROR) + Connection closed + Actual: DATA Frame (length:687, flags:0x01, stream_id:1) + [send] SETTINGS Frame (length:6, flags:0x00, stream_id:0) + [recv] SETTINGS Frame (length:24, flags:0x00, stream_id:0) + [send] SETTINGS Frame (length:0, flags:0x01, stream_id:0) + [recv] WINDOW_UPDATE Frame (length:4, flags:0x00, stream_id:0) + [recv] SETTINGS Frame (length:0, flags:0x01, stream_id:0) + [send] HEADERS Frame (length:18, flags:0x04, stream_id:1) + [send] DATA Frame (length:4, flags:0x00, stream_id:1) + [send] DATA Frame (length:4, flags:0x01, stream_id:1) + [recv] HEADERS Frame (length:100, flags:0x04, stream_id:1) + [recv] DATA Frame (length:687, flags:0x01, stream_id:1) + [recv] Timeout + × 2: Sends a HEADERS frame with the "content-length" header field which does not equal the sum of the multiple DATA frames payload length + -> The endpoint MUST treat this as a stream error of type PROTOCOL_ERROR. + Expected: GOAWAY Frame (Error Code: PROTOCOL_ERROR) + RST_STREAM Frame (Error Code: PROTOCOL_ERROR) + Connection closed + Actual: DATA Frame (length:687, flags:0x01, stream_id:1) + + 8.2. Server Push + [send] SETTINGS Frame (length:6, flags:0x00, stream_id:0) + [recv] SETTINGS Frame (length:24, flags:0x00, stream_id:0) + [send] SETTINGS Frame (length:0, flags:0x01, stream_id:0) + [recv] WINDOW_UPDATE Frame (length:4, flags:0x00, stream_id:0) + [recv] SETTINGS Frame (length:0, flags:0x01, stream_id:0) + [send] PUSH_PROMISE Frame (length:19, flags:0x04, stream_id:1) + [recv] GOAWAY Frame (length:20, flags:0x00, stream_id:0) + ✔ 1: Sends a PUSH_PROMISE frame + + HPACK: Header Compression for HTTP/2 + 2. Compression Process Overview + 2.3. Indexing Tables + 2.3.3. Index Address Space + [send] SETTINGS Frame (length:6, flags:0x00, stream_id:0) + [recv] SETTINGS Frame (length:24, flags:0x00, stream_id:0) + [send] SETTINGS Frame (length:0, flags:0x01, stream_id:0) + [recv] WINDOW_UPDATE Frame (length:4, flags:0x00, stream_id:0) + [recv] SETTINGS Frame (length:0, flags:0x01, stream_id:0) + [send] HEADERS Frame (length:16, flags:0x05, stream_id:1) + [recv] GOAWAY Frame (length:20, flags:0x00, stream_id:0) + [recv] Connection closed + ✔ 1: Sends a header field representation with invalid index + + 4. Dynamic Table Management + 4.2. Maximum Table Size + [send] SETTINGS Frame (length:6, flags:0x00, stream_id:0) + [recv] SETTINGS Frame (length:24, flags:0x00, stream_id:0) + [send] SETTINGS Frame (length:0, flags:0x01, stream_id:0) + [recv] WINDOW_UPDATE Frame (length:4, flags:0x00, stream_id:0) + [recv] SETTINGS Frame (length:0, flags:0x01, stream_id:0) + [send] HEADERS Frame (length:16, flags:0x05, stream_id:1) + [recv] HEADERS Frame (length:101, flags:0x04, stream_id:1) + [recv] DATA Frame (length:687, flags:0x01, stream_id:1) + [recv] Timeout + × 1: Sends a dynamic table size update at the end of header block + -> The endpoint MUST treat this as a decoding error. + Expected: GOAWAY Frame (Error Code: COMPRESSION_ERROR) + Connection closed + Actual: DATA Frame (length:687, flags:0x01, stream_id:1) + + 5. Primitive Type Representations + 5.2. String Literal Representation + [send] SETTINGS Frame (length:6, flags:0x00, stream_id:0) + [recv] SETTINGS Frame (length:24, flags:0x00, stream_id:0) + [send] SETTINGS Frame (length:0, flags:0x01, stream_id:0) + [recv] WINDOW_UPDATE Frame (length:4, flags:0x00, stream_id:0) + [recv] SETTINGS Frame (length:0, flags:0x01, stream_id:0) + [send] HEADERS Frame (length:27, flags:0x05, stream_id:1) + [recv] HEADERS Frame (length:101, flags:0x04, stream_id:1) + [recv] DATA Frame (length:687, flags:0x01, stream_id:1) + [recv] Timeout + × 1: Sends a Huffman-encoded string literal representation with padding longer than 7 bits + -> The endpoint MUST treat this as a decoding error. + Expected: GOAWAY Frame (Error Code: COMPRESSION_ERROR) + Connection closed + Actual: DATA Frame (length:687, flags:0x01, stream_id:1) + [send] SETTINGS Frame (length:6, flags:0x00, stream_id:0) + [recv] SETTINGS Frame (length:24, flags:0x00, stream_id:0) + [send] SETTINGS Frame (length:0, flags:0x01, stream_id:0) + [recv] WINDOW_UPDATE Frame (length:4, flags:0x00, stream_id:0) + [recv] SETTINGS Frame (length:0, flags:0x01, stream_id:0) + [send] HEADERS Frame (length:26, flags:0x05, stream_id:1) + [recv] HEADERS Frame (length:101, flags:0x04, stream_id:1) + [recv] DATA Frame (length:687, flags:0x01, stream_id:1) + [recv] Timeout + × 2: Sends a Huffman-encoded string literal representation padded by zero + -> The endpoint MUST treat this as a decoding error. + Expected: GOAWAY Frame (Error Code: COMPRESSION_ERROR) + Connection closed + Actual: DATA Frame (length:687, flags:0x01, stream_id:1) + [send] SETTINGS Frame (length:6, flags:0x00, stream_id:0) + [recv] SETTINGS Frame (length:24, flags:0x00, stream_id:0) + [send] SETTINGS Frame (length:0, flags:0x01, stream_id:0) + [recv] WINDOW_UPDATE Frame (length:4, flags:0x00, stream_id:0) + [recv] SETTINGS Frame (length:0, flags:0x01, stream_id:0) + [send] HEADERS Frame (length:28, flags:0x05, stream_id:1) + [recv] GOAWAY Frame (length:20, flags:0x00, stream_id:0) + [recv] Connection closed + ✔ 3: Sends a Huffman-encoded string literal representation containing the EOS symbol + + */ + } diff --git a/jetty-http2/http2-hpack/src/test/resources/jetty-logging.properties b/jetty-http2/http2-hpack/src/test/resources/jetty-logging.properties index e40e8e43ce1..d33a7c32778 100644 --- a/jetty-http2/http2-hpack/src/test/resources/jetty-logging.properties +++ b/jetty-http2/http2-hpack/src/test/resources/jetty-logging.properties @@ -1,3 +1,3 @@ org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog org.eclipse.jetty.http2.LEVEL=INFO -org.eclipse.jetty.http2.hpack.LEVEL=INFO +org.eclipse.jetty.http2.hpack.LEVEL=DEBUG From 42844f2c5fd70d69645763b69c15db55785421f1 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Wed, 18 Jul 2018 11:08:05 +0200 Subject: [PATCH 09/10] WIP Signed-off-by: Greg Wilkins --- .../jetty/http2/hpack/MetaDataBuilder.java | 16 +++++++++------- .../org/eclipse/jetty/http2/hpack/HpackTest.java | 15 +++++++-------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/MetaDataBuilder.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/MetaDataBuilder.java index a53b70de97c..dc65c4fcc9e 100644 --- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/MetaDataBuilder.java +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/MetaDataBuilder.java @@ -236,27 +236,29 @@ public class MetaDataBuilder if (_request && _response) throw new HpackException.StreamException("Request and Response headers"); + + HttpFields fields = _fields; try { - HttpFields fields = _fields; - _fields = new HttpFields(Math.max(10,fields.size()+5)); - - if (_method!=null || _path!=null || _authority!=null || _scheme!=null) + if (_request) return new MetaData.Request(_method,_scheme,_authority,_path,HttpVersion.HTTP_2,fields,_contentLength); - if (_status>0) + if (_response) return new MetaData.Response(HttpVersion.HTTP_2,_status,fields,_contentLength); return new MetaData(HttpVersion.HTTP_2,fields,_contentLength); } finally { - _status=0; + _fields = new HttpFields(Math.max(10,fields.size()+5)); + _request=false; + _response=false; + _status=-1; _method=null; _scheme=null; _authority=null; _path=null; _size=0; - _contentLength=Long.MIN_VALUE; + _contentLength=Long.MIN_VALUE; } } diff --git a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackTest.java b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackTest.java index 9c7d2a24eb5..2f3f461de31 100644 --- a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackTest.java +++ b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackTest.java @@ -18,15 +18,18 @@ package org.eclipse.jetty.http2.hpack; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.Matchers.containsString; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; + import java.nio.ByteBuffer; import java.util.concurrent.TimeUnit; -import org.eclipse.jetty.http.BadMessageException; import org.eclipse.jetty.http.DateGenerator; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpHeader; -import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.http.MetaData; import org.eclipse.jetty.http.MetaData.Response; @@ -35,10 +38,6 @@ import org.eclipse.jetty.util.BufferUtil; import org.junit.Assert; import org.junit.Test; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; - public class HpackTest { final static HttpField ServerJetty = new PreEncodedHttpField(HttpHeader.SERVER,"jetty"); @@ -131,9 +130,9 @@ public class HpackTest decoder.decode(buffer); Assert.fail(); } - catch(BadMessageException e) + catch(HpackException.SessionException e) { - assertEquals(HttpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE_431,e.getCode()); + assertThat(e.getMessage(),containsString("Header too large")); } } From 98ea112fd38155797acda7698be6bf5f26ac7dde Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Wed, 18 Jul 2018 12:26:55 +0200 Subject: [PATCH 10/10] wip Signed-off-by: Greg Wilkins --- .../jetty/http2/parser/HeaderBlockParser.java | 50 ++++++++++++++++--- .../jetty/http2/hpack/HpackDecoder.java | 2 +- .../jetty/http2/hpack/HpackException.java | 16 +++++- .../jetty/http2/hpack/MetaDataBuilder.java | 3 -- .../jetty/http2/hpack/HpackDecoderTest.java | 2 +- 5 files changed, 58 insertions(+), 15 deletions(-) diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/HeaderBlockParser.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/HeaderBlockParser.java index f9a048d8471..922388be2a1 100644 --- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/HeaderBlockParser.java +++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/HeaderBlockParser.java @@ -20,8 +20,11 @@ package org.eclipse.jetty.http2.parser; import java.nio.ByteBuffer; +import org.eclipse.jetty.http.BadMessageException; import org.eclipse.jetty.http.MetaData; import org.eclipse.jetty.http2.hpack.HpackDecoder; +import org.eclipse.jetty.http2.hpack.HpackException.SessionException; +import org.eclipse.jetty.http2.hpack.HpackException.StreamException; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.util.BufferUtil; @@ -72,17 +75,48 @@ public class HeaderBlockParser toDecode = buffer; } - MetaData result = hpackDecoder.decode(toDecode); - - buffer.limit(limit); - - if (blockBuffer != null) + try { - byteBufferPool.release(blockBuffer); - blockBuffer = null; + MetaData metadata = hpackDecoder.decode(toDecode); + + if (metadata instanceof MetaData.Request) + { + // TODO this must be an initial HEADERs frame received by the server OR + // TODO a push promise received by the client. + // TODO this must not be a trailers frame + } + else if (metadata instanceof MetaData.Response) + { + // TODO this must be an initial HEADERs frame received by the client + // TODO this must not be a trailers frame + } + else + { + // TODO this must be a trailers frame + } + + return metadata; } + catch(StreamException ex) + { + // TODO reset the stream + throw new BadMessageException("TODO"); + } + catch(SessionException ex) + { + // TODO reset the session + throw new BadMessageException("TODO"); + } + finally + { + buffer.limit(limit); - return result; + if (blockBuffer != null) + { + byteBufferPool.release(blockBuffer); + blockBuffer = null; + } + } } } } diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackDecoder.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackDecoder.java index 5526eb44772..74b8b87deb9 100644 --- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackDecoder.java +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackDecoder.java @@ -66,7 +66,7 @@ public class HpackDecoder _localMaxDynamicTableSize=localMaxdynamciTableSize; } - public MetaData decode(ByteBuffer buffer) throws HpackException + public MetaData decode(ByteBuffer buffer) throws HpackException.SessionException, HpackException.StreamException { if (LOG.isDebugEnabled()) LOG.debug(String.format("CtxTbl[%x] decoding %d octets",_context.hashCode(),buffer.remaining())); diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackException.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackException.java index 287e02f2e13..78516c73bd2 100644 --- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackException.java +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackException.java @@ -18,15 +18,22 @@ package org.eclipse.jetty.http2.hpack; -import java.io.IOException; -public abstract class HpackException extends RuntimeException +@SuppressWarnings("serial") +public abstract class HpackException extends Exception { HpackException(String messageFormat, Object... args) { super(String.format(messageFormat, args)); } + /** + * A Stream HPACK exception. + *

Stream exceptions are not fatal to the connection and the + * hpack state is complete and able to continue handling other + * decoding/encoding for the session. + *

+ */ public static class StreamException extends HpackException { StreamException(String messageFormat, Object... args) @@ -35,6 +42,11 @@ public abstract class HpackException extends RuntimeException } } + /** + * A Session HPACK Exception. + *

Session exceptions are fatal for the stream and the HPACK + * state is unable to decode/encode further.

+ */ public static class SessionException extends HpackException { SessionException(String messageFormat, Object... args) diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/MetaDataBuilder.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/MetaDataBuilder.java index dc65c4fcc9e..3a01e8a593a 100644 --- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/MetaDataBuilder.java +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/MetaDataBuilder.java @@ -228,10 +228,7 @@ public class MetaDataBuilder public MetaData build() throws HpackException.StreamException { if (_streamException!=null) - { - _streamException.addSuppressed(new Throwable()); throw _streamException; - } if (_request && _response) throw new HpackException.StreamException("Request and Response headers"); diff --git a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackDecoderTest.java b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackDecoderTest.java index dd861890d7e..1e74d100fd7 100644 --- a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackDecoderTest.java +++ b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackDecoderTest.java @@ -231,7 +231,7 @@ public class HpackDecoderTest /* 8.1.2.1. Pseudo-Header Fields */ @Test() - public void test8_1_2_1_PsuedoHeaderFields() + public void test8_1_2_1_PsuedoHeaderFields() throws Exception { // 1:Sends a HEADERS frame that contains a unknown pseudo-header field MetaDataBuilder mdb = new MetaDataBuilder(4096);