working commit
This commit is contained in:
@@ -0,0 +1,10 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
const (
|
||||||
|
confdir = "/home/ziggi/Projects/mproxy/etc/mproxy"
|
||||||
|
rundir = "/home/ziggi/Projects/mproxy/tmp/run"
|
||||||
|
logdir = "/home/ziggi/Projects/mproxy/tmp/log"
|
||||||
|
datadir = "/home/ziggi/Projects/mproxy/tmp/data"
|
||||||
|
version = "0.0.1"
|
||||||
|
srvname = "mproxyd"
|
||||||
|
)
|
||||||
+38
-38
@@ -4,66 +4,66 @@
|
|||||||
package handler
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sync"
|
"sync"
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"mproxy/app/router"
|
"mproxy/app/router"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (hand *Handler) ConnectTo(rctx *router.Context) {
|
func (hand *Handler) ConnectTo(rctx *router.Context) {
|
||||||
hostaddr := rctx.GetHost()
|
hostaddr := rctx.GetHost()
|
||||||
hand.logg.Debugf("Hostaddr: [%s]", hostaddr)
|
hand.logg.Debugf("Hostaddr: [%s]", hostaddr)
|
||||||
|
|
||||||
destConn, err := net.Dial("tcp", hostaddr)
|
destConn, err := net.Dial("tcp", hostaddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
hand.logg.Errorf("ConnectTo error: %v", err)
|
hand.logg.Errorf("ConnectTo error: %v", err)
|
||||||
rctx.SetStatus(http.StatusInternalServerError)
|
rctx.SetStatus(http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
hand.logg.Errorf("ConnectTo error: %v", err)
|
hand.logg.Errorf("ConnectTo error: %v", err)
|
||||||
rctx.SetStatus(http.StatusInternalServerError)
|
rctx.SetStatus(http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer destConn.Close()
|
defer destConn.Close()
|
||||||
hijacker, hijackerOk := rctx.Writer.(http.Hijacker)
|
hijacker, hijackerOk := rctx.Writer.(http.Hijacker)
|
||||||
if !hijackerOk {
|
if !hijackerOk {
|
||||||
hand.logg.Errorf("Hijacking not OK")
|
hand.logg.Errorf("Hijacking not OK")
|
||||||
rctx.SetStatus(http.StatusInternalServerError)
|
rctx.SetStatus(http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
|
|
||||||
}
|
}
|
||||||
rctx.SetStatus(http.StatusOK)
|
rctx.SetStatus(http.StatusOK)
|
||||||
clientConn, _, err := hijacker.Hijack()
|
clientConn, _, err := hijacker.Hijack()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
hand.logg.Errorf("Hijacking error: %v", err)
|
hand.logg.Errorf("Hijacking error: %v", err)
|
||||||
rctx.SetStatus(http.StatusInternalServerError)
|
rctx.SetStatus(http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
copyTo := func() {
|
copyTo := func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
_, err = io.Copy(clientConn, destConn)
|
_, err = io.Copy(clientConn, destConn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
hand.logg.Errorf("CopyTo error: %v", err)
|
hand.logg.Errorf("CopyTo error: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
copyFrom := func() {
|
copyFrom := func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
_, err = io.Copy(destConn, clientConn)
|
_, err = io.Copy(destConn, clientConn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
hand.logg.Errorf("CopyFrom error: %v", err)
|
hand.logg.Errorf("CopyFrom error: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go copyTo()
|
go copyTo()
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go copyFrom()
|
go copyFrom()
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+38
-40
@@ -4,56 +4,54 @@
|
|||||||
package handler
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"io"
|
"time"
|
||||||
"context"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"mproxy/app/router"
|
"mproxy/app/router"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (hand *Handler) PlainCall(rctx *router.Context) {
|
func (hand *Handler) PlainCall(rctx *router.Context) {
|
||||||
hostaddr := rctx.GetHost()
|
hostaddr := rctx.GetHost()
|
||||||
hand.logg.Debugf("Hostaddr: [%s]", hostaddr)
|
hand.logg.Debugf("Hostaddr: [%s]", hostaddr)
|
||||||
|
|
||||||
|
|
||||||
ctx := rctx.GetContext()
|
ctx := rctx.GetContext()
|
||||||
ctx, _ = context.WithTimeout(ctx, 5*time.Second)
|
ctx, _ = context.WithTimeout(ctx, 5*time.Second)
|
||||||
reqMethod := rctx.Request.Method
|
reqMethod := rctx.Request.Method
|
||||||
reqUrl := rctx.URL().String()
|
reqUrl := rctx.URL().String()
|
||||||
reqBody := rctx.Request.Body
|
reqBody := rctx.Request.Body
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, reqMethod, reqUrl, reqBody)
|
||||||
|
if err != nil {
|
||||||
|
hand.logg.Errorf("Create request error: %v", err)
|
||||||
|
rctx.SetStatus(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for key := range rctx.Request.Header {
|
||||||
|
val := rctx.Request.Header.Get(key)
|
||||||
|
req.Header.Add(key, val)
|
||||||
|
}
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, reqMethod, reqUrl, reqBody)
|
httpClient := &http.Client{}
|
||||||
if err != nil {
|
resp, err := httpClient.Do(req)
|
||||||
hand.logg.Errorf("Create request error: %v", err)
|
if err != nil {
|
||||||
rctx.SetStatus(http.StatusInternalServerError)
|
hand.logg.Errorf("Call request error: %v", err)
|
||||||
return
|
rctx.SetStatus(http.StatusInternalServerError)
|
||||||
}
|
return
|
||||||
for key := range rctx.Request.Header {
|
}
|
||||||
val := rctx.Request.Header.Get(key)
|
// Copy headers from remote side
|
||||||
req.Header.Add(key, val)
|
for key := range resp.Header {
|
||||||
}
|
val := resp.Header.Get(key)
|
||||||
|
rctx.Writer.Header().Set(key, val)
|
||||||
httpClient := &http.Client{}
|
}
|
||||||
resp, err := httpClient.Do(req)
|
// Copy status code
|
||||||
if err != nil {
|
rctx.SetStatus(resp.StatusCode)
|
||||||
hand.logg.Errorf("Call request error: %v", err)
|
// Copy body
|
||||||
rctx.SetStatus(http.StatusInternalServerError)
|
_, err = io.Copy(rctx.Writer, resp.Body)
|
||||||
return
|
if err != nil {
|
||||||
}
|
hand.logg.Errorf("Copy resp body error: %v", err)
|
||||||
// Copy headers from remote side
|
return
|
||||||
for key := range resp.Header {
|
}
|
||||||
val := resp.Header.Get(key)
|
|
||||||
rctx.Writer.Header().Set(key, val)
|
|
||||||
}
|
|
||||||
// Copy status code
|
|
||||||
rctx.SetStatus(resp.StatusCode)
|
|
||||||
// Copy body
|
|
||||||
_, err = io.Copy(rctx.Writer, resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
hand.logg.Errorf("Copy resp body error: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import (
|
|||||||
"mproxy/app/servoper"
|
"mproxy/app/servoper"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (hand *Handler) SendHello(rctx *router.Context) {
|
func (hand *Handler) GetHello(rctx *router.Context) {
|
||||||
params := &servoper.SendHelloParams{}
|
params := &servoper.GetHelloParams{}
|
||||||
res, _ := hand.seop.SendHello(params)
|
res, _ := hand.seop.GetHello(params)
|
||||||
hand.SendResult(rctx, res)
|
hand.SendResult(rctx, res)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -158,9 +158,9 @@ func (route Route) Match(req *http.Request) bool {
|
|||||||
if req.Method != route.Method {
|
if req.Method != route.Method {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if req.Method == http.MethodConnect {
|
if req.Method == http.MethodConnect {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
match := route.Regexp.MatchString(req.URL.Path)
|
match := route.Regexp.MatchString(req.URL.Path)
|
||||||
if match {
|
if match {
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ func (svc *Service) Build() error {
|
|||||||
svc.rout.Use(router.NewCorsMiddleware())
|
svc.rout.Use(router.NewCorsMiddleware())
|
||||||
svc.rout.Use(svc.hand.AuthMiddleware)
|
svc.rout.Use(svc.hand.AuthMiddleware)
|
||||||
|
|
||||||
svc.rout.Get(`/v3/api/service/hello`, svc.hand.SendHello)
|
svc.rout.Get(`/v3/api/service/hello`, svc.hand.GetHello)
|
||||||
|
|
||||||
svc.rout.Connect(``, svc.hand.ConnectTo)
|
svc.rout.Connect(``, svc.hand.ConnectTo)
|
||||||
|
|
||||||
|
|||||||
@@ -3,16 +3,16 @@
|
|||||||
*/
|
*/
|
||||||
package servoper
|
package servoper
|
||||||
|
|
||||||
type SendHelloParams struct{}
|
type GetHelloParams struct{}
|
||||||
|
|
||||||
type SendHelloResult struct {
|
type GetHelloResult struct {
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
Alive bool `json:"alive"`
|
Alive bool `json:"alive"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (oper *Operator) SendHello(param *SendHelloParams) (*SendHelloResult, error) {
|
func (oper *Operator) GetHello(param *GetHelloParams) (*GetHelloResult, error) {
|
||||||
var err error
|
var err error
|
||||||
res := &SendHelloResult{
|
res := &GetHelloResult{
|
||||||
Alive: true,
|
Alive: true,
|
||||||
Message: "hello",
|
Message: "hello",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
|
||||||
|
*/
|
||||||
|
package servcmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
uuidRegex = `^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$`
|
||||||
|
defaultHostname = "localhost:1025"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewServiceUtil() *ServiceUtil {
|
||||||
|
return &ServiceUtil{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServiceUtil struct {
|
||||||
|
getHelloParams GetHelloParams
|
||||||
|
commonServiceParams CommonServiceParams
|
||||||
|
}
|
||||||
|
|
||||||
|
type CommonServiceParams struct {
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
Hostname string
|
||||||
|
Timeout uint64
|
||||||
|
SkipTLSVerify bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (util *ServiceUtil) Makeservcmds() *cobra.Command {
|
||||||
|
var subCmd = &cobra.Command{
|
||||||
|
Use: "service",
|
||||||
|
Short: "Service operations",
|
||||||
|
}
|
||||||
|
const defaultTimeout uint64 = 10
|
||||||
|
|
||||||
|
subCmd.PersistentFlags().StringVarP(&util.commonServiceParams.Username, "user", "U", util.commonServiceParams.Username, "Username")
|
||||||
|
subCmd.PersistentFlags().StringVarP(&util.commonServiceParams.Password, "pass", "P", util.commonServiceParams.Password, "Password")
|
||||||
|
subCmd.PersistentFlags().StringVarP(&util.commonServiceParams.Hostname, "host", "X", defaultHostname, "Hostname")
|
||||||
|
subCmd.PersistentFlags().Uint64VarP(&util.commonServiceParams.Timeout, "timeout", "T", defaultTimeout, "Operation timeout")
|
||||||
|
subCmd.PersistentFlags().BoolVarP(&util.commonServiceParams.SkipTLSVerify, "skipVerify", "S", true, "Skip server certificate verify")
|
||||||
|
subCmd.MarkFlagsRequiredTogether("user", "pass")
|
||||||
|
|
||||||
|
vi := viper.New()
|
||||||
|
vi.SetEnvPrefix("mproxy")
|
||||||
|
vi.BindEnv("user")
|
||||||
|
vi.BindEnv("pass")
|
||||||
|
util.commonServiceParams.Username = vi.GetString("user")
|
||||||
|
util.commonServiceParams.Password = vi.GetString("pass")
|
||||||
|
|
||||||
|
// GetService
|
||||||
|
var getservcmd = &cobra.Command{
|
||||||
|
Use: "get [user:pass@]hostname[:port]",
|
||||||
|
Short: "Get service hello",
|
||||||
|
Args: cobra.ExactArgs(1),
|
||||||
|
Run: util.GetHello,
|
||||||
|
}
|
||||||
|
subCmd.AddCommand(getservcmd)
|
||||||
|
|
||||||
|
return subCmd
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
|
||||||
|
*/
|
||||||
|
package servcmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"mproxy/pkg/servcli"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetHello
|
||||||
|
type GetHelloParams struct {
|
||||||
|
Address string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (util *ServiceUtil) GetHello(cmd *cobra.Command, args []string) {
|
||||||
|
util.getHelloParams.Address = args[0]
|
||||||
|
res, err := util.getHello(&util.commonServiceParams, &util.getHelloParams)
|
||||||
|
printResponse(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetHelloResult struct{}
|
||||||
|
|
||||||
|
func (util *ServiceUtil) getHello(common *CommonServiceParams, params *GetHelloParams) (*GetHelloResult, error) {
|
||||||
|
var err error
|
||||||
|
res := &GetHelloResult{}
|
||||||
|
|
||||||
|
timeout := time.Duration(common.Timeout) * time.Second
|
||||||
|
ctx, _ := context.WithTimeout(context.Background(), timeout)
|
||||||
|
ref, err := servcli.ParsePath(params.Address)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
ref.SetUserinfo(common.Username, common.Password)
|
||||||
|
mw := servcli.NewBasicAuthMiddleware(ref.Userinfo())
|
||||||
|
cli := servcli.NewClient(nil, mw)
|
||||||
|
err = cli.GetHello(ctx, ref.Raw())
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
|
||||||
|
*/
|
||||||
|
package servcmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
func printResponse(res any, err error) {
|
||||||
|
type Response struct {
|
||||||
|
Error bool `json:"error" yaml:"error"`
|
||||||
|
Message string `json:"message,omitempty" yaml:"message,omitempty"`
|
||||||
|
Result any `json:"result,omitempty" yaml:"result,omitempty"`
|
||||||
|
}
|
||||||
|
resp := Response{}
|
||||||
|
if err != nil {
|
||||||
|
resp.Error = true
|
||||||
|
resp.Message = err.Error()
|
||||||
|
} else {
|
||||||
|
resp.Result = res
|
||||||
|
}
|
||||||
|
respBytes, _ := yaml.Marshal(resp)
|
||||||
|
fmt.Printf("---\n%s\n", string(respBytes))
|
||||||
|
}
|
||||||
@@ -8,6 +8,8 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"mproxy/cmd/mproxyctl/servcmd"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -28,12 +30,15 @@ func (util *Util) Build() error {
|
|||||||
execName := filepath.Base(os.Args[0])
|
execName := filepath.Base(os.Args[0])
|
||||||
rootCmd := &cobra.Command{
|
rootCmd := &cobra.Command{
|
||||||
Use: execName,
|
Use: execName,
|
||||||
Short: "\nOperation with artefacts: files, images, service accounts and grants",
|
Short: "\nGet hello from service",
|
||||||
SilenceUsage: true,
|
SilenceUsage: true,
|
||||||
}
|
}
|
||||||
rootCmd.CompletionOptions.DisableDefaultCmd = true
|
rootCmd.CompletionOptions.DisableDefaultCmd = true
|
||||||
util.rootCmd = rootCmd
|
util.rootCmd = rootCmd
|
||||||
|
|
||||||
|
ServiceUtil := servcmd.NewServiceUtil()
|
||||||
|
rootCmd.AddCommand(ServiceUtil.MakeServiceCmds())
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,17 +5,30 @@ go 1.26.2
|
|||||||
require (
|
require (
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/spf13/cobra v1.10.2
|
github.com/spf13/cobra v1.10.2
|
||||||
|
github.com/spf13/viper v1.21.0
|
||||||
github.com/stretchr/testify v1.11.1
|
github.com/stretchr/testify v1.11.1
|
||||||
go.yaml.in/yaml/v4 v4.0.0-rc.4
|
go.yaml.in/yaml/v4 v4.0.0-rc.4
|
||||||
|
sigs.k8s.io/yaml v1.6.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
|
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.9 // indirect
|
github.com/sagikazarmark/locafero v0.11.0 // indirect
|
||||||
|
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
|
||||||
|
github.com/spf13/afero v1.15.0 // indirect
|
||||||
|
github.com/spf13/cast v1.10.0 // indirect
|
||||||
|
github.com/spf13/pflag v1.0.10 // indirect
|
||||||
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
|
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||||
|
golang.org/x/sys v0.29.0 // indirect
|
||||||
|
golang.org/x/text v0.28.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,25 +2,63 @@ github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo
|
|||||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||||
|
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||||
|
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||||
|
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||||
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||||
|
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
|
||||||
|
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
|
||||||
|
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
|
||||||
|
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
|
||||||
|
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||||
|
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
|
||||||
|
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
||||||
|
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
||||||
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
||||||
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
||||||
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
|
|
||||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
|
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||||
|
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
|
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
|
||||||
|
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
|
||||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
|
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||||
|
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||||
|
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
|
||||||
|
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
|
||||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||||
go.yaml.in/yaml/v4 v4.0.0-rc.4 h1:UP4+v6fFrBIb1l934bDl//mmnoIZEDK0idg1+AIvX5U=
|
go.yaml.in/yaml/v4 v4.0.0-rc.4 h1:UP4+v6fFrBIb1l934bDl//mmnoIZEDK0idg1+AIvX5U=
|
||||||
go.yaml.in/yaml/v4 v4.0.0-rc.4/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0=
|
go.yaml.in/yaml/v4 v4.0.0-rc.4/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||||
|
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||||
|
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
|
||||||
|
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
|
||||||
|
|||||||
+1
-14
@@ -2,20 +2,7 @@
|
|||||||
AUTOMAKE_OPTIONS = foreign no-dependencies no-installinfo
|
AUTOMAKE_OPTIONS = foreign no-dependencies no-installinfo
|
||||||
|
|
||||||
dist_man1_MANS = \
|
dist_man1_MANS = \
|
||||||
mproxyctl.1 \
|
mproxyctl.1
|
||||||
mproxyctl-accounts.1 \
|
|
||||||
mproxyctl-accounts-create.1 \
|
|
||||||
mproxyctl-accounts-delete.1 \
|
|
||||||
mproxyctl-accounts-get.1 \
|
|
||||||
mproxyctl-accounts-list.1 \
|
|
||||||
mproxyctl-accounts-update.1 \
|
|
||||||
mproxyctl-grants.1 \
|
|
||||||
mproxyctl-grants-create.1 \
|
|
||||||
mproxyctl-grants-delete.1 \
|
|
||||||
mproxyctl-grants-get.1 \
|
|
||||||
mproxyctl-grants-list.1 \
|
|
||||||
mproxyctl-grants-update.1
|
|
||||||
|
|
||||||
|
|
||||||
dist_man8_MANS = \
|
dist_man8_MANS = \
|
||||||
mproxyd.8
|
mproxyd.8
|
||||||
|
|||||||
+1
-13
@@ -267,19 +267,7 @@ top_builddir = @top_builddir@
|
|||||||
top_srcdir = @top_srcdir@
|
top_srcdir = @top_srcdir@
|
||||||
AUTOMAKE_OPTIONS = foreign no-dependencies no-installinfo
|
AUTOMAKE_OPTIONS = foreign no-dependencies no-installinfo
|
||||||
dist_man1_MANS = \
|
dist_man1_MANS = \
|
||||||
mproxyctl.1 \
|
mproxyctl.1
|
||||||
mproxyctl-accounts.1 \
|
|
||||||
mproxyctl-accounts-create.1 \
|
|
||||||
mproxyctl-accounts-delete.1 \
|
|
||||||
mproxyctl-accounts-get.1 \
|
|
||||||
mproxyctl-accounts-list.1 \
|
|
||||||
mproxyctl-accounts-update.1 \
|
|
||||||
mproxyctl-grants.1 \
|
|
||||||
mproxyctl-grants-create.1 \
|
|
||||||
mproxyctl-grants-delete.1 \
|
|
||||||
mproxyctl-grants-get.1 \
|
|
||||||
mproxyctl-grants-list.1 \
|
|
||||||
mproxyctl-grants-update.1
|
|
||||||
|
|
||||||
dist_man8_MANS = \
|
dist_man8_MANS = \
|
||||||
mproxyd.8
|
mproxyd.8
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package servcli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BasicAuthMiddleware
|
||||||
|
func NewBasicAuthMiddleware(user, pass string) MiddlewareFunc {
|
||||||
|
return func(next http.RoundTripper) http.RoundTripper {
|
||||||
|
return newBasicAuthMW(next, user, pass)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type BasicAuthMW struct {
|
||||||
|
user, pass string
|
||||||
|
next http.RoundTripper
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBasicAuthMW(next http.RoundTripper, user, pass string) *BasicAuthMW {
|
||||||
|
return &BasicAuthMW{
|
||||||
|
user: user,
|
||||||
|
pass: pass,
|
||||||
|
next: next,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tran BasicAuthMW) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
|
if tran.user != "" && tran.pass != "" {
|
||||||
|
pair := base64.StdEncoding.EncodeToString([]byte(tran.user + ":" + tran.pass))
|
||||||
|
req.Header.Set("Authorization", "Basic "+pair)
|
||||||
|
}
|
||||||
|
return tran.next.RoundTrip(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BearerAuthMiddleware
|
||||||
|
func NewBearerAuthMiddleware(token string) MiddlewareFunc {
|
||||||
|
return func(next http.RoundTripper) http.RoundTripper {
|
||||||
|
return newBearerAuthMW(next, token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type BearerAuthMW struct {
|
||||||
|
token string
|
||||||
|
next http.RoundTripper
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBearerAuthMW(next http.RoundTripper, token string) *BearerAuthMW {
|
||||||
|
return &BearerAuthMW{
|
||||||
|
token: token,
|
||||||
|
next: next,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tran BearerAuthMW) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
|
req.Header.Set("Authorization", "Bearer "+tran.token)
|
||||||
|
return tran.next.RoundTrip(req)
|
||||||
|
}
|
||||||
+3
-98
@@ -1,11 +1,11 @@
|
|||||||
package servcli
|
package servcli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
|
||||||
"encoding/base64"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type MiddlewareFunc func(next http.RoundTripper) http.RoundTripper
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
httpClient *http.Client
|
httpClient *http.Client
|
||||||
userAgent string
|
userAgent string
|
||||||
@@ -21,10 +21,9 @@ func NewClient(transport http.RoundTripper, mwFuncs ...MiddlewareFunc) *Client {
|
|||||||
httpClient := &http.Client{
|
httpClient := &http.Client{
|
||||||
Transport: transport,
|
Transport: transport,
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Client{
|
return &Client{
|
||||||
httpClient: httpClient,
|
httpClient: httpClient,
|
||||||
userAgent: "ociClient/1.0",
|
userAgent: "proxyClient/1.0",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,100 +31,6 @@ func (cli *Client) SetTransport(transport http.RoundTripper) {
|
|||||||
cli.httpClient.Transport = transport
|
cli.httpClient.Transport = transport
|
||||||
}
|
}
|
||||||
|
|
||||||
type MiddlewareFunc func(next http.RoundTripper) http.RoundTripper
|
|
||||||
|
|
||||||
func (cli *Client) UseMiddleware(mwFunc MiddlewareFunc) {
|
func (cli *Client) UseMiddleware(mwFunc MiddlewareFunc) {
|
||||||
cli.httpClient.Transport = mwFunc(cli.httpClient.Transport)
|
cli.httpClient.Transport = mwFunc(cli.httpClient.Transport)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BasicAuthMiddleware
|
|
||||||
func NewBasicAuthMiddleware(user, pass string) MiddlewareFunc {
|
|
||||||
return func(next http.RoundTripper) http.RoundTripper {
|
|
||||||
return newBasicAuthMW(next, user, pass)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type BasicAuthMW struct {
|
|
||||||
user, pass string
|
|
||||||
next http.RoundTripper
|
|
||||||
}
|
|
||||||
|
|
||||||
func newBasicAuthMW(next http.RoundTripper, user, pass string) *BasicAuthMW {
|
|
||||||
return &BasicAuthMW{
|
|
||||||
user: user,
|
|
||||||
pass: pass,
|
|
||||||
next: next,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tran BasicAuthMW) RoundTrip(req *http.Request) (*http.Response, error) {
|
|
||||||
if tran.user != "" && tran.pass != "" {
|
|
||||||
pair := base64.StdEncoding.EncodeToString([]byte(tran.user + ":" + tran.pass))
|
|
||||||
req.Header.Set("Authorization", "Basic "+pair)
|
|
||||||
}
|
|
||||||
return tran.next.RoundTrip(req)
|
|
||||||
}
|
|
||||||
|
|
||||||
// BearerAuthMiddleware
|
|
||||||
func NewBearerAuthMiddleware(token string) MiddlewareFunc {
|
|
||||||
return func(next http.RoundTripper) http.RoundTripper {
|
|
||||||
return newBearerAuthMW(next, token)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type BearerAuthMW struct {
|
|
||||||
token string
|
|
||||||
next http.RoundTripper
|
|
||||||
}
|
|
||||||
|
|
||||||
func newBearerAuthMW(next http.RoundTripper, token string) *BearerAuthMW {
|
|
||||||
return &BearerAuthMW{
|
|
||||||
token: token,
|
|
||||||
next: next,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tran BearerAuthMW) RoundTrip(req *http.Request) (*http.Response, error) {
|
|
||||||
req.Header.Set("Authorization", "Bearer "+tran.token)
|
|
||||||
return tran.next.RoundTrip(req)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultTransport
|
|
||||||
type DefaultTransport struct {
|
|
||||||
transport http.RoundTripper
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDefaultTransport() *DefaultTransport {
|
|
||||||
return &DefaultTransport{
|
|
||||||
transport: &http.Transport{
|
|
||||||
TLSClientConfig: &tls.Config{
|
|
||||||
InsecureSkipVerify: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wrap *DefaultTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
|
||||||
return wrap.transport.RoundTrip(req)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExampleMiddleware
|
|
||||||
func NewExampleMiddleware() MiddlewareFunc {
|
|
||||||
return func(next http.RoundTripper) http.RoundTripper {
|
|
||||||
return newExampleTransport(next)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type ExampleTransport struct {
|
|
||||||
next http.RoundTripper
|
|
||||||
}
|
|
||||||
|
|
||||||
func newExampleTransport(next http.RoundTripper) *ExampleTransport {
|
|
||||||
return &ExampleTransport{
|
|
||||||
next: next,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tran ExampleTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
|
||||||
return tran.next.RoundTrip(req)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
|
||||||
|
*/
|
||||||
|
package servcli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Copy(ctx context.Context, writer io.Writer, reader io.Reader) (int64, error) {
|
||||||
|
var err error
|
||||||
|
var size int64
|
||||||
|
var halt bool
|
||||||
|
buffer := make([]byte, 1024*4)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
err = errors.New("Break copy by context")
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
rsize, err := reader.Read(buffer)
|
||||||
|
if err == io.EOF {
|
||||||
|
err = nil
|
||||||
|
halt = true
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return size, err
|
||||||
|
}
|
||||||
|
wsize, err := writer.Write(buffer[0:rsize])
|
||||||
|
size += int64(wsize)
|
||||||
|
if err != nil {
|
||||||
|
return size, err
|
||||||
|
}
|
||||||
|
if halt {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return size, err
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
|
||||||
|
*/
|
||||||
|
package servcli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"mproxy/app/servoper"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (cli *Client) GetHello(ctx context.Context, rawpath string) error {
|
||||||
|
var err error
|
||||||
|
req := servoper.GetHelloParams{}
|
||||||
|
reqdata, err := json.Marshal(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = cli.DoCall(ctx, rawpath, reqdata)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cli *Client) DoCall(ctx context.Context, rawpath string, reqdata []byte) ([]byte, error) {
|
||||||
|
var err error
|
||||||
|
res := make([]byte, 0)
|
||||||
|
|
||||||
|
ref, err := ParsePath(rawpath)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
uri := ref.HelloEP()
|
||||||
|
reader := bytes.NewReader(reqdata)
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodPost, uri, reader)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
req.Header.Set("User-Agent", cli.userAgent)
|
||||||
|
req.Header.Set("Accept", "*/*")
|
||||||
|
resp, err := cli.httpClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode == http.StatusNotFound {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
err := fmt.Errorf("Unexpected response code %s", resp.Status)
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
contentLength := resp.Header.Get("Content-Length")
|
||||||
|
if contentLength == "" {
|
||||||
|
err := fmt.Errorf("Content-Length header is missing")
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
blobSize, err := strconv.ParseInt(contentLength, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
buffer := bytes.NewBuffer(nil)
|
||||||
|
recSize, err := Copy(ctx, buffer, resp.Body)
|
||||||
|
if blobSize != recSize {
|
||||||
|
err := fmt.Errorf("Mismatch declared and actual body size: %d and %d", blobSize, recSize)
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
res = buffer.Bytes()
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
|
|
||||||
*/
|
|
||||||
package servcli
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (cli *Client) doHTTPCall(ctx context.Context, host, obj, oper string, req []byte) ([]byte, error) {
|
|
||||||
var err error
|
|
||||||
var res []byte
|
|
||||||
|
|
||||||
reader := bytes.NewReader(req)
|
|
||||||
ref, err := NewReferer(host, obj, oper)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, ref.Point(), reader)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
httpReq.Header.Set("User-Agent", cli.userAgent)
|
|
||||||
httpReq.Header.Set("Accept", "*/*")
|
|
||||||
httpResp, err := cli.httpClient.Do(httpReq)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
defer httpResp.Body.Close()
|
|
||||||
if httpResp.StatusCode != http.StatusOK {
|
|
||||||
err := fmt.Errorf("Unexpected response code: %s", httpResp.Status)
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
contentLength := httpResp.Header.Get("Content-Length")
|
|
||||||
if contentLength == "" {
|
|
||||||
err := fmt.Errorf("Content-Length header is missing")
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
blobSize, err := strconv.ParseInt(contentLength, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
buffer := bytes.NewBuffer(nil)
|
|
||||||
recSize, err := io.Copy(buffer, httpResp.Body)
|
|
||||||
if blobSize != recSize {
|
|
||||||
err := fmt.Errorf("Mismatch declared and actual body size, %d and %d", blobSize, recSize)
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
res = buffer.Bytes()
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
+43
-33
@@ -1,26 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
|
||||||
|
*/
|
||||||
package servcli
|
package servcli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"path"
|
"path"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PathTypeIdentic = "identic"
|
||||||
|
PathTypePrefix = "prefix"
|
||||||
|
PathTypeRegexp = "regexp"
|
||||||
|
)
|
||||||
|
|
||||||
type Referer struct {
|
type Referer struct {
|
||||||
urlobj *url.URL
|
urlobj *url.URL
|
||||||
user, pass string
|
user, pass string
|
||||||
obj, oper string
|
resource string
|
||||||
|
values url.Values
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewReferer(hostname, object, operation string) (*Referer, error) {
|
func ParsePath(rawpath string) (*Referer, error) {
|
||||||
ref := &Referer{
|
ref := &Referer{
|
||||||
obj: object,
|
values: url.Values{},
|
||||||
oper: operation,
|
|
||||||
}
|
}
|
||||||
if !strings.Contains(hostname, "://") {
|
if !strings.Contains(rawpath, "://") {
|
||||||
hostname = "https://" + hostname
|
rawpath = "https://" + rawpath
|
||||||
}
|
}
|
||||||
urlobj, err := url.Parse(hostname)
|
urlobj, err := url.Parse(rawpath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ref, err
|
return ref, err
|
||||||
}
|
}
|
||||||
@@ -29,40 +39,40 @@ func NewReferer(hostname, object, operation string) (*Referer, error) {
|
|||||||
ref.pass, _ = urlobj.User.Password()
|
ref.pass, _ = urlobj.User.Password()
|
||||||
urlobj.User = nil
|
urlobj.User = nil
|
||||||
}
|
}
|
||||||
|
ref.resource = path.Join("/", urlobj.Path)
|
||||||
urlobj.Path = "/"
|
urlobj.Path = "/"
|
||||||
ref.urlobj = urlobj
|
ref.urlobj = urlobj
|
||||||
|
ref.values = urlobj.Query()
|
||||||
return ref, err
|
return ref, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseHostinfo(hostname string) (*Referer, error) {
|
|
||||||
ref := &Referer{}
|
|
||||||
if !strings.Contains(hostname, "://") {
|
|
||||||
hostname = "https://" + hostname
|
|
||||||
}
|
|
||||||
urlobj, err := url.Parse(hostname)
|
|
||||||
if err != nil {
|
|
||||||
return ref, err
|
|
||||||
}
|
|
||||||
if urlobj.User != nil {
|
|
||||||
ref.user = urlobj.User.Username()
|
|
||||||
ref.pass, _ = urlobj.User.Password()
|
|
||||||
urlobj.User = nil
|
|
||||||
}
|
|
||||||
urlobj.Path = "/"
|
|
||||||
ref.urlobj = urlobj
|
|
||||||
return ref, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ref *Referer) Host() string {
|
|
||||||
return ref.urlobj.Host
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ref *Referer) Raw() string {
|
func (ref *Referer) Raw() string {
|
||||||
return path.Join(ref.urlobj.Host, "/v3/api/", ref.obj, ref.oper)
|
res := path.Join(ref.urlobj.Host, ref.resource)
|
||||||
|
query := ref.values.Encode()
|
||||||
|
if query != "" {
|
||||||
|
return res + "?" + query
|
||||||
|
}
|
||||||
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ref *Referer) Point() string {
|
func (ref *Referer) SetResource(resource string) {
|
||||||
curl := ref.urlobj.JoinPath("/v3/api/", ref.obj, ref.oper)
|
ref.resource = path.Join("/", resource)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ref *Referer) JoinResource(resource string) {
|
||||||
|
ref.resource = path.Join("/", ref.resource, resource)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ref *Referer) PathType(typ string) {
|
||||||
|
ref.values.Set("pathType", typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ref *Referer) DryRun(yesno bool) {
|
||||||
|
ref.values.Set("dryRun", strconv.FormatBool(yesno))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ref *Referer) HelloEP() string {
|
||||||
|
curl := ref.urlobj.JoinPath("/v3/api/service/hello", ref.resource)
|
||||||
return curl.String()
|
return curl.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
package servcli
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"mproxy/app/handler"
|
|
||||||
"mproxy/app/servoper"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (cli *Client) ServiceHello(ctx context.Context, host string) (bool, error) {
|
|
||||||
var res bool
|
|
||||||
var err error
|
|
||||||
|
|
||||||
params := servoper.SendHelloParams{}
|
|
||||||
reqdata, err := json.Marshal(params)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
resdata, err := cli.doHTTPCall(ctx, host, "service", "hello", reqdata)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
response := handler.Response[servoper.SendHelloResult]{}
|
|
||||||
err = json.Unmarshal(resdata, &response)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
if response.Error {
|
|
||||||
err = errors.New(response.Message)
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
res = response.Result.Alive
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package servcli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultTransport
|
||||||
|
type DefaultTransport struct {
|
||||||
|
transport http.RoundTripper
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDefaultTransport() *DefaultTransport {
|
||||||
|
return &DefaultTransport{
|
||||||
|
transport: &http.Transport{
|
||||||
|
TLSClientConfig: &tls.Config{
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wrap *DefaultTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
|
return wrap.transport.RoundTrip(req)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user