harhar

package module
v0.0.0-...-ff0c349 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 1, 2014 License: MIT Imports: 9 Imported by: 0

README

harhar

Simple, transparent HTTP Archive (HAR) logging for Go code using the http.Client interface. For existing code that already uses the net/http package, updating existing code to generate HAR logs can typically be done with only 2 lines of code.

Getting Started

First, convert your existing http.Client instance (or http.DefaultClient) to a harhar.Client:

// before
webClient := &http.Client{}

// after
httpClient := &http.Client{}
webClient := harhar.NewClient(httpClient)

Then, whenever you're ready to generate the HAR output, call WriteLog:

webClient.WriteLog("output.har")

That's it! harhar.Client implements all the same methods as http.Client, so no other code will need to be changed. However, if you set Timeouts, Cookies, etc. dynamically then you will want to retain a copy of the wrapped http.Client. harhar.Client only stores the pointer, so changes to the underlying http.Client will be used immediately.

Optional periodic logging

To dynamically enable or disable HAR logging, code can use harhar.ClientInterface to represent either an http.Client or harhar.Client. The included harhar example command shows one way to use this interface. When using this interface, you can write logs (if enabled) by using this simple block of code:

if harCli, ok := myClient.(*harhar.Client); ok {
	harCli.WriteLog("output.har")
}

When combined with a long-running process, the interface makes it possible to toggle logging off and on, and periodically write to disk throughout a processes lifetime. An example is the following (never-ending) goroutine:

go func(){
	for _ = range time.Tick(time.Minute*5) {
		if harCli, ok := myClient.(*harhar.Client); ok {
			sz, err := harCli.WriteLog("output.har")
			if err!=nil {
				log.Println("error writing .har log:", err)
			} else {
				log.Printf("wrote .har log (%.1fkb)\n", float64(sz)/1024.0)
			}
		}
	}
}()

Note that when logging is enabled, harhar memory usage can grow pretty quickly, especially if Responses are large. If you don't want to disable logging in code when output size grows too large, you should at least display it so that users can decide to stop before the OOM killer comes to play.

Documentation

Overview

Package harhar provides a minimal set of methods and structs to enable HAR logging in a go net/http-based application.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Client

type Client struct {

	// Creator describes the source of the logged requests/responses.
	Creator struct {
		// Name defaults to the name of the program (os.Args[0])
		Name string `json:"name"`

		// Version defaults to the current time (formatted as "20060102150405")
		Version string `json:"version"`
	} `json:"creator"`

	// Version defaults to the current time (formatted as "20060102150405")
	Version string `json:"version"`

	// Comment can be added to the log to describe the particulars of this data.
	Comment string `json:"comment,omitempty"`

	// Entries contains all of the Request and Response details that passed
	// through this Client.
	Entries []Entry `json:"entries"`
	// contains filtered or unexported fields
}

Client embeds a http.Client and wraps its methods to perform transparent HAR logging for every request and response. It contains the properties for the root "log" node of the HAR, with Creator, Version, and Comment strings.

func NewClient

func NewClient(client *http.Client) *Client

func (*Client) Do

func (c *Client) Do(req *http.Request) (*http.Response, error)

Do works by calling http.Client.Do on the wrapped client instance. However, it also tracks the request start and end times, and parses elements from the request and response data into HAR log Entries.

func (*Client) Get

func (c *Client) Get(url string) (*http.Response, error)

Get works just like http.Client.Get, creating a GET Request and calling Do.

func (*Client) Head

func (c *Client) Head(url string) (*http.Response, error)

Head works just like http.Client.Head, creating a HEAD Request and calling Do.

func (*Client) Post

func (c *Client) Post(url string, bodyType string, body io.Reader) (*http.Response, error)

Post works just like http.Client.Post, creating a POST Request with the provided body data, setting the content-type to bodyType, and calling Do.

func (*Client) PostForm

func (c *Client) PostForm(url string, data url.Values) (*http.Response, error)

PostForm works just like http.Client.PostForm, creating a POST Request by urlencoding data, setting the content-type appropriately, and calling Do.

func (*Client) WriteLog

func (c *Client) WriteLog(filename string) (int, error)

WriteLog writes the HAR log format to the filename given, then returns the number of bytes.

type ClientInterface

type ClientInterface interface {
	Get(url string) (*http.Response, error)
	Head(url string) (*http.Response, error)
	Post(url string, bodyType string, body io.Reader) (*http.Response, error)
	PostForm(url string, data url.Values) (*http.Response, error)
	Do(req *http.Request) (*http.Response, error)
}

ClientInterface allows you to dynamically swap in a harhar.Client for a http.Client if needed. (Although you'll still need to type-convert to use http.Client fields or WriteLog)

type Cookie struct {
	Name     string `json:"name"`
	Value    string `json:"value"`
	Path     string `json:"path,omitempty"`
	Domain   string `json:"domain,omitempty"`
	Expires  string `json:"expires,omitempty"` // ISO8601 time
	Secure   bool   `json:"secure"`
	HttpOnly bool   `json:"httpOnly"`
}

type Entry

type Entry struct {
	Request  Request  `json:"request"`
	Response Response `json:"response"`

	Start string `json:"startedDateTime"` // ISO8601 time

	// Total time in milliseconds, Time=SUM(Timings.*)
	Time    int `json:"time"`
	Timings struct {
		Send    int `json:"send"`
		Wait    int `json:"wait"`
		Receive int `json:"receive"`
	} `json:"timings"`

	// always empty
	Cache map[string]string `json:"cache"`
}

type NameValuePair

type NameValuePair struct {
	Name  string `json:"name"`
	Value string `json:"value"`
}

type Request

type Request struct {
	Method      string          `json:"method"` // in caps, GET/POST/etc
	URL         string          `json:"url"`
	HttpVersion string          `json:"httpVersion"` // ex "HTTP/1.1"
	Headers     []NameValuePair `json:"headers"`
	Cookies     []Cookie        `json:"cookies"`
	QueryParams []NameValuePair `json:"queryString"`

	Body struct {
		MIMEType string `json:"mimeType"`
		Content  string `json:"text"`
	} `json:"postData"`

	// always -1, too lazy
	HeadersSize int `json:"headersSize"`
	BodySize    int `json:"bodySize"`
}

type Response

type Response struct {
	StatusCode  int             `json:"status"`      // 200
	StatusText  string          `json:"statusText"`  // "OK"
	HttpVersion string          `json:"httpVersion"` // ex "HTTP/1.1"
	RedirectURL string          `json:"redirectURL"`
	Headers     []NameValuePair `json:"headers"`
	Cookies     []Cookie        `json:"cookies"`

	Body struct {
		Size     int    `json:"size"`
		MIMEType string `json:"mimeType"`
		Content  string `json:"text"`
	} `json:"content"`

	// always -1, too lazy
	HeadersSize int `json:"headersSize"`
	BodySize    int `json:"bodySize"`
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL