mirror of
https://github.com/v2fly/v2ray-core.git
synced 2025-07-05 16:38:17 -04:00
Add Persistent Storage Support to V2Ray (#3300)
* update protogen to strip unused part * add persistent storage support * fix coding style * update linter setting * update github integration
This commit is contained in:
parent
3ee9045697
commit
78cd513b82
6
.github/linters/.golangci.yml
vendored
6
.github/linters/.golangci.yml
vendored
@ -1,8 +1,5 @@
|
||||
run:
|
||||
timeout: 5m
|
||||
skip-files:
|
||||
- generated.*
|
||||
- .pb.go
|
||||
|
||||
issues:
|
||||
new: true
|
||||
@ -13,6 +10,9 @@ issues:
|
||||
- linters:
|
||||
- stylecheck
|
||||
text: "ST1016:"
|
||||
exclude-files:
|
||||
- generated.*
|
||||
- .pb.go
|
||||
|
||||
linters:
|
||||
enable:
|
||||
|
2
.github/workflows/deb.yml
vendored
2
.github/workflows/deb.yml
vendored
@ -53,7 +53,7 @@ jobs:
|
||||
cp ../*.deb ./
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: v2ray-debian-packages
|
||||
path: ./*.deb
|
||||
|
10
.github/workflows/release.yml
vendored
10
.github/workflows/release.yml
vendored
@ -188,7 +188,7 @@ jobs:
|
||||
openssl dgst -sha512 $FILE | sed 's/([^)]*)//g' >>$DGST
|
||||
|
||||
- name: Upload ZIP file to Artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: v2ray-${{ steps.get_filename.outputs.ASSET_NAME }}.zip
|
||||
path: v2ray-${{ steps.get_filename.outputs.ASSET_NAME }}.zip
|
||||
@ -217,7 +217,7 @@ jobs:
|
||||
with:
|
||||
go-version: ^1.23
|
||||
|
||||
- uses: actions/download-artifact@v3
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: build_artifacts
|
||||
|
||||
@ -252,17 +252,17 @@ jobs:
|
||||
openssl dgst -sha256 $FILE | sed 's/([^)]*)//g' >>$DGST
|
||||
openssl dgst -sha512 $FILE | sed 's/([^)]*)//g' >>$DGST
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Release.unsigned
|
||||
path: build_artifacts/Release.unsigned
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Release.unsigned.dgst
|
||||
path: build_artifacts/Release.unsigned.dgst
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: v2ray-extra.zip
|
||||
path: build_artifacts/v2ray-extra.zip
|
||||
|
@ -365,11 +365,12 @@ func (x *Intensity) GetProbeInterval() uint32 {
|
||||
type Config struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// @Document The selectors for outbound under observation
|
||||
SubjectSelector []string `protobuf:"bytes,2,rep,name=subject_selector,json=subjectSelector,proto3" json:"subject_selector,omitempty"`
|
||||
ProbeUrl string `protobuf:"bytes,3,opt,name=probe_url,json=probeUrl,proto3" json:"probe_url,omitempty"`
|
||||
ProbeInterval int64 `protobuf:"varint,4,opt,name=probe_interval,json=probeInterval,proto3" json:"probe_interval,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
SubjectSelector []string `protobuf:"bytes,2,rep,name=subject_selector,json=subjectSelector,proto3" json:"subject_selector,omitempty"`
|
||||
ProbeUrl string `protobuf:"bytes,3,opt,name=probe_url,json=probeUrl,proto3" json:"probe_url,omitempty"`
|
||||
ProbeInterval int64 `protobuf:"varint,4,opt,name=probe_interval,json=probeInterval,proto3" json:"probe_interval,omitempty"`
|
||||
PersistentProbeResult bool `protobuf:"varint,5,opt,name=persistent_probe_result,json=persistentProbeResult,proto3" json:"persistent_probe_result,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *Config) Reset() {
|
||||
@ -423,6 +424,13 @@ func (x *Config) GetProbeInterval() int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetPersistentProbeResult() bool {
|
||||
if x != nil {
|
||||
return x.PersistentProbeResult
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var File_app_observatory_config_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_app_observatory_config_proto_rawDesc = string([]byte{
|
||||
@ -476,24 +484,28 @@ var file_app_observatory_config_proto_rawDesc = string([]byte{
|
||||
0x22, 0x32, 0x0a, 0x09, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x79, 0x12, 0x25, 0x0a,
|
||||
0x0e, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x49, 0x6e, 0x74, 0x65,
|
||||
0x72, 0x76, 0x61, 0x6c, 0x22, 0x9d, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12,
|
||||
0x72, 0x76, 0x61, 0x6c, 0x22, 0xd5, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12,
|
||||
0x29, 0x0a, 0x10, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63,
|
||||
0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x75, 0x62, 0x6a, 0x65,
|
||||
0x63, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x72,
|
||||
0x6f, 0x62, 0x65, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70,
|
||||
0x72, 0x6f, 0x62, 0x65, 0x55, 0x72, 0x6c, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x62, 0x65,
|
||||
0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52,
|
||||
0x0d, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x3a, 0x24,
|
||||
0x82, 0xb5, 0x18, 0x20, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x15, 0x62,
|
||||
0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61,
|
||||
0x74, 0x6f, 0x72, 0x79, 0x42, 0x6f, 0x0a, 0x1e, 0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x72, 0x61,
|
||||
0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72,
|
||||
0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x50, 0x01, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
|
||||
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76, 0x32, 0x66, 0x6c, 0x79, 0x2f, 0x76, 0x32, 0x72, 0x61, 0x79,
|
||||
0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x35, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x6f, 0x62, 0x73,
|
||||
0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0xaa, 0x02, 0x1a, 0x56, 0x32, 0x52, 0x61, 0x79,
|
||||
0x2e, 0x43, 0x6f, 0x72, 0x65, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x4f, 0x62, 0x73, 0x65, 0x72, 0x76,
|
||||
0x61, 0x74, 0x6f, 0x72, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x0d, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x36,
|
||||
0x0a, 0x17, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x72, 0x6f,
|
||||
0x62, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52,
|
||||
0x15, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x62, 0x65,
|
||||
0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3a, 0x24, 0x82, 0xb5, 0x18, 0x20, 0x0a, 0x07, 0x73, 0x65,
|
||||
0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x15, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e,
|
||||
0x64, 0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x42, 0x6f, 0x0a, 0x1e,
|
||||
0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61,
|
||||
0x70, 0x70, 0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x50, 0x01,
|
||||
0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76, 0x32, 0x66,
|
||||
0x6c, 0x79, 0x2f, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x35,
|
||||
0x2f, 0x61, 0x70, 0x70, 0x2f, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79,
|
||||
0xaa, 0x02, 0x1a, 0x56, 0x32, 0x52, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x72, 0x65, 0x2e, 0x41, 0x70,
|
||||
0x70, 0x2e, 0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x62, 0x06, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
})
|
||||
|
||||
var (
|
||||
|
@ -84,4 +84,6 @@ message Config {
|
||||
string probe_url = 3;
|
||||
|
||||
int64 probe_interval = 4;
|
||||
|
||||
bool persistent_probe_result = 5;
|
||||
}
|
@ -12,6 +12,11 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/v2fly/v2ray-core/v5/app/persistentstorage"
|
||||
"github.com/v2fly/v2ray-core/v5/app/persistentstorage/protostorage"
|
||||
"github.com/v2fly/v2ray-core/v5/common/environment"
|
||||
"github.com/v2fly/v2ray-core/v5/common/environment/envctx"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
|
||||
core "github.com/v2fly/v2ray-core/v5"
|
||||
@ -34,7 +39,10 @@ type Observer struct {
|
||||
|
||||
finished *done.Instance
|
||||
|
||||
ohm outbound.Manager
|
||||
ohm outbound.Manager
|
||||
persistStorage persistentstorage.ScopedPersistentStorage
|
||||
|
||||
persistOutboundStatusProtoStorage protostorage.ProtoPersistentStorage
|
||||
}
|
||||
|
||||
func (o *Observer) GetObservation(ctx context.Context) (proto.Message, error) {
|
||||
@ -47,6 +55,24 @@ func (o *Observer) Type() interface{} {
|
||||
|
||||
func (o *Observer) Start() error {
|
||||
if o.config != nil && len(o.config.SubjectSelector) != 0 {
|
||||
if o.config.PersistentProbeResult {
|
||||
appEnvironment := envctx.EnvironmentFromContext(o.ctx).(environment.AppEnvironment)
|
||||
o.persistStorage = appEnvironment.PersistentStorage()
|
||||
|
||||
outboundStatusStorage, err := o.persistStorage.NarrowScope(o.ctx, []byte("outbound_status"))
|
||||
if err != nil {
|
||||
return newError("failed to get persistent storage for outbound_status").Base(err)
|
||||
}
|
||||
o.persistOutboundStatusProtoStorage = outboundStatusStorage.(protostorage.ProtoPersistentStorage)
|
||||
list, err := outboundStatusStorage.List(o.ctx, []byte(""))
|
||||
if err != nil {
|
||||
newError("failed to list persisted outbound status").Base(err).WriteToLog()
|
||||
} else {
|
||||
for _, v := range list {
|
||||
o.loadOutboundStatus(string(v))
|
||||
}
|
||||
}
|
||||
}
|
||||
o.finished = done.New()
|
||||
go o.background()
|
||||
}
|
||||
@ -195,6 +221,12 @@ func (o *Observer) updateStatusForResult(outbound string, result *ProbeResult) {
|
||||
status.LastErrorReason = result.LastErrorReason
|
||||
status.Delay = 99999999
|
||||
}
|
||||
if o.config.PersistentProbeResult {
|
||||
err := o.persistOutboundStatusProtoStorage.PutProto(o.ctx, outbound, status)
|
||||
if err != nil {
|
||||
newError("failed to persist outbound status").Base(err).WriteToLog()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Observer) findStatusLocationLockHolderOnly(outbound string) int {
|
||||
@ -206,19 +238,33 @@ func (o *Observer) findStatusLocationLockHolderOnly(outbound string) int {
|
||||
return -1
|
||||
}
|
||||
|
||||
func (o *Observer) loadOutboundStatus(name string) {
|
||||
if o.persistOutboundStatusProtoStorage == nil {
|
||||
return
|
||||
}
|
||||
status := &OutboundStatus{}
|
||||
err := o.persistOutboundStatusProtoStorage.GetProto(o.ctx, name, status)
|
||||
if err != nil {
|
||||
newError("failed to load outbound status").Base(err).WriteToLog()
|
||||
return
|
||||
}
|
||||
o.status = append(o.status, status)
|
||||
}
|
||||
|
||||
func New(ctx context.Context, config *Config) (*Observer, error) {
|
||||
var outboundManager outbound.Manager
|
||||
obs := &Observer{
|
||||
config: config,
|
||||
ctx: ctx,
|
||||
}
|
||||
|
||||
err := core.RequireFeatures(ctx, func(om outbound.Manager) {
|
||||
outboundManager = om
|
||||
obs.ohm = om
|
||||
})
|
||||
if err != nil {
|
||||
return nil, newError("Cannot get depended features").Base(err)
|
||||
}
|
||||
return &Observer{
|
||||
config: config,
|
||||
ctx: ctx,
|
||||
ohm: outboundManager,
|
||||
}, nil
|
||||
|
||||
return obs, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
214
app/persistentstorage/filesystemstorage/config.pb.go
Normal file
214
app/persistentstorage/filesystemstorage/config.pb.go
Normal file
@ -0,0 +1,214 @@
|
||||
package filesystemstorage
|
||||
|
||||
import (
|
||||
_ "github.com/v2fly/v2ray-core/v5/common/protoext"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type StateStorageRoot int32
|
||||
|
||||
const (
|
||||
StateStorageRoot_WorkDir StateStorageRoot = 0
|
||||
)
|
||||
|
||||
// Enum value maps for StateStorageRoot.
|
||||
var (
|
||||
StateStorageRoot_name = map[int32]string{
|
||||
0: "WorkDir",
|
||||
}
|
||||
StateStorageRoot_value = map[string]int32{
|
||||
"WorkDir": 0,
|
||||
}
|
||||
)
|
||||
|
||||
func (x StateStorageRoot) Enum() *StateStorageRoot {
|
||||
p := new(StateStorageRoot)
|
||||
*p = x
|
||||
return p
|
||||
}
|
||||
|
||||
func (x StateStorageRoot) String() string {
|
||||
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||
}
|
||||
|
||||
func (StateStorageRoot) Descriptor() protoreflect.EnumDescriptor {
|
||||
return file_app_persistentstorage_filesystemstorage_config_proto_enumTypes[0].Descriptor()
|
||||
}
|
||||
|
||||
func (StateStorageRoot) Type() protoreflect.EnumType {
|
||||
return &file_app_persistentstorage_filesystemstorage_config_proto_enumTypes[0]
|
||||
}
|
||||
|
||||
func (x StateStorageRoot) Number() protoreflect.EnumNumber {
|
||||
return protoreflect.EnumNumber(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use StateStorageRoot.Descriptor instead.
|
||||
func (StateStorageRoot) EnumDescriptor() ([]byte, []int) {
|
||||
return file_app_persistentstorage_filesystemstorage_config_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
StateStorageRoot StateStorageRoot `protobuf:"varint,1,opt,name=state_storage_root,json=stateStorageRoot,proto3,enum=v2ray.core.app.persistentstorage.filesystemstorage.StateStorageRoot" json:"state_storage_root,omitempty"`
|
||||
InstanceName string `protobuf:"bytes,4,opt,name=instance_name,json=instanceName,proto3" json:"instance_name,omitempty"`
|
||||
Protojson bool `protobuf:"varint,5,opt,name=protojson,proto3" json:"protojson,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *Config) Reset() {
|
||||
*x = Config{}
|
||||
mi := &file_app_persistentstorage_filesystemstorage_config_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *Config) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Config) ProtoMessage() {}
|
||||
|
||||
func (x *Config) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_persistentstorage_filesystemstorage_config_proto_msgTypes[0]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
|
||||
func (*Config) Descriptor() ([]byte, []int) {
|
||||
return file_app_persistentstorage_filesystemstorage_config_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *Config) GetStateStorageRoot() StateStorageRoot {
|
||||
if x != nil {
|
||||
return x.StateStorageRoot
|
||||
}
|
||||
return StateStorageRoot_WorkDir
|
||||
}
|
||||
|
||||
func (x *Config) GetInstanceName() string {
|
||||
if x != nil {
|
||||
return x.InstanceName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Config) GetProtojson() bool {
|
||||
if x != nil {
|
||||
return x.Protojson
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var File_app_persistentstorage_filesystemstorage_config_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_app_persistentstorage_filesystemstorage_config_proto_rawDesc = string([]byte{
|
||||
0x0a, 0x34, 0x61, 0x70, 0x70, 0x2f, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x74,
|
||||
0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73, 0x74,
|
||||
0x65, 0x6d, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x32, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f,
|
||||
0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e,
|
||||
0x74, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73,
|
||||
0x74, 0x65, 0x6d, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x1a, 0x20, 0x63, 0x6f, 0x6d, 0x6d,
|
||||
0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x65, 0x78, 0x74, 0x2f, 0x65, 0x78, 0x74, 0x65,
|
||||
0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe1, 0x01, 0x0a,
|
||||
0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x72, 0x0a, 0x12, 0x73, 0x74, 0x61, 0x74, 0x65,
|
||||
0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x0e, 0x32, 0x44, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65,
|
||||
0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x74, 0x73,
|
||||
0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73, 0x74, 0x65,
|
||||
0x6d, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x74,
|
||||
0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x52, 0x10, 0x73, 0x74, 0x61, 0x74, 0x65,
|
||||
0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x69,
|
||||
0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65,
|
||||
0x12, 0x1c, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x05, 0x20,
|
||||
0x01, 0x28, 0x08, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x20,
|
||||
0x82, 0xb5, 0x18, 0x1c, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x11, 0x66,
|
||||
0x69, 0x6c, 0x65, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
|
||||
0x2a, 0x1f, 0x0a, 0x10, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
|
||||
0x52, 0x6f, 0x6f, 0x74, 0x12, 0x0b, 0x0a, 0x07, 0x57, 0x6f, 0x72, 0x6b, 0x44, 0x69, 0x72, 0x10,
|
||||
0x00, 0x42, 0xb3, 0x01, 0x0a, 0x32, 0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e,
|
||||
0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x74, 0x73,
|
||||
0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73, 0x74, 0x65,
|
||||
0x6d, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x50, 0x01, 0x5a, 0x46, 0x67, 0x69, 0x74, 0x68,
|
||||
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76, 0x32, 0x66, 0x6c, 0x79, 0x2f, 0x76, 0x32, 0x72,
|
||||
0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x35, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x70,
|
||||
0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
|
||||
0x2f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x74, 0x6f, 0x72, 0x61,
|
||||
0x67, 0x65, 0xaa, 0x02, 0x32, 0x56, 0x32, 0x52, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x72, 0x65, 0x2e,
|
||||
0x41, 0x70, 0x70, 0x2e, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x74,
|
||||
0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d,
|
||||
0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
})
|
||||
|
||||
var (
|
||||
file_app_persistentstorage_filesystemstorage_config_proto_rawDescOnce sync.Once
|
||||
file_app_persistentstorage_filesystemstorage_config_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_app_persistentstorage_filesystemstorage_config_proto_rawDescGZIP() []byte {
|
||||
file_app_persistentstorage_filesystemstorage_config_proto_rawDescOnce.Do(func() {
|
||||
file_app_persistentstorage_filesystemstorage_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_persistentstorage_filesystemstorage_config_proto_rawDesc), len(file_app_persistentstorage_filesystemstorage_config_proto_rawDesc)))
|
||||
})
|
||||
return file_app_persistentstorage_filesystemstorage_config_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_app_persistentstorage_filesystemstorage_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||
var file_app_persistentstorage_filesystemstorage_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||
var file_app_persistentstorage_filesystemstorage_config_proto_goTypes = []any{
|
||||
(StateStorageRoot)(0), // 0: v2ray.core.app.persistentstorage.filesystemstorage.StateStorageRoot
|
||||
(*Config)(nil), // 1: v2ray.core.app.persistentstorage.filesystemstorage.Config
|
||||
}
|
||||
var file_app_persistentstorage_filesystemstorage_config_proto_depIdxs = []int32{
|
||||
0, // 0: v2ray.core.app.persistentstorage.filesystemstorage.Config.state_storage_root:type_name -> v2ray.core.app.persistentstorage.filesystemstorage.StateStorageRoot
|
||||
1, // [1:1] is the sub-list for method output_type
|
||||
1, // [1:1] is the sub-list for method input_type
|
||||
1, // [1:1] is the sub-list for extension type_name
|
||||
1, // [1:1] is the sub-list for extension extendee
|
||||
0, // [0:1] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_app_persistentstorage_filesystemstorage_config_proto_init() }
|
||||
func file_app_persistentstorage_filesystemstorage_config_proto_init() {
|
||||
if File_app_persistentstorage_filesystemstorage_config_proto != nil {
|
||||
return
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_persistentstorage_filesystemstorage_config_proto_rawDesc), len(file_app_persistentstorage_filesystemstorage_config_proto_rawDesc)),
|
||||
NumEnums: 1,
|
||||
NumMessages: 1,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_app_persistentstorage_filesystemstorage_config_proto_goTypes,
|
||||
DependencyIndexes: file_app_persistentstorage_filesystemstorage_config_proto_depIdxs,
|
||||
EnumInfos: file_app_persistentstorage_filesystemstorage_config_proto_enumTypes,
|
||||
MessageInfos: file_app_persistentstorage_filesystemstorage_config_proto_msgTypes,
|
||||
}.Build()
|
||||
File_app_persistentstorage_filesystemstorage_config_proto = out.File
|
||||
file_app_persistentstorage_filesystemstorage_config_proto_goTypes = nil
|
||||
file_app_persistentstorage_filesystemstorage_config_proto_depIdxs = nil
|
||||
}
|
23
app/persistentstorage/filesystemstorage/config.proto
Normal file
23
app/persistentstorage/filesystemstorage/config.proto
Normal file
@ -0,0 +1,23 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package v2ray.core.app.persistentstorage.filesystemstorage;
|
||||
option csharp_namespace = "V2Ray.Core.App.Persistentstorage.Filesystemstorage";
|
||||
option go_package = "github.com/v2fly/v2ray-core/v5/app/persistentstorage/filesystemstorage";
|
||||
option java_package = "com.v2ray.core.persistentstorage.filesystemstorage";
|
||||
option java_multiple_files = true;
|
||||
|
||||
import "common/protoext/extensions.proto";
|
||||
|
||||
enum StateStorageRoot {
|
||||
WorkDir = 0;
|
||||
}
|
||||
|
||||
message Config {
|
||||
option (v2ray.core.common.protoext.message_opt).type = "service";
|
||||
option (v2ray.core.common.protoext.message_opt).short_name = "filesystemstorage";
|
||||
|
||||
StateStorageRoot state_storage_root = 1;
|
||||
string instance_name = 4;
|
||||
|
||||
bool protojson = 5;
|
||||
}
|
133
app/persistentstorage/filesystemstorage/fs.go
Normal file
133
app/persistentstorage/filesystemstorage/fs.go
Normal file
@ -0,0 +1,133 @@
|
||||
package filesystemstorage
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/v2fly/v2ray-core/v5/app/persistentstorage/protostorage"
|
||||
"github.com/v2fly/v2ray-core/v5/common"
|
||||
"github.com/v2fly/v2ray-core/v5/common/environment"
|
||||
"github.com/v2fly/v2ray-core/v5/common/environment/envctx"
|
||||
"github.com/v2fly/v2ray-core/v5/features/extension/storage"
|
||||
)
|
||||
|
||||
func newFileSystemStorage(ctx context.Context, config *Config) storage.ScopedPersistentStorageService {
|
||||
appEnvironment := envctx.EnvironmentFromContext(ctx).(environment.AppEnvironment)
|
||||
fss := &fileSystemStorage{
|
||||
fs: appEnvironment,
|
||||
pathRoot: config.InstanceName,
|
||||
currentLocation: nil,
|
||||
config: config,
|
||||
}
|
||||
|
||||
protoStorageInst := protostorage.NewProtoStorage(fss, config.Protojson)
|
||||
fss.proto = protoStorageInst
|
||||
return fss
|
||||
}
|
||||
|
||||
type fileSystemStorage struct {
|
||||
fs environment.FileSystemCapabilitySet
|
||||
proto protostorage.ProtoPersistentStorage
|
||||
|
||||
pathRoot string
|
||||
currentLocation []string
|
||||
config *Config
|
||||
}
|
||||
|
||||
func (f *fileSystemStorage) Type() interface{} {
|
||||
return storage.ScopedPersistentStorageServiceType
|
||||
}
|
||||
|
||||
func (f *fileSystemStorage) Start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *fileSystemStorage) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *fileSystemStorage) PutProto(ctx context.Context, key string, pb proto.Message) error {
|
||||
return f.proto.PutProto(ctx, key, pb)
|
||||
}
|
||||
|
||||
func (f *fileSystemStorage) GetProto(ctx context.Context, key string, pb proto.Message) error {
|
||||
return f.proto.GetProto(ctx, key, pb)
|
||||
}
|
||||
|
||||
func (f *fileSystemStorage) ScopedPersistentStorageEngine() {
|
||||
}
|
||||
|
||||
func (f *fileSystemStorage) Put(ctx context.Context, key []byte, value []byte) error {
|
||||
finalPath := filepath.Join(f.pathRoot, filepath.Join(f.currentLocation...), string(key))
|
||||
if value == nil {
|
||||
return f.fs.RemoveFile()(finalPath)
|
||||
}
|
||||
writer, err := f.fs.OpenFileForWrite()(finalPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer writer.Close()
|
||||
_, err = io.Copy(writer, io.NopCloser(bytes.NewReader(value)))
|
||||
return err
|
||||
}
|
||||
|
||||
func (f *fileSystemStorage) Get(ctx context.Context, key []byte) ([]byte, error) {
|
||||
finalPath := filepath.Join(f.pathRoot, filepath.Join(f.currentLocation...), string(key))
|
||||
reader, err := f.fs.OpenFileForRead()(finalPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer reader.Close()
|
||||
return io.ReadAll(reader)
|
||||
}
|
||||
|
||||
func (f *fileSystemStorage) List(ctx context.Context, keyPrefix []byte) ([][]byte, error) {
|
||||
res, err := f.fs.ReadDir()(filepath.Join(f.pathRoot, filepath.Join(f.currentLocation...)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var result [][]byte
|
||||
for _, entry := range res {
|
||||
if !entry.IsDir() && bytes.HasPrefix([]byte(entry.Name()), keyPrefix) {
|
||||
result = append(result, []byte(entry.Name()))
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (f *fileSystemStorage) Clear(ctx context.Context) {
|
||||
allFile, err := f.List(ctx, []byte{})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, file := range allFile {
|
||||
_ = f.Put(ctx, file, nil)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *fileSystemStorage) NarrowScope(ctx context.Context, key []byte) (storage.ScopedPersistentStorage, error) {
|
||||
escapedKey := strings.ReplaceAll(string(key), "/", "_")
|
||||
fss := &fileSystemStorage{
|
||||
fs: f.fs,
|
||||
pathRoot: f.pathRoot,
|
||||
currentLocation: append(f.currentLocation, escapedKey),
|
||||
config: f.config,
|
||||
}
|
||||
fss.proto = protostorage.NewProtoStorage(fss, f.config.Protojson)
|
||||
return fss, nil
|
||||
}
|
||||
|
||||
func (f *fileSystemStorage) DropScope(ctx context.Context, key []byte) error {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
func init() {
|
||||
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
||||
return newFileSystemStorage(ctx, config.(*Config)), nil
|
||||
}))
|
||||
}
|
51
app/persistentstorage/protostorage/protokv.go
Normal file
51
app/persistentstorage/protostorage/protokv.go
Normal file
@ -0,0 +1,51 @@
|
||||
package protostorage
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/v2fly/v2ray-core/v5/features/extension/storage"
|
||||
)
|
||||
|
||||
type ProtoPersistentStorage interface {
|
||||
PutProto(ctx context.Context, key string, pb proto.Message) error
|
||||
GetProto(ctx context.Context, key string, pb proto.Message) error
|
||||
}
|
||||
|
||||
type protoStorage struct {
|
||||
storage storage.ScopedPersistentStorage
|
||||
textFormat bool
|
||||
}
|
||||
|
||||
func (p *protoStorage) PutProto(ctx context.Context, key string, pb proto.Message) error {
|
||||
if !p.textFormat {
|
||||
data, err := proto.Marshal(pb)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return p.storage.Put(ctx, []byte(key), data)
|
||||
} else {
|
||||
protojsonStr := protojson.Format(pb)
|
||||
return p.storage.Put(ctx, []byte(key), []byte(protojsonStr))
|
||||
}
|
||||
}
|
||||
|
||||
func (p *protoStorage) GetProto(ctx context.Context, key string, pb proto.Message) error {
|
||||
data, err := p.storage.Get(ctx, []byte(key))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !p.textFormat {
|
||||
return proto.Unmarshal(data, pb)
|
||||
}
|
||||
return protojson.Unmarshal(data, pb)
|
||||
}
|
||||
|
||||
func NewProtoStorage(storage storage.ScopedPersistentStorage, textFormat bool) ProtoPersistentStorage {
|
||||
return &protoStorage{
|
||||
storage: storage,
|
||||
textFormat: textFormat,
|
||||
}
|
||||
}
|
5
app/persistentstorage/storage.go
Normal file
5
app/persistentstorage/storage.go
Normal file
@ -0,0 +1,5 @@
|
||||
package persistentstorage
|
||||
|
||||
import "github.com/v2fly/v2ray-core/v5/features/extension/storage"
|
||||
|
||||
type ScopedPersistentStorage = storage.ScopedPersistentStorage
|
@ -5,18 +5,18 @@ import (
|
||||
"testing"
|
||||
_ "unsafe"
|
||||
|
||||
"github.com/v2fly/v2ray-core/v5/common/environment/systemnetworkimpl"
|
||||
|
||||
"github.com/v2fly/v2ray-core/v5/common/environment"
|
||||
"github.com/v2fly/v2ray-core/v5/common/environment/envctx"
|
||||
"github.com/v2fly/v2ray-core/v5/common/environment/transientstorageimpl"
|
||||
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
|
||||
core "github.com/v2fly/v2ray-core/v5"
|
||||
"github.com/v2fly/v2ray-core/v5/app/policy"
|
||||
. "github.com/v2fly/v2ray-core/v5/app/proxyman/outbound"
|
||||
"github.com/v2fly/v2ray-core/v5/app/stats"
|
||||
"github.com/v2fly/v2ray-core/v5/common/environment"
|
||||
"github.com/v2fly/v2ray-core/v5/common/environment/deferredpersistentstorage"
|
||||
"github.com/v2fly/v2ray-core/v5/common/environment/envctx"
|
||||
"github.com/v2fly/v2ray-core/v5/common/environment/filesystemimpl"
|
||||
"github.com/v2fly/v2ray-core/v5/common/environment/systemnetworkimpl"
|
||||
"github.com/v2fly/v2ray-core/v5/common/environment/transientstorageimpl"
|
||||
"github.com/v2fly/v2ray-core/v5/common/net"
|
||||
"github.com/v2fly/v2ray-core/v5/common/serial"
|
||||
"github.com/v2fly/v2ray-core/v5/features/outbound"
|
||||
@ -50,7 +50,11 @@ func TestOutboundWithoutStatCounter(t *testing.T) {
|
||||
v.AddFeature((outbound.Manager)(new(Manager)))
|
||||
ctx := toContext(context.Background(), v)
|
||||
defaultNetworkImpl := systemnetworkimpl.NewSystemNetworkDefault()
|
||||
rootEnv := environment.NewRootEnvImpl(ctx, transientstorageimpl.NewScopedTransientStorageImpl(), defaultNetworkImpl.Dialer(), defaultNetworkImpl.Listener())
|
||||
defaultFilesystemImpl := filesystemimpl.NewDefaultFileSystemDefaultImpl()
|
||||
deferredPersistentStorageImpl := deferredpersistentstorage.NewDeferredPersistentStorage(ctx)
|
||||
rootEnv := environment.NewRootEnvImpl(ctx,
|
||||
transientstorageimpl.NewScopedTransientStorageImpl(), defaultNetworkImpl.Dialer(), defaultNetworkImpl.Listener(),
|
||||
defaultFilesystemImpl, deferredPersistentStorageImpl)
|
||||
proxyEnvironment := rootEnv.ProxyEnvironment("o")
|
||||
ctx = envctx.ContextWithEnvironment(ctx, proxyEnvironment)
|
||||
h, _ := NewHandler(ctx, &core.OutboundHandlerConfig{
|
||||
@ -83,7 +87,11 @@ func TestOutboundWithStatCounter(t *testing.T) {
|
||||
v.AddFeature((outbound.Manager)(new(Manager)))
|
||||
ctx := toContext(context.Background(), v)
|
||||
defaultNetworkImpl := systemnetworkimpl.NewSystemNetworkDefault()
|
||||
rootEnv := environment.NewRootEnvImpl(ctx, transientstorageimpl.NewScopedTransientStorageImpl(), defaultNetworkImpl.Dialer(), defaultNetworkImpl.Listener())
|
||||
defaultFilesystemImpl := filesystemimpl.NewDefaultFileSystemDefaultImpl()
|
||||
deferredPersistentStorageImpl := deferredpersistentstorage.NewDeferredPersistentStorage(ctx)
|
||||
rootEnv := environment.NewRootEnvImpl(ctx,
|
||||
transientstorageimpl.NewScopedTransientStorageImpl(), defaultNetworkImpl.Dialer(), defaultNetworkImpl.Listener(),
|
||||
defaultFilesystemImpl, deferredPersistentStorageImpl)
|
||||
proxyEnvironment := rootEnv.ProxyEnvironment("o")
|
||||
ctx = envctx.ContextWithEnvironment(ctx, proxyEnvironment)
|
||||
h, _ := NewHandler(ctx, &core.OutboundHandlerConfig{
|
||||
|
@ -0,0 +1,111 @@
|
||||
package deferredpersistentstorage
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/v2fly/v2ray-core/v5/app/persistentstorage"
|
||||
"github.com/v2fly/v2ray-core/v5/common/errors"
|
||||
"github.com/v2fly/v2ray-core/v5/features/extension/storage"
|
||||
)
|
||||
|
||||
type DeferredPersistentStorage interface {
|
||||
storage.ScopedPersistentStorage
|
||||
ProvideInner(ctx context.Context, inner persistentstorage.ScopedPersistentStorage)
|
||||
}
|
||||
|
||||
var errNotExist = errors.New("persistent storage does not exist")
|
||||
|
||||
type deferredPersistentStorage struct {
|
||||
ready context.Context
|
||||
done context.CancelFunc
|
||||
inner persistentstorage.ScopedPersistentStorage
|
||||
|
||||
awaitingChildren []*deferredPersistentStorage
|
||||
|
||||
intoScopes []string
|
||||
}
|
||||
|
||||
func (d *deferredPersistentStorage) ScopedPersistentStorageEngine() {
|
||||
}
|
||||
|
||||
func (d *deferredPersistentStorage) Put(ctx context.Context, key []byte, value []byte) error {
|
||||
<-d.ready.Done()
|
||||
if d.inner == nil {
|
||||
return errNotExist
|
||||
}
|
||||
return d.inner.Put(ctx, key, value)
|
||||
}
|
||||
|
||||
func (d *deferredPersistentStorage) Get(ctx context.Context, key []byte) ([]byte, error) {
|
||||
<-d.ready.Done()
|
||||
if d.inner == nil {
|
||||
return nil, errNotExist
|
||||
}
|
||||
return d.inner.Get(ctx, key)
|
||||
}
|
||||
|
||||
func (d *deferredPersistentStorage) List(ctx context.Context, keyPrefix []byte) ([][]byte, error) {
|
||||
<-d.ready.Done()
|
||||
if d.inner == nil {
|
||||
return nil, errNotExist
|
||||
}
|
||||
return d.inner.List(ctx, keyPrefix)
|
||||
}
|
||||
|
||||
func (d *deferredPersistentStorage) Clear(ctx context.Context) {
|
||||
<-d.ready.Done()
|
||||
if d.inner == nil {
|
||||
return
|
||||
}
|
||||
d.inner.Clear(ctx)
|
||||
}
|
||||
|
||||
func (d *deferredPersistentStorage) NarrowScope(ctx context.Context, key []byte) (storage.ScopedPersistentStorage, error) {
|
||||
if d.ready.Err() != nil {
|
||||
return d.inner.NarrowScope(ctx, key)
|
||||
}
|
||||
ready, done := context.WithCancel(ctx)
|
||||
swallowCopyScopes := d.intoScopes
|
||||
dps := &deferredPersistentStorage{
|
||||
ready: ready,
|
||||
done: done,
|
||||
inner: nil,
|
||||
intoScopes: append(swallowCopyScopes, string(key)),
|
||||
}
|
||||
d.awaitingChildren = append(d.awaitingChildren, dps)
|
||||
return dps, nil
|
||||
}
|
||||
|
||||
func (d *deferredPersistentStorage) DropScope(ctx context.Context, key []byte) error {
|
||||
<-d.ready.Done()
|
||||
if d.inner == nil {
|
||||
return errNotExist
|
||||
}
|
||||
return d.inner.DropScope(ctx, key)
|
||||
}
|
||||
|
||||
func (d *deferredPersistentStorage) ProvideInner(ctx context.Context, inner persistentstorage.ScopedPersistentStorage) {
|
||||
d.inner = inner
|
||||
if inner != nil {
|
||||
for _, scope := range d.intoScopes {
|
||||
newScope, err := inner.NarrowScope(ctx, []byte(scope))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
d.inner = newScope
|
||||
}
|
||||
}
|
||||
for _, child := range d.awaitingChildren {
|
||||
child.ProvideInner(ctx, d.inner)
|
||||
}
|
||||
d.done()
|
||||
}
|
||||
|
||||
func NewDeferredPersistentStorage(ctx context.Context) DeferredPersistentStorage {
|
||||
ready, done := context.WithCancel(ctx)
|
||||
return &deferredPersistentStorage{
|
||||
ready: ready,
|
||||
done: done,
|
||||
inner: nil,
|
||||
}
|
||||
}
|
@ -8,6 +8,14 @@ import (
|
||||
|
||||
type fileSystemDefaultImpl struct{}
|
||||
|
||||
func (f fileSystemDefaultImpl) ReadDir() fsifce.FileReadDirFunc {
|
||||
return filesystem.NewFileReadDir
|
||||
}
|
||||
|
||||
func (f fileSystemDefaultImpl) RemoveFile() fsifce.FileRemoveFunc {
|
||||
return filesystem.NewFileRemover
|
||||
}
|
||||
|
||||
func (f fileSystemDefaultImpl) OpenFileForReadSeek() fsifce.FileSeekerFunc {
|
||||
return filesystem.NewFileSeeker
|
||||
}
|
||||
|
@ -6,4 +6,6 @@ type FileSystemCapabilitySet interface {
|
||||
OpenFileForReadSeek() fsifce.FileSeekerFunc
|
||||
OpenFileForRead() fsifce.FileReaderFunc
|
||||
OpenFileForWrite() fsifce.FileWriterFunc
|
||||
ReadDir() fsifce.FileReadDirFunc
|
||||
RemoveFile() fsifce.FileRemoveFunc
|
||||
}
|
||||
|
33
common/environment/filesystemimpl/fsimpl.go
Normal file
33
common/environment/filesystemimpl/fsimpl.go
Normal file
@ -0,0 +1,33 @@
|
||||
package filesystemimpl
|
||||
|
||||
import (
|
||||
"github.com/v2fly/v2ray-core/v5/common/environment"
|
||||
"github.com/v2fly/v2ray-core/v5/common/platform/filesystem"
|
||||
"github.com/v2fly/v2ray-core/v5/common/platform/filesystem/fsifce"
|
||||
)
|
||||
|
||||
func NewDefaultFileSystemDefaultImpl() environment.FileSystemCapabilitySet {
|
||||
return fsCapImpl{}
|
||||
}
|
||||
|
||||
type fsCapImpl struct{}
|
||||
|
||||
func (f fsCapImpl) OpenFileForReadSeek() fsifce.FileSeekerFunc {
|
||||
return filesystem.NewFileSeeker
|
||||
}
|
||||
|
||||
func (f fsCapImpl) OpenFileForRead() fsifce.FileReaderFunc {
|
||||
return filesystem.NewFileReader
|
||||
}
|
||||
|
||||
func (f fsCapImpl) OpenFileForWrite() fsifce.FileWriterFunc {
|
||||
return filesystem.NewFileWriter
|
||||
}
|
||||
|
||||
func (f fsCapImpl) ReadDir() fsifce.FileReadDirFunc {
|
||||
return filesystem.NewFileReadDir
|
||||
}
|
||||
|
||||
func (f fsCapImpl) RemoveFile() fsifce.FileRemoveFunc {
|
||||
return filesystem.NewFileRemover
|
||||
}
|
@ -11,19 +11,24 @@ import (
|
||||
|
||||
func NewRootEnvImpl(ctx context.Context, transientStorage storage.ScopedTransientStorage,
|
||||
systemDialer internet.SystemDialer, systemListener internet.SystemListener,
|
||||
filesystem FileSystemCapabilitySet, persistStorage storage.ScopedPersistentStorage,
|
||||
) RootEnvironment {
|
||||
return &rootEnvImpl{
|
||||
transientStorage: transientStorage,
|
||||
systemListener: systemListener,
|
||||
systemDialer: systemDialer,
|
||||
filesystem: filesystem,
|
||||
persistStorage: persistStorage,
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
type rootEnvImpl struct {
|
||||
persistStorage storage.ScopedPersistentStorage
|
||||
transientStorage storage.ScopedTransientStorage
|
||||
systemDialer internet.SystemDialer
|
||||
systemListener internet.SystemListener
|
||||
filesystem FileSystemCapabilitySet
|
||||
|
||||
ctx context.Context
|
||||
}
|
||||
@ -37,10 +42,16 @@ func (r *rootEnvImpl) AppEnvironment(tag string) AppEnvironment {
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
persistStorage, err := r.persistStorage.NarrowScope(r.ctx, []byte(tag))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return &appEnvImpl{
|
||||
transientStorage: transientStorage,
|
||||
persistStorage: persistStorage,
|
||||
systemListener: r.systemListener,
|
||||
systemDialer: r.systemDialer,
|
||||
filesystem: r.filesystem,
|
||||
ctx: r.ctx,
|
||||
}
|
||||
}
|
||||
@ -67,9 +78,11 @@ func (r *rootEnvImpl) DropProxyEnvironment(tag string) error {
|
||||
}
|
||||
|
||||
type appEnvImpl struct {
|
||||
persistStorage storage.ScopedPersistentStorage
|
||||
transientStorage storage.ScopedTransientStorage
|
||||
systemDialer internet.SystemDialer
|
||||
systemListener internet.SystemListener
|
||||
filesystem FileSystemCapabilitySet
|
||||
|
||||
ctx context.Context
|
||||
}
|
||||
@ -95,19 +108,27 @@ func (a *appEnvImpl) OutboundDialer() tagged.DialFunc {
|
||||
}
|
||||
|
||||
func (a *appEnvImpl) OpenFileForReadSeek() fsifce.FileSeekerFunc {
|
||||
panic("implement me")
|
||||
return a.filesystem.OpenFileForReadSeek()
|
||||
}
|
||||
|
||||
func (a *appEnvImpl) OpenFileForRead() fsifce.FileReaderFunc {
|
||||
panic("implement me")
|
||||
return a.filesystem.OpenFileForRead()
|
||||
}
|
||||
|
||||
func (a *appEnvImpl) OpenFileForWrite() fsifce.FileWriterFunc {
|
||||
panic("implement me")
|
||||
return a.filesystem.OpenFileForWrite()
|
||||
}
|
||||
|
||||
func (a *appEnvImpl) ReadDir() fsifce.FileReadDirFunc {
|
||||
return a.filesystem.ReadDir()
|
||||
}
|
||||
|
||||
func (a *appEnvImpl) RemoveFile() fsifce.FileRemoveFunc {
|
||||
return a.filesystem.RemoveFile()
|
||||
}
|
||||
|
||||
func (a *appEnvImpl) PersistentStorage() storage.ScopedPersistentStorage {
|
||||
panic("implement me")
|
||||
return a.persistStorage
|
||||
}
|
||||
|
||||
func (a *appEnvImpl) TransientStorage() storage.ScopedTransientStorage {
|
||||
|
@ -3,6 +3,7 @@ package filesystem
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/v2fly/v2ray-core/v5/common/buf"
|
||||
"github.com/v2fly/v2ray-core/v5/common/platform"
|
||||
@ -18,9 +19,17 @@ var NewFileReader fsifce.FileReaderFunc = func(path string) (io.ReadCloser, erro
|
||||
}
|
||||
|
||||
var NewFileWriter fsifce.FileWriterFunc = func(path string) (io.WriteCloser, error) {
|
||||
basePath := filepath.Dir(path)
|
||||
if err := os.MkdirAll(basePath, 0o700); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return os.Create(path)
|
||||
}
|
||||
|
||||
var NewFileRemover fsifce.FileRemoveFunc = os.Remove
|
||||
|
||||
var NewFileReadDir fsifce.FileReadDirFunc = os.ReadDir
|
||||
|
||||
func ReadFile(path string) ([]byte, error) {
|
||||
reader, err := NewFileReader(path)
|
||||
if err != nil {
|
||||
|
@ -1,9 +1,16 @@
|
||||
package fsifce
|
||||
|
||||
import "io"
|
||||
import (
|
||||
"io"
|
||||
"io/fs"
|
||||
)
|
||||
|
||||
type FileSeekerFunc func(path string) (io.ReadSeekCloser, error)
|
||||
|
||||
type FileReaderFunc func(path string) (io.ReadCloser, error)
|
||||
|
||||
type FileWriterFunc func(path string) (io.WriteCloser, error)
|
||||
|
||||
type FileReadDirFunc func(path string) ([]fs.DirEntry, error)
|
||||
|
||||
type FileRemoveFunc func(path string) error
|
||||
|
@ -2,6 +2,8 @@ package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/v2fly/v2ray-core/v5/features"
|
||||
)
|
||||
|
||||
type ScopedPersistentStorage interface {
|
||||
@ -23,3 +25,10 @@ type ScopedTransientStorage interface {
|
||||
NarrowScope(ctx context.Context, key string) (ScopedTransientStorage, error)
|
||||
DropScope(ctx context.Context, key string) error
|
||||
}
|
||||
|
||||
type ScopedPersistentStorageService interface {
|
||||
ScopedPersistentStorage
|
||||
features.Feature
|
||||
}
|
||||
|
||||
var ScopedPersistentStorageServiceType = (*ScopedPersistentStorageService)(nil)
|
||||
|
@ -108,9 +108,9 @@ func getProjectProtocVersion(url string) (string, error) {
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("can not read from body")
|
||||
}
|
||||
versionRegexp := regexp.MustCompile(`\/\/\s*protoc\s*v(\d+\.\d+\.\d+)`)
|
||||
versionRegexp := regexp.MustCompile(`\/\/\s*protoc\s*v(\d+\.(\d+\.\d+))`)
|
||||
matched := versionRegexp.FindStringSubmatch(string(body))
|
||||
return matched[1], nil
|
||||
return matched[2], nil
|
||||
}
|
||||
|
||||
func getInstalledProtocVersion(protocPath string) (string, error) {
|
||||
@ -126,10 +126,6 @@ func getInstalledProtocVersion(protocPath string) (string, error) {
|
||||
if len(matched) == 0 {
|
||||
return "", errors.New("can not parse protoc version")
|
||||
}
|
||||
|
||||
if len(matched) == 2 {
|
||||
installedVersion += "4." // in contrast to getProjectProtocVersion()
|
||||
}
|
||||
installedVersion += matched[1]
|
||||
fmt.Println("Using protoc version: " + installedVersion)
|
||||
return installedVersion, nil
|
||||
@ -139,9 +135,6 @@ func parseVersion(s string, width int) int64 {
|
||||
strList := strings.Split(s, ".")
|
||||
format := fmt.Sprintf("%%s%%0%ds", width)
|
||||
v := ""
|
||||
if len(strList) == 2 {
|
||||
strList = append([]string{"4"}, strList...)
|
||||
}
|
||||
for _, value := range strList {
|
||||
v = fmt.Sprintf(format, v, value)
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ import (
|
||||
_ "github.com/v2fly/v2ray-core/v5/app/commander/webcommander"
|
||||
_ "github.com/v2fly/v2ray-core/v5/app/instman"
|
||||
_ "github.com/v2fly/v2ray-core/v5/app/observatory"
|
||||
_ "github.com/v2fly/v2ray-core/v5/app/persistentstorage/filesystemstorage"
|
||||
_ "github.com/v2fly/v2ray-core/v5/app/tun"
|
||||
|
||||
// Inbound and outbound proxies.
|
||||
|
@ -7,6 +7,9 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/v2fly/v2ray-core/v5/common/environment/deferredpersistentstorage"
|
||||
"github.com/v2fly/v2ray-core/v5/common/environment/filesystemimpl"
|
||||
|
||||
"github.com/v2fly/v2ray-core/v5/common/environment"
|
||||
"github.com/v2fly/v2ray-core/v5/common/environment/envctx"
|
||||
"github.com/v2fly/v2ray-core/v5/common/environment/systemnetworkimpl"
|
||||
@ -25,7 +28,11 @@ import (
|
||||
func TestDialAndListen(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
defaultNetworkImpl := systemnetworkimpl.NewSystemNetworkDefault()
|
||||
rootEnv := environment.NewRootEnvImpl(ctx, transientstorageimpl.NewScopedTransientStorageImpl(), defaultNetworkImpl.Dialer(), defaultNetworkImpl.Listener())
|
||||
defaultFilesystemImpl := filesystemimpl.NewDefaultFileSystemDefaultImpl()
|
||||
deferredPersistentStorageImpl := deferredpersistentstorage.NewDeferredPersistentStorage(ctx)
|
||||
rootEnv := environment.NewRootEnvImpl(ctx,
|
||||
transientstorageimpl.NewScopedTransientStorageImpl(), defaultNetworkImpl.Dialer(), defaultNetworkImpl.Listener(),
|
||||
defaultFilesystemImpl, deferredPersistentStorageImpl)
|
||||
proxyEnvironment := rootEnv.ProxyEnvironment("o")
|
||||
transportEnvironment, err := proxyEnvironment.NarrowScopeToTransport("kcp")
|
||||
if err != nil {
|
||||
|
@ -13,6 +13,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
|
||||
"github.com/v2fly/v2ray-core/v5/common/net"
|
||||
"github.com/v2fly/v2ray-core/v5/transport/internet"
|
||||
)
|
||||
|
19
v2ray.go
19
v2ray.go
@ -5,6 +5,10 @@ import (
|
||||
"reflect"
|
||||
sync "sync"
|
||||
|
||||
"github.com/v2fly/v2ray-core/v5/common/environment/deferredpersistentstorage"
|
||||
"github.com/v2fly/v2ray-core/v5/common/environment/filesystemimpl"
|
||||
"github.com/v2fly/v2ray-core/v5/features/extension/storage"
|
||||
|
||||
"github.com/v2fly/v2ray-core/v5/common"
|
||||
"github.com/v2fly/v2ray-core/v5/common/environment"
|
||||
"github.com/v2fly/v2ray-core/v5/common/environment/systemnetworkimpl"
|
||||
@ -205,7 +209,14 @@ func initInstanceWithConfig(config *Config, server *Instance) (bool, error) {
|
||||
}
|
||||
|
||||
defaultNetworkImpl := systemnetworkimpl.NewSystemNetworkDefault()
|
||||
server.env = environment.NewRootEnvImpl(server.ctx, transientstorageimpl.NewScopedTransientStorageImpl(), defaultNetworkImpl.Dialer(), defaultNetworkImpl.Listener())
|
||||
defaultFilesystemImpl := filesystemimpl.NewDefaultFileSystemDefaultImpl()
|
||||
deferredPersistentStorageImpl := deferredpersistentstorage.NewDeferredPersistentStorage(server.ctx)
|
||||
server.env = environment.NewRootEnvImpl(server.ctx,
|
||||
transientstorageimpl.NewScopedTransientStorageImpl(),
|
||||
defaultNetworkImpl.Dialer(),
|
||||
defaultNetworkImpl.Listener(),
|
||||
defaultFilesystemImpl,
|
||||
deferredPersistentStorageImpl)
|
||||
|
||||
for _, appSettings := range config.App {
|
||||
settings, err := serial.GetInstanceOf(appSettings)
|
||||
@ -247,6 +258,12 @@ func initInstanceWithConfig(config *Config, server *Instance) (bool, error) {
|
||||
return true, newError("not all dependency are resolved.")
|
||||
}
|
||||
|
||||
if persistentStorageService := server.GetFeature(storage.ScopedPersistentStorageServiceType); persistentStorageService != nil {
|
||||
deferredPersistentStorageImpl.ProvideInner(server.ctx, persistentStorageService.(storage.ScopedPersistentStorage))
|
||||
} else {
|
||||
deferredPersistentStorageImpl.ProvideInner(server.ctx, nil)
|
||||
}
|
||||
|
||||
if err := addInboundHandlers(server, config.Inbound); err != nil {
|
||||
return true, err
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user