init
This commit is contained in:
parent
8550e86395
commit
9cf8968a82
8
.gitignore
vendored
8
.gitignore
vendored
@ -19,3 +19,11 @@
|
||||
|
||||
# Go workspace file
|
||||
go.work
|
||||
|
||||
# Cgo files
|
||||
*.a
|
||||
*.h
|
||||
test
|
||||
|
||||
# MacOS system files
|
||||
.DS_Store
|
||||
|
33
Makefile
Normal file
33
Makefile
Normal file
@ -0,0 +1,33 @@
|
||||
PROJECT_NAME := comandy
|
||||
BUILD_PATH := build
|
||||
GOOS := android
|
||||
GOARCH := arm64
|
||||
BUILD_MACHINE := darwin
|
||||
BUILD_ARCH := x86_64
|
||||
NDK_VERSION := 26.3.11579264
|
||||
TARGET_SDK := android23
|
||||
|
||||
CGO_ENABLED := 1
|
||||
GO_SRC := $(shell find . -name '*.go')
|
||||
NDK_TOOLCHAIN := ~/Library/Android/sdk/ndk/$(NDK_VERSION)/toolchains/llvm/prebuilt/$(BUILD_MACHINE)-$(BUILD_ARCH)
|
||||
CC := $(NDK_TOOLCHAIN)/bin/aarch64-linux-$(TARGET_SDK)-clang
|
||||
TEST_OUTPUT = '$(shell cd $(BUILD_PATH) && ./test)'
|
||||
TEST_EXPECTED = '{"code":500,"data":"aW52YWxpZCB1cmwgJyc="}'
|
||||
|
||||
all: shared
|
||||
|
||||
shared: $(GO_SRC) dir
|
||||
GOOS=$(GOOS) GOARCH=$(GOARCH) CGO_ENABLED=$(CGO_ENABLED) NDK_TOOLCHAIN=$(NDK_TOOLCHAIN) CC=$(CC) go build -buildmode=c-shared -o $(BUILD_PATH)/lib$(PROJECT_NAME).so $(GO_SRC)
|
||||
test: dir
|
||||
@GOOS=$(BUILD_MACHINE) CC=cc NDK_TOOLCHAIN="" $(MAKE) -e shared
|
||||
cc -o $(BUILD_PATH)/test $(BUILD_PATH)/test.c -l$(PROJECT_NAME) -L$(BUILD_PATH)
|
||||
runtest: test
|
||||
@if [ $(TEST_OUTPUT) = $(TEST_EXPECTED) ]; then \
|
||||
echo "test succeeded."; \
|
||||
else \
|
||||
echo "test failed, expected:" $(TEST_EXPECTED) "but got:" $(TEST_OUTPUT); \
|
||||
fi
|
||||
dir:
|
||||
@if [ ! -d "$(BUILD_PATH)" ]; then mkdir $(BUILD_PATH); fi
|
||||
clean:
|
||||
@if [ -d "$(BUILD_PATH)" ]; then rm -rf $(BUILD_PATH)/lib$(PROJECT_NAME).*; fi
|
8
build/test.c
Normal file
8
build/test.c
Normal file
@ -0,0 +1,8 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "libcomandy.h"
|
||||
|
||||
int main() {
|
||||
char* msg = request("{}");
|
||||
puts(msg);
|
||||
}
|
5
go.mod
Normal file
5
go.mod
Normal file
@ -0,0 +1,5 @@
|
||||
module comandy
|
||||
|
||||
go 1.22.1
|
||||
|
||||
require github.com/fumiama/terasu v0.0.0-20240414143030-44fae3a81905
|
2
go.sum
Normal file
2
go.sum
Normal file
@ -0,0 +1,2 @@
|
||||
github.com/fumiama/terasu v0.0.0-20240414143030-44fae3a81905 h1:PHf84+ujLpFGJbfytrwZT6/D7KojmjFm5Itv6te6WUA=
|
||||
github.com/fumiama/terasu v0.0.0-20240414143030-44fae3a81905/go.mod h1:BFl0X1+rGJf8bLHl/kO+v05ryHrj/R4kyCrK89NvegA=
|
130
main.go
Normal file
130
main.go
Normal file
@ -0,0 +1,130 @@
|
||||
package main
|
||||
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/fumiama/terasu"
|
||||
)
|
||||
|
||||
func main() {}
|
||||
|
||||
var dialer = net.Dialer{
|
||||
Timeout: time.Minute,
|
||||
}
|
||||
|
||||
var cli = http.Client{
|
||||
Transport: &http.Transport{
|
||||
DialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
conn, err := dialer.DialContext(ctx, "tcp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
host, _, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return terasu.Use(tls.Client(conn, &tls.Config{
|
||||
ServerName: host,
|
||||
InsecureSkipVerify: true,
|
||||
})), nil
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
type capsule struct {
|
||||
C int `json:"code,omitempty"`
|
||||
M string `json:"method,omitempty"`
|
||||
U string `json:"url,omitempty"`
|
||||
H map[string]any `json:"headers,omitempty"`
|
||||
D string `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
func (r *capsule) printerr(err error) string {
|
||||
buf := strings.Builder{}
|
||||
r.C = http.StatusInternalServerError
|
||||
r.D = base64.StdEncoding.EncodeToString(stringToBytes(err.Error()))
|
||||
_ = json.NewEncoder(&buf).Encode(r)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func (r *capsule) printstrerr(err string) string {
|
||||
buf := strings.Builder{}
|
||||
r.C = http.StatusInternalServerError
|
||||
r.D = base64.StdEncoding.EncodeToString(stringToBytes(err))
|
||||
_ = json.NewEncoder(&buf).Encode(r)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
//export request
|
||||
func request(para *C.char) *C.char {
|
||||
r := capsule{}
|
||||
err := json.Unmarshal(stringToBytes(C.GoString(para)), &r)
|
||||
if err != nil {
|
||||
return C.CString(r.printerr(err))
|
||||
}
|
||||
if r.U == "" || !strings.HasPrefix(r.U, "http") {
|
||||
return C.CString(r.printstrerr("invalid url '" + r.U + "'"))
|
||||
}
|
||||
if r.M != "GET" && r.M != "POST" && r.M != "DELETE" {
|
||||
return C.CString(r.printstrerr("invalid method '" + r.U + "'"))
|
||||
}
|
||||
req, err := http.NewRequest(r.M, r.U, strings.NewReader(r.D))
|
||||
if err != nil {
|
||||
return C.CString(r.printerr(err))
|
||||
}
|
||||
for k, vs := range r.H {
|
||||
lk := strings.ToLower(k)
|
||||
if strings.HasPrefix(lk, "x-") {
|
||||
continue
|
||||
}
|
||||
switch x := vs.(type) {
|
||||
case string:
|
||||
req.Header.Add(k, x)
|
||||
case []string:
|
||||
for _, v := range x {
|
||||
req.Header.Add(k, v)
|
||||
}
|
||||
default:
|
||||
return C.CString(r.printstrerr("unsupported H type " + reflect.ValueOf(x).Type().Name()))
|
||||
}
|
||||
}
|
||||
resp, err := cli.Do(req)
|
||||
if err != nil {
|
||||
return C.CString(r.printerr(err))
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
sb := strings.Builder{}
|
||||
enc := base64.NewEncoder(base64.StdEncoding, &sb)
|
||||
_, err = io.CopyN(enc, resp.Body, resp.ContentLength)
|
||||
_ = enc.Close()
|
||||
if err != nil {
|
||||
return C.CString(r.printerr(err))
|
||||
}
|
||||
r.C = resp.StatusCode
|
||||
r.H = make(map[string]any, len(resp.Header)*2)
|
||||
for k, vs := range resp.Header {
|
||||
if len(vs) == 1 {
|
||||
r.H[k] = vs[0]
|
||||
continue
|
||||
}
|
||||
r.H[k] = vs
|
||||
}
|
||||
r.D = sb.String()
|
||||
outbuf := strings.Builder{}
|
||||
err = json.NewEncoder(&outbuf).Encode(&r)
|
||||
if err != nil {
|
||||
return C.CString(r.printerr(err))
|
||||
}
|
||||
return C.CString(outbuf.String())
|
||||
}
|
32
utils.go
Normal file
32
utils.go
Normal file
@ -0,0 +1,32 @@
|
||||
package main
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// slice is the runtime representation of a slice.
|
||||
// It cannot be used safely or portably and its representation may
|
||||
// change in a later release.
|
||||
//
|
||||
// Unlike reflect.SliceHeader, its Data field is sufficient to guarantee the
|
||||
// data it references will not be garbage collected.
|
||||
type slice struct {
|
||||
data unsafe.Pointer
|
||||
len int
|
||||
cap int
|
||||
}
|
||||
|
||||
/*
|
||||
// bytesToString 没有内存开销的转换
|
||||
func bytesToString(b []byte) string {
|
||||
return *(*string)(unsafe.Pointer(&b))
|
||||
}
|
||||
*/
|
||||
|
||||
// stringToBytes 没有内存开销的转换
|
||||
func stringToBytes(s string) (b []byte) {
|
||||
bh := (*slice)(unsafe.Pointer(&b))
|
||||
sh := (*slice)(unsafe.Pointer(&s))
|
||||
bh.data = sh.data
|
||||
bh.len = sh.len
|
||||
bh.cap = sh.len
|
||||
return b
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user