Skip to content

Repository files navigation

httpparser

A small, dependency-free Go library for parsing .http files — the request format used by JetBrains IDEs and the VS Code REST Client extension — into structured Go types.

httpparser is a pure parsing library. It does not execute requests and it does not perform variable substitution: {{placeholder}} tokens are preserved verbatim in URLs, header values, and bodies so that callers can resolve them however they like. It depends only on the standard library.

Install

go get github.com/uradical/httpparser

Requires Go 1.21 or later.

Usage

package main

import (
	"fmt"
	"log"

	"github.com/uradical/httpparser"
)

func main() {
	f, err := httpparser.ParseFile("requests.http")
	if err != nil {
		log.Fatal(err)
	}

	for k, v := range f.Vars {
		fmt.Printf("var %s = %s\n", k, v)
	}

	for _, req := range f.Requests {
		fmt.Printf("%s %s %s (line %d)\n", req.Name, req.Method, req.URL, req.Line)
	}
}

Three entry points are provided, all returning a *File:

Function Input
Parse(r io.Reader) any reader
ParseFile(path string) a file on disk
ParseString(s string) an in-memory string

Types

type File struct {
	Vars     map[string]string // file-level @key=value declarations
	Requests []Request         // requests in file order
}

type Request struct {
	Name    string      // value of a "# @name <value>" annotation, or ""
	Method  string      // upper-cased HTTP method
	URL     string      // request target, placeholders left intact
	Headers http.Header // request headers, values left intact
	Body    []byte      // body trimmed of surrounding whitespace, or nil
	Line    int         // 1-indexed line number of the "METHOD URL" line
}

Supported .http syntax

  • Requests are written as METHOD URL [HTTP-version]. The optional HTTP version is accepted and ignored. Supported methods are GET, POST, PUT, DELETE, PATCH, HEAD, and OPTIONS.
  • Multiple requests in one file are separated by a line beginning with ###. Any text after ### (e.g. ### create a user) is treated as a separator label and ignored.
  • Headers follow the request line as Key: Value pairs, up to the first blank line.
  • Body is everything after that blank line, trimmed of leading and trailing whitespace.
  • File-level variables are declared with @key = value before the first request and collected into File.Vars.
  • Request names are set with a # @name <value> (or // @name <value>) comment. The value may be separated by whitespace and/or =.
  • Comments begin with # or // and are ignored (except for the @name annotation).

Example

@baseUrl = https://api.example.com
@token = abc123

# @name getUser
GET {{baseUrl}}/users/1 HTTP/1.1
Authorization: Bearer {{token}}

### create a user

# @name createUser
POST {{baseUrl}}/users
Content-Type: application/json

{
  "name": "Ada",
  "token": "{{token}}"
}

Parsing this yields two requests (getUser and createUser) and two file-level variables (baseUrl and token), with all {{...}} placeholders left untouched.

Errors

Syntax errors are reported as a *ParseError, which carries the 1-indexed Line, the offending Detail text, and a Kind that is one of the exported sentinels — ErrMalformedRequestLine, ErrUnsupportedMethod, or ErrMalformedHeader. Use errors.Is to check the kind and errors.As to read the location:

f, err := httpparser.ParseString(src)
if err != nil {
	var pe *httpparser.ParseError
	if errors.As(err, &pe) {
		log.Printf("syntax error on line %d: %v", pe.Line, pe.Kind)
	}
	if errors.Is(err, httpparser.ErrUnsupportedMethod) {
		// handle an unsupported method specifically
	}
}

I/O errors from ParseFile (e.g. a missing file) are returned as-is and are not *ParseError.

Testing

go test ./...   # unit tests and examples

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages