github.com/aacfactory/fns@v1.2.86-0.20240310083819-80d667fc0a17/transports/response.go (about)

     1  /*
     2   * Copyright 2023 Wang Min Xiang
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   * 	http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   *
    16   */
    17  
    18  package transports
    19  
    20  import (
    21  	"bufio"
    22  	"fmt"
    23  	"github.com/aacfactory/errors"
    24  	"github.com/aacfactory/fns/commons/bytex"
    25  	"github.com/aacfactory/fns/commons/objects"
    26  	"github.com/aacfactory/fns/context"
    27  	"github.com/valyala/bytebufferpool"
    28  	"io"
    29  	"net"
    30  	"net/http"
    31  	"strconv"
    32  	"sync"
    33  	"time"
    34  )
    35  
    36  type ResponseWriter interface {
    37  	context.Context
    38  	Status() int
    39  	SetStatus(status int)
    40  	SetCookie(cookie *Cookie)
    41  	Header() Header
    42  	Succeed(v any)
    43  	Failed(cause error)
    44  	Write(body []byte) (int, error)
    45  	Body() []byte
    46  	BodyLen() int
    47  	ResetBody()
    48  	Hijack(func(ctx context.Context, conn net.Conn, rw *bufio.ReadWriter) (err error)) (async bool, err error)
    49  	Hijacked() bool
    50  	WriteTimeout() time.Duration
    51  	WriteDeadline() time.Time
    52  }
    53  
    54  type WriteBuffer interface {
    55  	io.Writer
    56  	Bytes() []byte
    57  }
    58  
    59  var (
    60  	responseWriterPool = sync.Pool{}
    61  )
    62  
    63  func AcquireResultResponseWriter(timeout time.Duration, contentType []byte) *ResultResponseWriter {
    64  	deadline := time.Time{}
    65  	if timeout > 0 {
    66  		deadline = deadline.Add(timeout)
    67  	}
    68  	buf := bytebufferpool.Get()
    69  	cached := responseWriterPool.Get()
    70  	if cached == nil {
    71  		return &ResultResponseWriter{
    72  			contentType: contentType,
    73  			status:      0,
    74  			timeout:     timeout,
    75  			deadline:    deadline,
    76  			header:      NewHeader(),
    77  			body:        buf,
    78  		}
    79  	}
    80  	r := cached.(*ResultResponseWriter)
    81  	r.contentType = contentType
    82  	r.body = buf
    83  	r.timeout = timeout
    84  	r.deadline = deadline
    85  	return r
    86  }
    87  
    88  func ReleaseResultResponseWriter(w *ResultResponseWriter) {
    89  	bytebufferpool.Put(w.body)
    90  	w.header.Reset()
    91  	w.contentType = nil
    92  	w.body = nil
    93  	w.status = 0
    94  	w.timeout = 0
    95  	w.deadline = time.Time{}
    96  	responseWriterPool.Put(w)
    97  }
    98  
    99  type ResultResponseWriter struct {
   100  	contentType []byte
   101  	status      int
   102  	timeout     time.Duration
   103  	deadline    time.Time
   104  	header      Header
   105  	body        *bytebufferpool.ByteBuffer
   106  }
   107  
   108  func (w *ResultResponseWriter) Status() int {
   109  	return w.status
   110  }
   111  
   112  func (w *ResultResponseWriter) SetStatus(status int) {
   113  	w.status = status
   114  }
   115  
   116  func (w *ResultResponseWriter) Header() (h Header) {
   117  	h = w.header
   118  	return
   119  }
   120  
   121  func (w *ResultResponseWriter) Body() []byte {
   122  	return w.body.Bytes()
   123  }
   124  
   125  func (w *ResultResponseWriter) BodyLen() int {
   126  	return w.body.Len()
   127  }
   128  
   129  func (w *ResultResponseWriter) ResetBody() {
   130  	w.body.Reset()
   131  }
   132  
   133  func (w *ResultResponseWriter) Succeed(v any) {
   134  	if v == nil {
   135  		w.status = http.StatusOK
   136  		return
   137  	}
   138  	obj, isObject := v.(objects.Object)
   139  	if isObject {
   140  		v = obj.Value()
   141  	}
   142  	if v == nil {
   143  		w.status = http.StatusOK
   144  		return
   145  	}
   146  	encoder, contentType := GetMarshaler(w.contentType)
   147  	p, err := encoder(v)
   148  	if err != nil {
   149  		w.Failed(errors.Warning("fns: transport write succeed result failed").WithCause(err))
   150  		return
   151  	}
   152  	w.status = http.StatusOK
   153  	w.header.Set(ContentTypeHeaderName, contentType)
   154  	_, _ = w.Write(p)
   155  	return
   156  }
   157  
   158  func (w *ResultResponseWriter) Failed(cause error) {
   159  	if cause == nil {
   160  		cause = errors.Warning("fns: error is lost")
   161  	}
   162  	err := errors.Wrap(cause)
   163  	encoder, contentType := GetMarshaler(w.contentType)
   164  	body, bodyErr := encoder(err)
   165  	if bodyErr != nil {
   166  		w.status = 666
   167  		w.header.Set(ContentTypeHeaderName, ContentTypeTextHeaderValue)
   168  		_, _ = w.Write([]byte(fmt.Sprintf("%+v", bodyErr)))
   169  		return
   170  	}
   171  	w.status = err.Code()
   172  	w.header.Set(ContentTypeHeaderName, contentType)
   173  	_, _ = w.Write(body)
   174  	return
   175  }
   176  
   177  func (w *ResultResponseWriter) Write(body []byte) (int, error) {
   178  	bodyLen := len(body)
   179  	w.Header().Set(ContentLengthHeaderName, bytex.FromString(strconv.Itoa(bodyLen)))
   180  	if bodyLen > 0 {
   181  		return w.body.Write(body)
   182  	}
   183  	return 0, nil
   184  }
   185  
   186  func (w *ResultResponseWriter) WriteTimeout() time.Duration {
   187  	return w.timeout
   188  }
   189  
   190  func (w *ResultResponseWriter) WriteDeadline() time.Time {
   191  	return w.deadline
   192  }
   193  
   194  var (
   195  	responseContextKey = []byte("@fns:context:transports:response")
   196  )
   197  
   198  func WithResponse(ctx context.Context, w ResponseWriter) context.Context {
   199  	ctx.SetLocalValue(responseContextKey, w)
   200  	return ctx
   201  }
   202  
   203  func TryLoadResponseWriter(ctx context.Context) (ResponseWriter, bool) {
   204  	w, ok := ctx.(ResponseWriter)
   205  	if ok {
   206  		return w, ok
   207  	}
   208  	v := ctx.LocalValue(responseContextKey)
   209  	if v == nil {
   210  		return nil, false
   211  	}
   212  	w, ok = v.(ResponseWriter)
   213  	return w, ok
   214  }
   215  
   216  func LoadResponseWriter(ctx context.Context) ResponseWriter {
   217  	w, ok := TryLoadResponseWriter(ctx)
   218  	if ok {
   219  		return w
   220  	}
   221  	return nil
   222  }
   223  
   224  func TryLoadResponseHeader(ctx context.Context) (Header, bool) {
   225  	w, ok := TryLoadResponseWriter(ctx)
   226  	if !ok {
   227  		return nil, false
   228  	}
   229  	return w.Header(), ok
   230  }