summaryrefslogtreecommitdiffstats
path: root/dev/h2/mkhdr.sh
diff options
context:
space:
mode:
Diffstat (limited to 'dev/h2/mkhdr.sh')
-rwxr-xr-xdev/h2/mkhdr.sh151
1 files changed, 151 insertions, 0 deletions
diff --git a/dev/h2/mkhdr.sh b/dev/h2/mkhdr.sh
new file mode 100755
index 0000000..4d129fa
--- /dev/null
+++ b/dev/h2/mkhdr.sh
@@ -0,0 +1,151 @@
+#!/usr/bin/env bash
+
+# Usage: mkhdr -l <len> -t <type> -f <flags> -sid <sid> > hdr.bin
+# All fields are optional. 0 assumed when absent.
+
+USAGE=\
+"Usage: %s [-l <len> ] [-t <type>] [-f <flags>] [-i <sid>] [ -d <data> ] > hdr.bin
+ Numbers are decimal or 0xhex. Not set=0. If <data> is passed, it points
+ to a file that is read and chunked into frames of <len> bytes.
+
+Supported symbolic types (case insensitive prefix match):
+ DATA (0x00) PUSH_PROMISE (0x05)
+ HEADERS (0x01) PING (0x06)
+ PRIORITY (0x02) GOAWAY (0x07)
+ RST_STREAM (0x03) WINDOW_UPDATE (0x08)
+ SETTINGS (0x04) CONTINUATION (0x09)
+
+Supported symbolic flags (case insensitive prefix match):
+ ES (0x01) PAD (0x08)
+ EH (0x04) PRIO (0x20)
+
+"
+
+LEN=
+TYPE=
+FLAGS=
+ID=
+
+die() {
+ [ "$#" -eq 0 ] || echo "$*" >&2
+ exit 1
+}
+
+quit() {
+ [ "$#" -eq 0 ] || echo "$*"
+ exit 0
+}
+
+# print usage with $1 as the cmd name
+usage() {
+ printf "$USAGE" "$1";
+}
+
+# Send frame made of $1 $2 $3 $4 to stdout.
+# Usage: mkframe <len> <type> <flags> <id>
+mkframe() {
+ local L="${1:-0}"
+ local T="${2:-0}"
+ local F="${3:-0}"
+ local I="${4:-0}"
+ local t f
+
+ # get the first match in this order
+ for t in DATA:0x00 HEADERS:0x01 RST_STREAM:0x03 SETTINGS:0x04 PING:0x06 \
+ GOAWAY:0x07 WINDOW_UPDATE:0x08 CONTINUATION:0x09 PRIORITY:0x02 \
+ PUSH_PROMISE:0x05; do
+ if [ -z "${t##${T^^*}*}" ]; then
+ T="${t##*:}"
+ break
+ fi
+ done
+
+ if [ -n "${T##[0-9]*}" ]; then
+ echo "Unknown type '$T'" >&2
+ usage "${0##*}"
+ die
+ fi
+
+ # get the first match in this order
+ for f in ES:0x01 EH:0x04 PAD:0x08 PRIO:0x20; do
+ if [ -z "${f##${F^^*}*}" ]; then
+ F="${f##*:}"
+ fi
+ done
+
+ if [ -n "${F##[0-9]*}" ]; then
+ echo "Unknown type '$T'" >&2
+ usage "${0##*}"
+ die
+ fi
+
+ L=$(( L )); T=$(( T )); F=$(( F )); I=$(( I ))
+
+ L0=$(( (L >> 16) & 255 )); L0=$(printf "%02x" $L0)
+ L1=$(( (L >> 8) & 255 )); L1=$(printf "%02x" $L1)
+ L2=$(( (L >> 0) & 255 )); L2=$(printf "%02x" $L2)
+
+ T0=$(( (T >> 0) & 255 )); T0=$(printf "%02x" $T0)
+ F0=$(( (F >> 0) & 255 )); F0=$(printf "%02x" $F0)
+
+ I0=$(( (I >> 24) & 127 )); I0=$(printf "%02x" $I0)
+ I1=$(( (I >> 16) & 255 )); I1=$(printf "%02x" $I1)
+ I2=$(( (I >> 8) & 255 )); I2=$(printf "%02x" $I2)
+ I3=$(( (I >> 0) & 255 )); I3=$(printf "%02x" $I3)
+
+ printf "\x$L0\x$L1\x$L2\x$T0\x$F0\x$I0\x$I1\x$I2\x$I3"
+}
+
+## main
+
+if [ $# -le 1 ]; then
+ usage "${0##*}"
+ die
+fi
+
+while [ -n "$1" -a -z "${1##-*}" ]; do
+ case "$1" in
+ -l) LEN="$2" ; shift 2 ;;
+ -t) TYPE="$2" ; shift 2 ;;
+ -f) FLAGS="$2" ; shift 2 ;;
+ -i) ID="$2" ; shift 2 ;;
+ -d) DATA="$2" ; shift 2 ;;
+ -h|--help) usage "${0##*}"; quit;;
+ *) usage "${0##*}"; die ;;
+ esac
+done
+
+if [ $# -gt 0 ]; then
+ usage "${0##*}"
+ die
+fi
+
+# default values for LEN and ID
+LEN=${LEN:-0};
+if [ -n "${LEN##[0-9]*}" ]; then
+ echo "Unparsable length '$LEN'" >&2
+ usage "${0##*}"
+ die
+fi
+
+ID=${ID:-0};
+if [ -n "${ID##[0-9]*}" ]; then
+ echo "Unparsable stream ID '$ID'" >&2
+ usage "${0##*}"
+ die
+fi
+
+if [ -z "$DATA" ]; then
+ mkframe "$LEN" "$TYPE" "$FLAGS" "$ID"
+else
+ # read file $DATA in <LEN> chunks and send it in multiple frames
+ # advertising their respective lengths.
+ [ $LEN -gt 0 ] || LEN=16384
+
+ while read -rN "$LEN" payload || [ ${#payload} -gt 0 ]; do
+ mkframe "${#payload}" "$TYPE" "$FLAGS" "$ID"
+ echo -n "$payload"
+ done < "$DATA"
+fi
+
+exit 0