# Features

# Sending HTTP Request

Possible HTTP request methods are GET, POST, PUT, DELETE, OPTIONS, HEAD

# GET request

 request get-example {
  method = "GET"
  url = "http://httpbin.org/get"
}

Execution

$ restbeast r get-example
HTTP/1.1 200 OK

{
  "args": {}, 
  "headers": {
    "Accept-Encoding": "gzip", 
    "Host": "httpbin.org", 
    "User-Agent": "RestBeast/0.13.0", 
    "X-Amzn-Trace-Id": "Root=1-600b6b8f-639ca132489f3d353fcd8760"
  }, 
  "origin": "213.127.110.240", 
  "url": "http://httpbin.org/get"
}

# POST request

request post-example {
  method = "POST"
  url = "http://httpbin.org/post"

  body = {
    key = "value"
    key-2 = "value-2"
  }
}
$ restbeast r post-example
HTTP/1.1 200 OK

{
  "args": {}, 
  "data": "{\n  \"key\": \"value\",\n  \"key-2\": \"value-2\"\n}", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept-Encoding": "gzip", 
    "Content-Length": "42", 
    "Host": "httpbin.org", 
    "User-Agent": "RestBeast/0.13.0", 
    "X-Amzn-Trace-Id": "Root=1-600b6c10-1f2c17c43cd1077112ce8302"
  }, 
  "json": {
    "key": "value", 
    "key-2": "value-2"
  }, 
  "origin": "213.127.110.240", 
  "url": "http://httpbin.org/post"
}

# Content Type Handling

Special cases:

  • application/json

    body will be parsed into a json string

  • application/x-www-form-urlencoded

    body will be parsed as url-encoded key=value string

  • multipart/form-data

    body will be half parsed for attributes, any readfile operations will be added as boundary.

  • files uploads

    Request body will be the contents of the file, content-type header will be set with file's mime type in case of left blank

# Built-in Functions

A variety of functions are available.

See built-in go-cty functions and gofakeit functions

# External (Custom) Functions

It's possible to define external programs or scripts as functions.

Possible argument types are string, list, map, number.

Given contents of test-function.js as follows.

process.stdout.write(process.argv[2].toUpperCase());
external-function testFunction {
  interpreter = "node"
  script = "test-function.js"
  args = ["string"]
}

request function-example {
  method = "GET"
  url = "localhost/${testFunction("hello")}"
}

# Multi Environment Configurations

Environment variables and related secrets can be changed with just a simple env flag.

env prod {
  variables = {
    url = "http://httpbin.org"
    apiKey = "oh-my-secret"
  }
}

env local {
  default = true
  variables = {
    url = "http://localhost"
    apiKey = "not-so-important-secret"
  }
}

request env-example {
  method = "GET"
  url = "${env.url}/get"
  headers = {
    "x-api-key" = env.apiKey
  }
}

Execute with -e, useful for testing against various environments or testing in CI pipelines

$ restbeast request env-example --env prod

# Secrets

env local {
  default = true

  secrets from_shell_env {
    type = "env-var"
    paths = {
      apikey = "APIKEY"
    }
  }

  variables = {
    url = "http://localhost:8080"
    apiKey = secret.from_shell_env.apikey
  }
}

request secret-example {
  method = "GET"
  url = "${env.url}/get"
  headers = {
    "x-api-key" = env.apiKey
  }
}
$ restbeast_var_APIKEY="very-secret-key" restbeast request secret-example --env local

# Secrets from environment variables

env test {
  secrets env {
    type = "env-var"
    paths = {
      val1 = "VAL1"
      val2 = "VAL2"
    }
  }
}

Prefix environment variables with restbeast_var_

$ restbeast_var_VAL1=secret1 restbeast_var_VAL2=secret2 restbeast r xxx --env test

# Generating Random Data

Leverage https://github.com/brianvoe/gofakeit library in your requests.

An example with randomized user data

request random-example {
  method = "POST"
  url = "https://httpbin.org/post"
  headers = {
    "content-type" = "application/json"
  }
  body = {
    firstName = gofakeitFirstName()
    lastName = gofakeitLastName()
  }
}

