Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 3 additions & 34 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,7 @@ jobs:
artifact: windows-x64
- runner: macos-26
target: apple
apple_tool: go
artifact: apple-cgo
- runner: macos-26
target: apple
apple_tool: gomobile
artifact: apple-gomobile

runs-on: ${{ matrix.runner }}
timeout-minutes: 60
Expand Down Expand Up @@ -115,32 +110,6 @@ jobs:
ndk-version: ${{ env.NDK_VERSION }}
add-to-path: true

- name: Install gomobile (Android)
if: matrix.target == 'android'
run: |
go install golang.org/x/mobile/cmd/gomobile@latest
gomobile init

# =========================
# Windows toolchain
# =========================
- name: Setup MSYS2 (Windows GCC)
if: matrix.target == 'windows'
uses: msys2/setup-msys2@v2
with:
install: >-
mingw-w64-x86_64-gcc
mingw-w64-x86_64-toolchain

# =========================
# Apple toolchain
# =========================
- name: Setup gomobile (Apple)
if: matrix.target == 'apple' && matrix.apple_tool == 'gomobile'
run: |
go install golang.org/x/mobile/cmd/gomobile@latest
gomobile init

# =========================
# Build steps
# =========================
Comment thread
yiguodev marked this conversation as resolved.
Expand All @@ -163,7 +132,7 @@ jobs:
- name: Build Apple (macOS/iOS)
if: matrix.target == 'apple'
run: |
python3 build/main.py apple ${{ matrix.apple_tool }}
python3 build/main.py apple go

# =========================
# Collect artifacts
Expand All @@ -178,13 +147,11 @@ jobs:
mkdir -p "dist/${{ matrix.artifact }}"
cp linux_so/libXray.so "dist/${{ matrix.artifact }}/"
cp linux_so/libXray.h "dist/${{ matrix.artifact }}/"
cp bin/xray "dist/${{ matrix.artifact }}/"
;;
windows)
mkdir -p "dist/${{ matrix.artifact }}"
cp windows_dll/libXray.dll "dist/${{ matrix.artifact }}/"
cp windows_dll/libXray.h "dist/${{ matrix.artifact }}/"
cp bin/xray.exe "dist/${{ matrix.artifact }}/"
;;
android)
mkdir -p "dist/${{ matrix.artifact }}"
Expand All @@ -208,6 +175,7 @@ jobs:
if-no-files-found: error

