I wrote a single file web server in my last post about Getting Started With tor Hidden Services on FreeBSD. I figured it might be useful to have a couple of variations of simple web servers in their own post. This post covers single line and single file web servers with nc on FreeBSD. Content served from a static file or a script is presented.

UPDATE: A follow up post exists. A Better Scripted Netcat Server

Software Versions

$ date
February  7, 2016 at 02:27:30 AM JST
$ uname -vm
FreeBSD 11.0-CURRENT #0 r287598: Thu Sep 10 14:45:48 JST 2015     root@:/usr/obj/usr/src/sys/MIRAGE_KERNEL  amd64

Instructions

First, add the following to index.html so we have a file to serve.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Hello World!</title>
  </head>
  <body>
    <p>Hello World!</p>
  </body>
</html>

The netcat Wikipedia page lists a one-line one-shot web server. It has been adapted for FreeBSD below.

FILE=index.html; { printf "HTTP/1.0 200 OK\r\nContent-Length: $(wc -c <$FILE)\r\n\r\n"; cat $FILE; } | nc -N -l 127.0.0.1 8080

The server body can be put in a loop to serve the file multiple times. The host and port definitions have been moved to the beginning of the line in the version below.

FILE=index.html; HOST=127.0.0.1; PORT=8080; while true; do { printf "HTTP/1.0 200 OK\r\nContent-Length: $(wc -c <$FILE)\r\n\r\n"; cat $FILE; } | nc -N -l $HOST $PORT ; done

The above line is complicated enough that it might want live in a file, like server.sh. A content type definition has been moved to the beginning of the file. A body and response have been defined. A couple of useful headers have been added. Finally, the response number is echoed before each response.

#/bin/sh

HOST=127.0.0.1
PORT=8080
FILE=index.html
CONTENT_TYPE=text/html

while true; do
BODY=$(cat $FILE)
RESPONSE=$(cat <<EOF
HTTP/1.0 200 OK
Content-Type: ${CONTENT_TYPE}
Content-Length: $((${#BODY}+1))
Connection: close

${BODY}
EOF
)
echo "---$((X=X+1))---"
echo "$RESPONSE" | nc -N -l $HOST $PORT
done

Make the server script executable before running it.

chmod +x server.sh
./server.sh

By redefining the body clause, the script can serve dynamic content. A simple JSON date server is listed below.

#/bin/sh

HOST=127.0.0.1
PORT=8080
CONTENT_TYPE=application/json

while true; do
BODY=$(cat <<EOF
{
  "Date": "$(date)"
}
EOF
)
RESPONSE=$(cat <<EOF
HTTP/1.0 200 OK
Content-Type: ${CONTENT_TYPE}
Content-Length: $((${#BODY}+1))
Connection: close

${BODY}
EOF
)
echo "---$((X=X+1))---"
echo "$RESPONSE" | nc -N -l $HOST $PORT
done

A one line version of the JSON date server is listed below.

HOST=127.0.0.1; PORT=8080; CONTENT_TYPE=application/json; while true; do BODY=$(printf "{\r\n  \"Date\": \"$(date)\"\r\n}\r\n");RESPONSE=$(printf "HTTP/1.0 200 OK\r\nContent-Type: ${CONTENT_TYPE}\r\nContent-Length: $((${#BODY}+1))\r\nConnection: close\r\n\r\n${BODY}"); echo "---$((X=X+1))---"; echo "$RESPONSE" | nc -N -l $HOST $PORT; done

A one line version of the single file web server is listed below.

HOST=127.0.0.1; PORT=8080; FILE=index.html; CONTENT_TYPE=text/html; while true; do BODY=$(cat $FILE);RESPONSE=$(printf "HTTP/1.0 200 OK\r\nContent-Type: ${CONTENT_TYPE}\r\nContent-Length: $((${#BODY}+1))\r\nConnection: close\r\n\r\n${BODY}"); echo "---$((X=X+1))---"; echo "$RESPONSE" | nc -N -l $HOST $PORT; done

Either of the following lines can be used to test the server from the command line. date is piped into nc so the client will send EOF to the server. The server will also log something to the terminal this way.

curl -v 127.0.0.1:8080
# log request date on the server and send EOF
date | nc 127.0.0.1 8080

Note that these servers are not necessarily robust. They may trip up clients that do not close the connection.

References: