Newer
Older
httpcat / src / display.go
/**
  Author: Mark George <mark.george@otago.ac.nz>
  License: Zero-Clause BSD License
*/

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httputil"
	"strings"
	"time"

	"github.com/fatih/color"
)

func showRequest(req *http.Request) {
	showTimestamp()

	if !globalOptions.NoColour {
		color.Set(color.FgBlue)
	}

	if globalOptions.HeadersOnly {
		showRequestHeaders(req)
	} else if globalOptions.BodiesOnly {
		showRequestBody(req)
	} else {
		showRequestHeaders(req)
		fmt.Println()
		showRequestBody(req)
	}

	color.Unset()
}

func showFilteredRequest(req *http.Request) {
	showTimestamp()

	if !globalOptions.NoColour {
		color.Set(color.FgBlue)
	}

	if !globalOptions.BodiesOnly {
		showRequestHeaders(req)
	}

	color.Unset()

	fmt.Println("\n[Request body filtered]")
}

func showResponse(rsp *http.Response) {
	showTimestamp()

	if !globalOptions.NoColour {
		color.Set(color.FgRed)
	}

	if globalOptions.HeadersOnly {
		showResponseHeaders(rsp)
	} else if globalOptions.BodiesOnly {
		showResponseBody(rsp)
	} else {
		showResponseHeaders(rsp)
		fmt.Println()
		showResponseBody(rsp)
	}

	color.Unset()
}

func showFilteredResponse(req *http.Response) {
	showTimestamp()

	if !globalOptions.NoColour {
		color.Set(color.FgRed)
	}

	if !globalOptions.BodiesOnly {
		showResponseHeaders(req)
	}

	color.Unset()

	fmt.Println("\n[Response body filtered]")
}

func showRequestBody(req *http.Request) {
	// is there a body?
	if req.Body == nil || req.Body == http.NoBody {
		// nope, nothing to do
		return
	}

	originalBody, _ := ioutil.ReadAll(req.Body)

	// clone body since reading from request it will consume it
	copy1 := ioutil.NopCloser(bytes.NewBuffer(originalBody))
	copy2 := ioutil.NopCloser(bytes.NewBuffer(originalBody))

	// put one copy back into original request
	req.Body = copy1

	// read body from other copy
	body, _ := ioutil.ReadAll(copy2)
	defer copy2.Close()

	if !globalOptions.NoIndent {
		if strings.Contains(req.Header.Get("Content-Type"), "application/json") {

			// if req.Header.Get("Content-Type") == "application/json" {
			var indentedJson bytes.Buffer
			err := json.Indent(&indentedJson, body, "", "   ")
			if err == nil {
				// display indented version
				fmt.Println(&indentedJson)
				return
			}
		}
	}

	fmt.Println(string(body[:]))
}

func showResponseBody(rsp *http.Response) {
	// is there a body?
	if rsp.Body == nil || rsp.Body == http.NoBody {
		// nope, nothing to do
		return
	}

	originalBody, _ := ioutil.ReadAll(rsp.Body)

	// clone body since reading from request it will consume it
	copy1 := ioutil.NopCloser(bytes.NewBuffer(originalBody))
	copy2 := ioutil.NopCloser(bytes.NewBuffer(originalBody))

	// put one copy back into original request
	rsp.Body = copy1

	// read body from other copy
	body, _ := ioutil.ReadAll(copy2)
	defer copy2.Close()

	if !globalOptions.NoIndent {
		if strings.Contains(rsp.Header.Get("Content-Type"), "application/json") {
			var indentedJson bytes.Buffer
			err := json.Indent(&indentedJson, body, "", "   ")
			if err == nil {
				// display indented version
				fmt.Println(&indentedJson)
				return
			}
		}
	}

	fmt.Println(string(body[:]))
}

func showRequestHeaders(req *http.Request) {
	dumpBody := false
	dump, _ := httputil.DumpRequest(req, dumpBody)
	fmt.Println(strings.TrimSpace(string(dump[:])))
}

func showResponseHeaders(rsp *http.Response) {
	dumpBody := false
	dumpRsp, _ := httputil.DumpResponse(rsp, dumpBody)
	fmt.Println(strings.TrimSpace(string(dumpRsp[:])))
}

func showTimestamp() {
	if !globalOptions.NoTimestamps {
		fmt.Println(time.Now().Format(time.StampMilli))
	}
}