package module
v0.0.0-...-32020e2 Latest Latest

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

Go to latest
Published: Jul 20, 2020 License: MIT Imports: 14 Imported by: 10



httpUnit tests web and net servers for basic functionality.

The tool can open an http/https connection and verify that the expected status code is received. It can also verify that the resulting HTML output contains a string or regular expression. It can direct the request to a particular IP address, ignoring DNS (similar to curl --resolve). The tool can also open a TCP connection and verify that the connection was completed. For https connections it will output various TLS-related information.

Tests can be input three ways:

  • Command line: A single test can be listed on the command line. This is useful for interactive debugging.
  • TOML file: A list of tests, with a full range of features, can be listed in a TOML file. This is the recommended mode for tests done on a regular basis. The format is described below.
  • JSON/Hiera mode: A simple list tcp tests can be specified in JSON format. These are in the format of an iptables "set". This mode is highly specific to a local requirement.

When specifying a single test on the command line, the only tests performed are status code, and regex. If the URL does not contain a scheme ("https://", "http://"), "http://" is prefixed. The IP may be an empty string to indicate all IP addresses resolved to from the URL's hostname.


httpUnit [flag] [-hiera="path/to/sets.json"] [-toml="/path/to/httpunit.toml"] [url] [ip] [code] [regex]

The flags are:

	if specified, only uses this IP address; may end with "." to
	filter by IPs that start with the filter
	no RFC1918 addresses
	connection timeout
        if specified, only runs plans that are tagged with one of the
	tags specified. You can specify more than one tag, seperated by commas
	if specified, only runs plans where the URL contains the given
	protocol. Valid protocols are: http,https,tcp,tcp4,tcp6,udp,udp4,udp6,ip,ip4,ip6
	You can specify more than one protocol, seperated by commas
	in more verbose mode, print this HTTP header
	verbose output: show successes
	more verbose output: show -header, cert details

