0
0
mirror of https://github.com/thegeeklab/wp-plugin-go.git synced 2024-11-23 06:40:40 +00:00

feat: add network struct to expose http client and options (#12)

This commit is contained in:
Robert Kaussow 2023-08-15 22:46:42 +02:00 committed by GitHub
parent d3d0d5bb06
commit b5465c55af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 158 additions and 15 deletions

View File

@ -41,7 +41,7 @@ func Flags() []cli.Flag {
// Plugin flags // Plugin flags
flags = append(flags, loggingFlags(FlagsPluginCategory)...) flags = append(flags, loggingFlags(FlagsPluginCategory)...)
flags = append(flags, httpClientFlags(FlagsPluginCategory)...) flags = append(flags, networkFlags(FlagsPluginCategory)...)
return flags return flags
} }

View File

@ -22,7 +22,9 @@ import (
"net/http" "net/http"
"time" "time"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/thegeeklab/wp-plugin-go/trace"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"golang.org/x/net/proxy" "golang.org/x/net/proxy"
) )
@ -34,7 +36,23 @@ const (
HTTPTransportMaxIdleConns = 100 HTTPTransportMaxIdleConns = 100
) )
func httpClientFlags(category string) []cli.Flag { // Network contains options for connecting to the network.
type Network struct {
// Context for making network requests.
//
// If `trace` logging is requested the context will use `httptrace` to
// capture all network requests.
//nolint:containedctx
Context context.Context
/// Whether SSL verification is skipped
SkipVerify bool
// Client for making network requests.
Client *http.Client
}
func networkFlags(category string) []cli.Flag {
return []cli.Flag{ return []cli.Flag{
&cli.BoolFlag{ &cli.BoolFlag{
Name: "transport.skip-verify", Name: "transport.skip-verify",
@ -57,9 +75,10 @@ func httpClientFlags(category string) []cli.Flag {
} }
} }
func HTTPClientFromContext(ctx *cli.Context) *http.Client { func NetworkFromContext(ctx *cli.Context) Network {
var ( var (
skip = ctx.Bool("transport.skip-verify") skip = ctx.Bool("transport.skip-verify")
defaultContext = context.Background()
socks = ctx.String("transport.socks-proxy") socks = ctx.String("transport.socks-proxy")
socksoff = ctx.Bool("transport.socks-proxy-off") socksoff = ctx.Bool("transport.socks-proxy-off")
) )
@ -106,7 +125,17 @@ func HTTPClientFromContext(ctx *cli.Context) *http.Client {
transport.DialContext = dialer.DialContext transport.DialContext = dialer.DialContext
} }
return &http.Client{ if zerolog.GlobalLevel() == zerolog.TraceLevel {
defaultContext = trace.HTTP(defaultContext)
}
client := &http.Client{
Transport: transport, Transport: transport,
} }
return Network{
Context: defaultContext,
SkipVerify: skip,
Client: client,
}
} }

View File

@ -17,7 +17,6 @@ package plugin
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"os" "os"
"strings" "strings"
@ -46,7 +45,8 @@ type Options struct {
type Plugin struct { type Plugin struct {
app *cli.App app *cli.App
execute ExecuteFunc execute ExecuteFunc
client *http.Client // Network options.
Network Network
// Metadata of the current pipeline. // Metadata of the current pipeline.
Metadata Metadata Metadata Metadata
} }
@ -87,7 +87,7 @@ func (p *Plugin) action(ctx *cli.Context) error {
} }
p.Metadata = MetadataFromContext(ctx) p.Metadata = MetadataFromContext(ctx)
p.client = HTTPClientFromContext(ctx) p.Network = NetworkFromContext(ctx)
if p.execute == nil { if p.execute == nil {
panic("plugin execute function is not set") panic("plugin execute function is not set")
@ -96,11 +96,6 @@ func (p *Plugin) action(ctx *cli.Context) error {
return p.execute(ctx.Context, ctx) return p.execute(ctx.Context, ctx)
} }
// HTTPClient returns the http.Client instance.
func (p *Plugin) HTTPClient() *http.Client {
return p.client
}
// Run the plugin. // Run the plugin.
func (p *Plugin) Run() { func (p *Plugin) Run() {
if err := p.app.Run(os.Args); err != nil { if err := p.app.Run(os.Args); err != nil {

119
trace/http.go Normal file
View File

@ -0,0 +1,119 @@
// Copyright (c) 2019, Drone Plugins project authors
// Copyright (c) 2021, Robert Kaussow <mail@thegeeklab.de>
// Use of this source code is governed by an Apache 2.0 license that can be
// found in the LICENSE file.
package trace
import (
"context"
"crypto/tls"
"fmt"
"net/http/httptrace"
"net/textproto"
"github.com/rs/zerolog/log"
)
// HTTP uses httptrace to log all network activity for HTTP requests.
func HTTP(ctx context.Context) context.Context {
return httptrace.WithClientTrace(ctx, &httptrace.ClientTrace{
GetConn: func(hostPort string) {
log.Trace().Str("host-port", hostPort).Msg("ClientTrace.GetConn")
},
GotConn: func(connInfo httptrace.GotConnInfo) {
log.Trace().
Str("local-address", connInfo.Conn.LocalAddr().String()).
Str("remote-address", connInfo.Conn.RemoteAddr().String()).
Bool("reused", connInfo.Reused).
Bool("was-idle", connInfo.WasIdle).
Dur("idle-time", connInfo.IdleTime).
Msg("ClientTrace.GoConn")
},
PutIdleConn: func(err error) {
log.Trace().Err(err).Msg("ClientTrace.GoConn")
},
GotFirstResponseByte: func() {
log.Trace().Msg("ClientTrace.GotFirstResponseByte")
},
Got100Continue: func() {
log.Trace().Msg("ClientTrace.Got100Continue")
},
Got1xxResponse: func(code int, header textproto.MIMEHeader) error {
log.Trace().
Int("code", code).
Str("header", fmt.Sprint(header)).
Msg("ClientTrace.Got1xxxResponse")
return nil
},
DNSStart: func(dnsInfo httptrace.DNSStartInfo) {
log.Trace().Str("host", dnsInfo.Host).Msg("ClientTrace.DNSStart")
},
DNSDone: func(dnsInfo httptrace.DNSDoneInfo) {
log.Trace().
Str("addresses", fmt.Sprint(dnsInfo.Addrs)).
Err(dnsInfo.Err).
Bool("coalesced", dnsInfo.Coalesced).
Msg("ClientTrace.DNSDone")
},
ConnectStart: func(network, addr string) {
log.Trace().
Str("network", network).
Str("address", addr).
Msg("ClientTrace.ConnectStart")
},
ConnectDone: func(network, addr string, err error) {
log.Trace().
Str("network", network).
Str("address", addr).
Err(err).
Msg("ClientTrace.ConnectDone")
},
TLSHandshakeStart: func() {
log.Trace().Msg("ClientTrace.TLSHandshakeStart")
},
TLSHandshakeDone: func(connState tls.ConnectionState, err error) {
log.Trace().
Uint16("version", connState.Version).
Bool("handshake-complete", connState.HandshakeComplete).
Bool("did-resume", connState.DidResume).
Uint16("cipher-suite", connState.CipherSuite).
Str("negotiated-protocol", connState.NegotiatedProtocol).
Str("server-name", connState.ServerName).
Err(err).
Msg("ClientTrace.TLSHandshakeDone")
},
WroteHeaderField: func(key string, value []string) {
log.Trace().
Str("key", key).
Strs("values", value).
Msg("ClientTrace.WroteHeaderField")
},
WroteHeaders: func() {
log.Trace().Msg("ClientTrace.WroteHeaders")
},
Wait100Continue: func() {
log.Trace().Msg("ClientTrace.Wait100Continue")
},
WroteRequest: func(reqInfo httptrace.WroteRequestInfo) {
log.Trace().Err(reqInfo.Err).Msg("ClientTrace.WroteRequest")
},
})
}