chore: repo maintenance, minor code refinements
This commit is contained in:
parent
fd57b9ac75
commit
eb5a369502
11 changed files with 186 additions and 141 deletions
1
benchmark/requirements.txt
Normal file
1
benchmark/requirements.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
locust
|
82
src/constants.sh
Normal file
82
src/constants.sh
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
# shellcheck disable=SC2034 # for now
|
||||||
|
|
||||||
|
readonly HTTP_SERVER_VERSION='0.1.0'
|
||||||
|
readonly HTTP_PROTOCOL_VERSION='HTTP/1.1'
|
||||||
|
readonly HTTP_SERVER_STRING="httb/${HTTP_SERVER_VERSION}"
|
||||||
|
|
||||||
|
readonly HTTP_DEFAULT_HOST='127.0.0.1'
|
||||||
|
readonly HTTP_DEFAULT_PORT='8081'
|
||||||
|
|
||||||
|
declare -Ar HTTP_METHOD_NAMES=(
|
||||||
|
[100]='Continue'
|
||||||
|
[101]='Switching Protocols'
|
||||||
|
[200]='OK'
|
||||||
|
[201]='Created'
|
||||||
|
[202]='Accepted'
|
||||||
|
[203]='Non-Authoritative Information'
|
||||||
|
[204]='No Content'
|
||||||
|
[205]='Reset Content'
|
||||||
|
[206]='Partial Content'
|
||||||
|
[239]='Pratusevic'
|
||||||
|
[300]='Multiple Choices'
|
||||||
|
[301]='Moved Permanently'
|
||||||
|
[302]='Found'
|
||||||
|
[303]='See Other'
|
||||||
|
[304]='Not Modified'
|
||||||
|
[305]='Use Proxy'
|
||||||
|
[307]='Temporary Redirect'
|
||||||
|
[308]='Permanent Redirect'
|
||||||
|
[400]='Bad Request'
|
||||||
|
[401]='Unauthorized'
|
||||||
|
[402]='Payment Required'
|
||||||
|
[403]='Forbidden'
|
||||||
|
[404]='Not Found'
|
||||||
|
[405]='Method Not Allowed'
|
||||||
|
[406]='Not Acceptable'
|
||||||
|
[407]='Proxy Authentication Required'
|
||||||
|
[408]='Request Timeout'
|
||||||
|
[409]='Conflict'
|
||||||
|
[410]='Gone'
|
||||||
|
[411]='Length Required'
|
||||||
|
[412]='Precondition Failed'
|
||||||
|
[413]='Content Too Large'
|
||||||
|
[414]='URI Too Long'
|
||||||
|
[415]='Unsupported Media Type'
|
||||||
|
[416]='Range Not Satisfiable'
|
||||||
|
[417]='Expectation Failed'
|
||||||
|
[418]="I'm a teapot"
|
||||||
|
[421]='Misdirected Request'
|
||||||
|
[422]='Unprocessable Content'
|
||||||
|
[426]='Upgrade Required'
|
||||||
|
[500]='Internal Server Error'
|
||||||
|
[501]='Not Implemented'
|
||||||
|
[502]='Bad Gateway'
|
||||||
|
[503]='Service Unavailable'
|
||||||
|
[504]='Gateway Timeout'
|
||||||
|
[505]='HTTP Version Not Supported'
|
||||||
|
)
|
||||||
|
|
||||||
|
declare -Ar HTTP_SUPPORTED_METHODS=(
|
||||||
|
[GET]=1
|
||||||
|
[HEAD]=1
|
||||||
|
[POST]=''
|
||||||
|
[PUT]=''
|
||||||
|
[DELETE]=''
|
||||||
|
[CONNECT]=''
|
||||||
|
[OPTIONS]=''
|
||||||
|
[TRACE]=''
|
||||||
|
)
|
||||||
|
|
||||||
|
http::_status_code_page() {
|
||||||
|
cat <<-EOF
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>${1} ${HTTP_METHOD_NAMES["$1"]}</title>
|
||||||
|
<body>
|
||||||
|
<center><h1>${1} ${HTTP_METHOD_NAMES["$1"]}</h1></center>
|
||||||
|
<hr>
|
||||||
|
<center><address>Server: ${HTTP_SERVER_STRING}</address></center>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
EOF
|
||||||
|
}
|
8
src/helpers.sh
Normal file
8
src/helpers.sh
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# shellcheck disable=SC2015
|
||||||
|
|
||||||
|
http::_tempfile() {
|
||||||
|
[ ! -d "/dev/shm" ] && TMP_FOLDER="/tmp" || TMP_FOLDER="/dev/shm"
|
||||||
|
[ ! -d "${TMP_FOLDER}/httb-server" ] && mkdir "${TMP_FOLDER}/httb-server" ||
|
||||||
|
mktemp -t 'httb-server.XXXXXXXXXXXX' -p "${TMP_FOLDER}/httb-server"
|
||||||
|
}
|
|
@ -1 +0,0 @@
|
||||||
Hello, World!
|
|
|
@ -1,13 +1,15 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
# shellcheck shell=bash
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
shopt -s globstar extglob dotglob
|
shopt -s globstar extglob dotglob
|
||||||
|
|
||||||
# readonly HTTP_SCRIPT_PATH="$(dirname "$(readlink -e "${BASH_SOURCE[0]:-$0}")")"
|
# shellcheck source-path=../
|
||||||
# . "${SCRIPT_PATH}/mock_tcp_server.sh"
|
HTTP_SCRIPT_PATH="$(dirname "$(readlink -e "${BASH_SOURCE[0]:-$0}")")"
|
||||||
# . "${SCRIPT_PATH}/constants.sh"
|
readonly HTTP_SCRIPT_PATH
|
||||||
|
|
||||||
. "./lib/constants.sh" && . "./lib/helpers.sh"
|
. "${HTTP_SCRIPT_PATH}/constants.sh"
|
||||||
|
. "${HTTP_SCRIPT_PATH}/helpers.sh"
|
||||||
|
|
||||||
|
|
||||||
declare -A http__handlers=()
|
declare -A http__handlers=()
|
||||||
|
@ -16,7 +18,7 @@ declare -A http__all_routes=()
|
||||||
# -- CONFIGURATION --
|
# -- CONFIGURATION --
|
||||||
|
|
||||||
http::host() { export HTTP_HOST_NAME="$1"; }
|
http::host() { export HTTP_HOST_NAME="$1"; }
|
||||||
http::bind() { export HTTP_HOST="${1:-127.0.0.1}"; export HTTP_PORT="${2:-8081}"; }
|
http::bind() { export HTTP_HOST="${1:-${HTTP_DEFAULT_HOST}}"; export HTTP_PORT="${2:-${HTTP_DEFAULT_PORT}}"; }
|
||||||
http::markdown_base() { export HTTP_MARKDOWN_BASE="$1"; }
|
http::markdown_base() { export HTTP_MARKDOWN_BASE="$1"; }
|
||||||
|
|
||||||
http::static_folder() {
|
http::static_folder() {
|
||||||
|
@ -31,7 +33,7 @@ http::route() {
|
||||||
local _method
|
local _method
|
||||||
while read -r _method; do
|
while read -r _method; do
|
||||||
# temp until all methods are suppoorted
|
# temp until all methods are suppoorted
|
||||||
[[ -z "${HTTP_SUPPORTED_METHODS[${_method}]}" ]] && echo "$1: Method ${_method} not implemented!" >&2 && exit 1
|
[[ -z "${HTTP_SUPPORTED_METHODS["${_method}"]}" ]] && echo "$1: Method ${_method} not implemented!" >&2 && exit 1
|
||||||
http__handlers["$_method,$3"]="$1"
|
http__handlers["$_method,$3"]="$1"
|
||||||
http__all_routes["$3"]=1
|
http__all_routes["$3"]=1
|
||||||
done <<< "${2/ /$'\n'}"
|
done <<< "${2/ /$'\n'}"
|
||||||
|
@ -42,67 +44,66 @@ http::get() { http::route "$1" "GET" "$2"; }
|
||||||
# -- REQUEST PROCESSING --
|
# -- REQUEST PROCESSING --
|
||||||
|
|
||||||
http::_parse_headers() {
|
http::_parse_headers() {
|
||||||
local -n _h=$1 _m=$2 _p=$3 _v=$4
|
read -r request_method request_path request_version
|
||||||
|
|
||||||
read -r header_first_
|
|
||||||
read -r _m _p _v <<<"$header_first_"
|
|
||||||
|
|
||||||
while read -r header_; do
|
while read -r header_; do
|
||||||
[[ -z "${header_/$'\r'/}" ]] && break
|
[[ -z "${header_/$'\r'/}" ]] && break
|
||||||
local h_name h_value
|
local h_name h_value
|
||||||
IFS=: read -r h_name h_value <<< "${header_/': '/:}"
|
IFS=: read -r h_name h_value <<< "${header_/': '/:}"
|
||||||
_h["${h_name}"]="${h_value}"
|
request_headers["${h_name}"]="${h_value}"
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
http::_parse_body() { :; } # TODO
|
http::_parse_body() { :; } # TODO
|
||||||
|
|
||||||
http::_route_request() {
|
http::_route_request() {
|
||||||
[[ -z "${HTTP_SUPPORTED_METHODS[${HTTP_REQUEST_METHOD}]}" ]] && http::response 501 && return 1
|
[[ -z "${HTTP_SUPPORTED_METHODS[${request_method}]}" ]] && http::response 501 && return 1
|
||||||
local glob_endpoint handler path_found
|
local glob_endpoint handler path_found
|
||||||
|
|
||||||
for glob_endpoint in "${!http__all_routes[@]}"; do # FIXME
|
for glob_endpoint in "${!http__all_routes[@]}"; do # FIXME
|
||||||
# shellcheck disable=SC2053
|
# shellcheck disable=SC2053
|
||||||
if [[ "${HTTP_REQUEST_PATH}" == ${glob_endpoint} ]]; then
|
if [[ "${request_path}" == ${glob_endpoint} ]]; then
|
||||||
declare -rx HTTP_REQUEST_GLOB="${glob_endpoint}"
|
declare -rx HTTP_REQUEST_GLOB="${glob_endpoint}"
|
||||||
path_found=1
|
path_found=1
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
handler=${http__handlers["$HTTP_REQUEST_METHOD,$glob_endpoint"]}
|
handler="${http__handlers["$request_method,$glob_endpoint"]}"
|
||||||
|
|
||||||
if [[ -z "${path_found}" ]]; then
|
if [[ -z "${path_found}" ]]; then
|
||||||
http::response 404; return 1
|
http::response 404; return 1
|
||||||
elif [[ -z "${handler}" ]]; then
|
elif [[ -z "${handler}" ]]; then
|
||||||
http::response 405; return 1
|
http::response 405; return 1
|
||||||
else
|
else
|
||||||
${handler}
|
"${handler}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# -- RESPONSE PROCESSING --
|
# -- RESPONSE PROCESSING --
|
||||||
|
|
||||||
http::_response_base_headers() {
|
http::_response_base_headers() {
|
||||||
printf "%s %d %s\n" "${HTTP_PROTOCOL_VERSION}" "$1" "${HTTP_METHOD_NAMES["$1"]}"
|
connection_string="${HTTP_PROTOCOL_VERSION} $1 ${HTTP_METHOD_NAMES["$1"]}"
|
||||||
cat <<-EOS
|
response_headers["Server"]="${HTTP_SERVER_STRING}"
|
||||||
Server: ${HTTP_SERVER_STRING}
|
response_headers["Connection"]="close"
|
||||||
Connection: close
|
response_headers["Date"]="$(date -Ru)"
|
||||||
Date: $(date -Ru)
|
response_headers["Cache-control"]="no-cache"
|
||||||
Cache-control: no-cache
|
response_headers["Cache-control"]="max-age=0"
|
||||||
Cache-control: max-age=0
|
|
||||||
EOS
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
http::_set_response_content_type() {
|
||||||
|
response_headers["Content-Type"]="$(file -ib "$1")"
|
||||||
|
}
|
||||||
|
|
||||||
|
# $1 = file with content $2 = filename
|
||||||
http::_response_content_headers() {
|
http::_response_content_headers() {
|
||||||
echo "Content-Length: $(wc -c "$1" | cut -d ' ' -f 1)"
|
response_headers["Content-Length"]="$(wc -c "$1" | cut -d ' ' -f 1)"
|
||||||
echo "Content-Type: ${HTTP_RESPONSE_CONTENT_TYPE:="$(file -ib "$1")"}"
|
[[ -z "${response_headers["Content-Type"]}" ]] && response_headers["Content-Type"]="$(file -ib "$1")"
|
||||||
echo
|
echo
|
||||||
}
|
}
|
||||||
|
|
||||||
http::_response_content() {
|
http::_response_content() {
|
||||||
local -r content_file="$(http::_ramfile)" # TODO mb adapt to mkfifo pipes
|
local -r content_file="$(http::_tempfile)" # TODO mb adapt to mkfifo pipes
|
||||||
cat - > "${content_file}"
|
cat - > "${content_file}"
|
||||||
|
|
||||||
http::_response_content_headers "${content_file}"
|
http::_response_content_headers "${content_file}"
|
||||||
|
@ -112,9 +113,9 @@ http::_response_content() {
|
||||||
}
|
}
|
||||||
|
|
||||||
http::_static_file() {
|
http::_static_file() {
|
||||||
local uri_path="${HTTP_REQUEST_PATH#"${HTTP_REQUEST_GLOB%'**'}"}"
|
local uri_path="${request_path#"${HTTP_REQUEST_GLOB%'**'}"}"
|
||||||
local filename="${HTTP_STATIC_FOLDER}/${uri_path}"
|
local filename="${HTTP_STATIC_FOLDER}/${uri_path}"
|
||||||
[[ -z "${uri_path}" || ! -f "${filename}" ]] && http::response 404 && return 1
|
[[ -z "${uri_path}" || ! -f "${filename}" || "${filename}" == *".."* ]] && http::response 404 && return 1
|
||||||
http::file "${filename}"
|
http::file "${filename}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,6 +123,7 @@ http::response() {
|
||||||
local status_code page
|
local status_code page
|
||||||
[[ -n "${HTTP_METHOD_NAMES["$1"]}" ]] && status_code="$1" || status_code="500"
|
[[ -n "${HTTP_METHOD_NAMES["$1"]}" ]] && status_code="$1" || status_code="500"
|
||||||
page="$(http::_status_code_page "${status_code}")"
|
page="$(http::_status_code_page "${status_code}")"
|
||||||
|
response_headers["Content-Type"]="text/html"
|
||||||
http::_response_base_headers "${status_code}"
|
http::_response_base_headers "${status_code}"
|
||||||
http::_response_content <<< "${page}"
|
http::_response_content <<< "${page}"
|
||||||
}
|
}
|
||||||
|
@ -129,32 +131,37 @@ http::response() {
|
||||||
http::file() {
|
http::file() {
|
||||||
[[ ! -f "$1" ]] && http::response 500 && return 1
|
[[ ! -f "$1" ]] && http::response 500 && return 1
|
||||||
http::_response_base_headers "200"
|
http::_response_base_headers "200"
|
||||||
|
http::_set_response_content_type "$1"
|
||||||
http::_response_content < "$1"
|
http::_response_content < "$1"
|
||||||
}
|
}
|
||||||
|
|
||||||
http::html() { HTTP_RESPONSE_CONTENT_TYPE="text/html" http::file "$@"; }
|
http::html() { response_headers["Content-Type"]="text/html" http::file "$@"; }
|
||||||
|
|
||||||
http::format_markdown() { :; } # TODO
|
http::format_markdown() { :; } # TODO
|
||||||
|
|
||||||
# -- COMMAND LINE LOGIC --
|
# -- COMMAND LINE LOGIC --
|
||||||
|
|
||||||
http::_process_request() {
|
http::_process_request() {
|
||||||
declare -Ax HTTP_REQUEST_HEADERS
|
declare -Ax request_headers response_headers
|
||||||
export HTTP_REQUEST_METHOD HTTP_REQUEST_PATH HTTP_REQUEST_VERSION HTTP_REQUEST_BODY
|
export request_method request_path request_version connection_string
|
||||||
|
|
||||||
http::_parse_headers HTTP_REQUEST_HEADERS HTTP_REQUEST_METHOD HTTP_REQUEST_PATH HTTP_REQUEST_VERSION || return 1
|
http::_parse_headers || { http::response 500 && return 0; }
|
||||||
http::_parse_body HTTP_REQUEST_BODY || return 1
|
# { [[ "${request_method}" != "GET" ]] && http::_parse_body; || { http::response 500 && return 0; }
|
||||||
if [[ -n "${HTTP_HOST_NAME}" && "${HTTP_HOST_NAME}" != "${HTTP_REQUEST_HEADERS["Host"]%:*}" ]]; then
|
|
||||||
|
if [[ -n "${HTTP_HOST_NAME}" && "${HTTP_HOST_NAME}" != "${request_headers["Host"]%:*}" ]]; then
|
||||||
http::response 404
|
http::response 404
|
||||||
return
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
http::_route_request
|
http::_route_request
|
||||||
|
[[ -z "${connection_string}" ]] && http::response 500 && return 0
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
http::_cmd_help() {
|
http::_cmd_help() {
|
||||||
echo "USAGE:"
|
echo "USAGE:"
|
||||||
echo " $0 <run|parse> [OPTIONS]"
|
echo " $0 <run|parse>"
|
||||||
echo
|
echo
|
||||||
echo "ARGUMENTS:"
|
echo "ARGUMENTS:"
|
||||||
echo " run Run the server"
|
echo " run Run the server"
|
||||||
|
@ -163,7 +170,7 @@ http::_cmd_help() {
|
||||||
|
|
||||||
http::run() {
|
http::run() {
|
||||||
if [[ "$1" = "run" ]]; then
|
if [[ "$1" = "run" ]]; then
|
||||||
echo "Listening on ${HTTP_HOST}:${HTTP_PORT}"
|
echo "Listening on ${HTTP_HOST:=${HTTP_DEFAULT_HOST}}:${HTTP_PORT:=${HTTP_DEFAULT_PORT}}"
|
||||||
socat "TCP-LISTEN:${HTTP_PORT},bind=${HTTP_HOST},fork,reuseport" "EXEC:$(readlink -e "$0") parse"
|
socat "TCP-LISTEN:${HTTP_PORT},bind=${HTTP_HOST},fork,reuseport" "EXEC:$(readlink -e "$0") parse"
|
||||||
elif [[ "$1" = "parse" ]]; then
|
elif [[ "$1" = "parse" ]]; then
|
||||||
http::_process_request
|
http::_process_request
|
|
@ -1,78 +0,0 @@
|
||||||
# shellcheck disable=SC2034 # for now
|
|
||||||
|
|
||||||
readonly HTTP_PROTOCOL_VERSION="HTTP/1.1"
|
|
||||||
readonly HTTP_SERVER_STRING="httb/0.1.0"
|
|
||||||
|
|
||||||
declare -Ar HTTP_METHOD_NAMES=(
|
|
||||||
[100]="Continue"
|
|
||||||
[101]="Switching Protocols"
|
|
||||||
[200]="OK"
|
|
||||||
[201]="Created"
|
|
||||||
[202]="Accepted"
|
|
||||||
[203]="Non-Authoritative Information"
|
|
||||||
[204]="No Content"
|
|
||||||
[205]="Reset Content"
|
|
||||||
[206]="Partial Content"
|
|
||||||
[239]="Pratusevic"
|
|
||||||
[300]="Multiple Choices"
|
|
||||||
[301]="Moved Permanently"
|
|
||||||
[302]="Found"
|
|
||||||
[303]="See Other"
|
|
||||||
[304]="Not Modified"
|
|
||||||
[305]="Use Proxy"
|
|
||||||
[307]="Temporary Redirect"
|
|
||||||
[308]="Permanent Redirect"
|
|
||||||
[400]="Bad Request"
|
|
||||||
[401]="Unauthorized"
|
|
||||||
[402]="Payment Required"
|
|
||||||
[403]="Forbidden"
|
|
||||||
[404]="Not Found"
|
|
||||||
[405]="Method Not Allowed"
|
|
||||||
[406]="Not Acceptable"
|
|
||||||
[407]="Proxy Authentication Required"
|
|
||||||
[408]="Request Timeout"
|
|
||||||
[409]="Conflict"
|
|
||||||
[410]="Gone"
|
|
||||||
[411]="Length Required"
|
|
||||||
[412]="Precondition Failed"
|
|
||||||
[413]="Content Too Large"
|
|
||||||
[414]="URI Too Long"
|
|
||||||
[415]="Unsupported Media Type"
|
|
||||||
[416]="Range Not Satisfiable"
|
|
||||||
[417]="Expectation Failed"
|
|
||||||
[418]="I'm a teapot"
|
|
||||||
[421]="Misdirected Request"
|
|
||||||
[422]="Unprocessable Content"
|
|
||||||
[426]="Upgrade Required"
|
|
||||||
[500]="Internal Server Error"
|
|
||||||
[501]="Not Implemented"
|
|
||||||
[502]="Bad Gateway"
|
|
||||||
[503]="Service Unavailable"
|
|
||||||
[504]="Gateway Timeout"
|
|
||||||
[505]="HTTP Version Not Supported"
|
|
||||||
)
|
|
||||||
|
|
||||||
declare -Ar HTTP_SUPPORTED_METHODS=(
|
|
||||||
[GET]=1
|
|
||||||
[HEAD]=1
|
|
||||||
[POST]=''
|
|
||||||
[PUT]=''
|
|
||||||
[DELETE]=''
|
|
||||||
[CONNECT]=''
|
|
||||||
[OPTIONS]=''
|
|
||||||
[TRACE]=''
|
|
||||||
)
|
|
||||||
|
|
||||||
http::_status_code_page() {
|
|
||||||
cat <<-EOF
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>${1} ${HTTP_METHOD_NAMES["$1"]}</title>
|
|
||||||
<body>
|
|
||||||
<h1>${1} ${HTTP_METHOD_NAMES["$1"]}</h1>
|
|
||||||
<hr>
|
|
||||||
<address>Server: ${HTTP_SERVER_STRING}</address>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
EOF
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
http::_ramfile() { # TODO use tempdir instead of files
|
|
||||||
mktemp -t 'httb-server.XXXXXXXXXXXX' -p "/dev/shm"
|
|
||||||
}
|
|
19
src/main.sh
19
src/main.sh
|
@ -1,19 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# shellcheck disable=SC1090
|
|
||||||
. lib/http_server.sh
|
|
||||||
. routes/*.sh
|
|
||||||
|
|
||||||
http::static_folder "/static" "./static"
|
|
||||||
|
|
||||||
root() {
|
|
||||||
http::html "html/index.html"
|
|
||||||
} && http::get root "/"
|
|
||||||
|
|
||||||
|
|
||||||
main() {
|
|
||||||
http::file "./main.sh"
|
|
||||||
} && http::route main "GET" '/main'
|
|
||||||
|
|
||||||
|
|
||||||
http::run "$@"
|
|
50
src/tcp_server.sh
Normal file
50
src/tcp_server.sh
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
#!/bin/bash
|
||||||
|
MAX_CONNECTIONS=1
|
||||||
|
|
||||||
|
_workers_=()
|
||||||
|
|
||||||
|
tcp::worker_() {
|
||||||
|
local socket_input socket_output request src_address
|
||||||
|
local host="${1:-0.0.0.0}"
|
||||||
|
local port="${2:-8081}"
|
||||||
|
local delimiter=""
|
||||||
|
local delimiter_placeholder='DELIMTIER_PLACEHOLDER'
|
||||||
|
|
||||||
|
exec {socket_input}<> <(:)
|
||||||
|
exec {socket_output}<> <(:)
|
||||||
|
|
||||||
|
# { nc -lknv -s "${host}" -p "${port}" > >( stdbuf -o0 sed "s/$delimiter/$delimiter_placeholder/g" ) 2> >( stdbuf -o0 sed "/Listening on.*/d;s/Connection received on /$delimiter/" >&2; ) ; } <&$socket_input >&$socket_output 2>&1 &
|
||||||
|
{
|
||||||
|
socat "TCP-LISTEN:${host:-80},bind=${port:-127.0.0.1},fork,reuseport" \
|
||||||
|
> >( stdbuf -o0 sed "s/$delimiter/$delimiter_placeholder/g" ) \
|
||||||
|
2> >( stdbuf -o0 sed "/Listening on.*/d;s/Connection received on /$delimiter/" >&2; ) ;
|
||||||
|
} <&$socket_input >&$socket_output 2>&1 &
|
||||||
|
|
||||||
|
echo "listenning"
|
||||||
|
|
||||||
|
while :; do
|
||||||
|
read -d "$delimiter" -r -u $socket_output request
|
||||||
|
read -r -u $socket_output src_address; src_address="${src_address/ /:}"
|
||||||
|
|
||||||
|
printf -- '--- NEW REQUEST ---\n'
|
||||||
|
echo "$request"
|
||||||
|
printf -- '--- SOURCE ADDR ---\n'
|
||||||
|
echo "$src_address"
|
||||||
|
printf -- '--- END REQUEST ---\n\n\n'
|
||||||
|
|
||||||
|
printf "%s\n\n%s" "$(cat response.txt)" "$(cat index.html)" >&$socket_input # main logic
|
||||||
|
done
|
||||||
|
|
||||||
|
exec {socket_input}<&-
|
||||||
|
exec {socket_output}<&-
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
tcp::listen() {
|
||||||
|
for _ in $(seq $MAX_CONNECTIONS); do
|
||||||
|
tcp::worker_ "$1" "$2" & _workers_+=("$!")
|
||||||
|
done
|
||||||
|
wait
|
||||||
|
}
|
||||||
|
|
||||||
|
tcp::listen "$@"
|
Loading…
Add table
Add a link
Reference in a new issue