github.com/machinefi/w3bstream@v1.6.5-rc9.0.20240426031326-b8c7c4876e72/pkg/depends/kit/httptransport/httpx/httpx_response.go (about)

     1  package httpx
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  	"mime"
     7  	"net/http"
     8  	"net/textproto"
     9  	"net/url"
    10  
    11  	"github.com/machinefi/w3bstream/pkg/depends/kit/kit"
    12  	"github.com/machinefi/w3bstream/pkg/depends/kit/statusx"
    13  )
    14  
    15  type Encode func(context.Context, io.Writer, interface{}) error
    16  
    17  type ResolveEncode func(*Response) (Encode, error)
    18  
    19  type ResponseWrapper func(v interface{}) *Response
    20  
    21  func Compose(rws ...ResponseWrapper) ResponseWrapper {
    22  	return func(v interface{}) *Response {
    23  		rsp := ResponseFrom(v)
    24  		for i := len(rws) - 1; i >= 0; i-- {
    25  			rsp = rws[i](rsp)
    26  		}
    27  		return rsp
    28  	}
    29  }
    30  
    31  func WrapStatusCode(statusCode int) ResponseWrapper {
    32  	return func(v interface{}) *Response {
    33  		rsp := ResponseFrom(v)
    34  		rsp.StatusCode = statusCode
    35  		return rsp
    36  	}
    37  }
    38  
    39  func WrapCookies(cookies ...*http.Cookie) ResponseWrapper {
    40  	return func(v interface{}) *Response {
    41  		rsp := ResponseFrom(v)
    42  		rsp.Cookies = cookies
    43  		return rsp
    44  	}
    45  }
    46  
    47  func WrapSchema(s interface{}) ResponseWrapper {
    48  	return func(v interface{}) *Response {
    49  		rsp := ResponseFrom(v)
    50  		return rsp
    51  	}
    52  }
    53  
    54  func WrapContentType(ct string) ResponseWrapper {
    55  	return func(v interface{}) *Response {
    56  		rsp := ResponseFrom(v)
    57  		rsp.ContentType = ct
    58  		return rsp
    59  	}
    60  }
    61  
    62  func WrapMeta(metas ...kit.Metadata) ResponseWrapper {
    63  	return func(v interface{}) *Response {
    64  		rsp := ResponseFrom(v)
    65  		rsp.Meta = kit.FromMetas(metas...)
    66  		return rsp
    67  	}
    68  }
    69  
    70  func Metadata(k string, vs ...string) kit.Metadata {
    71  	return kit.Metadata{k: vs}
    72  }
    73  
    74  func ResponseFrom(v interface{}) *Response {
    75  	if r, ok := v.(*Response); ok {
    76  		return r
    77  	}
    78  
    79  	rsp := &Response{}
    80  
    81  	if redirectDescriber, ok := v.(RedirectDescriber); ok {
    82  		rsp.Location = redirectDescriber.Location()
    83  		rsp.StatusCode = redirectDescriber.StatusCode()
    84  		return rsp
    85  	}
    86  
    87  	if e, ok := v.(error); ok {
    88  		if e != nil {
    89  			se, ok := statusx.IsStatusErr(e)
    90  			if !ok {
    91  				if e == context.Canceled {
    92  					// https://httpstatuses.com/499
    93  					se = statusx.Wrap(e, 499, "ContextCanceled")
    94  				} else {
    95  					se = statusx.Wrap(e, http.StatusInternalServerError, "UnknownError")
    96  				}
    97  			}
    98  			v = se
    99  		}
   100  	}
   101  
   102  	rsp.Value = v
   103  
   104  	if with, ok := v.(kit.MetadataCarrier); ok {
   105  		rsp.Meta = with.Meta()
   106  	}
   107  
   108  	if with, ok := v.(WithCookies); ok {
   109  		rsp.Cookies = with.Cookies()
   110  	}
   111  
   112  	if with, ok := v.(WithContentType); ok {
   113  		rsp.ContentType = with.ContentType()
   114  	}
   115  
   116  	if with, ok := v.(WithStatusCode); ok {
   117  		rsp.StatusCode = with.StatusCode()
   118  	}
   119  
   120  	return rsp
   121  }
   122  
   123  type Response struct {
   124  	Value       interface{} // Value of body
   125  	Meta        kit.Metadata
   126  	Cookies     []*http.Cookie
   127  	Location    *url.URL
   128  	ContentType string
   129  	StatusCode  int
   130  }
   131  
   132  func (r *Response) Unwrap() error {
   133  	if err, ok := r.Value.(error); ok {
   134  		return err
   135  	}
   136  	return nil
   137  }
   138  
   139  func (r *Response) Error() string {
   140  	if err := r.Unwrap(); err != nil {
   141  		return err.Error()
   142  	}
   143  	return "response error"
   144  }
   145  
   146  func (r *Response) WriteTo(rw http.ResponseWriter, req *http.Request, resolve ResolveEncode) error {
   147  	defer func() { r.Value = nil }()
   148  	if upgrader, ok := r.Value.(Upgrader); ok {
   149  		return upgrader.Upgrade(rw, req)
   150  	}
   151  	if r.StatusCode == 0 {
   152  		if r.Value == nil {
   153  			r.StatusCode = http.StatusNoContent
   154  		} else {
   155  			if req.Method == http.MethodPost {
   156  				r.StatusCode = http.StatusCreated
   157  			} else {
   158  				r.StatusCode = http.StatusOK
   159  			}
   160  		}
   161  	}
   162  	if r.Meta != nil {
   163  		header := rw.Header()
   164  		for k, vs := range r.Meta {
   165  			header[textproto.CanonicalMIMEHeaderKey(k)] = vs
   166  		}
   167  	}
   168  	if r.Cookies != nil {
   169  		for _, cookie := range r.Cookies {
   170  			if cookie != nil {
   171  				http.SetCookie(rw, cookie)
   172  			}
   173  		}
   174  	}
   175  	if r.Location != nil {
   176  		http.Redirect(rw, req, r.Location.String(), r.StatusCode)
   177  		return nil
   178  	}
   179  	if r.StatusCode == http.StatusNoContent {
   180  		rw.WriteHeader(r.StatusCode)
   181  		return nil
   182  	}
   183  	if r.ContentType != "" {
   184  		rw.Header().Set(HeaderContentType, r.ContentType)
   185  	}
   186  	switch v := r.Value.(type) {
   187  	case kit.Result:
   188  		rw.WriteHeader(r.StatusCode)
   189  		_, err := v.Into(rw)
   190  		return err
   191  	case io.Reader:
   192  		rw.WriteHeader(r.StatusCode)
   193  		defer func() {
   194  			if c, ok := v.(io.Closer); ok {
   195  				c.Close()
   196  			}
   197  		}()
   198  		_, err := io.Copy(rw, v)
   199  		return err
   200  	default:
   201  		enc, err := resolve(r)
   202  		if err != nil {
   203  			return err
   204  		}
   205  		return enc(
   206  			ContextWithStatusCode(req.Context(), r.StatusCode),
   207  			rw, r.Value,
   208  		)
   209  	}
   210  }
   211  
   212  type ResponseWriteError interface {
   213  	WriteError(err error) (int, error)
   214  }
   215  
   216  type Upgrader interface {
   217  	Upgrade(http.ResponseWriter, *http.Request) error
   218  }
   219  
   220  func MaybeWriteHeader(ctx context.Context, w io.Writer, ct string, param map[string]string) {
   221  	if rw, ok := w.(WithHeader); ok {
   222  		if len(param) == 0 {
   223  			rw.Header().Set(HeaderContentType, ct)
   224  		} else {
   225  			rw.Header().Set(
   226  				HeaderContentType,
   227  				mime.FormatMediaType(ct, param),
   228  			)
   229  		}
   230  	}
   231  	if rw, ok := w.(http.ResponseWriter); ok {
   232  		rw.WriteHeader(StatusCodeFromContext(ctx))
   233  	}
   234  }