github.com/coyove/nj@v0.0.0-20221110084952-c7f8db1065c3/lib.go (about) 1 package nj 2 3 import ( 4 "bytes" 5 "encoding/base32" 6 "encoding/base64" 7 "encoding/json" 8 "fmt" 9 "io" 10 "io/fs" 11 "io/ioutil" 12 "math" 13 "math/rand" 14 "mime/multipart" 15 "net/http" 16 "net/url" 17 "os" 18 "os/exec" 19 "path/filepath" 20 "regexp" 21 "runtime" 22 "strconv" 23 "strings" 24 "sync" 25 "time" 26 "unicode/utf8" 27 "unsafe" 28 29 "github.com/coyove/nj/bas" 30 "github.com/coyove/nj/internal" 31 "github.com/coyove/nj/parser" 32 "github.com/coyove/nj/typ" 33 ) 34 35 func init() { 36 bas.AddTopFunc("chr", func(e *bas.Env) { e.A = bas.Rune(rune(e.Int(0))) }) 37 bas.AddTopFunc("byte", func(e *bas.Env) { e.A = bas.Byte(byte(e.Int(0))) }) 38 bas.AddTopFunc("ord", func(e *bas.Env) { 39 r, _ := utf8.DecodeRuneInString(e.Str(0)) 40 e.A = bas.Int64(int64(r)) 41 }) 42 43 bas.AddTopValue("json", bas.NewNamedObject("json", 0). 44 SetProp("stringify", bas.Func("stringify", func(e *bas.Env) { e.A = bas.Str(e.Get(0).JSONString()) })). 45 SetProp("dump", bas.Func("dump", func(e *bas.Env) { e.Get(1).Stringify(e.Get(0).Writer(), typ.MarshalToJSON) })). 46 SetProp("parse", bas.Func("parse", func(e *bas.Env) { 47 s := strings.TrimSpace(e.Str(0)) 48 if s == "" { 49 e.SetError(fmt.Errorf("empty value")) 50 return 51 } 52 switch s[0] { 53 case 'n': 54 _ = s == "null" && e.SetA(bas.Nil) || e.SetError(fmt.Errorf("invalid value")) 55 case 't', 'f': 56 e.A = (*env)(e).valueOrError(strconv.ParseBool(s)) 57 case '[': 58 a := []interface{}{} 59 err := json.Unmarshal([]byte(s), &a) 60 e.A = (*env)(e).valueOrError(bas.ValueOf(a), err) 61 case '{': 62 a := map[string]interface{}{} 63 err := json.Unmarshal([]byte(s), &a) 64 e.A = (*env)(e).valueOrError(bas.ValueOf(a), err) 65 default: 66 e.SetError(fmt.Errorf("invalid value")) 67 } 68 })). 69 ToValue()) 70 bas.AddTopFunc("loadfile", func(e *bas.Env) { 71 path := e.Str(0) 72 if e.Shape(1, "Nb").IsTrue() && e.MustProgram().File != "" { 73 path = filepath.Join(filepath.Dir(e.MustProgram().File), path) 74 } 75 e.A = MustRun(LoadFile(path, &LoadOptions{ 76 MaxStackSize: e.MustProgram().MaxStackSize, 77 Globals: e.MustProgram().Globals, 78 })) 79 }) 80 bas.AddTopValue("eval", bas.Func("eval", func(e *bas.Env) { 81 p, err := LoadString(e.Str(0), &LoadOptions{ 82 Globals: e.Shape(1, "No").Object().ToMap(), 83 }) 84 if err != nil { 85 e.A = bas.Error(e, err) 86 } else { 87 e.A = p.Run() 88 } 89 }).Object(). 90 SetProp("parse", bas.Func("parse", func(e *bas.Env) { 91 e.A = (*env)(e).valueOrError(parser.Parse(e.Str(0), "eval.parse")) 92 })). 93 SetProp("op", bas.NewObject(0). 94 SetProp("set", bas.Int(typ.OpSet)). 95 SetProp("store", bas.Int(typ.OpStore)). 96 SetProp("load", bas.Int(typ.OpLoad)). 97 SetProp("add", bas.Int(typ.OpAdd)). 98 SetProp("sub", bas.Int(typ.OpSub)). 99 SetProp("mul", bas.Int(typ.OpMul)). 100 SetProp("div", bas.Int(typ.OpDiv)). 101 SetProp("idiv", bas.Int(typ.OpIDiv)). 102 SetProp("inc", bas.Int(typ.OpInc)). 103 SetProp("mod", bas.Int(typ.OpMod)). 104 SetProp("len", bas.Int(typ.OpLen)). 105 SetProp("next", bas.Int(typ.OpNext)). 106 SetProp("not", bas.Int(typ.OpNot)). 107 SetProp("eq", bas.Int(typ.OpEq)). 108 SetProp("neq", bas.Int(typ.OpNeq)). 109 SetProp("less", bas.Int(typ.OpLess)). 110 SetProp("lesseq", bas.Int(typ.OpLessEq)). 111 SetProp("ifnot", bas.Int(typ.OpJmpFalse)). 112 SetProp("jmp", bas.Int(typ.OpJmp)). 113 SetProp("function", bas.Int(typ.OpFunction)). 114 SetProp("push", bas.Int(typ.OpPush)). 115 SetProp("pushunpack", bas.Int(typ.OpPushUnpack)). 116 SetProp("createarray", bas.Int(typ.OpCreateArray)). 117 SetProp("createobject", bas.Int(typ.OpCreateObject)). 118 SetProp("call", bas.Int(typ.OpCall)). 119 SetProp("tailcall", bas.Int(typ.OpTailCall)). 120 SetProp("isproto", bas.Int(typ.OpIsProto)). 121 SetProp("slice", bas.Int(typ.OpSlice)). 122 SetProp("ret", bas.Int(typ.OpRet)). 123 SetProp("loadtop", bas.Int(typ.OpLoadTop)). 124 SetProp("ext", bas.Int(typ.OpExt)). 125 ToValue(), 126 ). 127 ToValue()) 128 129 bas.AddTopValue("printf", bas.Func("printf", func(e *bas.Env) { 130 bas.Fprintf(e.MustProgram().Stdout, e.Str(0), e.Stack()[1:]...) 131 })) 132 bas.AddTopValue("println", bas.Func("println", func(e *bas.Env) { 133 for _, a := range e.Stack() { 134 fmt.Fprint(e.MustProgram().Stdout, a.String(), " ") 135 } 136 fmt.Fprintln(e.MustProgram().Stdout) 137 })) 138 bas.AddTopValue("print", bas.Func("print", func(e *bas.Env) { 139 for _, a := range e.Stack() { 140 fmt.Fprint(e.MustProgram().Stdout, a.String()) 141 } 142 fmt.Fprintln(e.MustProgram().Stdout) 143 })) 144 bas.AddTopValue("input", bas.NewObject(0). 145 SetProp("int", bas.Func("int", func(e *bas.Env) { 146 fmt.Fprint(e.MustProgram().Stdout, e.StrDefault(0, "", 0)) 147 var v int64 148 fmt.Fscanf(e.MustProgram().Stdin, "%d", &v) 149 e.A = bas.Int64(v) 150 })). 151 SetProp("num", bas.Func("num", func(e *bas.Env) { 152 fmt.Fprint(e.MustProgram().Stdout, e.StrDefault(0, "", 0)) 153 var v float64 154 fmt.Fscanf(e.MustProgram().Stdin, "%f", &v) 155 e.A = bas.Float64(v) 156 })). 157 SetProp("str", bas.Func("str", func(e *bas.Env) { 158 fmt.Fprint(e.MustProgram().Stdout, e.StrDefault(0, "", 0)) 159 var v string 160 fmt.Fscanln(e.MustProgram().Stdin, &v) 161 e.A = bas.Str(v) 162 })). 163 ToValue()) 164 165 rxMeta := bas.NewEmptyNativeMeta("RegExp", bas.NewObject(0). 166 AddMethod("match", func(e *bas.Env) { 167 rx := e.This().Interface().(*regexp.Regexp) 168 e.A = bas.Bool(rx.MatchString(e.Str(0))) 169 }). 170 AddMethod("find", func(e *bas.Env) { 171 rx := e.This().Interface().(*regexp.Regexp) 172 e.A = bas.NewNative(rx.FindStringSubmatch(e.Str(0))).ToValue() 173 }). 174 AddMethod("findall", func(e *bas.Env) { 175 rx := e.This().Interface().(*regexp.Regexp) 176 e.A = bas.NewNative(rx.FindAllStringSubmatch(e.Str(0), e.IntDefault(1, -1))).ToValue() 177 }). 178 AddMethod("replace", func(e *bas.Env) { 179 rx := e.This().Interface().(*regexp.Regexp) 180 e.A = bas.Str(rx.ReplaceAllString(e.Str(0), e.Str(1))) 181 }). 182 SetPrototype(&bas.Proto.Native)) 183 184 bas.AddTopFunc("re", func(e *bas.Env) { 185 if rx, err := regexp.Compile(e.Str(0)); err != nil { 186 e.SetError(err) 187 } else { 188 e.A = bas.NewNativeWithMeta(rx, rxMeta).ToValue() 189 } 190 }) 191 192 fileMeta := bas.NewEmptyNativeMeta("File", bas.NewObject(0). 193 AddMethod("name", func(e *bas.Env) { 194 e.A = bas.Str(e.A.Interface().(*os.File).Name()) 195 }). 196 AddMethod("seek", func(e *bas.Env) { 197 e.A = (*env)(e).valueOrError(e.A.Interface().(*os.File).Seek(e.Int64(0), e.Int(1))) 198 }). 199 AddMethod("sync", func(e *bas.Env) { 200 e.A = bas.Error(e, e.A.Interface().(*os.File).Sync()) 201 }). 202 AddMethod("stat", func(e *bas.Env) { 203 e.A = (*env)(e).valueOrError(e.A.Interface().(*os.File).Stat()) 204 }). 205 AddMethod("truncate", func(e *bas.Env) { 206 f := e.A.Interface().(*os.File) 207 if err := f.Truncate(e.Int64(1)); err != nil { 208 e.A = bas.Error(e, err) 209 } else { 210 e.A = (*env)(e).valueOrError(f.Seek(0, 2)) 211 } 212 }). 213 SetPrototype(bas.Proto.ReadWriteCloser.Proto)) 214 215 bas.AddTopValue("open", bas.Func("open", func(e *bas.Env) { 216 path, flag, perm := e.Str(0), e.StrDefault(1, "r", 1), e.IntDefault(2, 0644) 217 var opt int 218 for _, f := range flag { 219 switch f { 220 case 'w': 221 opt &^= os.O_RDWR | os.O_RDONLY 222 opt |= os.O_WRONLY | os.O_CREATE | os.O_TRUNC 223 case 'r': 224 opt &^= os.O_RDWR | os.O_WRONLY 225 opt |= os.O_RDONLY 226 case 'a': 227 opt |= os.O_APPEND | os.O_CREATE 228 case 'x': 229 opt |= os.O_EXCL 230 case '+': 231 opt &^= os.O_RDONLY | os.O_WRONLY 232 opt |= os.O_RDWR | os.O_CREATE 233 } 234 } 235 f, err := os.OpenFile(path, opt, fs.FileMode(perm)) 236 if err != nil { 237 e.A = bas.Error(e, err) 238 return 239 } 240 e.Object(-1).Set(bas.Zero, bas.ValueOf(f)) 241 242 e.A = bas.NewNativeWithMeta(f, fileMeta).ToValue() 243 }).Object(). 244 AddMethod("close", func(e *bas.Env) { 245 if f, _ := e.Object(-1).Get(bas.Zero).Interface().(*os.File); f != nil { 246 e.A = bas.Error(e, f.Close()) 247 } else { 248 panic("no opened file yet") 249 } 250 }).ToValue(), 251 ) 252 253 bas.AddTopValue("math", bas.NewNamedObject("math", 0). 254 SetProp("INF", bas.Float64(math.Inf(1))). 255 SetProp("NEG_INF", bas.Float64(math.Inf(-1))). 256 SetProp("PI", bas.Float64(math.Pi)). 257 SetProp("E", bas.Float64(math.E)). 258 SetProp("randomseed", bas.Func("randomseed", func(e *bas.Env) { rand.Seed(int64(e.IntDefault(0, 1))) })). 259 SetProp("random", bas.Func("random", func(e *bas.Env) { 260 switch len(e.Stack()) { 261 case 2: 262 ai, bi := e.Int64(0), e.Int64(1) 263 e.A = bas.Int64(rand.Int63n(bi-ai+1) + ai) 264 case 1: 265 e.A = bas.Int64(rand.Int63n(e.Int64(0))) 266 default: 267 e.A = bas.Float64(rand.Float64()) 268 } 269 })). 270 SetProp("sqrt", bas.Func("sqrt", func(e *bas.Env) { e.A = bas.Float64(math.Sqrt(e.Float64(0))) })). 271 SetProp("floor", bas.Func("floor", func(e *bas.Env) { e.A = bas.Float64(math.Floor(e.Float64(0))) })). 272 SetProp("ceil", bas.Func("ceil", func(e *bas.Env) { e.A = bas.Float64(math.Ceil(e.Float64(0))) })). 273 SetProp("min", bas.Func("min", func(e *bas.Env) { minMax(e, false) })). 274 SetProp("max", bas.Func("max", func(e *bas.Env) { minMax(e, true) })). 275 SetProp("pow", bas.Func("pow", func(e *bas.Env) { e.A = bas.Float64(math.Pow(e.Float64(0), e.Float64(1))) })). 276 SetProp("abs", bas.Func("abs", func(e *bas.Env) { 277 if e.A = e.Num(0); e.A.IsInt64() { 278 if i := e.A.Int64(); i < 0 { 279 e.A = bas.Int64(-i) 280 } 281 } else { 282 e.A = bas.Float64(math.Abs(e.A.Float64())) 283 } 284 })). 285 SetProp("remainder", bas.Func("remainder", func(e *bas.Env) { e.A = bas.Float64(math.Remainder(e.Float64(0), e.Float64(1))) })). 286 SetProp("mod", bas.Func("mod", func(e *bas.Env) { e.A = bas.Float64(math.Mod(e.Float64(0), e.Float64(1))) })). 287 SetProp("cos", bas.Func("cos", func(e *bas.Env) { e.A = bas.Float64(math.Cos(e.Float64(0))) })). 288 SetProp("sin", bas.Func("sin", func(e *bas.Env) { e.A = bas.Float64(math.Sin(e.Float64(0))) })). 289 SetProp("tan", bas.Func("tan", func(e *bas.Env) { e.A = bas.Float64(math.Tan(e.Float64(0))) })). 290 SetProp("acos", bas.Func("acos", func(e *bas.Env) { e.A = bas.Float64(math.Acos(e.Float64(0))) })). 291 SetProp("asin", bas.Func("asin", func(e *bas.Env) { e.A = bas.Float64(math.Asin(e.Float64(0))) })). 292 SetProp("atan", bas.Func("atan", func(e *bas.Env) { e.A = bas.Float64(math.Atan(e.Float64(0))) })). 293 SetProp("atan2", bas.Func("atan2", func(e *bas.Env) { e.A = bas.Float64(math.Atan2(e.Float64(0), e.Float64(1))) })). 294 SetProp("ldexp", bas.Func("ldexp", func(e *bas.Env) { e.A = bas.Float64(math.Ldexp(e.Float64(0), e.Int(0))) })). 295 SetProp("modf", bas.Func("modf", func(e *bas.Env) { 296 a, b := math.Modf(e.Float64(0)) 297 e.A = bas.Array(bas.Float64(a), bas.Float64(b)) 298 })). 299 ToValue()) 300 301 bas.AddTopValue("os", bas.NewNamedObject("os", 0). 302 SetProp("stdout", bas.ValueOf(os.Stdout)). 303 SetProp("stdin", bas.ValueOf(os.Stdin)). 304 SetProp("stderr", bas.ValueOf(os.Stderr)). 305 SetProp("pid", bas.Int(os.Getpid())). 306 SetProp("numcpus", bas.Int(runtime.NumCPU())). 307 SetProp("args", bas.ValueOf(os.Args)). 308 SetProp("exit", bas.Func("exit", func(e *bas.Env) { os.Exit(e.IntDefault(0, 0)) })). 309 SetProp("environ", bas.Func("environ", func(e *bas.Env) { 310 if env := os.Environ(); e.Get(0).IsTrue() { 311 obj := bas.NewObject(len(env)) 312 for _, e := range env { 313 if idx := strings.Index(e, "="); idx > -1 { 314 obj.SetProp(e[:idx], bas.Str(e[idx+1:])) 315 } 316 } 317 e.A = obj.ToValue() 318 } else { 319 e.A = bas.ValueOf(env) 320 } 321 })). 322 SetProp("shell", bas.Func("shell", func(e *bas.Env) { 323 win := runtime.GOOS == "windows" 324 p := exec.Command(internal.IfStr(win, "cmd", "sh"), internal.IfStr(win, "/c", "-c"), e.Str(0)) 325 opt := e.Shape(1, "No").Object() 326 opt.Get(bas.Str("env")).AssertShape("No", "environment").Object().Foreach(func(k bas.Value, v *bas.Value) bool { 327 p.Env = append(p.Env, k.String()+"="+v.String()) 328 return true 329 }) 330 stdout := &bytes.Buffer{} 331 p.Stdout, p.Stderr = stdout, stdout 332 p.Dir = opt.GetDefault(bas.Str("dir"), bas.NullStr).AssertShape("s", "directory").Str() 333 if tmp := opt.Get(bas.Str("stdout")); tmp != bas.Nil { 334 p.Stdout = tmp.Writer() 335 } 336 if tmp := opt.Get(bas.Str("stderr")); tmp != bas.Nil { 337 p.Stderr = tmp.Writer() 338 } 339 if tmp := opt.Get(bas.Str("stdin")); tmp != bas.Nil { 340 p.Stdin = tmp.Reader() 341 } 342 343 to := opt.GetDefault(bas.Str("timeout"), bas.Float64(1<<30)).AssertNumber("timeout seconds").Duration() 344 out := make(chan error) 345 go func() { out <- p.Run() }() 346 select { 347 case r := <-out: 348 if r != nil { 349 e.A = bas.Error(e, r) 350 return 351 } 352 case <-time.After(to): 353 p.Process.Kill() 354 e.A = bas.Error(e, fmt.Errorf("os.shell timeout: %v", e.Get(0))) 355 return 356 } 357 e.A = bas.Bytes(stdout.Bytes()) 358 })). 359 SetProp("readdir", bas.Func("readdir", func(e *bas.Env) { 360 e.A = (*env)(e).valueOrError(ioutil.ReadDir(e.Str(0))) 361 })). 362 SetProp("remove", bas.Func("remove", func(e *bas.Env) { 363 path := e.Str(0) 364 fi, err := os.Stat(path) 365 if err != nil { 366 e.A = bas.Error(e, err) 367 } else if fi.IsDir() { 368 e.A = bas.Error(e, os.RemoveAll(path)) 369 } else { 370 e.A = bas.Error(e, os.Remove(path)) 371 } 372 })). 373 SetProp("pstat", bas.Func("pstat", func(e *bas.Env) { 374 fi, err := os.Stat(e.Str(0)) 375 _ = err == nil && e.SetA(bas.ValueOf(fi)) || e.SetA(bas.Nil) 376 })). 377 ToValue()) 378 379 bas.AddTopValue("sync", bas.NewNamedObject("sync", 0). 380 SetProp("mutex", bas.Func("mutex", func(e *bas.Env) { e.A = bas.ValueOf(&sync.Mutex{}) })). 381 SetProp("rwmutex", bas.Func("rwmutex", func(e *bas.Env) { e.A = bas.ValueOf(&sync.RWMutex{}) })). 382 SetProp("waitgroup", bas.Func("waitgroup", func(e *bas.Env) { e.A = bas.ValueOf(&sync.WaitGroup{}) })). 383 ToValue()) 384 385 bas.AddTopValue("base64", bas.NewObject(0). 386 SetProp("std", bas.ValueOf(base64.StdEncoding)).SetProp("rawstd", bas.ValueOf(base64.RawStdEncoding)). 387 SetProp("url", bas.ValueOf(base64.URLEncoding)).SetProp("rawurl", bas.ValueOf(base64.RawURLEncoding)). 388 ToValue()) 389 390 bas.AddTopValue("base32", bas.NewObject(0). 391 SetProp("std", bas.ValueOf(base32.StdEncoding)).SetProp("rawstd", bas.ValueOf(base32.StdEncoding.WithPadding(-1))). 392 SetProp("hex", bas.ValueOf(base32.HexEncoding)).SetProp("rawhex", bas.ValueOf(base32.HexEncoding.WithPadding(-1))). 393 ToValue()) 394 395 bas.AddTopValue("time", bas.Func("time", func(e *bas.Env) { 396 e.A = bas.Float64(float64(time.Now().UnixNano()) / 1e9) 397 }).Object(). 398 SetProp("sleep", bas.Func("sleep", func(e *bas.Env) { time.Sleep(time.Duration(e.Float64(0)*1e6) * 1e3) })). 399 SetProp("ymd", bas.Func("ymd", func(e *bas.Env) { 400 e.A = bas.ValueOf(time.Date(e.Int(0), time.Month(e.Int(1)), e.Int(2), 401 e.IntDefault(3, 0), e.IntDefault(4, 0), e.IntDefault(5, 0), e.IntDefault(6, 0), time.UTC)) 402 })). 403 SetProp("clock", bas.Func("clock", func(e *bas.Env) { 404 x := time.Now() 405 s := *(*[2]int64)(unsafe.Pointer(&x)) 406 e.A = bas.Float64(float64(s[1]) / 1e9) 407 })). 408 SetProp("now", bas.Func("now", func(e *bas.Env) { e.A = bas.ValueOf(time.Now()) })). 409 SetProp("after", bas.Func("after", func(e *bas.Env) { e.A = bas.ValueOf(time.After(time.Duration(e.Float64(0)*1e6) * 1e3)) })). 410 SetProp("parse", bas.Func("parse", func(e *bas.Env) { 411 e.A = (*env)(e).valueOrError(time.Parse(getTimeFormat(e.Str(0)), e.Str(1))) 412 })). 413 SetProp("format", bas.Func("format", func(e *bas.Env) { 414 tt, ok := e.Get(1).Interface().(time.Time) 415 if !ok { 416 if t := e.Get(1); t.Type() == typ.Number { 417 tt = time.Unix(0, int64(t.Float64()*1e9)) 418 } else { 419 tt = time.Now() 420 } 421 } 422 e.A = bas.Str(tt.Format(getTimeFormat(e.StrDefault(0, "", 0)))) 423 })). 424 ToValue()) 425 426 httpLib := bas.Func("http", func(e *bas.Env) { 427 args := e.Object(0) 428 method := strings.ToUpper(args.GetDefault(bas.Str("method"), bas.Str("GET")).Str()) 429 430 u, err := url.Parse(args.Get(bas.Str("url")).AssertString("http URL")) 431 if err != nil { 432 e.A = bas.Error(e, err) 433 return 434 } 435 436 addKV := func(k string, add func(k, v string)) { 437 x := args.Get(bas.Str(k)).AssertShape("No", k) 438 x.Object().Foreach(func(k bas.Value, v *bas.Value) bool { add(k.String(), v.String()); return true }) 439 } 440 441 additionalQueries := u.Query() 442 addKV("query", additionalQueries.Add) // append queries to url 443 u.RawQuery = additionalQueries.Encode() 444 445 var bodyReader io.Reader 446 dataForm, urlForm, jsonForm := (*multipart.Writer)(nil), false, false 447 448 if j := args.Get(bas.Str("json")); j != bas.Nil { 449 bodyReader = strings.NewReader(j.JSONString()) 450 jsonForm = true 451 } else { 452 var form url.Values 453 if args.Contains(bas.Str("form")) { 454 form = url.Values{} 455 addKV("form", form.Add) // check "form" 456 } 457 urlForm = len(form) > 0 458 if urlForm { 459 bodyReader = strings.NewReader(form.Encode()) 460 } else if rd := args.Get(bas.Str("data")); rd != bas.Nil { 461 bodyReader = rd.Reader() 462 } 463 } 464 465 if bodyReader == nil && (method == "POST" || method == "PUT") { 466 // Check form-data 467 payload := bytes.Buffer{} 468 writer := multipart.NewWriter(&payload) 469 outError := (error)(nil) 470 args.Get(bas.Str("multipart")).AssertShape("No", "multipart").Object(). 471 Foreach(func(k bas.Value, v *bas.Value) bool { 472 key, rd := k.String(), *v 473 rd.AssertShape("<s,(s,R)>", "http multipart form data format") 474 if rd.Type() == typ.Native && rd.Len() == 2 { // [filename, reader] 475 fn := rd.Native().Get(0).Str() 476 if part, err := writer.CreateFormFile(key, fn); err != nil { 477 outError = fmt.Errorf("%s: %v", fn, err) 478 return false 479 } else if _, err = io.Copy(part, rd.Native().Get(1).Reader()); err != nil { 480 outError = fmt.Errorf("%s: %v", fn, err) 481 return false 482 } 483 } else { 484 if part, err := writer.CreateFormField(key); err != nil { 485 outError = fmt.Errorf("%s: %v", key, err) 486 return false 487 } else if _, err = io.Copy(part, rd.Reader()); err != nil { 488 outError = fmt.Errorf("%s: %v", key, err) 489 return false 490 } 491 } 492 return true 493 }) 494 if outError != nil { 495 e.A = bas.Error(e, err) 496 return 497 } 498 if err := writer.Close(); err != nil { 499 e.A = bas.Error(e, err) 500 return 501 } 502 if payload.Len() > 0 { 503 bodyReader = &payload 504 dataForm = writer 505 } 506 } 507 508 req, err := http.NewRequest(method, u.String(), bodyReader) 509 if err != nil { 510 e.A = bas.Error(e, err) 511 return 512 } 513 514 switch { 515 case urlForm: 516 req.Header.Add("Content-Type", "application/x-www-form-urlencoded") 517 case jsonForm: 518 req.Header.Add("Content-Type", "application/json") 519 case dataForm != nil: 520 req.Header.Add("Content-Type", dataForm.FormDataContentType()) 521 } 522 523 addKV("header", req.Header.Add) // append headers 524 525 // Construct HTTP client 526 client := &http.Client{} 527 client.Timeout = args.GetDefault(bas.Str("timeout"), bas.Float64(1<<30)).AssertNumber("timeout seconds").Duration() 528 if v := args.Get(bas.Str("jar")); v.Type() == typ.Native { 529 client.Jar, _ = v.Interface().(http.CookieJar) 530 } 531 if !args.Get(bas.Str("noredirect")).IsFalse() { 532 client.CheckRedirect = func(*http.Request, []*http.Request) error { 533 return http.ErrUseLastResponse 534 } 535 } 536 if p := args.GetDefault(bas.Str("proxy"), bas.NullStr).Str(); p != "" { 537 client.Transport = &http.Transport{ 538 Proxy: func(r *http.Request) (*url.URL, error) { return url.Parse(p) }, 539 } 540 } 541 send := func(e *bas.Env) (code, headers, buf, jar bas.Value) { 542 resp, err := client.Do(req) 543 if err != nil { 544 err := bas.Error(e, err) 545 return err, err, err, err 546 } 547 548 if args.Get(bas.Str("br")).IsFalse() { 549 resp.Body.Close() 550 } else { 551 buf = bas.NewNativeWithMeta(resp.Body, &bas.Proto.ReadCloser).ToValue() 552 } 553 return bas.Int(resp.StatusCode), bas.ValueOf(resp.Header), buf, bas.ValueOf(client.Jar) 554 } 555 if f := args.Get(bas.Str("async")); f.IsObject() { 556 go func(e *bas.Env) { 557 code, hdr, buf, jar := send(e) 558 f.Object().Call(e, code, hdr, buf, jar) 559 }(e.Copy()) 560 return 561 } 562 e.A = bas.Array(send(e)) 563 }).Object(). 564 SetProp("urlescape", bas.Func("urlescape", func(e *bas.Env) { e.A = bas.Str(url.QueryEscape(e.Str(0))) })). 565 SetProp("urlunescape", bas.Func("urlunescape", func(e *bas.Env) { 566 e.A = (*env)(e).valueOrError(url.QueryUnescape(e.Str(0))) 567 })). 568 SetProp("pathescape", bas.Func("pathescape", func(e *bas.Env) { e.A = bas.Str(url.PathEscape(e.Str(0))) })). 569 SetProp("pathunescape", bas.Func("pathunescape", func(e *bas.Env) { 570 e.A = (*env)(e).valueOrError(url.PathUnescape(e.Str(0))) 571 })) 572 for _, m := range []string{"get", "post", "put", "delete", "head", "patch"} { 573 m := m 574 httpLib = httpLib.AddMethod(m, func(e *bas.Env) { 575 ex := e.Shape(1, "No").Object() 576 e.A = e.Object(-1).Call(e, bas.NewObject(0).SetProp("method", bas.Str(m)).SetProp("url", e.Get(0)).Merge(ex).ToValue()) 577 }) 578 } 579 bas.AddTopValue("http", httpLib.ToValue()) 580 581 bufferMeta := bas.NewEmptyNativeMeta("Buffer", bas.NewObject(0). 582 SetPrototype(bas.Proto.ReadWriter.Proto). 583 AddMethod("reset", func(e *bas.Env) { e.A.Interface().(*internal.LimitedBuffer).Reset() }). 584 AddMethod("value", func(e *bas.Env) { e.A = bas.UnsafeStr(e.A.Interface().(*internal.LimitedBuffer).Bytes()) }). 585 AddMethod("bytes", func(e *bas.Env) { e.A = bas.Bytes(e.A.Interface().(*internal.LimitedBuffer).Bytes()) })) 586 587 bas.AddTopFunc("buffer", func(e *bas.Env) { 588 b := &internal.LimitedBuffer{Limit: e.IntDefault(1, 0)} 589 bas.Write(b, e.Get(0)) 590 e.A = bas.NewNativeWithMeta(b, bufferMeta).ToValue() 591 }) 592 } 593 594 func minMax(e *bas.Env, max bool) { 595 v := e.Get(0) 596 for i := 1; i < e.Size(); i++ { 597 if x := e.Get(i); v.Less(x) == max { 598 v = x 599 } 600 } 601 e.A = v 602 } 603 604 var timeFormatMapping = map[interface{}]string{ 605 "ansic": time.ANSIC, "ANSIC": time.ANSIC, 606 "unixdate": time.UnixDate, "UnixDate": time.UnixDate, 607 "rubydate": time.RubyDate, "RubyDate": time.RubyDate, 608 "rfc822": time.RFC822, "RFC822": time.RFC822, 609 "rfc822z": time.RFC822Z, "RFC822Z": time.RFC822Z, 610 "rfc850": time.RFC850, "RFC850": time.RFC850, 611 "rfc1123": time.RFC1123, "RFC1123": time.RFC1123, 612 "rfc1123z": time.RFC1123Z, "RFC1123Z": time.RFC1123Z, 613 "rfc3339": time.RFC3339, "RFC3339": time.RFC3339, 614 "rfc3339nano": time.RFC3339Nano, "RFC3339Nano": time.RFC3339Nano, 615 "kitchen": time.Kitchen, "Kitchen": time.Kitchen, 616 "stamp": time.Stamp, "Stamp": time.Stamp, 617 "stampmilli": time.StampMilli, "StampMilli": time.StampMilli, 618 "stampmicro": time.StampMicro, "StampMicro": time.StampMicro, 619 "stampnano": time.StampNano, "StampNano": time.StampNano, 620 'd': "02", 'D': "Mon", 'j': "2", 'l': "Monday", 'F': "January", 'z': "002", 'm': "01", 621 'M': "Jan", 'n': "1", 'Y': "2006", 'y': "06", 'a': "pm", 'A': "PM", 'g': "3", 'G': "15", 622 'h': "03", 'H': "15", 'i': "04", 's': "05", 'u': "05.000000", 'v': "05.000", 'O': "+0700", 623 'P': "-07:00", 'T': "MST", 624 'c': "2006-01-02T15:04:05-07:00", // ISO 860, 625 'r': "Mon, 02 Jan 2006 15:04:05 -0700", // RFC 282, 626 } 627 628 func getTimeFormat(f string) string { 629 if tf, ok := timeFormatMapping[f]; ok { 630 return tf 631 } 632 buf := bytes.Buffer{} 633 for len(f) > 0 { 634 r, sz := utf8.DecodeRuneInString(f) 635 if sz == 0 { 636 break 637 } 638 if tf, ok := timeFormatMapping[r]; ok { 639 buf.WriteString(tf) 640 } else { 641 buf.WriteRune(r) 642 } 643 f = f[sz:] 644 } 645 return buf.String() 646 } 647 648 type env bas.Env 649 650 func (e *env) valueOrError(v interface{}, err error) bas.Value { 651 if err != nil { 652 return bas.Error((*bas.Env)(e), err) 653 } 654 return bas.ValueOf(v) 655 }