URLs may be specified with various protocols: http, https, tcp, udp, ip. "4" or "6" may be appended to tcp, udp, and ip (as per net/#Dial). tcp and udp must specify a port, or default to 0. http and https may specify a port to override the default.


The toml file has two sections: Plan is a list of test plans. IPs are a table of search and replace regexes.

Each [[plan]] lists:

  • label = A label for documentation purposes. It must be unique.
  • url = The URL to retrieve.
  • ips = For http/https, a list of IPs to send the URL to. Default is "use DNS". Otherwise the connection is made to the IP address listed, ignoring DNS.
  • code = For http/https, the expected status code, default 200.
  • string = For http/https, a string we expect to find in the result.
  • regex = For http/https, a regular expression we expect to match in the result.
  • timeout = An optional timeout for the test in seconds. Default is 3 seconds.
  • tags = An optional set of tags for the test. Used for when you want to only run a subset of tests with the -tags flag
  • insecureSkipVerify = true Will allow testing of untrusted or self-signed certificates.

The test plan is run once for each item in the ips list, or more if macros are in effect.

In the ips list, * will be substituted by all the A and AAAA records returned when DNS is performed on the hostname of the URL.

The [[IPs]] section is for defining macros. Here are some typical use-cases:

Specify a value n to mean "10.0.0.n". This may save you a lot of typing in a big configuration file:

'^(\d+)$' = ["10.0.0.$1"]

Similar to the previous example, but specify a base address:

BASEIP = ["10.0.0."]
'^(\d+)$' = ["BASEIP$1"]

Specify a value n to mean the 16th IP address in many CIDR bocks:

'^(\d+)$' = ["10.0.0.$1", "10.1.1.$1", "10.2.2.$1", "10.3.3.$1", "10.4.4.$1"]

Specify a value nINT to mean .n and .n+64, plus whatever DNS returns:

BASEIP = ["10.0.0."]
'^(\d+)$' = ["BASEIP$1", "BASEIP($1+64)", "*"]

Why we made this?

This tool makes it easy to do test-driven development on your web server.

Every request to our web server passes through three systems that are all complex and error prone: the firewall, the load balancer, and the web server itself. Even when running in a test environment with automated tools that generate the configurations we still needed a way to test our results.

Before this tool each change was followed by a few simple manual tests, often by manually typing curl commands. We missed a lot of errors.

With this tool, we now have hundreds of tests that we can run with a single command. The tests run in parallel therefore all the testing is done very quickly.

When we need to make a change we first add the test, then we proceed making the change until the test passes. This test-driven development has accumulated 200 tests that we run for any change, considerably more than we'd ever run manually. This improves confidence in our ability to make changes quickly.

While making unrelated changes we often run it in a loop to make sure we don't unintentionally break anything.

The command-line mode has been useful both in development of changes, and diagnosing outages.

A simple example file:

# Verify that this URL returns text that matches "some regex":
  label = "api"
  url = ""
  text = "API for"
  regex = "some regex"

# Verify that this URL returns a redirect. Send to both
# the IP address listed in DNS, plus and
  label = "redirect"
  url = ""
  ips = ["*", "", ""]
  code = 301

A more complex example file:

In this example we want an IP address to mean the IP address, but if we specify a single number (e.g. "16") we want that to expand to the .16 address of a few different CIDR blocks. We also want to be able to specify a number + INT (e.g. "16INT") to indicate a slightly different list.

  BASEIP = ["87.65.43."]
  '^(\d+)$' = ["*", "BASEIP$1", "BASEIP($1+64)", "1.2.3.$1"]
  '^(\d+)INT$' = ["10.0.1.$1", "10.0.2.$1", "BASEIP$1", "BASEIP($1+64)"]

  label = "api"
  url = ""
  # This will generate the DNS A/AAAA records,,, and
  ips = ["16", ""]
  text = "API for"
  regex = "some regex"
  tags = ["apis",""]

  label = "redirect"
  url = ""
  # This will generate the DNS A/AAAA records,,,,
  ips = ["*", "20INT"]
  code = 301
  tags = ["redirect",""]

  label = "mail"
  url = "tcp://"



Package httpunit tests compliance of net services.



This section is empty.


View Source
var Timeout = time.Second * 10

Timeout is the connection timeout.


This section is empty.


type IPMap

type IPMap map[string][]string

IPMap is a map of regular expressions to replacements.

func (IPMap) Expand

func (i IPMap) Expand(s string) ([]string, error)

Expand expands s into IP addresses. A successful return will consist of valid IP addresses or "*". The following process is repeated until there are no changes. If an address is a valid IP address or "*", no further changes to it are done. If it contains the form "(x+y)", x and y are added and replaced. Otherwise a regular expression search and replace is done for each key of i with its values.

type PlanResult

type PlanResult struct {
	Plan   *TestPlan
	Case   *TestCase
	Result *TestResult

type Plans

type Plans struct {
	Plans []*TestPlan `toml:"plan"`
	IPs   IPMap

func (*Plans) Test

func (ps *Plans) Test(filter string, no10 bool, tagFilters []string, protoFilters []string) (<-chan *PlanResult, int, error)

Test performs tests. Filter optionally specifies an IP filter to use. no10 disallows 10.* addresses. It returns a channel where results will be sent when complete, and the total number of results to expect. The channel is closed once all results are completed.

type Results

type Results []*PlanResult

type TestCase

type TestCase struct {
	URL  *url.URL
	IP   net.IP
	Port string

	Plan *TestPlan
	// FromDNS is true if IP was determined with a DNS lookup.
	FromDNS bool
	// Error is populated if the TestPlan could not create a TestCase. In this case
	// the test is not run, and this error is passed directly to the TestResult.
	Error error

	ExpectCode  int
	ExpectText  string
	ExpectRegex *regexp.Regexp

	Timeout time.Duration

func (*TestCase) Test

func (c *TestCase) Test() *TestResult

Test performs this test case.

type TestPlan

type TestPlan struct {
	Label string
	URL   string
	IPs   []string
	Tags  []string

	Code               int
	Text               string
	Regex              string
	InsecureSkipVerify bool
	Timeout            time.Duration

TestPlan describes a test and its permutations (IP addresses).

func ExtractHiera

func ExtractHiera(fname string) ([]*TestPlan, error)

ExtractHiera returns listener members from a hiera sets.json file. For example, the following file would create three entries for the given IPs and ports. Other items present in the file are ignored.

  "iptables::sets::sets": {
    "listeners": {
      "members": {
        ",tcp:80": {
          "comment": "first load balancer"
        ",tcp:80": {
          "comment": "second load balancer"
        ",tcp:25": {
          "comment": "mail servers"

func (*TestPlan) Cases

func (p *TestPlan) Cases(filter string, no10 bool, IPs IPMap, protoFilters []string) ([]*TestCase, error)

Cases computes the actual test cases from a test plan. filter and no10 are described in Plans.Test.

type TestResult

type TestResult struct {
	Result error
	Resp   *http.Response

	Connected   bool
	GotCode     bool
	GotText     bool
	GotRegex    bool
	InvalidCert bool
	TimeTotal   time.Duration


Path Synopsis
httpunit tests compliance of web and net servers with desired output.
httpunit tests compliance of web and net servers with desired output.

Jump to

Keyboard shortcuts

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