mirror of
https://github.com/XTLS/Xray-core.git
synced 2026-10-14 01:59:09 +03:00
Lua: Support inline scripts; Add ./xray base64 encode/decode [-gzip] command (#7132)
https://github.com/XTLS/Xray-core/pull/7124#issuecomment-6110557806 https://github.com/XTLS/Xray-core/pull/7132#issuecomment-6118169645 https://github.com/XTLS/Xray-core/pull/7132#issuecomment-6126428359
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
package base64
|
||||
|
||||
const (
|
||||
Prefix = "base64:"
|
||||
GzipPrefix = "base64+gzip:"
|
||||
)
|
||||
@@ -0,0 +1,38 @@
|
||||
package base64
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"encoding/base64"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
)
|
||||
|
||||
// Decode decodes Base64 text with an optional [Prefix] or [GzipPrefix].
|
||||
func Decode(value string) ([]byte, error) {
|
||||
value, zip := strings.CutPrefix(value, GzipPrefix)
|
||||
if !zip {
|
||||
value = strings.TrimPrefix(value, Prefix)
|
||||
}
|
||||
|
||||
data, err := base64.StdEncoding.DecodeString(value)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to decode base64").Base(err)
|
||||
}
|
||||
if !zip {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
reader, err := gzip.NewReader(bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to decompress gzip").Base(err)
|
||||
}
|
||||
defer reader.Close()
|
||||
data, err = io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to decompress gzip").Base(err)
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package base64
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDecodeFormats(t *testing.T) {
|
||||
compressed, err := Encode([]byte("x"), true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, tc := range []struct {
|
||||
name, value, want string
|
||||
}{
|
||||
{"unprefixed", "eA==", "x"},
|
||||
{"base64", Prefix + "eA==", "x"},
|
||||
{"gzip", compressed, "x"},
|
||||
{"empty", Prefix, ""},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
data, err := Decode(tc.value)
|
||||
if err != nil || !bytes.Equal(data, []byte(tc.want)) {
|
||||
t.Fatalf("Decode = %q, %v; want %q", data, err, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package base64
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"encoding/base64"
|
||||
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
)
|
||||
|
||||
// Encode returns prefixed Base64 text, optionally compressed with gzip at the highest level.
|
||||
func Encode(data []byte, zip bool) (string, error) {
|
||||
if !zip {
|
||||
return Prefix + base64.StdEncoding.EncodeToString(data), nil
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
writer, err := gzip.NewWriterLevel(&buf, gzip.BestCompression)
|
||||
if err != nil {
|
||||
return "", errors.New("failed to compress gzip").Base(err)
|
||||
}
|
||||
if _, err := writer.Write(data); err != nil {
|
||||
return "", errors.New("failed to compress gzip").Base(err)
|
||||
}
|
||||
if err := writer.Close(); err != nil {
|
||||
return "", errors.New("failed to compress gzip").Base(err)
|
||||
}
|
||||
return GzipPrefix + base64.StdEncoding.EncodeToString(buf.Bytes()), nil
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package base64
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"encoding/base64"
|
||||
"io"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEncodeFormats(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
data []byte
|
||||
}{
|
||||
{"empty", nil},
|
||||
{"bytes", []byte("你好\r\n\x00\xff")},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
for _, zip := range []bool{false, true} {
|
||||
prefix := Prefix
|
||||
if zip {
|
||||
prefix = GzipPrefix
|
||||
}
|
||||
t.Run(prefix, func(t *testing.T) {
|
||||
value, err := Encode(tc.data, zip)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
payload, ok := strings.CutPrefix(value, prefix)
|
||||
if !ok {
|
||||
t.Fatalf("encoded value does not start with %q", prefix)
|
||||
}
|
||||
// Read the output independently of Decode so matching bugs cannot hide.
|
||||
data, err := base64.StdEncoding.DecodeString(payload)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if zip {
|
||||
// gzip's XFL byte identifies the compression level we selected.
|
||||
if len(data) < 10 || data[8] != 2 {
|
||||
t.Fatal("gzip output does not use the highest compression level")
|
||||
}
|
||||
reader, err := gzip.NewReader(bytes.NewReader(data))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer reader.Close()
|
||||
data, err = io.ReadAll(reader)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if !bytes.Equal(data, tc.data) {
|
||||
t.Fatal("encoded output does not preserve the input bytes")
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user