77 "github.com/indigo-web/indigo/http/status"
88 "github.com/indigo-web/indigo/internal/response"
99 "github.com/indigo-web/indigo/internal/strutil"
10- "github.com/indigo-web/indigo/internal/types"
1110 "github.com/indigo-web/indigo/kv"
1211 json "github.com/json-iterator/go"
1312 "io"
@@ -25,7 +24,7 @@ const (
2524)
2625
2726type Response struct {
28- fields * response.Fields
27+ fields response.Fields
2928}
3029
3130// NewResponse returns a new instance of the Response object with status code set to 200 OK,
@@ -34,10 +33,11 @@ type Response struct {
3433// clear reason otherwise
3534func NewResponse () * Response {
3635 return & Response {
37- & response.Fields {
36+ response.Fields {
3837 Code : status .OK ,
3938 Headers : make ([]kv.Pair , 0 , preallocRespHeaders ),
4039 ContentType : response .DefaultContentType ,
40+ StreamSize : - 1 ,
4141 },
4242 }
4343}
@@ -112,13 +112,13 @@ func (r *Response) String(body string) *Response {
112112// Bytes sets the response's body to passed slice WITHOUT COPYING. Changing
113113// the passed slice later will affect the response by itself
114114func (r * Response ) Bytes (body []byte ) * Response {
115- r .fields .Body = body
115+ r .fields .BufferedBody = body
116116 return r
117117}
118118
119119// Write implements io.Reader interface. It always returns n=len(b) and err=nil
120120func (r * Response ) Write (b []byte ) (n int , err error ) {
121- r .fields .Body = append (r .fields .Body , b ... )
121+ r .fields .BufferedBody = append (r .fields .BufferedBody , b ... )
122122 return len (b ), nil
123123}
124124
@@ -132,7 +132,6 @@ func (r *Response) TryFile(path string) (*Response, error) {
132132
133133 stat , err := fd .Stat ()
134134 if err != nil {
135- // ...and if we can't get stats on it, it exists, however something in system went wrong
136135 return r , status .ErrInternalServerError
137136 }
138137 if stat .IsDir () {
@@ -144,7 +143,7 @@ func (r *Response) TryFile(path string) (*Response, error) {
144143 r .fields .ContentType = defaultFileMIME
145144 }
146145
147- return r .Attachment (fd , int ( stat .Size () )), nil
146+ return r .SizedStream (fd , stat .Size ()), nil
148147}
149148
150149// File opens a file for reading and returns a new Response with attachment, set to the file
@@ -158,10 +157,21 @@ func (r *Response) File(path string) *Response {
158157 return resp
159158}
160159
161- // Attachment sets a Response's attachment. In this case Response body will be ignored.
162- // If size <= 0, then Transfer-Encoding: chunked will be used
163- func (r * Response ) Attachment (reader io.Reader , size int ) * Response {
164- r .fields .Attachment = types .NewAttachment (reader , size )
160+ // Stream sets a reader to be the source of the response's body.
161+ func (r * Response ) Stream (reader io.Reader ) * Response {
162+ // TODO: we can check whether the reader implements Len() int interface and in that
163+ // TODO: case elide the chunked transfer encoding
164+ r .fields .Stream = reader
165+ r .fields .StreamSize = - 1
166+ return r
167+ }
168+
169+ // SizedStream receives a hint of the stream's future size. This helps, for example, uploading files,
170+ // as in this case we can rely on io.WriterTo interface, which might use more effective kernel mechanisms
171+ // available, e.g. sendfile(2) for Linux.
172+ func (r * Response ) SizedStream (reader io.Reader , size int64 ) * Response {
173+ r .fields .Stream = reader
174+ r .fields .StreamSize = size
165175 return r
166176}
167177
@@ -174,7 +184,7 @@ func (r *Response) Cookie(cookies ...cookie.Cookie) *Response {
174184// TryJSON receives a model (must be a pointer to the structure) and returns a new Response
175185// object and an error
176186func (r * Response ) TryJSON (model any ) (* Response , error ) {
177- r .fields .Body = r .fields .Body [:0 ]
187+ r .fields .BufferedBody = r .fields .BufferedBody [:0 ]
178188 stream := json .ConfigDefault .BorrowStream (r )
179189 stream .WriteVal (model )
180190 err := stream .Flush ()
@@ -220,7 +230,7 @@ func (r *Response) Error(err error, code ...status.Code) *Response {
220230
221231// Reveal returns a struct with values, filled by builder. Used mostly in internal purposes
222232func (r * Response ) Reveal () * response.Fields {
223- return r .fields
233+ return & r .fields
224234}
225235
226236// Clear discards everything was done with Response object before
@@ -229,42 +239,52 @@ func (r *Response) Clear() *Response {
229239 return r
230240}
231241
232- // Respond is a predicate to request.Respond(). May be used as a dummy handler
242+ // Respond is a shorthand for request.Respond(). May be used as a dummy handler
233243func Respond (request * Request ) * Response {
234244 return request .Respond ()
235245}
236246
237- // Code is a predicate to request.Respond().Code(...)
247+ // Code is a shorthand for request.Respond().Code(...)
238248func Code (request * Request , code status.Code ) * Response {
239249 return request .Respond ().Code (code )
240250}
241251
242- // String is a predicate to request.Respond().String(...)
252+ // String is a shorthand for request.Respond().String(...)
243253func String (request * Request , str string ) * Response {
244254 return request .Respond ().String (str )
245255}
246256
247- // Bytes is a predicate to request.Respond().Bytes(...)
257+ // Bytes is a shorthand for request.Respond().Bytes(...)
248258func Bytes (request * Request , b []byte ) * Response {
249259 return request .Respond ().Bytes (b )
250260}
251261
252- // File is a predicate to request.Respond().File(...)
262+ // File is a shorthand for request.Respond().File(...)
253263func File (request * Request , path string ) * Response {
254264 return request .Respond ().File (path )
255265}
256266
257- // JSON is a predicate to request.Respond().JSON(...)
267+ // Stream is a shorthand for request.Respond().Stream(...)
268+ func Stream (request * Request , reader io.Reader ) * Response {
269+ return request .Respond ().Stream (reader )
270+ }
271+
272+ // SizedStream is a shorthand for request.Respond().SizedStream(...)
273+ func SizedStream (request * Request , reader io.Reader , size int64 ) * Response {
274+ return request .Respond ().SizedStream (reader , size )
275+ }
276+
277+ // JSON is a shorthand for request.Respond().JSON(...)
258278func JSON (request * Request , model any ) * Response {
259279 return request .Respond ().JSON (model )
260280}
261281
262- // Error is a predicate to request.Respond().Error(...)
282+ // Error is a shorthand for request.Respond().Error(...)
263283//
264- // Error returns a response builder with an error set. If passed err is nil, nothing will happen.
265- // If an instance of status.HTTPError is passed, error code will be automatically set. Custom
266- // codes can be passed, however only first will be used. By default, the error is
267- // status.ErrInternalServerError
284+ // Error returns the response builder with an error set. If passed err is nil, nothing will happen.
285+ // If an instance of status.HTTPError is passed, its status code is automatically set. Otherwise,
286+ // status.ErrInternalServerError is used. A custom code can be set. Passing multiple status codes
287+ // will discard all except the first one.
268288func Error (request * Request , err error , code ... status.Code ) * Response {
269289 return request .Respond ().Error (err , code ... )
270290}
0 commit comments