Skip to content

Commit

Permalink
Use new http client and cookie jar across executions
Browse files Browse the repository at this point in the history
This ensures that cookies from one app aren't leaked to other apps. Instead of InitHTTP setting a single global instance of http.Client, it now sets a factory method. Each time the http module is loaded, that factory is used to create a new http client. Those clients all share the same cache, but not cookie jars
  • Loading branch information
dinosaursrarr committed Apr 24, 2024
1 parent df16041 commit ec22f51
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 18 deletions.
28 changes: 15 additions & 13 deletions runtime/httpcache.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,20 +56,22 @@ func InitHTTP(cache Cache) {
transport: http.DefaultTransport,
}

// Providing a cookie jar allows sessions and redirects to work properly. With a
// jar present, any cookies set in a response will automatically be added to
// subsequent requests. This means that we can follow redirects after logging into
// a session. Without a jar, any cookies will be dropped from redirects unless explicitly
// set in the original outgoing request.
// https://cs.opensource.google/go/go/+/master:src/net/http/client.go;drc=4c394b5638cc2694b1eff6418bc3e7db8132de0e;l=88
jar, _ := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List}) // never returns non-nil err

httpClient := &http.Client{
Jar: jar,
Transport: cc,
Timeout: HTTPTimeout * 2,
starlarkhttp.StarlarkHTTPClient = func() *http.Client {
// Providing a cookie jar allows sessions and redirects to work properly. With a
// jar present, any cookies set in a response will automatically be added to
// subsequent requests. This means that we can follow redirects after logging into
// a session. Without a jar, any cookies will be dropped from redirects unless explicitly
// set in the original outgoing request.
// https://cs.opensource.google/go/go/+/master:src/net/http/client.go;drc=4c394b5638cc2694b1eff6418bc3e7db8132de0e;l=88
jar, _ := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List}) // never returns non-nil err

httpClient := &http.Client{
Jar: jar,
Transport: cc,
Timeout: HTTPTimeout * 2,
}
return httpClient
}
starlarkhttp.StarlarkHTTPClient = httpClient
}

// RoundTrip is an approximation of what our internal HTTP proxy does. It should
Expand Down
13 changes: 12 additions & 1 deletion runtime/httpcache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,9 @@ func TestSetCookieOnRedirect(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Requests to "/login" set a cookie and redirect to /destination
if strings.HasSuffix(r.URL.Path, "/login") {
if len(r.Cookies()) != 0 {
t.Errorf("Cookie was already set on initial call")
}
w.Header().Set("Set-Cookie", "doodad=foobar; path=/; HttpOnly")
w.Header().Set("Location", "/destination")
w.WriteHeader(302)
Expand All @@ -200,7 +203,7 @@ func TestSetCookieOnRedirect(t *testing.T) {
if strings.HasSuffix(r.URL.Path, "/destination") {
c, err := r.Cookie("doodad")
if err != nil {
t.Errorf("Expected cookie `doodad` not present") // Occurs if client has no cookie jar
t.Errorf("Expected cookie `doodad` not present") // Occurs if client has no cookie jar
}
if c.Value != "foobar" {
t.Errorf("Cookie `doodad` value mismatch. Expected foobar, got %s", c.Value)
Expand All @@ -225,4 +228,12 @@ func TestSetCookieOnRedirect(t *testing.T) {

_, err = app.Run(context.Background())
assert.NoError(t, err)

// Run it again and check that we're not using the same cookie jar
// across executions. If we were, the first request would error out.
app2, err := NewApplet("httpredirect.star", b)
assert.NoError(t, err)

_, err = app2.Run(context.Background())
assert.NoError(t, err)
}
10 changes: 6 additions & 4 deletions runtime/modules/starlarkhttp/starlarkhttp.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,11 @@ func AsString(x starlark.Value) (string, error) {
const ModuleName = "http.star"

var (
// StarlarkHTTPClient is the http client used to create the http module. override with
// a custom client before calling LoadModule
StarlarkHTTPClient = http.DefaultClient
// StarlarkHTTPClient is a factory method for creating the http client used to create the http module.
// override with a custom function before calling LoadModule
StarlarkHTTPClient = func() *http.Client {
return http.DefaultClient
}
// StarlarkHTTPGuard is a global RequestGuard used in LoadModule. override with a custom
// implementation before calling LoadModule
StarlarkHTTPGuard RequestGuard
Expand All @@ -69,7 +71,7 @@ const (

// LoadModule creates an http Module
func LoadModule() (starlark.StringDict, error) {
var m = &Module{cli: StarlarkHTTPClient}
var m = &Module{cli: StarlarkHTTPClient()}
if StarlarkHTTPGuard != nil {
m.rg = StarlarkHTTPGuard
}
Expand Down

0 comments on commit ec22f51

Please sign in to comment.