feat(script): replace followRedirects with maxRedirects http option

This commit is contained in:
aykhans committed 2026-09-20 23:18:21 +04:00
1 parent 8b437b487c
commit 529420ee5a
4 files changed
+66 -33

No files matched your search

+3 -6
View File
@@ -11,9 +11,6 @@ import (
"go.aykhans.me/sarin/internal/types"
)
// scriptHTTPMaxRedirects is the redirect limit when followRedirects is set.
const scriptHTTPMaxRedirects = 10
// scriptHTTPDefaultTimeout is the default timeout for script requests, independent of -T.
const scriptHTTPDefaultTimeout = 30 * time.Second
@@ -65,7 +62,7 @@ func newScriptHTTPClient(dialFunc fasthttp.DialFunc, defaultTimeout time.Duratio
}
}
// Do sends the request. With followRedirects the timeout applies to each hop.
// Do sends the request. With maxRedirects the timeout applies to each hop.
// It can return the following errors:
// - types.ScriptHTTPRequestError
func (c *scriptHTTPClient) Do(r *script.HTTPRequest) (*script.HTTPResponse, error) {
@@ -116,8 +113,8 @@ func (c *scriptHTTPClient) Do(r *script.HTTPRequest) (*script.HTTPResponse, erro
}
req.SetTimeout(timeout)
if r.FollowRedirects {
err = client.DoRedirects(req, resp, scriptHTTPMaxRedirects)
if r.MaxRedirects > 0 {
err = client.DoRedirects(req, resp, r.MaxRedirects)
} else {
err = client.Do(req, resp)
}
+32 -17
View File
@@ -1,7 +1,9 @@
package script
import (
"math"
"net/http"
"strconv"
"strings"
"time"
@@ -10,15 +12,15 @@ import (
// HTTPRequest is a request made by a script through the http.* functions.
type HTTPRequest struct {
Method string
URL string
Headers map[string][]string
Params map[string][]string
Cookies map[string][]string
Body string
Timeout time.Duration // zero means the doer's default timeout
Insecure bool
FollowRedirects bool
Method string
URL string
Headers map[string][]string
Params map[string][]string
Cookies map[string][]string
Body string
Timeout time.Duration // zero means the doer's default timeout
Insecure bool
MaxRedirects int
}
// HTTPResponse is the response returned to a script by the http.* functions.
@@ -63,14 +65,14 @@ const httpDefaultMethod = http.MethodGet
// Option keys accepted by the http function.
const (
httpOptionMethod = "method"
httpOptionHeaders = "headers"
httpOptionParams = "params"
httpOptionCookies = "cookies"
httpOptionBody = "body"
httpOptionTimeout = "timeout"
httpOptionInsecure = "insecure"
httpOptionFollowRedirects = "followRedirects"
httpOptionMethod = "method"
httpOptionHeaders = "headers"
httpOptionParams = "params"
httpOptionCookies = "cookies"
httpOptionBody = "body"
httpOptionTimeout = "timeout"
httpOptionInsecure = "insecure"
httpOptionMaxRedirects = "maxRedirects"
)
// httpBridge connects http to the doer. Calls only work while transform runs.
@@ -97,6 +99,19 @@ func (b *httpBridge) do(req *HTTPRequest) (*HTTPResponse, error) {
return b.doer.Do(req)
}
// parseHTTPMaxRedirects checks that the redirect limit is a non-negative whole number.
// It can return the following errors:
// - types.ScriptHTTPOptionError
func parseHTTPMaxRedirects(count float64) (int, error) {
if count < 0 || count != math.Trunc(count) {
return 0, types.NewScriptHTTPOptionError(
httpOptionMaxRedirects,
types.NewScriptTypeError("a whole number of 0 or more", strconv.FormatFloat(count, 'f', -1, 64)),
)
}
return int(count), nil
}
// parseHTTPTimeout parses a Go duration string such as "500ms" or "2s".
// It can return the following errors:
// - types.ScriptHTTPOptionError
+23 -5
View File
@@ -100,16 +100,22 @@ func (e *JsEngine) parseHTTPOptions(opts *goja.Object, req *HTTPRequest) error {
if req.Timeout, err = parseHTTPTimeout(timeout); err != nil {
return err
}
case httpOptionInsecure, httpOptionFollowRedirects:
case httpOptionInsecure:
flag, ok := value.Export().(bool)
if !ok {
return types.NewScriptHTTPOptionError(name, types.NewScriptTypeError("boolean", jsTypeName(value)))
}
if name == httpOptionInsecure {
req.Insecure = flag
} else {
req.FollowRedirects = flag
req.Insecure = flag
case httpOptionMaxRedirects:
count, ok := numberValue(value)
if !ok {
return types.NewScriptHTTPOptionError(name, types.NewScriptTypeError("number", jsTypeName(value)))
}
var err error
if req.MaxRedirects, err = parseHTTPMaxRedirects(count); err != nil {
return err
}
default:
return types.NewScriptHTTPOptionError(name, types.ErrScriptHTTPUnknownOption)
@@ -190,3 +196,15 @@ func (e *JsEngine) throw(err error) {
}
panic(errorValue)
}
// numberValue reports whether value is a JavaScript number, and returns it.
func numberValue(value goja.Value) (float64, bool) {
switch number := value.Export().(type) {
case int64:
return float64(number), true
case float64:
return number, true
default:
return 0, false
}
}
+8 -5
View File
@@ -92,17 +92,20 @@ func (e *LuaEngine) parseHTTPOptions(opts *lua.LTable, req *HTTPRequest) error {
return
}
req.Timeout, err = parseHTTPTimeout(string(timeout))
case httpOptionInsecure, httpOptionFollowRedirects:
case httpOptionInsecure:
flag, ok := v.(lua.LBool)
if !ok {
err = types.NewScriptHTTPOptionError(name, types.NewScriptTypeError("boolean", v.Type().String()))
return
}
if name == httpOptionInsecure {
req.Insecure = bool(flag)
} else {
req.FollowRedirects = bool(flag)
req.Insecure = bool(flag)
case httpOptionMaxRedirects:
count, ok := v.(lua.LNumber)
if !ok {
err = types.NewScriptHTTPOptionError(name, types.NewScriptTypeError("number", v.Type().String()))
return
}
req.MaxRedirects, err = parseHTTPMaxRedirects(float64(count))
default:
err = types.NewScriptHTTPOptionError(name, types.ErrScriptHTTPUnknownOption)
}