Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
ZOMG a bash powers HTTP web service that runs via the `socat` web app container like so: socat TCP4-LISTEN:8080 EXEC:/path/to/server/script
#!/usr/bin/env bash
# Purpose: Provide HTTP server that displays the current server date to
# validate the artifact structure and play with.
declare -a _http_responses=(
[200]="OK"
[201]="Created"
[202]="Accepted"
[204]="No Content"
[301]="Moved Permanently"
[302]="Found"
[307]="Temporary Redirect"
[400]="Bad Request"
[401]="Unauthorized"
[403]="Forbidden"
[404]="Not Found"
[405]="Method Not Allowed"
[500]="Internal Server Error"
[501]="Not Implemented Error"
[502]="Bad Gateway"
[503]="Temporarily Unavailable"
)
function _recv() {
echo "< $*" >&2
}
function _send() {
printf '%s\r\n' "$*"
}
function _send_headers() {
local -r dt="$(date +"%a, %d %b %Y %H:%M:%S %Z")"
local -r hst="$(hostname -s)"
local -a hdrs=(
"Date: ${dt}"
"Expires: ${dt}"
"Content-Type: text/plain"
"Server: hello-bash@${hst}/0.0.1"
)
for h in "${hdrs[@]}"; do
_send "${h}"
done
}
function _send_body() {
_send "Yo yo yo!"
}
function _send_response() {
_send "${1}"
_send_headers
_send ""
_send_body
}
function _respond_with() {
if [ "$#" -ne 1 ]; then
>&2 "Error: Expected one argument for ${FUNCNAME}, received $#."
>&2 "Usage: ${0} CODE"
return 1
fi
local -r code="${1}"
_send_response "${code} ${_http_responses[${code}]}"
}
function _parse_request() {
if [ "$#" -ne 1 ]; then
>&2 "Error: Expected one argument for ${FUNCNAME}, received $#."
>&2 "Usage: ${0} REQUEST_LINE"
return 1
fi
local -r req="${1%%$'r'}"
read -r req_method req_uri req_http_ver <<<"${req}"
# Validate request (only support GET and / URI so far :)
if [ "${req_method}" = "GET" -a "${req_uri}" = "/" -a -n "${req_http_ver}" ]; then
_respond_with 200
elif [ "${req_uri}" != "/" ]; then
_respond_with 404
elif [ -z "${req_http_ver}" ]; then
_respond_with 400
else
_respond_with 500
fi
}
function _main() {
read -r line || _respond_with 400
_parse_request "${line}"
}
if [ "${BASH_SOURCE[0]}" != "${0}" ]; then
export -f _respond_with
export -f _parse_request
else
set -eu
_main
fi
@polynomial

This comment has been minimized.

Copy link

commented Apr 14, 2015

@aljex

This comment has been minimized.

Copy link

commented Apr 15, 2015

You can eliminate 2 forks if you want. Bash has both date and hostname built-in.
Also if you take the care to make dt local, the so should h.

replace:
local -r dt="$(date +"%a, %d %b %Y %H:%M:%S %Z")"
local -r hst="$(hostname -s)"
...
"Server: hello-bash@${hst}/0.0.1"

with:
local h dt
printf -v dt "%(%a, %d %b %Y %H:%M:%S %Z)T" -1
...
"Server: hello-bash@${HOSTNAME}/0.0.1"

@aljex

This comment has been minimized.

Copy link

commented Apr 15, 2015

Erm, second thought, this is not a web service after all.
The output all ends up in the content and the headers are not treated as headers by real http clients.
try:
curl -D apache_headers.txt localhost >apache_content.txt
curl -D bash_headers.txt localhost:8080 >bash_content.txt

Perhaps somehow a newline is getting output before the headers, or perhaps it's something else entirely.

@aljex

This comment has been minimized.

Copy link

commented Apr 15, 2015

It's the missing HTTP/1.0
See the example here:
https://stuff.mit.edu/afs/sipb/machine/penguin-lust/src/socat-1.7.1.2/EXAMPLES

socat -T 1 -d -d tcp-l:10081,reuseaddr,fork,crlf system:"echo -e \"\\\"HTTP/1.0 200 OK\\\nDocumentType: text/html\\\n\\\n<html>date: \$\(date\)<br>server:\$SOCAT_SOCKADDR:\$SOCAT_SOCKPORT<br>client: \$SOCAT_PEERADDR:\$SOCAT_PEERPORT\\\n<pre>\\\"\"; cat; echo -e \"\\\"\\\n</pre></html>\\\"\""

That example is also using socat to convert LF to CRLF, so, if following that example, you wouldn't need _send(), just plain echo everywhere.

We don't need to use the system: option from that example, exec: is fine, since we are using a script in a file, not shell commands directly on the commandline.

@aljex

This comment has been minimized.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.