103 lines
2.6 KiB
Go
103 lines
2.6 KiB
Go
package api
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
"time"
|
|
|
|
"greq/internal/storage"
|
|
)
|
|
|
|
|
|
// RunPreScript executes rf.PreScript before sending the request.
|
|
// The script receives REQUEST_URL, REQUEST_METHOD, REQUEST_BODY as env vars.
|
|
// Lines of stdout matching "Key: Value" are returned as extra headers to merge
|
|
// into the request. A non-zero exit is treated as an error (request is aborted).
|
|
func RunPreScript(script string, rf storage.RequestFile, envs map[string]string) (map[string]string, error) {
|
|
if strings.TrimSpace(script) == "" {
|
|
return nil, nil
|
|
}
|
|
|
|
out, err := runScript(script, map[string]string{
|
|
"REQUEST_URL": ResolveVariables(rf.URL, envs),
|
|
"REQUEST_METHOD": rf.Method,
|
|
"REQUEST_BODY": rf.Body,
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("pre_script failed: %w\nOutput: %s", err, out)
|
|
}
|
|
|
|
headers := make(map[string]string)
|
|
for _, line := range strings.Split(out, "\n") {
|
|
line = strings.TrimSpace(line)
|
|
if k, v, ok := splitScriptHeader(line); ok {
|
|
headers[k] = v
|
|
}
|
|
}
|
|
return headers, nil
|
|
}
|
|
|
|
// RunTestScript executes the test_script field of a request using the Lua runner.
|
|
// See RunLuaTestScript for available globals and helper functions.
|
|
func RunTestScript(script string, resp *Response) ([]TestResult, error) {
|
|
return RunLuaTestScript(script, resp)
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Internal helpers
|
|
// ---------------------------------------------------------------------------
|
|
|
|
func runScript(script string, vars map[string]string) (string, error) {
|
|
f, err := os.CreateTemp("", "greq-script-*.sh")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer os.Remove(f.Name())
|
|
|
|
if _, err := f.WriteString("#!/bin/sh\n" + script + "\n"); err != nil {
|
|
f.Close()
|
|
return "", err
|
|
}
|
|
f.Close()
|
|
|
|
if err := os.Chmod(f.Name(), 0o700); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
cmd := exec.CommandContext(ctx, "sh", f.Name())
|
|
// Start with the current process env, then overlay our custom vars
|
|
// so the script has access to PATH, HOME, etc.
|
|
cmd.Env = os.Environ()
|
|
for k, v := range vars {
|
|
cmd.Env = append(cmd.Env, k+"="+v)
|
|
}
|
|
|
|
var out bytes.Buffer
|
|
cmd.Stdout = &out
|
|
cmd.Stderr = &out
|
|
|
|
err = cmd.Run()
|
|
return out.String(), err
|
|
}
|
|
|
|
func splitScriptHeader(s string) (key, val string, ok bool) {
|
|
idx := strings.Index(s, ": ")
|
|
if idx < 1 {
|
|
return "", "", false
|
|
}
|
|
k := strings.TrimSpace(s[:idx])
|
|
v := strings.TrimSpace(s[idx+2:])
|
|
// Reject lines that look like script output, not headers
|
|
if strings.ContainsAny(k, " \t=/") {
|
|
return "", "", false
|
|
}
|
|
return k, v, true
|
|
}
|