#!/bin/sh -eu # vim: set ts=4 sw=4 et: BODY_TX_TIMEOUT=${BODY_TX_TIMEOUT:-3} REQUEST_TIMEOUT=${REQUEST_TIMEOUT:-$((BODY_TX_TIMEOUT + 3))} fin() { printf "HTTP/1.1 %d \r\n" "$1" >&4 printf "Content-Type: text/plain;charset=utf-8\r\n" >&4 printf "Content-Length: %d\r\n" "${#2}" >&4 printf "Access-Control-Allow-Origin: *\r\n" >&4 printf "\r\n" >&4 printf "%s" "$2" >&4 [ "$1" -eq 200 ] && exit 0 exit 1 } if test ! "${1:-}" = "client"; then export MAX_BODY=${MAX_BODY:-65536} PORT=${PORT:-20021} usage() { cat <&2 Usage: $0 filter_cmd | ... Read and filter points from a HTTP POST request. For example from http://sol.alinkspace.org/web-sign The filter command is called for every HTTP Post request This command should output the points it accepts to stdout or exit with an error code The accepted points are put on stdin for further processing Examples: $0 lk filter query "hello:[#:pub]:/world//*" | lk f $0 lk filter one-of domain test | LKDB=$HOME/test lk save $0 lk filter fset -- ":[#:test]:[kv**:friends]" > ./friends.spool NOTE: The input CAN contain multiple points. use lk filter --limit 1 ... to disallow. PORT=$PORT MAX_BODY=$MAX_BODY REQUEST_TIMEOUT=$REQUEST_TIMEOUT BODY_TX_TIMEOUT=$BODY_TX_TIMEOUT Error: $1 EOF exit 1 } command -v lk >/dev/null || usage "missing lk binary" echo "Accepting HTTP POST requests on port: ${PORT}" >&2 # store cmd & arguments so we can call it later with ... | xargs -a $fargv $fcmd if [ "${1+isset}" = isset ]; then export fcmd="${1:-cat}" command -v "$fcmd" >/dev/null || usage "unknown cmd $fcmd" shift if [ "${1+isset}" = isset ]; then export fargv="$(mktemp)" printf '%s\0' "$@" >"$fargv" fi printf "filtering using: %s %s" "$fcmd" "$*" >&2 fi cleanup() { trap '' TERM kill -- -$$ || echo already closing exit 0 } trap 'cleanup' TERM INT # start receiving - run this script in client mode socat tcp-listen:$PORT,fork exec:"timeout $REQUEST_TIMEOUT \"$0\" client",fdout=4 cleanup fi read -r HEAD case "$HEAD" in "POST"*) ;; *) fin 404 "method not supported" ;; esac CONTENT_LEN=0 for i in $(seq 1 32); do read -r line || fin 500 "bad header" line="${line%?}" case "$line" in "Content-Length: "*[!0-9]*) fin 418 "Teabag available on request" ;; "Content-Length: "*) CONTENT_LEN="${line#Content-Length: }" ;; "") break ;; *) [ "$i" -eq 32 ] && fin 500 "too many headers" ;; esac done if ! [ "$CONTENT_LEN" -gt 68 ] || ! [ "$CONTENT_LEN" -le "$MAX_BODY" ]; then fin 500 "expects between 68 and 65536 bytes" fi read_body() { timeout "$BODY_TX_TIMEOUT" dd count=1 bs="$CONTENT_LEN" status=none } filter() { case "${fcmd:+FILTER}_${fargv:+FARGV}" in "_") cat - ;; "FILTER_") $fcmd || fin 401 "not accepted" && exit ;; "FILTER_FARGV") xargs -a "$fargv" -0 "$fcmd" || fin 401 "not accepted" && exit ;; esac } ack_point() { lk link http-rx::/ack -t ack --forward /stdout --write "[point:http-reply]/fd:4" || false } read_body | filter | ack_point || fin 500 "Server pipe error"