uhttp

package module
v0.0.0-...-520f6d5 Latest Latest
Warning

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

Go to latest
Published: Nov 29, 2019 License: MIT Imports: 13 Imported by: 0

README

uhttp

uhttp is a rudimentary implementation of HTTP over UDP, with support for both unicast and multicast.

Presently only a client is implemented.

Documentation

Documentation

Overview

Package uhttp provides a transport for HTTP over UDP. It implements http.RoundTripper so that it can plug in to stock Go HTTP libraries.

Caveat: No real standard for HTTP over UDP exists. This may not work well for all protocols that look like HTTP over UDP.

Index

Examples

Constants

This section is empty.

Variables

View Source
var DefaultClient = &Client{
	Transport: DefaultTransport,
}

DefaultClient is the Client used by the top-level Do and Get functions.

View Source
var ErrTimeout error = timeoutErr("timeout waiting for responses")
View Source
var Stop = errors.New("stop processing")

Functions

func Do

func Do(r *http.Request, wait time.Duration, fn func(sender net.Addr, resp *http.Response) error) error

Do issues r via DefaultClient, waits up to wait, and delivers any responses received to fn. If an error occurs, fn returns an error, or r.Context() expires, execution will return immediately. The sentinal error Stop may be returned by fn to cause Do to return immediately with no error.

func Get

func Get(url string, wait time.Duration, fn func(sender net.Addr, resp *http.Response) error) error

Get makes a GET request for url via DefaultClient, waits up to wait, and delivers any responses received to fn. If an error occurs, or fn returns an error, execution will return immediately. The sentinal error Stop may be returned by fn to cause Get to return immediately with no error.

Types

type Client

type Client struct {
	Transport RoundTripMultier
}

Client is an HTTP over UDP client. It is largely a simplistic wrapper over RoundTripMultier, meant to ease use for people familiar with http.Client.

Example (SSDP)
package main

import (
	"fmt"
	"net"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/dnesting/uhttp"
)

func main() {
	// This example performs an SSDP M-SEARCH to the local Multicast SSDP address.
	// It uses the uhttp.Client so as to receive multiple responses, but this same
	// transport could be used with a stock http.Client is you're OK only getting
	// the first response.
	client := uhttp.Client{
		Transport: &uhttp.Transport{
			// SSDP requires upper-case header names
			HeaderCanon: func(n string) string { return strings.ToUpper(n) },
			Repeat:      uhttp.RepeatAfter(50*time.Millisecond, 1),
		},
	}

	// Build M-SEARCH request
	req, _ := http.NewRequest("M-SEARCH", "", nil)
	req.URL.Host = "239.255.255.250:1900"
	req.URL.Path = "*"
	req.Header.Add("MAN", `"ssdp:discover"`)
	req.Header.Add("MX", "1")
	req.Header.Add("ST", "upnp:rootdevice")
	req.Header.Add("CPFN.UPNP.ORG", "Test")

	err := client.Do(req, 3*time.Second, func(sender net.Addr, resp *http.Response) error {
		fmt.Printf("From %s:\n", sender)
		resp.Write(os.Stdout)
		fmt.Println("---")
		return nil
	})
	if err != nil {
		fmt.Printf("error: %s\n", err)
	}
}
Output:

func (*Client) Do

func (c *Client) Do(r *http.Request, wait time.Duration, fn func(sender net.Addr, resp *http.Response) error) error

Do issues r, waits up to wait, and delivers any responses received to fn. If an error occurs, fn returns an error, or r.Context() expires, execution will return immediately. The sentinal error Stop may be returned by fn to cause Do to return immediately with no error.

func (*Client) Get

func (c *Client) Get(url string, wait time.Duration, fn func(sender net.Addr, resp *http.Response) error) error

Get makes a GET request for url, waits up to wait, and delivers any responses received to fn. If an error occurs, or fn returns an error, execution will return immediately. The sentinal error Stop may be returned by fn to cause Get to return immediately with no error.

type RepeatFunc

type RepeatFunc func(prev time.Duration) *time.Duration

RepeatFunc generates delays between successive repeats of a request. prev will contain the value of the previous repeat (zero for the first). It should return nil to stop repeating, and should not be called after that.

type RepeatGenerator

type RepeatGenerator func() RepeatFunc

RepeatGenerator produces RepeatFuncs.

func RepeatAfter

func RepeatAfter(delay time.Duration, num int) RepeatGenerator

RepeatAfter generates a RepeatFunc that returns delay num times, and then returns nil. If num is <= 0, this will return delay forever.

func RepeatJoin

func RepeatJoin(gens ...RepeatGenerator) RepeatGenerator

