Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions jaws.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,13 @@ type Jaws struct {
// It defaults to [assets.DefaultCookieName], which is derived from the
// executable and falls back to "jaws". CookieName must be a valid, non-empty
// HTTP cookie name; see [http.Cookie.Valid].
CookieName string
AutoSession bool // Create a session during a successful WebSocket upgrade when a Request has none and the Session limits allow it. Defaults to false.
CookieName string
// AutoSession creates a [Session] on a successful WebSocket upgrade.
//
// It applies when the [Request] has no Session and Session limits allow one.
// The cookie's Secure flag follows the initial page request's scheme, honoring
// [Jaws.TrustForwardedHeaders]. AutoSession defaults to false.
AutoSession bool
// TrustForwardedHeaders enables trusted proxy header processing.
//
// It governs the session cookie Secure flag and WebSocket Origin scheme
Expand Down
2 changes: 1 addition & 1 deletion request.go
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ func (rq *Request) ensureAutoSession(w http.ResponseWriter, r *http.Request) {
// through Jaws session lookups.
func (rq *Request) newAutoSession(r *http.Request) (sess *Session) {
jw := rq.Jaws
secure := secureheaders.RequestIsSecure(r, jw.TrustForwardedHeaders)
secure := secureheaders.RequestIsSecure(rq.Initial(), jw.TrustForwardedHeaders)
remoteIP := jw.clientIP(r)
jw.mu.Lock()
defer jw.mu.Unlock()
Expand Down
58 changes: 58 additions & 0 deletions request_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4151,6 +4151,64 @@ func TestWS_AutoSessionCreatesSession(t *testing.T) {
}
}

func TestWS_AutoSessionSecureFollowsPage(t *testing.T) {
for _, tc := range []struct {
name string
pageScheme string
upgradeScheme string
wantSecure bool
}{
{"HTTP page", "http", "", false},
{"HTTPS scheme on both requests", "https", "https", true},
{"HTTPS scheme only on page", "https", "", true},
{"HTTPS scheme only on upgrade", "http", "https", false},
} {
t.Run(tc.name, func(t *testing.T) {
jw, err := New()
if err != nil {
t.Fatal(err)
}
jw.AutoSession = true
jw.TrustForwardedHeaders = true
go jw.Serve()
t.Cleanup(jw.Close)
server := httptest.NewServer(jw)
t.Cleanup(server.Close)
u, err := url.Parse(server.URL)
if err != nil {
t.Fatal(err)
}
page := httptest.NewRequest(http.MethodGet, server.URL+"/", nil)
page.Header.Set("X-Forwarded-For", "203.0.113.9")
page.Header.Set("X-Forwarded-Proto", tc.pageScheme)
rq := jw.NewRequest(httptest.NewRecorder(), page)
header := http.Header{}
header.Set("Origin", tc.pageScheme+"://"+u.Host)
header.Set("X-Forwarded-For", "203.0.113.9")
if tc.upgradeScheme != "" {
header.Set("X-Forwarded-Proto", tc.upgradeScheme)
}
ctx, cancel := context.WithTimeout(t.Context(), testTimeout)
defer cancel()
conn, resp, err := websocket.Dial(ctx, server.URL+"/jaws/"+rq.JawsKeyString(), &websocket.DialOptions{HTTPHeader: header})
if err != nil {
t.Fatal(err)
}
defer func() { _ = conn.CloseNow() }()
if resp.StatusCode != http.StatusSwitchingProtocols {
t.Fatalf("status = %d, want 101", resp.StatusCode)
}
cookies := resp.Cookies()
if len(cookies) != 1 {
t.Fatalf("AutoSession cookies = %v, want one", cookies)
}
if cookies[0].Secure != tc.wantSecure {
t.Fatalf("AutoSession cookie Secure = %v, want %v", cookies[0].Secure, tc.wantSecure)
}
})
}
}

func TestWS_AutoSessionDoesNotCreateAfterJawsClose(t *testing.T) {
jw, err := New()
if err != nil {
Expand Down
Loading