{"id":21970821,"url":"https://github.com/kingluo/burl","last_synced_at":"2026-04-13T13:02:49.408Z","repository":{"id":214641226,"uuid":"721009338","full_name":"kingluo/burl","owner":"kingluo","description":"A simple but flexible HTTP/3 testing framework based on bash and curl","archived":false,"fork":false,"pushed_at":"2024-01-04T07:29:42.000Z","size":45,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-10-23T20:19:08.879Z","etag":null,"topics":["api-rest","apisix","bash","curl","envoy","http3","jq","json","nginx","quic","shell-scripting","soap","test-automation","testing-tools","xml"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kingluo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-11-20T07:03:55.000Z","updated_at":"2023-12-30T09:45:33.000Z","dependencies_parsed_at":"2024-01-02T09:22:54.970Z","dependency_job_id":"e82c27cc-aa5b-4635-94ab-62843507f9b1","html_url":"https://github.com/kingluo/burl","commit_stats":null,"previous_names":["kingluo/curl-test"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/kingluo/burl","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kingluo%2Fburl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kingluo%2Fburl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kingluo%2Fburl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kingluo%2Fburl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kingluo","download_url":"https://codeload.github.com/kingluo/burl/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kingluo%2Fburl/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31753551,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-13T09:16:15.125Z","status":"ssl_error","status_checked_at":"2026-04-13T09:16:05.023Z","response_time":93,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api-rest","apisix","bash","curl","envoy","http3","jq","json","nginx","quic","shell-scripting","soap","test-automation","testing-tools","xml"],"created_at":"2024-11-29T14:43:12.964Z","updated_at":"2026-04-13T13:02:49.356Z","avatar_url":"https://github.com/kingluo.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# burl: bash + curl\n\nA simple but flexible HTTP/3 testing framework based on bash and curl.\n\nCheck out the blog post for background and design details:\n\nhttp://luajit.io/posts/a-simple-http3-testing-framework-based-on-bash-and-curl/\n\n## Design\n\n1. The test file contains one or more test cases, and an optional initial part of the file header, such as configuring nginx.conf and starting nginx via template rendering.\n2. Each test case consists of three parts:\n    1. Construct and send the request, and save the response header and response body to files for subsequent steps.\n    2. Verify the response headers, for example using \"grep\".\n    3. Parse and validate the response body, for example with the \"jq\" expression.\n3. **Easily extensible**, you can validate responses (steps ii and iii) using any command or other advanced script, such as Python.\n4. **Failure of any command will stop the testing process (enabled via the \"set -euo pipefail\" bash option).**\n5. The test process is echoed by default (enabled via the \"set -x\" bash option).\n\n## Installation\n\n```bash\ncurl -s https://raw.githubusercontent.com/kingluo/burl/main/install-burl.sh |bash\n```\n\n## Usage\n\nUse shebang (`#!`) or run the burl command:\n\n```\n# specify the test files or dirs\n# for dir, burl will find the test files recursively under the dir\nburl \u003ctest-file1\u003e \u003ctest-file2\u003e \u003cdir1\u003e \u003cdir2\u003e ...\n\n# run burl directly, it will try to find test files in ./t\nburl\n```\n\nTest case template:\n\n```bash\n#!/usr/bin/env burl\n\n# Optional initialization here...\n# Before all test cases are executed.\n# For example, render nginx.conf and start nginx.\nSET NGX_CONF_HTTP \u003c\u003cEOF\nupstream test_backend {\n    server $(dig +short nghttp2.org):443;\n\n    keepalive 320;\n    keepalive_requests 1000;\n    keepalive_timeout 60s;\n}\nEOF\n\nSET NGX_CONF \u003c\u003c'EOF'\nlocation / {\n    add_header Alt-Svc 'h3=\":443\"; ma=86400';\n    proxy_http_version 1.1;\n    proxy_set_header Connection \"\";\n    proxy_set_header Host \"nghttp2.org\";\n    proxy_pass https://test_backend;\n}\nEOF\n\nSTART_NGX\n\nTEST 1: test case\n\n# Send request\n# REQ is a curl wrapper so you can apply any curl options to suit your needs.\n# Check https://curl.se/docs/manpage.html for details.\nREQ /httpbin/anything --http3 -d foo=bar -d hello=world\n\n# Validate the response headers\n# HEADER is a grep wrapper so you can apply any grep options and regular expressions to suit your needs.\nHEADER -x \"HTTP/3 200\"\n\n# Validate the response body, e.g. JSON body\n# JQ is a jq wrapper so you can apply any jq options and jq expression to suit your needs.\nJQ '.method==\"POST\"'\nJQ '.form=={\"foo\":\"bar\",\"hello\":\"world\"}'\n\nTEST 2: another test case\n# ...\n\n# More test cases...\n```\n\n## Examples\n\nCheck [examples](examples).\n\n### APISIX\n\n1. Test MTLS whitelist\n\n```bash\nTEST 2: route-level mtls, skip mtls\n\nADMIN put /ssls/1 -d '{\n    \"cert\": \"'\"$(\u003c${BURL_ROOT}/examples/certs/server.crt)\"'\",\n    \"key\": \"'\"$(\u003c${BURL_ROOT}/examples/certs/server.key)\"'\",\n    \"snis\": [\n        \"localhost\"\n    ],\n    \"client\": {\n        \"ca\": \"'\"$(\u003c${BURL_ROOT}/examples/certs/ca.crt)\"'\",\n        \"depth\": 10,\n        \"skip_mtls_uri_regex\": [\n            \"/httpbin/get\"\n        ]\n    }\n}'\n\nsleep 1\n\nREQ /httpbin/get --http3-only\n\n# validate the response headers\nHEADER -x \"HTTP/3 200\"\n\n# validate the response body, e.g. JSON body\nJQ '.headers[\"X-Forwarded-Host\"] == \"localhost\"'\n```\n\n2. Test HTTP/3 Alt-Svc\n\n```bash\nADMIN put /ssls/1 -d '{\n    \"cert\": \"'\"$(\u003c${BURL_ROOT}/examples/certs/server.crt)\"'\",\n    \"key\": \"'\"$(\u003c${BURL_ROOT}/examples/certs/server.key)\"'\",\n    \"snis\": [\n        \"localhost\"\n    ]\n}'\n\nADMIN put /routes/1 -s -d '{\n    \"uri\": \"/httpbin/*\",\n    \"upstream\": {\n        \"scheme\": \"https\",\n        \"type\": \"roundrobin\",\n        \"nodes\": {\n            \"nghttp2.org\": 1\n        }\n    }\n}'\n\n\n\nTEST 1: check if alt-svc works\n\naltsvc_cache=$(mktemp)\nGC \"rm -f ${altsvc_cache}\"\n\nREQ /httpbin/get -k --alt-svc ${altsvc_cache}\nHEADER -x \"HTTP/1.1 200 OK\"\n\nREQ /httpbin/get -k --alt-svc ${altsvc_cache}\nHEADER -x \"HTTP/3 200\"\n\n```\n\n### SOAP\n\nSend a SOAP request to the web service and verify the response.\n\nConstruct JSON input using `jo` and validate JSON output using `jq` expressions.\n\nPowered by Python [zeep](https://docs.python-zeep.org/en/master/).\n\n```bash\nTEST 1: test a simple Web Service: Add two numbers: 1+2==3\n\nSOAP_REQ \\\n    'https://ecs.syr.edu/faculty/fawcett/Handouts/cse775/code/calcWebService/Calc.asmx?WSDL' \\\n    Add `jo a=1 b=2` '.==3'\n\n```\n\n### XML\n\nPowered by [xmltodict](https://pypi.org/project/xmltodict/).\n\n```bash\nTEST 2: GET XML\n\n# send request\nREQ /httpbin/xml\n\n# validate the response headers\nHEADER -x \"HTTP/1.1 200 OK\"\nHEADER -x \"Content-Type: application/xml\"\n\n# validate the response XML body\nXML '.slideshow[\"@author\"]==\"Yours Truly\"'\n\n```\n\n## Common Functions\n\n### REQ()\n\nThe curl wrapper function works similarly to the curl command, except that it saves the response headers and body into two temporary files for later use.\nYou can apply any [curl options](https://curl.se/docs/manpage.html) to it.\n\n### HEADER()\n\ngrep wrapper to validate current response headers (`${CURL_RSP_HEADERS}`).\n\n### BODY()\n\ngrep wrapper to validate current response headers (`${CURL_RSP_BODY}`).\n\n### JQ()\n\n[jq](https://jqlang.github.io/jq/) wrapper to validate current response headers (`${CURL_RSP_BODY}`).\n\n### GC()\n\nPut a code snippet or function into the global gc list so that it is executed at the end of the current test file execution, for example to clean up temporary files.\n\nYou can execute GC() multiple times to add multiple code snippets or functions.\n\n```bash\n# code snippet\nGC \"rm -f ${my_temp_files}\"\n\n# customized gc function\nGC my_gc_func\n```\n\n### START_NGX()\n\nRender the nginx.conf and start nginx. Assume you set the conf related variables.\n\n### ADMIN()\n\nConfigure APISIX via admin API, which is simple curl wrapper, so you can apply any curl options to it.\n\n### XML()\n\nParse and validate the XML response body (`${CURL_RSP_BODY}`).\n\n### SOAP()\n\nExecute SOAP operation on remote web service and validate the output via jq expression.\n\n### SET()\n\nAssign here-doc text to variable.\n\n```bash\nSET NGX_CONF \u003c\u003c'EOF'\nlocation / {\n    add_header Alt-Svc 'h3=\":443\"; ma=86400';\n    proxy_http_version 1.1;\n    proxy_set_header Connection \"\";\n    proxy_set_header Host \"nghttp2.org\";\n    proxy_pass https://test_backend;\n}\nEOF\n```\n\n### TEST()\n\necho the test case title.\n\n## Global Variables\n\n### BURL_ROOT\n\nThe root path of burl installation, i.e `/usr/local/burl`\n\n### TEST_HOST\n\ncurl host, only set when you need to test the origin server directly.\n\ndefault value: `localhost`.\n\n### TEST_PORT\n\ncurl port, only set when you need to test the origin server directly.\n\ndefault value: `443`.\n\n### TEST_SCHEME\n\ncurl scehme, only set when you need to test the origin server directly.\n\ndefault value: `https`.\n\n### CURL_RSP_HEADERS\n\nthe file that contains the current response headers content, which will be changed after next `REQ()`.\n\n### CURL_RSP_BODY\n\nthe file that contains the current response headers body content, which will be changed after next `REQ()`.\n\n### NGX_CONF_MAIN\n\nnginx main configuration directives, used by `START_NGX()`.\n\n### NGX_CONF_HTTP\n\nnginx http configuration directives, used by `START_NGX()`.\n\n### NGX_CONF\n\nnginx location configuration directives of the default server (80 and 443, including HTTP/3), used by `START_NGX()`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkingluo%2Fburl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkingluo%2Fburl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkingluo%2Fburl/lists"}