RepeatJoin generates a RepeatFunc that iterates over gens, instantiates RepeatFuncs from them, and returns values from each until they return nil, at which point it moves on to the next RepeatFunc. Returns nil once all resulting RepeatFunc instances have returned nil.

Example
tenHzFor3 := RepeatAfter(time.Second/10, 3)
oneHz := RepeatAfter(time.Second, 0)

joined := RepeatJoin(tenHzFor3, oneHz)()

for i := 0; i < 6; i++ {
	next := joined(0)
	if next == nil {
		break
	}
	fmt.Println(*next)
}
Output:

100ms
100ms
100ms
1s
1s
1s

func RepeatRandom

func RepeatRandom(low, high time.Duration, num int) RepeatGenerator

RepeatRandom generates a RepeatFunc that returns a random duration between [low, high), num times, and then returns nil. If num is 0, this will return delays forever.

type RoundTripMultier

type RoundTripMultier interface {
	http.RoundTripper

	// RoundTripMulti is responsible for delivering req, and waiting for responses to arrive.  For
	// each response received, fn will be invoked so that the caller can process it.  This method will
	// return when wait is reached (if non-zero), req.Context() expires, or an error occurs.  The fn
	// implementation may return the sentinal error Stop to halt processing without causing
	// RoundTripMulti to return an error.
	RoundTripMulti(req *http.Request, wait time.Duration, fn func(sender net.Addr, res *http.Response) error) error
}
var DefaultTransport RoundTripMultier = &Transport{
	MaxSize:  defaultPacketSize,
	WaitTime: 3 * time.Second,
}

type Transport

type Transport struct {
	// MaxSize is the maximum allowable size of an HTTP Request.  It cannot be larger than 64k (UDP limit).
	// A zero value will use the default of 8k.
	MaxSize int

	// WaitTime is the default time we will spend waiting for HTTP responses before returning.  A zero
	// value means wait forever.
	WaitTime time.Duration

	// HeaderCanon provides the canonical header name for the given header.  If this function returns an
	// empty string, the header will be omitted.  This is used when precise control over the case of the
	// resulting HTTP headers is needed.  Filtering headers such as "Host" may break compatibility with
	// HTTP/1.1 (but let's face it, none of this is standard).
	HeaderCanon func(name string) string

	// Repeat enables requests to be repeated, according to the delays returned by the resulting
	// RepeatFunc.
	Repeat RepeatGenerator
	// contains filtered or unexported fields
}

Transport implements http.RoundTripper and RoundTripMultier and allows for sending HTTP requests over UDP. It supports both unicast and multicast destinations.

Example (SSDP)
package main

import (
	"fmt"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/dnesting/uhttp"
)

func main() {
	// This example performs an SSDP M-SEARCH to the local Multicast SSDP address.
	// It leverages the stock Go http.Client with uhttp.Transport.  Only the first
	// response will be returned.
	client := http.Client{
		Transport: &uhttp.Transport{
			// SSDP requires upper-case header names
			HeaderCanon: func(n string) string { return strings.ToUpper(n) },
			WaitTime:    2 * time.Second,
			Repeat:      uhttp.RepeatAfter(50*time.Millisecond, 1),
		},
	}

	// Build M-SEARCH request
	req, _ := http.NewRequest("M-SEARCH", "", nil)
	req.URL.Host = "239.255.255.250:1900"
	req.URL.Path = "*"
	req.Header.Add("MAN", `"ssdp:discover"`)
	req.Header.Add("MX", "1")
	req.Header.Add("ST", "upnp:rootdevice")
	req.Header.Add("CPFN.UPNP.ORG", "Test")

	// Leverage stock http.Client to actually do the work.
	resp, err := client.Do(req)
	if err != nil {
		fmt.Printf("error: %s\n", err)
	} else {
		resp.Write(os.Stdout)
	}
}
Output:

func (*Transport) RoundTrip

func (t *Transport) RoundTrip(req *http.Request) (res *http.Response, err error)

RoundTrip issues a UDP HTTP request and waits for a single response. Returns when a response was received, when the req.Context() expires, or when t.MaxWait is reached (if non-zero).

func (*Transport) RoundTripMulti

func (t *Transport) RoundTripMulti(req *http.Request, wait time.Duration, fn func(sender net.Addr, r *http.Response) error) (err error)

RoundTripMulti issues a UDP HTTP request and calls fn for each response received. Returns when wait is reached (no error), req.Context() expires, an error occurs, or when fn returns an error. The sentinal error Stop may be returned by fn to cause this method to return immediately without error.

func (*Transport) WriteRequest

func (t *Transport) WriteRequest(w io.Writer, req *http.Request) error

WriteRequest writes req to w, in wire format. If req is larger than t.MaxSize, returns an error. This applies header canonicalization per t.HeaderCanon, if it's provided.

Jump to

Keyboard shortcuts

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