Skip to content

Commit 62e546c

Browse files
committed
Don't buffer responses when using LoadAndSave()
1 parent a07530f commit 62e546c

1 file changed

Lines changed: 43 additions & 42 deletions

File tree

session.go

Lines changed: 43 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
package scs
22

33
import (
4-
"bufio"
5-
"bytes"
64
"context"
75
"log"
8-
"net"
96
"net/http"
107
"time"
118

@@ -131,6 +128,8 @@ func NewSession() *SessionManager {
131128
// the client in a cookie.
132129
func (s *SessionManager) LoadAndSave(next http.Handler) http.Handler {
133130
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
131+
w.Header().Add("Vary", "Cookie")
132+
134133
var token string
135134
cookie, err := r.Cookie(s.Cookie.Name)
136135
if err == nil {
@@ -144,33 +143,36 @@ func (s *SessionManager) LoadAndSave(next http.Handler) http.Handler {
144143
}
145144

146145
sr := r.WithContext(ctx)
147-
bw := &bufferedResponseWriter{ResponseWriter: w}
148-
next.ServeHTTP(bw, sr)
149146

150-
if sr.MultipartForm != nil {
151-
sr.MultipartForm.RemoveAll()
147+
sw := &sessionResponseWriter{
148+
ResponseWriter: w,
149+
request: sr,
150+
sessionManager: s,
152151
}
153152

154-
switch s.Status(ctx) {
155-
case Modified:
156-
token, expiry, err := s.Commit(ctx)
157-
if err != nil {
158-
s.ErrorFunc(w, r, err)
159-
return
160-
}
161-
162-
s.WriteSessionCookie(ctx, w, token, expiry)
163-
case Destroyed:
164-
s.WriteSessionCookie(ctx, w, "", time.Time{})
153+
next.ServeHTTP(sw, sr)
154+
155+
if !sw.written {
156+
s.commitAndWriteSessionCookie(w, sr)
165157
}
158+
})
159+
}
166160

167-
w.Header().Add("Vary", "Cookie")
161+
func (s *SessionManager) commitAndWriteSessionCookie(w http.ResponseWriter, r *http.Request) {
162+
ctx := r.Context()
168163

169-
if bw.code != 0 {
170-
w.WriteHeader(bw.code)
164+
switch s.Status(ctx) {
165+
case Modified:
166+
token, expiry, err := s.Commit(ctx)
167+
if err != nil {
168+
s.ErrorFunc(w, r, err)
169+
return
171170
}
172-
w.Write(bw.buf.Bytes())
173-
})
171+
172+
s.WriteSessionCookie(ctx, w, token, expiry)
173+
case Destroyed:
174+
s.WriteSessionCookie(ctx, w, "", time.Time{})
175+
}
174176
}
175177

176178
// WriteSessionCookie writes a cookie to the HTTP response with the provided
@@ -211,32 +213,31 @@ func defaultErrorFunc(w http.ResponseWriter, r *http.Request, err error) {
211213
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
212214
}
213215

214-
type bufferedResponseWriter struct {
216+
type sessionResponseWriter struct {
215217
http.ResponseWriter
216-
buf bytes.Buffer
217-
code int
218-
wroteHeader bool
218+
request *http.Request
219+
sessionManager *SessionManager
220+
written bool
219221
}
220222

221-
func (bw *bufferedResponseWriter) Write(b []byte) (int, error) {
222-
return bw.buf.Write(b)
223+
func (sw *sessionResponseWriter) Write(b []byte) (int, error) {
224+
if !sw.written {
225+
sw.sessionManager.commitAndWriteSessionCookie(sw.ResponseWriter, sw.request)
226+
sw.written = true
227+
}
228+
229+
return sw.ResponseWriter.Write(b)
223230
}
224231

225-
func (bw *bufferedResponseWriter) WriteHeader(code int) {
226-
if !bw.wroteHeader {
227-
bw.code = code
228-
bw.wroteHeader = true
232+
func (sw *sessionResponseWriter) WriteHeader(code int) {
233+
if !sw.written {
234+
sw.sessionManager.commitAndWriteSessionCookie(sw.ResponseWriter, sw.request)
235+
sw.written = true
229236
}
230-
}
231237

232-
func (bw *bufferedResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
233-
hj := bw.ResponseWriter.(http.Hijacker)
234-
return hj.Hijack()
238+
sw.ResponseWriter.WriteHeader(code)
235239
}
236240

237-
func (bw *bufferedResponseWriter) Push(target string, opts *http.PushOptions) error {
238-
if pusher, ok := bw.ResponseWriter.(http.Pusher); ok {
239-
return pusher.Push(target, opts)
240-
}
241-
return http.ErrNotSupported
241+
func (sw *sessionResponseWriter) Unwrap() http.ResponseWriter {
242+
return sw.ResponseWriter
242243
}

0 commit comments

Comments
 (0)