htutil

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

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

Go to latest
Published: Jul 6, 2022 License: MIT Imports: 7 Imported by: 0

README

snai.pe/go-htutil

GoDoc

go get snai.pe/go-htutil

Go HTTP utilities with no dependencies.

This package provides the following utilities:

  • an alternate implemenation of github.com/golang/gddo/httputil.NegotiateContentType. This package was in part motivated in providing a no-dependency package providing similar functionality.
  • a wrapper over net/url.URL that implements encoding.TextMarshaler and encoding.TextUnmarshaler.

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Acceptable

type Acceptable struct {
	// The value that is acceptable for a response. May contain wildcard
	// ('*') characters.
	Value string

	// The quality, between 0 and 1, of the accepted value.
	// 0 is special and means "not acceptable" per RFC 9110.
	// Default quality is 1.
	Quality float32

	// Params contains any extra optional parameters for this value.
	Params map[string]string
}

Acceptable represents an acceptable value for a response; typical use is representing media (or MIME) type value as per RFC2616 §14.1 in an Accept header, as well as encoding (Accept-Encoding), language (Accept-Language), and character set (Accept-Charset).

func NegotiateContent

func NegotiateContent(hdr http.Header, key string, offers ...string) (string, *Acceptable)

NegotiateContent returns the best matching offer for the passed header, as well as the entry that it matched against. The best matching offer is determined by the first matching offer, in slice order, when iterating over the accepted media types by order of precedence.

If no offer matches, ("", nil) is returned.

Example
http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
	ctype, _ := htutil.NegotiateContent(req.Header, "Accept",
		"text/plain",
		"application/json",
	)
	if ctype == "" {
		w.WriteHeader(http.StatusNotAcceptable)
		return
	}

	w.Header().Set("Content-Type", ctype)
	w.WriteHeader(http.StatusOK)
	switch ctype {
	case "application/json":
		fmt.Fprint(w, `{"message":"OK"}`)
	case "text/plain":
		fmt.Fprint(w, `OK`)
	}
})

server := http.Server{Addr: ":8080"}
defer server.Shutdown(context.Background())

go server.ListenAndServe()

get := func(addr, accept string) string {
	req, err := http.NewRequest("GET", addr, nil)
	if err != nil {
		log.Fatal(err)
	}
	req.Header.Set("Accept", accept)

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		return fmt.Sprintf("%d %s", resp.StatusCode, http.StatusText(resp.StatusCode))
	}

	var out strings.Builder
	io.Copy(&out, resp.Body)
	return out.String()
}

fmt.Println(get("http://localhost:8080", "*/*"))
fmt.Println(get("http://localhost:8080", "text/plain"))
fmt.Println(get("http://localhost:8080", "application/json"))
fmt.Println(get("http://localhost:8080", "text/html"))
fmt.Println(get("http://localhost:8080", "application/json, text/*;q=0.5, */*;q=0.1"))
fmt.Println(get("http://localhost:8080", ""))
Output:

OK
OK
{"message":"OK"}
406 Not Acceptable
{"message":"OK"}
406 Not Acceptable

func ParseAccept

func ParseAccept(accepts ...string) []Acceptable

ParseAccept parses the accept header, and returns a list of acceptable values, sorted by precedence. Any unparseable value is silently dropped.

func ParseAcceptable

func ParseAcceptable(v string) (Acceptable, error)

ParseAcceptable parses a single acceptable value, as laid out in an Accept{,-*} or Content-* header as per RFC2616 §14.1

func (Acceptable) Less

func (lhs Acceptable) Less(rhs Acceptable) bool

Less is a comparison function for two Acceptables. lhs is less than rhs if:

  • it has a quality factor that is less than rhs's quality factor
  • or, if both quality factors are equal, it is more specific than rhs.

An Acceptable is more specific if its value does not contain patterns, and if it has additional parameters. For instance, given the following header:

Accept: text/*, text/html, text/html;level=1, */*

The types have the following precedence:

  1. text/html;level=1
  2. text/html
  3. text/*
  4. */*

func (Acceptable) String

func (acc Acceptable) String() string

type URL

type URL struct {
	*url.URL
}

URL embeds *url.URL, but implements encoding.TextMarshaler and encoding.TextUnmarshaler to simply call MarshalBinary and UnmarshalBinary respectively.

func (URL) MarshalText

func (u URL) MarshalText() ([]byte, error)
Example
u, err := url.Parse("https://google.com")
if err != nil {
	log.Fatal(err)
}

value := T{
	URL: htutil.URL{u},
}

txt, err := json.Marshal(value)
if err != nil {
	log.Fatal(err)
}

fmt.Println(string(txt))
Output:

{"url":"https://google.com"}

func (*URL) UnmarshalText

func (u *URL) UnmarshalText(data []byte) error
Example
jsonstr := []byte(`{"url":"https://google.com"}`)

var value T
if err := json.Unmarshal(jsonstr, &value); err != nil {
	log.Fatal(err)
}

fmt.Println(value.URL)
Output:

https://google.com

Jump to

Keyboard shortcuts

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