349 lines
7.5 KiB
Go
349 lines
7.5 KiB
Go
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
|
//go:build script
|
|
|
|
package js
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
|
"github.com/iwind/TeaGo/maps"
|
|
"io"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
var sharedHTTPClientPool = utils.NewHTTPClient(30 * time.Second)
|
|
|
|
func init() {
|
|
if !teaconst.IsMain {
|
|
return
|
|
}
|
|
|
|
SharedLibraryManager.Register(&JSNetHTTPClientLibrary{})
|
|
}
|
|
|
|
type JSNetHTTPClientLibrary struct {
|
|
JSBaseLibrary
|
|
}
|
|
|
|
func (this *JSNetHTTPClientLibrary) JSNamespace() string {
|
|
return "gojs.net.http.client.JSNetHTTPClientLibrary"
|
|
}
|
|
|
|
func (this *JSNetHTTPClientLibrary) JSPrototype() string {
|
|
return `
|
|
gojs.net.http.client.Request = class {
|
|
#url
|
|
#headers = {}
|
|
#method
|
|
#body
|
|
|
|
constructor(url) {
|
|
this.#url = url
|
|
}
|
|
|
|
get url() {
|
|
return this.#url
|
|
}
|
|
|
|
addHeader(name, value) {
|
|
if (typeof(name) == "string" && typeof(value) == "string") {
|
|
if (typeof(this.#headers[name]) == "undefined") {
|
|
this.#headers[name] = [value]
|
|
} else {
|
|
this.#headers[name].push(value)
|
|
}
|
|
}
|
|
}
|
|
|
|
get headers() {
|
|
return this.#headers
|
|
}
|
|
|
|
setContentType(contentType) {
|
|
this.#headers["Content-Type"] = [contentType]
|
|
}
|
|
|
|
setUserAgent(userAgent) {
|
|
this.#headers["User-Agent"] = [userAgent]
|
|
}
|
|
|
|
setMethod(method) {
|
|
this.#method = method
|
|
}
|
|
|
|
get method() {
|
|
return this.#method
|
|
}
|
|
|
|
setBody(body) {
|
|
this.#body = body
|
|
}
|
|
|
|
get body() {
|
|
return this.#body
|
|
}
|
|
|
|
get() {
|
|
this.setMethod("GET")
|
|
return gojs.net.http.client.DefaultClient.do(this)
|
|
}
|
|
|
|
post() {
|
|
this.setMethod("POST")
|
|
return gojs.net.http.client.DefaultClient.do(this)
|
|
}
|
|
}
|
|
|
|
gojs.net.http.client.Response = class {
|
|
#goObjectId
|
|
|
|
constructor(goObjectId) {
|
|
this.#goObjectId = goObjectId
|
|
}
|
|
|
|
get goObjectId() {
|
|
return this.#goObjectId
|
|
}
|
|
|
|
get error() {
|
|
return $this.ResponseError(this.#goObjectId)
|
|
}
|
|
|
|
get contentLength() {
|
|
return $this.ResponseContentLength(this.#goObjectId)
|
|
}
|
|
|
|
get status() {
|
|
return $this.ResponseStatus(this.#goObjectId)
|
|
}
|
|
|
|
get headers() {
|
|
return $this.ResponseHeaders(this.#goObjectId)
|
|
}
|
|
|
|
get body() {
|
|
return $this.ResponseBodyString(this.#goObjectId)
|
|
}
|
|
|
|
get bodyObject() {
|
|
return JSON.parse(this.body)
|
|
}
|
|
}
|
|
|
|
gojs.net.http.client.Client = class {
|
|
do(req) {
|
|
let respObjectId = $this.Do({
|
|
"url": req.url,
|
|
"method": req.method,
|
|
"headers": req.headers,
|
|
"headerKeys": Object.keys(req.headers),
|
|
"body": req.body,
|
|
})
|
|
return new gojs.net.http.client.Response(respObjectId)
|
|
}
|
|
}
|
|
|
|
gojs.net.http.client.DefaultClient = new gojs.net.http.client.Client()
|
|
`
|
|
}
|
|
|
|
func (this *JSNetHTTPClientLibrary) JSDone(ctx *Context) {
|
|
// TODO 因为每个请求会执行,所以这里需要性能,可能需要有调用的时候才执行
|
|
var objMap = ctx.GoObjectMap()
|
|
for _, obj := range objMap {
|
|
if obj != nil {
|
|
resp, ok := obj.(*JSNetHTTPClientLibraryResponse)
|
|
if ok {
|
|
resp.Close()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (this *JSNetHTTPClientLibrary) Do(arguments *FunctionArguments) (uint32, error) {
|
|
obj, ok := arguments.ObjectAt(0)
|
|
if !ok {
|
|
return 0, ErrGoObjectNotFound
|
|
}
|
|
|
|
if obj.IsNullOrUndefined() {
|
|
return 0, ErrGoObjectNotFound
|
|
}
|
|
|
|
urlString, _ := obj.GetString("url")
|
|
_, err := url.Parse(urlString)
|
|
if err != nil {
|
|
return 0, errors.New("invalid url: '" + urlString + "'")
|
|
}
|
|
|
|
method, _ := obj.GetString("method")
|
|
if len(method) == 0 {
|
|
method = http.MethodGet
|
|
}
|
|
method = strings.ToUpper(method)
|
|
|
|
var headers = http.Header{}
|
|
headerObj, ok := obj.GetObject("headers")
|
|
if ok && !headerObj.IsNullOrUndefined() {
|
|
headerKeysArray, _ := obj.GetStringsArray("headerKeys")
|
|
for _, headerKey := range headerKeysArray {
|
|
headerValues, _ := headerObj.GetStringsArray(headerKey)
|
|
if len(headerValues) > 0 {
|
|
headers[headerKey] = headerValues
|
|
}
|
|
}
|
|
}
|
|
|
|
var bodyReader io.Reader
|
|
bodyString, _ := obj.GetString("body")
|
|
if len(bodyString) > 0 {
|
|
bodyReader = bytes.NewReader([]byte(bodyString))
|
|
}
|
|
req, err := http.NewRequest(method, urlString, bodyReader)
|
|
if err != nil {
|
|
var goObjectId = arguments.Context().AddGoObject(NewJSNetHTTPClientLibraryResponse(nil, err))
|
|
return goObjectId, nil
|
|
}
|
|
|
|
var hasContentType = false
|
|
var hasUserAgent = false
|
|
req.Header = headers
|
|
for k := range headers {
|
|
if strings.ToLower(k) == "content-type" {
|
|
hasContentType = true
|
|
}
|
|
if strings.ToLower(k) == "user-agent" {
|
|
hasUserAgent = true
|
|
}
|
|
}
|
|
if !hasContentType && method == http.MethodPost {
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
}
|
|
if !hasUserAgent {
|
|
req.Header.Set("User-Agent", teaconst.GlobalProductName+"-Node-Script/"+teaconst.Version)
|
|
}
|
|
|
|
resp, err := sharedHTTPClientPool.Do(req)
|
|
if err != nil {
|
|
var goObjectId = arguments.Context().AddGoObject(NewJSNetHTTPClientLibraryResponse(nil, err))
|
|
return goObjectId, nil
|
|
}
|
|
|
|
var goObjectId = arguments.Context().AddGoObject(NewJSNetHTTPClientLibraryResponse(resp, err))
|
|
return goObjectId, nil
|
|
}
|
|
|
|
func (this *JSNetHTTPClientLibrary) ResponseError(arguments *FunctionArguments) (any, error) {
|
|
obj, ok := arguments.GoObjectAt(0)
|
|
if !ok {
|
|
return "", ErrGoObjectNotFound
|
|
}
|
|
var err = obj.(*JSNetHTTPClientLibraryResponse).Error()
|
|
if err != nil {
|
|
return maps.Map{"message": err.Error()}, nil
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
func (this *JSNetHTTPClientLibrary) ResponseStatus(arguments *FunctionArguments) (int, error) {
|
|
obj, ok := arguments.GoObjectAt(0)
|
|
if !ok {
|
|
return 0, ErrGoObjectNotFound
|
|
}
|
|
return obj.(*JSNetHTTPClientLibraryResponse).Status(), nil
|
|
}
|
|
|
|
func (this *JSNetHTTPClientLibrary) ResponseContentLength(arguments *FunctionArguments) (int64, error) {
|
|
obj, ok := arguments.GoObjectAt(0)
|
|
if !ok {
|
|
return 0, ErrGoObjectNotFound
|
|
}
|
|
return obj.(*JSNetHTTPClientLibraryResponse).ContentLength(), nil
|
|
}
|
|
|
|
func (this *JSNetHTTPClientLibrary) ResponseHeaders(arguments *FunctionArguments) (http.Header, error) {
|
|
obj, ok := arguments.GoObjectAt(0)
|
|
if !ok {
|
|
return nil, ErrGoObjectNotFound
|
|
}
|
|
return obj.(*JSNetHTTPClientLibraryResponse).Headers(), nil
|
|
}
|
|
|
|
func (this *JSNetHTTPClientLibrary) ResponseBodyString(arguments *FunctionArguments) (string, error) {
|
|
obj, ok := arguments.GoObjectAt(0)
|
|
if !ok {
|
|
return "", ErrGoObjectNotFound
|
|
}
|
|
s, _ := obj.(*JSNetHTTPClientLibraryResponse).BodyString()
|
|
return s, nil
|
|
}
|
|
|
|
type JSNetHTTPClientLibraryResponse struct {
|
|
resp *http.Response
|
|
err error
|
|
|
|
body []byte
|
|
bodyRead bool
|
|
}
|
|
|
|
func NewJSNetHTTPClientLibraryResponse(resp *http.Response, err error) *JSNetHTTPClientLibraryResponse {
|
|
return &JSNetHTTPClientLibraryResponse{resp: resp, err: err}
|
|
}
|
|
|
|
func (this *JSNetHTTPClientLibraryResponse) Close() {
|
|
if this.resp != nil && this.resp.Body != nil {
|
|
_ = this.resp.Body.Close()
|
|
}
|
|
}
|
|
|
|
func (this *JSNetHTTPClientLibraryResponse) Error() error {
|
|
return this.err
|
|
}
|
|
|
|
func (this *JSNetHTTPClientLibraryResponse) Status() int {
|
|
if this.resp != nil {
|
|
return this.resp.StatusCode
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func (this *JSNetHTTPClientLibraryResponse) ContentLength() int64 {
|
|
if this.resp != nil {
|
|
return this.resp.ContentLength
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func (this *JSNetHTTPClientLibraryResponse) Headers() http.Header {
|
|
if this.resp != nil {
|
|
return this.resp.Header
|
|
}
|
|
return http.Header{}
|
|
}
|
|
|
|
func (this *JSNetHTTPClientLibraryResponse) BodyString() (string, error) {
|
|
if this.resp != nil {
|
|
data, err := this.readBody()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return string(data), nil
|
|
}
|
|
return "", nil
|
|
}
|
|
|
|
func (this *JSNetHTTPClientLibraryResponse) readBody() ([]byte, error) {
|
|
if this.bodyRead {
|
|
return this.body, nil
|
|
}
|
|
data, err := io.ReadAll(this.resp.Body)
|
|
this.body = data
|
|
this.bodyRead = true
|
|
return data, err
|
|
}
|