# Chaining requests

# Without pre-configuration

Dependencies will be resolved through usage of request.* phrases.

When patch-example request executed, it will do a post-example request first and use its response as a dependency in patch-example request.

request post-example {
  method = "POST"
  url = "https://httpbin.org/post"
  headers = {
    "content-type" = "application/json"
  }
  body = {
    firstName = gofakeitFirstName()
    lastName = gofakeitLastName()
  }
}

request patch-example {
  method = "PATCH"
  url = "https://httpbin.org/patch"
  headers = {
    "content-type" = "application/json"
    "X-Amzn-Trace-Id": request.post-example.body.headers.X-Amzn-Trace-Id
  }
  body = {
    firstName = "Mr. ${upper(request.post-example.body.json.firstName)}"
    lastName = "Mr. ${upper(request.post-example.body.json.lastName)}"
    contentLengthOfFirstResponse = request.post-example.headers["content-length"][0]
  }
}
$ restbeast request patch-example | jq

Example response will contain the first, and the second X-Amzn-Trace-Id also it will include upper-cased values of firstName and lastName generated in post-example request

Output

{
  "args": {},
  "data": "{\n  \"firstName\": \"Mr. JUNIOR\",\n  \"lastName\": \"Mr. RUSSEL\"\n}",
  "files": {},
  "form": {},
  "headers": {
    "Accept-Encoding": "gzip",
    "Content-Length": "59",
    "Content-Type": "application/json",
    "Host": "httpbin.org",
    "User-Agent": "RestBeast-v0.4.0",
    "X-Amzn-Trace-Id": "Self=1-5f15a0f8-cd3f68948e10488e520dcb5a;Root=1-5f15a0f8-26cd99544146b6540841f272"
  },
  "json": {
    "firstName": "Mr. JUNIOR",
    "lastName": "Mr. RUSSEL"
  },
  "origin": "213.127.104.191",
  "url": "https://httpbin.org/patch"
}

# With depends_on parameter

The following example doesn't actually require explicit dependency declaration, however there might be some cases defining dependency order would be useful.





























 












request post-example-1 {
  method = "POST"
  url = "https://httpbin.org/post"
  headers = {
    "content-type" = "application/json"
  }
  body = {
    firstName = gofakeitFirstName()
    lastName = gofakeitLastName()
  }
}

request post-example-2 {
  method = "POST"
  url = "https://httpbin.org/post"
  headers = {
    "content-type" = "application/json"
  }
  body = {
    firstName = request.post-example-1.body.json.firstName
    lastName = gofakeitLastName()
  }
}

request patch-example {
  method = "PATCH"
  url = "https://httpbin.org/patch"
  
  depends_on = ["request.post-example-1", "request.post-example-2"]
  
  headers = {
    "content-type" = "application/json"
    "X-Amzn-Trace-Id": request.post-example.body.headers.X-Amzn-Trace-Id
  }
  body = {
    firstName = "Mr. ${upper(request.post-example-1.body.json.firstName)}"
    lastName = "Mr. ${upper(request.post-example-2.body.json.lastName)}"
    contentLengthOfFirstResponse = request.post-example.headers["content-length"][0]
  }
}

Should result as

$ restbeast r patch-example --detailed-timing
HTTP/2.0 200 OK
  │
  ├──PATCH https://httpbin.org/patch
  │  DNS Resolve Time: 0 ms
  │  Connection Time: 0 ms
  │  First Byte Time: 204 ms
  │  Total Time: 204 ms
  │  Bytes Received: 687 B
  │    │
  │    ├──POST https://httpbin.org/post
  │    │  DNS Resolve Time: 19 ms
  │    │  Connection Time: 187 ms
  │    │  TLS Handshake Time: 596 ms
  │    │  First Byte Time: 803 ms
  │    │  Total Time: 803 ms
  │    │  Bytes Received: 543 B
  │    │
  │    ├──POST https://httpbin.org/post
  │    │  DNS Resolve Time: 0 ms
  │    │  Connection Time: 0 ms
  │    │  First Byte Time: 203 ms
  │    │  Total Time: 203 ms
  │    │  Bytes Received: 539 B
  │    │
  │    ├──POST https://httpbin.org/post
  │    │  DNS Resolve Time: 0 ms
  │    │  Connection Time: 0 ms
  │    │  First Byte Time: 205 ms
  │    │  Total Time: 205 ms
  │    │  Bytes Received: 535 B