release:
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
needs: build
runs-on: ubuntu-24.04
permissions:
Expand All @@ -232,6 +200,7 @@ jobs:
with:
name: libXray ${{ github.ref_name }}
tag_name: ${{ github.ref_name }}
prerelease: true
files: dist/*.zip
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
108 changes: 89 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ depends on git and go.

By default, the build script does not clone [Xray-core](https://github.com/XTLS/Xray-core). It uses Go modules and pins Xray-core to tag `v26.6.27` (recorded by Go as the matching pseudo-version).
Pass the optional `local` argument to use an existing local checkout at `../Xray-core` through a Go module `replace`.
Linux and Windows builds only produce the libXray shared library. Applications that need a standalone Xray executable should use official Xray-core release binaries.

### Usage

Expand Down Expand Up @@ -101,13 +102,98 @@ depend on gcc and g++.

### Windows

depend on MinGW.
depend on LLVM MinGW.

you can use winget to install [LLVM MinGW](https://github.com/mstorsjo/llvm-mingw) or [WinLibs](https://github.com/brechtsanders/winlibs_mingw) .
you can use winget to install [LLVM MinGW](https://github.com/mstorsjo/llvm-mingw).

```shell
winget install MartinStorsjo.LLVM-MinGW.UCRT
winget install BrechtSanders.WinLibs.POSIX.UCRT
```

## API

libXray exposes a single structured entrypoint:

```go
func Invoke(requestJSON string) string
```

The C export is:

```c
char* CGoInvoke(char* requestJSON);
```

The request is a JSON object:

```json
{
"apiVersion": 1,
"method": "runXray",
"env": {
"xray.location.config": "/path/to/config.json",
"xray.location.asset": "/path/to/dat",
"xray.location.cert": "/path/to/dat",
"xray.tun.fd": "123"
},
"payload": {
"configPath": "/path/to/config.json"
}
}
```

The response is a JSON object:

```json
{
"success": true,
"data": {},
"error": ""
}
```

`env` is optional and only supports Xray-core environment variables that are
explicitly modeled by libXray:

| JSON key | Meaning |
| --- | --- |
| `xray.location.config` | Xray config file location |
| `xray.location.confdir` | Xray config directory location |
| `xray.location.asset` | Directory containing `geosite.dat`, `geoip.dat`, and custom GeoData files |
| `xray.location.cert` | Certificate directory used by Xray-core |
| `xray.buf.readv` | Xray-core readv buffer switch |
| `xray.buf.splice` | Xray-core splice buffer switch |
| `xray.vmess.padding` | VMess padding switch |
| `xray.cone.disabled` | Cone behavior switch |
| `xray.json.strict` | Strict JSON parsing switch |
| `xray.ray.buffer.size` | Ray buffer size |
| `xray.browser.dialer` | Browser dialer address |
| `xray.xudp.show` | XUDP log display switch |
| `xray.xudp.basekey` | XUDP base key |
| `xray.tun.fd` | TUN file descriptor for Android, iOS, and macOS packet tunnel integrations |

Design notes:

1. `env` is modeled as fixed fields in Go and Dart. It is not a free-form map.
2. Unknown `env` keys are ignored and are not written to the process environment.
3. `env` only sets modeled, non-empty fields. Missing fields are not unset.
4. libXray does not restore previous environment values after a method returns. Callers must pass the required env fields on every request that depends on them. This avoids concurrent calls restoring stale values over newer values.
5. `SetTunFd` has been removed. Pass `xray.tun.fd` in the `env` object of the `runXray` request.

Supported methods:

```text
getFreePorts
convertShareLinksToXrayJson
convertXrayJsonToShareLinks
countGeoData
ping
testXray
runXray
runXrayFromJson
stopXray
xrayVersion
getXrayState
```

## controller
Expand All @@ -120,10 +206,6 @@ Used to solve the socket protect problem on Android.

Read geo files and count the categories and rules.

### read

Read the Xray Json configuration and extract the geo file name used.

## main

Download geosite.dat and geoip.dat and count them.
Expand All @@ -142,10 +224,6 @@ Write data to a file.

Speed ​​test the Xray configuration.

### model

The response body of the wrapper interface.

### port

Get free ports.
Expand Down Expand Up @@ -224,14 +302,6 @@ Verify the Xray configuration.

Start and stop Xray instances.

## nodep_wrapper

export nodep.

### xray_wrapper

export xray.

# Credits

[Project X](https://github.com/XTLS/Xray-core)
Expand Down
24 changes: 0 additions & 24 deletions build/app/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import subprocess

from app.cmd import (
create_dir_if_not_exists,
delete_file_if_exists,
delete_dir_if_exists,
)
Expand All @@ -22,7 +21,6 @@ class Builder(object):
def __init__(self, build_dir: str, use_local_xray_core: bool = False):
self.build_dir = build_dir
self.lib_dir = os.path.abspath(os.path.join(self.build_dir, ".."))
self.bin_file = "xray"
self.use_local_xray_core = use_local_xray_core
self.xray_core_replace_path = f"../{LOCAL_XRAY_CORE_DIR_NAME}"
self.xray_core_dir = os.path.abspath(
Expand Down Expand Up @@ -167,28 +165,6 @@ def reset_package_name(self, file_name: str):
with open(file_path, "w") as f:
f.writelines(new_lines)

def build_desktop_bin(self):
bin_dir = os.path.join(self.lib_dir, "bin")
create_dir_if_not_exists(bin_dir)
output_file = os.path.join(bin_dir, self.bin_file)
run_env = os.environ.copy()
run_env["CGO_ENABLED"] = "0"

cmd = [
"go",
"build",
"-trimpath",
"-ldflags",
"-s -w",
f"-o={output_file}",
"./desktop_bin",
]
os.chdir(self.lib_dir)
print(cmd)
ret = subprocess.run(cmd, env=run_env)
if ret.returncode != 0:
raise Exception(f"build_desktop_bin failed")

def revert_go_env(self):
os.chdir(self.lib_dir)
ret = subprocess.run(
Expand Down
2 changes: 0 additions & 2 deletions build/app/linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ def build(self):
self.before_build()
self.build_linux()
self.after_build()

self.build_desktop_bin()
self.revert_go_env()

def build_linux(self):
Expand Down
4 changes: 0 additions & 4 deletions build/app/windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ def __init__(self, build_dir: str, use_local_xray_core: bool = False):
create_dir_if_not_exists(self.framework_dir)
self.lib_file = "libXray.dll"
self.lib_header_file = "libXray.h"
self.bin_file = "xray.exe"

def before_build(self):
super().before_build()
Expand All @@ -22,10 +21,7 @@ def before_build(self):
def build(self):
self.before_build()
self.build_windows()

self.after_build()

self.build_desktop_bin()
self.revert_go_env()

def build_windows(self):
Expand Down
70 changes: 4 additions & 66 deletions build/template/main.gotemplate
Original file line number Diff line number Diff line change
Expand Up @@ -4,70 +4,8 @@ import "C"

func main() {}

//export CGoSetTunFd
func CGoSetTunFd(fd C.int) {
SetTunFd(int32(fd))
}

//export CGoRunXrayFromJSON
func CGoRunXrayFromJSON(base64Text *C.char) *C.char {
text := C.GoString(base64Text)
return C.CString(RunXrayFromJSON(text))
}

//export CGoGetFreePorts
func CGoGetFreePorts(count int) *C.char {
return C.CString(GetFreePorts(count))
}

//export CGoConvertShareLinksToXrayJson
func CGoConvertShareLinksToXrayJson(base64Text *C.char) *C.char {
text := C.GoString(base64Text)
return C.CString(ConvertShareLinksToXrayJson(text))
}

//export CGOConvertXrayJsonToShareLinks
func CGOConvertXrayJsonToShareLinks(base64Text *C.char) *C.char {
text := C.GoString(base64Text)
return C.CString(ConvertXrayJsonToShareLinks(text))
}

//export CGoCountGeoData
func CGoCountGeoData(base64Text *C.char) *C.char {
text := C.GoString(base64Text)
return C.CString(CountGeoData(text))
}

//export CGoReadGeoFiles
func CGoReadGeoFiles(base64Text *C.char) *C.char {
text := C.GoString(base64Text)
return C.CString(ReadGeoFiles(text))
}

//export CGoPing
func CGoPing(base64Text *C.char) *C.char {
text := C.GoString(base64Text)
return C.CString(Ping(text))
}

//export CGoTestXray
func CGoTestXray(base64Text *C.char) *C.char {
text := C.GoString(base64Text)
return C.CString(TestXray(text))
}

//export CGoRunXray
func CGoRunXray(base64Text *C.char) *C.char {
text := C.GoString(base64Text)
return C.CString(RunXray(text))
}

//export CGoStopXray
func CGoStopXray() *C.char {
return C.CString(StopXray())
}

//export CGoXrayVersion
func CGoXrayVersion() *C.char {
return C.CString(XrayVersion())
//export CGoInvoke
func CGoInvoke(requestJSON *C.char) *C.char {
text := C.GoString(requestJSON)
return C.CString(Invoke(text))
}
Loading