File: http_bridge_recording_util.go

package info (click to toggle)
golang-github-viant-toolbox 0.33.2-4
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 1,280 kB
  • sloc: makefile: 16
file content (122 lines) | stat: -rw-r--r-- 3,406 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
package bridge

import (
	"bytes"
	"fmt"
	"github.com/pkg/errors"
	"github.com/viant/toolbox"
	"io/ioutil"
	"path"
	"strings"
)

//RecordedHttpTrip represents a recorded http trip
type RecordedHttpTrip struct {
	Request  *HttpRequest
	Response *HttpResponse
}

//ReadRecordedHttpTripsWithPrefix scans provided directory for request, response  pairs
func ReadRecordedHttpTripsWithTemplate(directory string, requestTemplate, respTemplate string) ([]*RecordedHttpTrip, error) {
	if !strings.Contains(requestTemplate, "%") {
		return nil, errors.New("invalid request template: it could contains counter '%' expressions")
	}
	if !strings.Contains(respTemplate, "%") {
		return nil, errors.New("invalid response template: it could contains counter '%' expressions")
	}
	var result = make([]*RecordedHttpTrip, 0)
	var requestTemplatePath = path.Join(directory, requestTemplate)
	var responseTemplatePath = path.Join(directory, respTemplate)

	requests, err := readAll(requestTemplatePath, func() interface{} {
		return &HttpRequest{}
	})
	if err != nil {
		return nil, err
	}
	responses, err := readAll(responseTemplatePath, func() interface{} {
		return &HttpResponse{}
	})
	if err != nil {
		return nil, err
	}

	if len(requests) != len(responses) {
		return nil, fmt.Errorf("request and Response count does not match req:%v, resp:%v ", len(requests), len(responses))
	}

	for i := 0; i < len(requests); i++ {
		var ok bool
		var trip = &RecordedHttpTrip{}
		trip.Request, ok = requests[i].(*HttpRequest)
		if !ok {
			return nil, fmt.Errorf("expected HttpRequest but had %T", requests[i])
		}
		if i < len(responses) {
			trip.Response, ok = responses[i].(*HttpResponse)
			if !ok {
				return nil, fmt.Errorf("expected HttpRequest but had %T", responses[i])
			}
		}
		result = append(result, trip)
	}
	return result, nil
}

//ReadRecordedHttpTrips scans provided directory for bridge.HttpRequest-%v.json and  bridge.HttpResponse-%v.json template pairs
func ReadRecordedHttpTrips(directory string) ([]*RecordedHttpTrip, error) {
	return ReadRecordedHttpTripsWithTemplate(directory, "bridge.HttpRequest-%v.json", "bridge.HttpResponse-%v.json")
}

func readAll(pathTemplate string, provider func() interface{}) ([]interface{}, error) {
	var result = make([]interface{}, 0)
	for i := 0; ; i++ {
		filename := fmt.Sprintf(pathTemplate, i)
		if !toolbox.FileExists(filename) {
			break
		}
		data, err := ioutil.ReadFile(filename)
		if err != nil {
			return nil, err
		}
		aStruct := provider()
		err = toolbox.NewJSONDecoderFactory().Create(bytes.NewReader(data)).Decode(aStruct)
		if err != nil {
			return nil, err
		}
		result = append(result, aStruct)
	}
	return result, nil
}

//StartRecordingBridge start recording bridge proxy
func StartRecordingBridge(port string, outputDirectory string, routes ...*HttpBridgeProxyRoute) (*HttpBridge, error) {

	if len(routes) == 0 {
		routes = append(routes, &HttpBridgeProxyRoute{
			Pattern: "/",
		})
	}

	config := &HttpBridgeConfig{
		Endpoint: &HttpBridgeEndpointConfig{
			Port: port,
		},
		Proxy: &HttpBridgeProxyConfig{
			BufferPoolSize: 2,
			BufferSize:     8 * 1024,
		},
		Routes: routes,
	}

	for _, route := range routes {
		route.Listener = HttpFileRecorder(outputDirectory, false)
	}

	httpBridge, err := NewHttpBridge(config, NewProxyRecordingHandler)
	if err != nil {
		return nil, err
	}
	go httpBridge.ListenAndServe()
	return httpBridge, nil
}