1// Copyright 2014 Manu Martinez-Almeida.  All rights reserved.
2// Use of this source code is governed by a MIT style
3// license that can be found in the LICENSE file.
4
5package gin
6
7import (
8	"bufio"
9	"io"
10	"net"
11	"net/http"
12)
13
14const (
15	noWritten     = -1
16	defaultStatus = 200
17)
18
19type (
20	ResponseWriter interface {
21		http.ResponseWriter
22		http.Hijacker
23		http.Flusher
24		http.CloseNotifier
25
26		// Returns the HTTP response status code of the current request.
27		Status() int
28
29		// Returns the number of bytes already written into the response http body.
30		// See Written()
31		Size() int
32
33		// Writes the string into the response body.
34		WriteString(string) (int, error)
35
36		// Returns true if the response body was already written.
37		Written() bool
38
39		// Forces to write the http header (status code + headers).
40		WriteHeaderNow()
41	}
42
43	responseWriter struct {
44		http.ResponseWriter
45		size   int
46		status int
47	}
48)
49
50var _ ResponseWriter = &responseWriter{}
51
52func (w *responseWriter) reset(writer http.ResponseWriter) {
53	w.ResponseWriter = writer
54	w.size = noWritten
55	w.status = defaultStatus
56}
57
58func (w *responseWriter) WriteHeader(code int) {
59	if code > 0 && w.status != code {
60		if w.Written() {
61			debugPrint("[WARNING] Headers were already written. Wanted to override status code %d with %d", w.status, code)
62		}
63		w.status = code
64	}
65}
66
67func (w *responseWriter) WriteHeaderNow() {
68	if !w.Written() {
69		w.size = 0
70		w.ResponseWriter.WriteHeader(w.status)
71	}
72}
73
74func (w *responseWriter) Write(data []byte) (n int, err error) {
75	w.WriteHeaderNow()
76	n, err = w.ResponseWriter.Write(data)
77	w.size += n
78	return
79}
80
81func (w *responseWriter) WriteString(s string) (n int, err error) {
82	w.WriteHeaderNow()
83	n, err = io.WriteString(w.ResponseWriter, s)
84	w.size += n
85	return
86}
87
88func (w *responseWriter) Status() int {
89	return w.status
90}
91
92func (w *responseWriter) Size() int {
93	return w.size
94}
95
96func (w *responseWriter) Written() bool {
97	return w.size != noWritten
98}
99
100// Implements the http.Hijacker interface
101func (w *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
102	if w.size < 0 {
103		w.size = 0
104	}
105	return w.ResponseWriter.(http.Hijacker).Hijack()
106}
107
108// Implements the http.CloseNotify interface
109func (w *responseWriter) CloseNotify() <-chan bool {
110	return w.ResponseWriter.(http.CloseNotifier).CloseNotify()
111}
112
113// Implements the http.Flush interface
114func (w *responseWriter) Flush() {
115	w.ResponseWriter.(http.Flusher).Flush()
116}
117