{
  "args": {}, 
  "data": "{\n  \"contentLengthOfFirstResponse\": \"535\",\n  \"firstName\": \"Mr. ULISES\",\n  \"lastName\": \"Mr. BRAUN\"\n}", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept-Encoding": "gzip", 
    "Content-Length": "99", 
    "Content-Type": "application/json", 
    "Cookie": "", 
    "Host": "httpbin.org", 
    "User-Agent": "RestBeast/0.13.0", 
    "X-Amzn-Trace-Id": "Self=1-60232377-4344fdb752fb80b01d766132;Root=1-60232377-3409ee3a3659b2ef438f84b6"
  }, 
  "json": {
    "contentLengthOfFirstResponse": "535", 
    "firstName": "Mr. ULISES", 
    "lastName": "Mr. BRAUN"
  }, 
  "origin": "213.127.105.230", 
  "url": "https://httpbin.org/patch"
}

# Attack request (Load testing)

Keep targeted server busy. This command will execute given request c times in given p period. Request count has to be equal or higher than 1 request per second.

$ restbeast ar test-request-name -c 60 -p 60s

Example output

Status 200 response: %78 (47)
Status 400 response: %15 (9)
Status 500 response: %6 (4)
95 Percentile: 1.091473938s
99 Percentile: 1.100081803s
AverageTime: 585.411933ms

# Fixing resbeast version

It's possible to fix your restbeast configuration to a specific version. See here for extra comparison options.

version = "~0.9"

# Authorization

It's possible to handle basic or bearer authorization through auth block.

Basic auth example

request example {
  auth {
    basic {
      username = "a-username"
      password = request.some-other-request.body.password
    }
  }
}

Bearer auth example

request example {
  auth {
    bearer {
      token = request.sign-in.body.jwt
    }
  }
}

# Testing and Assertions

request get-example {
  method = "GET"
  url = "http://httpbin.org/get"
}

test get-example {
  valid-origin = assertIpv4(request.get-example.body.origin)
  valid-host = assertEqual(request.get-example.body.headers.Host, "httpbin.org")
}

Running this example with test command will provide the output below. With exit code as 0. Failed tests will result exit code 1 and fail descriptions.

$ restbeast test get-example
PASS: valid-host
PASS: valid-origin

2 passes, 0 failures, 2 total.

Please see assertions.

Just run the test command without any arguments to run all tests at once

$ restbeast test

# Sending Cookies

It's possible to send cookies as a header or via cookies block. Both can be used simultaneously.

request cookies-example {
  method = "GET"
  url = "https://httpbin.org/cookies"

  headers = {
    cookie = "name=value; another-name=another-value"
  }

  cookies = {
    some-name: "some-value"
    some-other-name: "some-other-value"
  }
}

The example above will send 4 cookies; name, another-name, some-name, some-other-name

# Request Repeat

Both of the following examples will make 5 post requests with randomized first and last name each time.

# Via argument

request post-example {
  method = "POST"
  url = "https://httpbin.org/post"
  headers = {
    "content-type" = "application/json"
  }
  body = {
    firstName = gofakeitFirstName()
    lastName = gofakeitLastName()
  }
}
$ restbeast request post-example --repeat 5

# Via parameter

request post-example {
  method = "POST"
  url = "https://httpbin.org/post"
  headers = {
    "content-type" = "application/json"
  }
  body = {
    firstName = gofakeitFirstName()
    lastName = gofakeitLastName()
  }
  
  repeat = 5
}
Last Updated: 12/7/2021, 10:26:52 PM