Newer
Older
httpcat / src / server.go
package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httputil"
	"os"
	"strconv"
	"time"
)

type ServerCommand struct {
	Body   string `short:"b" long:"body" description:"Body message to respond with."`
	Port   int    `short:"p" long:"port" description:"Port to listen on." default:"8080"`
	Status int    `short:"s" long:"status" description:"Status code to respond with." default:"204"`
	Cors   bool   `shot:"c" long:"cors" description:"Enable Cross Origin Resource Sharing (CORS)."`
}

var (
	serverOptions *ServerCommand
)

func (c *ServerCommand) Execute(args []string) error {

	fmt.Println("HTTP server listening on port " + strconv.Itoa(c.Port) + "\n")

	http.HandleFunc("/", serverRequestHandler)

	serverOptions = c

	if err := http.ListenAndServe(":"+strconv.Itoa(c.Port), nil); err != nil {
		fmt.Fprintf(os.Stderr, "Could not start server.  Is port %d available?\n", c.Port)
		os.Exit(1)
	}

	return nil
}

func serverRequestHandler(rsp http.ResponseWriter, req *http.Request) {

	if globalOptions.Verbose {

		fmt.Printf("Received a %s request to %s\n", req.Method, req.RequestURI)
	}

	fmt.Println(time.Now().Format(time.StampMilli))

	if globalOptions.BodiesOnly {
		body, _ := ioutil.ReadAll(req.Body)
		fmt.Println(string(body[:]))

	} else if globalOptions.HeadersOnly {
		fmt.Println(req.Method + " " + req.URL.Path + " " + req.Proto)
		for k := range req.Header {
			fmt.Println(k + ": " + req.Header[k][0])
		}
		// TODO

	} else {
		// dump entire request
		dump, _ := httputil.DumpRequest(req, true)
		fmt.Println(string(dump[:]))
	}

	// cors
	if serverOptions.Cors && req.Method == http.MethodOptions {
		if globalOptions.Verbose {
			fmt.Println("CORS preflight")
		}

		if origin := req.Header.Get("Origin"); origin != "" {

			rsp.Header().Set("Access-Control-Allow-Origin", origin)

			if headers := req.Header.Get("Access-Control-Request-Headers"); headers != "" {
				rsp.Header().Set("Access-Control-Allow-Headers", headers)
			}

			if method := req.Header.Get("Access-Control-Request-Method"); method != "" {
				rsp.Header().Set("Access-Control-Allow-Method", method)
			}

			rsp.WriteHeader(200)

			return

		} else {
			if globalOptions.Verbose {
				fmt.Println("Preflight did not contain 'Origin' header!")
			}
		}
	}

	// send response
	if serverOptions.Body != "" {
		if serverOptions.Status == 204 {
			// body exists, so send a 200 response instead of 204
			serverOptions.Status = 200
		}
		rsp.WriteHeader(serverOptions.Status)
		fmt.Fprintf(rsp, serverOptions.Body)
	} else {
		// no body, so just write status header
		rsp.WriteHeader(serverOptions.Status)
	}

}