Documentation
¶
Overview ¶
Package httptask provides some helper to wrap http server and some client job into task.
For client job, timeout is controled by context. the timeout info should be applied to whole task (send request + get response + read body). For example:
resp := GetResp().
From(Request("GET", "http://example.com")).
RetryN(3).
Cached()
buf, err := ReadBody().
From(resp).
With(task.Timeout(10 * time.Second)).
Defer(Consume(GetBody().From(resp))).
Get(ctx)
Example ¶
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
package main
import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"time"
"github.com/raohwork/task"
"github.com/raohwork/task/action"
)
const Addr = ":32100"
func server() task.Task {
srv := &http.Server{Addr: Addr}
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
size, err := io.Copy(io.Discard, r.Body)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
json.NewEncoder(w).Encode(map[string]interface{}{
"size": size,
})
})
mux.HandleFunc("/get", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"size":9}`))
})
srv.Handler = mux
return Server(srv)
}
type APIResp struct {
Size int64 `json:"size"`
}
func apiResp(_ context.Context, res *http.Response) (ret *APIResp, err error) {
// refer example of [DecodeWith] for simpler way to handle response body
defer res.Body.Close()
defer io.Copy(io.Discard, res.Body)
var v APIResp
if err = json.NewDecoder(res.Body).Decode(&v); err == nil {
ret = &v
}
return
}
func main() {
ctx, cancel := context.WithCancel(context.TODO())
done := server().Go(ctx)
defer func() { <-done }()
defer cancel()
// this will open and close the file multiple times when retrying (file is
// closed by http client). this is simpler and has nearly no impact on
// performance comparing to reading and sending large file over network.
req := Req(http.MethodPost, "http://127.0.0.1"+Addr).
From(BodyGenFrom(os.Open).By("testdata/super_large_file"))
// this will cache the file, preventing from opening again, so you have to
// close it after retrying.
// file := BodyGenFrom(os.Open).By("testdata/super_large_file").Cached()
// req := Request(http.MethodPost, "http://127.0.0.1"+Addr).
// From(body)
resp := action.Get(apiResp).
From(GetResp().From(req)).
With(task.Timeout(time.Second)).
TimedFailF(task.FixedDur(100 * time.Millisecond)).
RetryN(3).
// if you have cached the file, close it here.
// Defer(file.Do(action.CloseIt).NoErr()).
Cached()
actual, err := resp.Get(ctx)
if err != nil {
fmt.Println("unexpected error:", err)
return
}
fmt.Println(actual.Size)
}
Output: 9
Index ¶
- func AddCookie() action.Converter2[*http.Cookie, *http.Request, *http.Request]
- func AddHeader(k string) action.Converter2[string, *http.Request, *http.Request]
- func BodyGenBy[T io.Reader](f func() (T, error)) action.Data[io.Reader]
- func BodyGenFrom[P any, T io.Reader](f func(P) (T, error)) action.Converter[P, io.Reader]
- func Consume(body action.Data[io.ReadCloser]) func()
- func DecodeWith[T any, D Decoder](f func(io.Reader) D) action.Converter[io.Reader, T]
- func GetBody() action.Converter[*http.Response, io.ReadCloser]
- func GetResp() action.Converter[*http.Request, *http.Response]
- func ParseWith[T any](f func([]byte, any) error) action.Converter[[]byte, T]
- func ReadBody() action.Converter[*http.Response, []byte]
- func ReadResp() action.Converter2[*http.Client, *http.Request, *http.Response]
- func Reader[T io.Reader](i action.Data[T]) action.Data[io.Reader]
- func Req(method, url string) action.Converter[io.Reader, *http.Request]
- func Request(method, uri string) action.Data[*http.Request]
- func Server(s *http.Server, shutdownMods ...task.CtxMod) task.Task
- func SetContentLength() action.Converter2[int64, *http.Request, *http.Request]
- func SetHeader(k string) action.Converter2[string, *http.Request, *http.Request]
- type Decoder
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AddCookie ¶ added in v0.3.0
AddCookie creates an action.Converter2 to add a cookie to the request. It's suggested to write tour own converter to setup request at once.
func AddHeader ¶ added in v0.3.0
AddHeader creates an action.Converter2 to add a header to the request. It's suggested to write tour own converter to setup request at once.
Common usage: request = request.Then(AddHeader("Content-Type").By("text/json"))
func BodyGenBy ¶ added in v0.3.0
BodyGenBy creates an action.Data to generate request body.
f is a function to generate request body, typically a custom function.
func BodyGenFrom ¶ added in v0.3.0
BodyGenFrom creates an action.Converter to generate request body.
f is a function to generate request body from input parameter, like os.Open.
func Consume ¶ added in v0.3.0
func Consume(body action.Data[io.ReadCloser]) func()
Consume creates a function to consume the body of a response.
See example in DecodeWith.
func DecodeWith ¶ added in v0.3.0
DecodeWith creates an action.Converter to decode response body.
Example ¶
// for content of server(), see package example
ctx, cancel := context.WithCancel(context.TODO())
done := server().Go(ctx)
defer func() { <-done }()
defer cancel()
req := Request(http.MethodPost, "http://localhost"+Addr+"/get")
resp := GetResp().From(req)
body := GetBody().From(resp)
data, err := DecodeWith[map[string]interface{}](json.NewDecoder).
From(Reader(body)).
Defer(Consume(body)).
Get(ctx)
if err != nil {
panic(err)
}
fmt.Println(data)
Output: map[size:9]
func GetBody ¶ added in v0.3.0
GetBody creates an action.Converter to get the body of a response.
Extracted body is guaranteed to be non-nil.
func GetResp ¶ added in v0.3.0
GetResp creates an action.Converter to convert client request into response by sending it using http.DefaultClient.
The context is applied to the request before sending.
Common usage: resp, err := GetResp().From(request).Get(ctx)
func ParseWith ¶ added in v0.3.0
ParseWith creates an action.Converter to parse response body.
Example ¶
// for content of server(), see package example
ctx, cancel := context.WithCancel(context.TODO())
done := server().Go(ctx)
defer func() { <-done }()
defer cancel()
req := Request(http.MethodPost, "http://localhost"+Addr+"/get")
resp := GetResp().From(req)
body := ReadBody().From(resp)
data, err := ParseWith[map[string]interface{}](json.Unmarshal).
From(body).
Get(ctx)
if err != nil {
panic(err)
}
fmt.Println(data)
Output: map[size:9]
func ReadBody ¶ added in v0.3.0
ReadBody creates an action.Converter to read the whole body of a response.
func ReadResp ¶ added in v0.3.0
ReadResp creates an action.Converter2 to convert client request into response by sending it using an http client.
The context is applied to the request before sending.
Common usage: resp, err := ReadResp().By(client).From(request).Get(ctx)
func Reader ¶ added in v0.3.0
Reader creates an action.Data to help type checking.
See example in DecodeWith.
func Req ¶ added in v0.3.0
Req creates an action.Converter to build http client request.
func Server ¶
Server wraps s into a task so it can shutdown gracefully when canceled.
s.Shutdown is called with a new context modified by shutdownMods.
func SetContentLength ¶ added in v0.3.0
SetContentLength creates a converter to update the request. It's suggested to write tour own converter to setup request at once.
Common usage: request = request.Then(SetContentLength().By(len(body)))
func SetHeader ¶ added in v0.3.0
SetHeader creates an action.Converter2 to set a header to the request. It's suggested to write tour own converter to setup request at once.
Common usage: request = request.Then(SetHeader("Content-Type").By("text/json"))