github.com/m3db/m3@v1.5.0/src/x/http/status.go (about)

     1  // Copyright (c) 2021 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  // Package http adds extension to the stdlib http package.
    22  package http
    23  
    24  import (
    25  	"encoding/json"
    26  	"io"
    27  	"net/http"
    28  
    29  	xhttp "github.com/m3db/m3/src/x/net/http"
    30  )
    31  
    32  // StatusCodeTracker tracks the status code written to a http.ResponseWriter.
    33  type StatusCodeTracker struct {
    34  	http.ResponseWriter
    35  	Status      int
    36  	WroteHeader bool
    37  	TrackError  bool
    38  	ErrMsg      string
    39  }
    40  
    41  // WriteHeader saves the status .
    42  func (w *StatusCodeTracker) WriteHeader(status int) {
    43  	w.Status = status
    44  	w.WroteHeader = true
    45  	w.ResponseWriter.WriteHeader(status)
    46  }
    47  
    48  // Write saves the status as 200 if it has not already been set.
    49  func (w *StatusCodeTracker) Write(b []byte) (int, error) {
    50  	if !w.WroteHeader {
    51  		w.WroteHeader = true
    52  		w.Status = 200
    53  	}
    54  	if w.TrackError && w.Status >= 400 {
    55  		var errResp xhttp.ErrorResponse
    56  		if err := json.Unmarshal(b, &errResp); err == nil {
    57  			w.ErrMsg = errResp.Error
    58  		}
    59  	}
    60  	return w.ResponseWriter.Write(b)
    61  }
    62  
    63  // WrappedResponseWriter returns a wrapped version of the original
    64  // ResponseWriter and only implements the same combination of additional
    65  // interfaces as the original.  This implementation is based on
    66  // https://github.com/felixge/httpsnoop.
    67  func (w *StatusCodeTracker) WrappedResponseWriter() http.ResponseWriter {
    68  	var (
    69  		hj, i0 = w.ResponseWriter.(http.Hijacker)
    70  		cn, i1 = w.ResponseWriter.(http.CloseNotifier) //nolint:staticcheck
    71  		pu, i2 = w.ResponseWriter.(http.Pusher)
    72  		fl, i3 = w.ResponseWriter.(http.Flusher)
    73  		rf, i4 = w.ResponseWriter.(io.ReaderFrom)
    74  	)
    75  
    76  	switch {
    77  	case !i0 && !i1 && !i2 && !i3 && !i4:
    78  		return struct {
    79  			http.ResponseWriter
    80  		}{w}
    81  	case !i0 && !i1 && !i2 && !i3 && i4:
    82  		return struct {
    83  			http.ResponseWriter
    84  			io.ReaderFrom
    85  		}{w, rf}
    86  	case !i0 && !i1 && !i2 && i3 && !i4:
    87  		return struct {
    88  			http.ResponseWriter
    89  			http.Flusher
    90  		}{w, fl}
    91  	case !i0 && !i1 && !i2 && i3 && i4:
    92  		return struct {
    93  			http.ResponseWriter
    94  			http.Flusher
    95  			io.ReaderFrom
    96  		}{w, fl, rf}
    97  	case !i0 && !i1 && i2 && !i3 && !i4:
    98  		return struct {
    99  			http.ResponseWriter
   100  			http.Pusher
   101  		}{w, pu}
   102  	case !i0 && !i1 && i2 && !i3 && i4:
   103  		return struct {
   104  			http.ResponseWriter
   105  			http.Pusher
   106  			io.ReaderFrom
   107  		}{w, pu, rf}
   108  	case !i0 && !i1 && i2 && i3 && !i4:
   109  		return struct {
   110  			http.ResponseWriter
   111  			http.Pusher
   112  			http.Flusher
   113  		}{w, pu, fl}
   114  	case !i0 && !i1 && i2 && i3 && i4:
   115  		return struct {
   116  			http.ResponseWriter
   117  			http.Pusher
   118  			http.Flusher
   119  			io.ReaderFrom
   120  		}{w, pu, fl, rf}
   121  	case !i0 && i1 && !i2 && !i3 && !i4:
   122  		return struct {
   123  			http.ResponseWriter
   124  			http.CloseNotifier
   125  		}{w, cn}
   126  	case !i0 && i1 && !i2 && !i3 && i4:
   127  		return struct {
   128  			http.ResponseWriter
   129  			http.CloseNotifier
   130  			io.ReaderFrom
   131  		}{w, cn, rf}
   132  	case !i0 && i1 && !i2 && i3 && !i4:
   133  		return struct {
   134  			http.ResponseWriter
   135  			http.CloseNotifier
   136  			http.Flusher
   137  		}{w, cn, fl}
   138  	case !i0 && i1 && !i2 && i3 && i4:
   139  		return struct {
   140  			http.ResponseWriter
   141  			http.CloseNotifier
   142  			http.Flusher
   143  			io.ReaderFrom
   144  		}{w, cn, fl, rf}
   145  	case !i0 && i1 && i2 && !i3 && !i4:
   146  		return struct {
   147  			http.ResponseWriter
   148  			http.CloseNotifier
   149  			http.Pusher
   150  		}{w, cn, pu}
   151  	case !i0 && i1 && i2 && !i3 && i4:
   152  		return struct {
   153  			http.ResponseWriter
   154  			http.CloseNotifier
   155  			http.Pusher
   156  			io.ReaderFrom
   157  		}{w, cn, pu, rf}
   158  	case !i0 && i1 && i2 && i3 && !i4:
   159  		return struct {
   160  			http.ResponseWriter
   161  			http.CloseNotifier
   162  			http.Pusher
   163  			http.Flusher
   164  		}{w, cn, pu, fl}
   165  	case !i0 && i1 && i2 && i3 && i4:
   166  		return struct {
   167  			http.ResponseWriter
   168  			http.CloseNotifier
   169  			http.Pusher
   170  			http.Flusher
   171  			io.ReaderFrom
   172  		}{w, cn, pu, fl, rf}
   173  	case i0 && !i1 && !i2 && !i3 && !i4:
   174  		return struct {
   175  			http.ResponseWriter
   176  			http.Hijacker
   177  		}{w, hj}
   178  	case i0 && !i1 && !i2 && !i3 && i4:
   179  		return struct {
   180  			http.ResponseWriter
   181  			http.Hijacker
   182  			io.ReaderFrom
   183  		}{w, hj, rf}
   184  	case i0 && !i1 && !i2 && i3 && !i4:
   185  		return struct {
   186  			http.ResponseWriter
   187  			http.Hijacker
   188  			http.Flusher
   189  		}{w, hj, fl}
   190  	case i0 && !i1 && !i2 && i3 && i4:
   191  		return struct {
   192  			http.ResponseWriter
   193  			http.Hijacker
   194  			http.Flusher
   195  			io.ReaderFrom
   196  		}{w, hj, fl, rf}
   197  	case i0 && !i1 && i2 && !i3 && !i4:
   198  		return struct {
   199  			http.ResponseWriter
   200  			http.Hijacker
   201  			http.Pusher
   202  		}{w, hj, pu}
   203  	case i0 && !i1 && i2 && !i3 && i4:
   204  		return struct {
   205  			http.ResponseWriter
   206  			http.Hijacker
   207  			http.Pusher
   208  			io.ReaderFrom
   209  		}{w, hj, pu, rf}
   210  	case i0 && !i1 && i2 && i3 && !i4:
   211  		return struct {
   212  			http.ResponseWriter
   213  			http.Hijacker
   214  			http.Pusher
   215  			http.Flusher
   216  		}{w, hj, pu, fl}
   217  	case i0 && !i1 && i2 && i3 && i4:
   218  		return struct {
   219  			http.ResponseWriter
   220  			http.Hijacker
   221  			http.Pusher
   222  			http.Flusher
   223  			io.ReaderFrom
   224  		}{w, hj, pu, fl, rf}
   225  	case i0 && i1 && !i2 && !i3 && !i4:
   226  		return struct {
   227  			http.ResponseWriter
   228  			http.Hijacker
   229  			http.CloseNotifier
   230  		}{w, hj, cn}
   231  	case i0 && i1 && !i2 && !i3 && i4:
   232  		return struct {
   233  			http.ResponseWriter
   234  			http.Hijacker
   235  			http.CloseNotifier
   236  			io.ReaderFrom
   237  		}{w, hj, cn, rf}
   238  	case i0 && i1 && !i2 && i3 && !i4:
   239  		return struct {
   240  			http.ResponseWriter
   241  			http.Hijacker
   242  			http.CloseNotifier
   243  			http.Flusher
   244  		}{w, hj, cn, fl}
   245  	case i0 && i1 && !i2 && i3 && i4:
   246  		return struct {
   247  			http.ResponseWriter
   248  			http.Hijacker
   249  			http.CloseNotifier
   250  			http.Flusher
   251  			io.ReaderFrom
   252  		}{w, hj, cn, fl, rf}
   253  	case i0 && i1 && i2 && !i3 && !i4:
   254  		return struct {
   255  			http.ResponseWriter
   256  			http.Hijacker
   257  			http.CloseNotifier
   258  			http.Pusher
   259  		}{w, hj, cn, pu}
   260  	case i0 && i1 && i2 && !i3 && i4:
   261  		return struct {
   262  			http.ResponseWriter
   263  			http.Hijacker
   264  			http.CloseNotifier
   265  			http.Pusher
   266  			io.ReaderFrom
   267  		}{w, hj, cn, pu, rf}
   268  	case i0 && i1 && i2 && i3 && !i4:
   269  		return struct {
   270  			http.ResponseWriter
   271  			http.Hijacker
   272  			http.CloseNotifier
   273  			http.Pusher
   274  			http.Flusher
   275  		}{w, hj, cn, pu, fl}
   276  	case i0 && i1 && i2 && i3 && i4:
   277  		return struct {
   278  			http.ResponseWriter
   279  			http.Hijacker
   280  			http.CloseNotifier
   281  			http.Pusher
   282  			http.Flusher
   283  			io.ReaderFrom
   284  		}{w, hj, cn, pu, fl, rf}
   285  	default:
   286  		return struct {
   287  			http.ResponseWriter
   288  		}{w}
   289  	}
   290  }