Skip to content

Commit eb7037d

Browse files
authored
http: add user agent handling for HTTP requests (#534)
1 parent 68ae972 commit eb7037d

File tree

11 files changed

+256
-58
lines changed

11 files changed

+256
-58
lines changed

internal/base/interfaces.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,10 @@ type RuntimeInfo struct {
240240
PluginDirPath string `json:"pluginDirPath"`
241241
}
242242

243+
type Navigator struct {
244+
UserAgent string `json:"userAgent"`
245+
}
246+
243247
type Package struct {
244248
Main *Info
245249
Additions []*Info

internal/base/world.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ var (
2020
)
2121

2222
const (
23-
PluginObjKey = "PLUGIN"
24-
OsType = "OS_TYPE"
25-
ArchType = "ARCH_TYPE"
26-
Runtime = "RUNTIME"
23+
PluginObjKey = "PLUGIN"
24+
NavigatorObjKey = "VFOX_NAVIGATOR"
25+
OsType = "OS_TYPE"
26+
ArchType = "ARCH_TYPE"
27+
Runtime = "RUNTIME"
2728
)

internal/plugin/luai/decode.go renamed to internal/plugin/luai/codec/decode.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
package luai
17+
package codec
1818

1919
import (
2020
"errors"

internal/plugin/luai/encode.go renamed to internal/plugin/luai/codec/encode.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
package luai
17+
package codec
1818

1919
import (
2020
"errors"
@@ -209,3 +209,11 @@ func Marshal(state *lua.LState, v any) (lua.LValue, error) {
209209
}
210210

211211
}
212+
213+
func MustMarshal(state *lua.LState, v interface{}) lua.LValue {
214+
value, err := Marshal(state, v)
215+
if err != nil {
216+
panic(err)
217+
}
218+
return value
219+
}

internal/plugin/luai/context.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright 2025 Han Li and contributors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package luai
18+
19+
import (
20+
"fmt"
21+
"strings"
22+
)
23+
24+
// computeUserAgent constructs a user agent string for the vfox runtime and plugin.
25+
//
26+
// Parameters:
27+
// - runtimeVersion: the version of the vfox runtime (may be empty).
28+
// - pluginName: the name of the plugin (will be prefixed with "vfox-" if not already).
29+
// - pluginVersion: the version of the plugin (may be empty).
30+
//
31+
// Returns:
32+
// A user agent string in the format "vfox/<runtimeVersion> vfox-<pluginName>/<pluginVersion>",
33+
// omitting version information if not provided, and trimming extra spaces.
34+
func computeUserAgent(runtimeVersion, pluginName, pluginVersion string) string {
35+
components := make([]string, 0, 2)
36+
if runtimeVersion != "" {
37+
components = append(components, fmt.Sprintf("vfox/%s", runtimeVersion))
38+
} else {
39+
components = append(components, "vfox")
40+
}
41+
42+
name := ensurePrefix(pluginName)
43+
if name != "" {
44+
if pluginVersion != "" {
45+
components = append(components, fmt.Sprintf("%s/%s", name, pluginVersion))
46+
} else {
47+
components = append(components, name)
48+
}
49+
}
50+
51+
return strings.TrimSpace(strings.Join(components, " "))
52+
}
53+
54+
func ensurePrefix(name string) string {
55+
if name == "" {
56+
return ""
57+
}
58+
if strings.HasPrefix(name, "vfox-") {
59+
return name
60+
}
61+
return "vfox-" + name
62+
}

internal/plugin/luai/example_test.go

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"testing"
2323

2424
"github.com/version-fox/vfox/internal/logger"
25+
"github.com/version-fox/vfox/internal/plugin/luai/codec"
2526
lua "github.com/yuin/gopher-lua"
2627
)
2728

@@ -80,7 +81,7 @@ func TestExample(t *testing.T) {
8081
}
8182

8283
table := L.ReturnedValue()
83-
err := Unmarshal(table, &output)
84+
err := codec.Unmarshal(table, &output)
8485
if err != nil {
8586
t.Fatalf("unmarshal map failed: %v", err)
8687
}
@@ -111,7 +112,7 @@ func TestMarshalGoFunctions2(t *testing.T) {
111112
goFunc := func() {
112113
called = true
113114
}
114-
luaFunc, err := Marshal(L.Instance, goFunc)
115+
luaFunc, err := codec.Marshal(L.Instance, goFunc)
115116
if err != nil {
116117
t.Fatalf("Marshal failed: %v", err)
117118
}
@@ -132,7 +133,7 @@ func TestMarshalGoFunctions2(t *testing.T) {
132133
receivedInt = a
133134
receivedString = b
134135
}
135-
luaFunc, err := Marshal(L.Instance, goFunc)
136+
luaFunc, err := codec.Marshal(L.Instance, goFunc)
136137
if err != nil {
137138
t.Fatalf("Marshal failed: %v", err)
138139
}
@@ -153,7 +154,7 @@ func TestMarshalGoFunctions2(t *testing.T) {
153154
goFunc := func() (int, string) {
154155
return 42, "world"
155156
}
156-
luaFunc, err := Marshal(L.Instance, goFunc)
157+
luaFunc, err := codec.Marshal(L.Instance, goFunc)
157158
if err != nil {
158159
t.Fatalf("Marshal failed: %v", err)
159160
}
@@ -176,7 +177,7 @@ func TestMarshalGoFunctions2(t *testing.T) {
176177
goFunc := func(x int, y int) int {
177178
return x + y
178179
}
179-
luaFunc, err := Marshal(L.Instance, goFunc)
180+
luaFunc, err := codec.Marshal(L.Instance, goFunc)
180181
if err != nil {
181182
t.Fatalf("Marshal failed: %v", err)
182183
}
@@ -199,7 +200,7 @@ func TestMarshalGoFunctions2(t *testing.T) {
199200
}
200201
return fmt.Sprintf("%s%d", prefix, sum)
201202
}
202-
luaFunc, err := Marshal(L.Instance, goFunc)
203+
luaFunc, err := codec.Marshal(L.Instance, goFunc)
203204
if err != nil {
204205
t.Fatalf("Marshal failed: %v", err)
205206
}
@@ -222,16 +223,16 @@ func TestMarshalGoFunctions2(t *testing.T) {
222223
goFunc := func(s MyStruct) MyStruct {
223224
return MyStruct{Name: s.Name + "_processed", Value: s.Value * 2}
224225
}
225-
luaFunc, err := Marshal(L.Instance, goFunc)
226+
luaFunc, err := codec.Marshal(L.Instance, goFunc)
226227
if err != nil {
227228
t.Fatalf("Marshal failed: %v", err)
228229
}
229230
L.Instance.SetGlobal("testFunc", luaFunc)
230-
// Marshal the input struct for Lua
231+
// codec.Marshal the input struct for Lua
231232
inputStruct := MyStruct{Name: "input", Value: 10}
232-
luaInput, err := Marshal(L.Instance, inputStruct)
233+
luaInput, err := codec.Marshal(L.Instance, inputStruct)
233234
if err != nil {
234-
t.Fatalf("Failed to marshal input struct: %v", err)
235+
t.Fatalf("Failed to codec.marshal input struct: %v", err)
235236
}
236237
L.Instance.SetGlobal("inputData", luaInput)
237238

@@ -243,7 +244,7 @@ func TestMarshalGoFunctions2(t *testing.T) {
243244

244245
outputDataLua := L.Instance.GetGlobal("outputData")
245246
var outputStruct MyStruct
246-
if err := Unmarshal(outputDataLua, &outputStruct); err != nil {
247+
if err := codec.Unmarshal(outputDataLua, &outputStruct); err != nil {
247248
t.Fatalf("Failed to unmarshal output struct: %v", err)
248249
}
249250

@@ -424,7 +425,7 @@ func TestCases(t *testing.T) {
424425
L := lua.NewState()
425426
defer L.Close()
426427

427-
table, err := Marshal(L, tt.in)
428+
table, err := codec.Marshal(L, tt.in)
428429
if err != nil {
429430
t.Fatalf("marshal map failed: %v", err)
430431
}
@@ -461,7 +462,7 @@ func TestCases(t *testing.T) {
461462
t.Fatalf("%s: unmarshalTest.ptr %#v is not a pointer to a zero value", tt.CaseName, tt.ptr)
462463
}
463464

464-
err = Unmarshal(table, v.Interface())
465+
err = codec.Unmarshal(table, v.Interface())
465466

466467
if err != tt.err {
467468
t.Errorf("expected %+v, got %+v", tt.err, err)
@@ -499,7 +500,7 @@ func TestEncodeFunc(t *testing.T) {
499500
L := NewLuaVM()
500501
defer L.Close()
501502

502-
table, err := Marshal(L.Instance, testdata)
503+
table, err := codec.Marshal(L.Instance, testdata)
503504
if err != nil {
504505
t.Fatalf("marshal map failed: %v", err)
505506
}
@@ -526,7 +527,7 @@ func TestMarshalGoFunctions(t *testing.T) {
526527
goFunc := func() {
527528
called = true
528529
}
529-
luaFunc, err := Marshal(L.Instance, goFunc)
530+
luaFunc, err := codec.Marshal(L.Instance, goFunc)
530531
if err != nil {
531532
t.Fatalf("Marshal failed: %v", err)
532533
}
@@ -547,7 +548,7 @@ func TestMarshalGoFunctions(t *testing.T) {
547548
receivedInt = a
548549
receivedString = b
549550
}
550-
luaFunc, err := Marshal(L.Instance, goFunc)
551+
luaFunc, err := codec.Marshal(L.Instance, goFunc)
551552
if err != nil {
552553
t.Fatalf("Marshal failed: %v", err)
553554
}
@@ -568,7 +569,7 @@ func TestMarshalGoFunctions(t *testing.T) {
568569
goFunc := func() (int, string) {
569570
return 42, "world"
570571
}
571-
luaFunc, err := Marshal(L.Instance, goFunc)
572+
luaFunc, err := codec.Marshal(L.Instance, goFunc)
572573
if err != nil {
573574
t.Fatalf("Marshal failed: %v", err)
574575
}
@@ -591,7 +592,7 @@ func TestMarshalGoFunctions(t *testing.T) {
591592
goFunc := func(x int, y int) int {
592593
return x + y
593594
}
594-
luaFunc, err := Marshal(L.Instance, goFunc)
595+
luaFunc, err := codec.Marshal(L.Instance, goFunc)
595596
if err != nil {
596597
t.Fatalf("Marshal failed: %v", err)
597598
}
@@ -614,7 +615,7 @@ func TestMarshalGoFunctions(t *testing.T) {
614615
}
615616
return fmt.Sprintf("%s%d", prefix, sum)
616617
}
617-
luaFunc, err := Marshal(L.Instance, goFunc)
618+
luaFunc, err := codec.Marshal(L.Instance, goFunc)
618619
if err != nil {
619620
t.Fatalf("Marshal failed: %v", err)
620621
}
@@ -637,16 +638,16 @@ func TestMarshalGoFunctions(t *testing.T) {
637638
goFunc := func(s MyStruct) MyStruct {
638639
return MyStruct{Name: s.Name + "_processed", Value: s.Value * 2}
639640
}
640-
luaFunc, err := Marshal(L.Instance, goFunc)
641+
luaFunc, err := codec.Marshal(L.Instance, goFunc)
641642
if err != nil {
642643
t.Fatalf("Marshal failed: %v", err)
643644
}
644645
L.Instance.SetGlobal("testFunc", luaFunc)
645-
// Marshal the input struct for Lua
646+
// codec.Marshal the input struct for Lua
646647
inputStruct := MyStruct{Name: "input", Value: 10}
647-
luaInput, err := Marshal(L.Instance, inputStruct)
648+
luaInput, err := codec.Marshal(L.Instance, inputStruct)
648649
if err != nil {
649-
t.Fatalf("Failed to marshal input struct: %v", err)
650+
t.Fatalf("Failed to codec.marshal input struct: %v", err)
650651
}
651652
L.Instance.SetGlobal("inputData", luaInput)
652653

@@ -656,7 +657,7 @@ func TestMarshalGoFunctions(t *testing.T) {
656657

657658
outputDataLua := L.Instance.GetGlobal("outputData")
658659
var outputStruct MyStruct
659-
if err := Unmarshal(outputDataLua, &outputStruct); err != nil {
660+
if err := codec.Unmarshal(outputDataLua, &outputStruct); err != nil {
660661
t.Fatalf("Failed to unmarshal output struct: %v", err)
661662
}
662663

internal/plugin/luai/module/http/http.go

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ import (
2525
"path/filepath"
2626

2727
"github.com/schollz/progressbar/v3"
28+
"github.com/version-fox/vfox/internal/base"
2829
"github.com/version-fox/vfox/internal/config"
30+
"github.com/version-fox/vfox/internal/plugin/luai/codec"
2931
lua "github.com/yuin/gopher-lua"
3032
)
3133

@@ -74,6 +76,8 @@ func (m *Module) Get(L *lua.LState) int {
7476
})
7577
}
7678
}
79+
m.ensureUserAgent(L, req)
80+
7781
resp, err := m.client.Do(req)
7882
if err != nil {
7983
L.Push(lua.LNil)
@@ -125,6 +129,8 @@ func (m *Module) Head(L *lua.LState) int {
125129
})
126130
}
127131
}
132+
m.ensureUserAgent(L, req)
133+
128134
resp, err := m.client.Do(req)
129135
if err != nil {
130136
L.Push(lua.LNil)
@@ -183,6 +189,7 @@ func (m *Module) DownloadFile(L *lua.LState) int {
183189
})
184190
}
185191
}
192+
m.ensureUserAgent(L, req)
186193
resp, err := m.client.Do(req)
187194
if err != nil {
188195
L.Push(lua.LString(err.Error()))
@@ -240,7 +247,7 @@ func (m *Module) luaMap() map[string]lua.LGFunction {
240247
}
241248
}
242249

243-
func NewModule(proxy *config.Proxy) lua.LGFunction {
250+
func createModule(proxy *config.Proxy) lua.LGFunction {
244251
return func(L *lua.LState) int {
245252
client := &http.Client{}
246253
if proxy.Enable {
@@ -261,3 +268,20 @@ func NewModule(proxy *config.Proxy) lua.LGFunction {
261268
return 1
262269
}
263270
}
271+
272+
func (m *Module) ensureUserAgent(L *lua.LState, req *http.Request) {
273+
if req.Header.Get("User-Agent") == "" {
274+
navigatorValue := L.GetGlobal(base.NavigatorObjKey)
275+
if navigatorValue != lua.LNil {
276+
var navigator base.Navigator
277+
err := codec.Unmarshal(navigatorValue, &navigator)
278+
if err == nil {
279+
req.Header.Set("User-Agent", navigator.UserAgent)
280+
}
281+
}
282+
}
283+
}
284+
285+
func Preload(L *lua.LState, proxy *config.Proxy) {
286+
L.PreloadModule("http", createModule(proxy))
287+
}

0 commit comments

Comments
 (0)