github.com/pingcap/failpoint@v0.0.0-20240412033321-fd0796e60f86/http.go (about)

     1  // Copyright 2019 PingCAP, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Copyright 2016 CoreOS, Inc.
    16  //
    17  // Licensed under the Apache License, Version 2.0 (the "License");
    18  // you may not use this file except in compliance with the License.
    19  // You may obtain a copy of the License at
    20  //
    21  //     http://www.apache.org/licenses/LICENSE-2.0
    22  //
    23  // Unless required by applicable law or agreed to in writing, software
    24  // distributed under the License is distributed on an "AS IS" BASIS,
    25  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    26  // See the License for the specific language governing permissions and
    27  // limitations under the License.
    28  
    29  package failpoint
    30  
    31  import (
    32  	"io/ioutil"
    33  	"net"
    34  	"net/http"
    35  	"sort"
    36  	"strings"
    37  )
    38  
    39  // HttpHandler is used to handle failpoint Enable/Disable/Status requests
    40  type HttpHandler struct{}
    41  
    42  func serve(host string) error {
    43  	ln, err := net.Listen("tcp", host)
    44  	if err != nil {
    45  		return err
    46  	}
    47  	go http.Serve(ln, &HttpHandler{})
    48  	return nil
    49  }
    50  
    51  func (*HttpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    52  	key := r.URL.Path
    53  	if len(key) == 0 || key[0] != '/' {
    54  		http.Error(w, "malformed request URI", http.StatusBadRequest)
    55  		return
    56  	}
    57  	key = key[1:]
    58  
    59  	switch {
    60  	// sets the failpoint
    61  	case r.Method == "PUT":
    62  		v, err := ioutil.ReadAll(r.Body)
    63  		if err != nil {
    64  			http.Error(w, "failed ReadAll in PUT", http.StatusBadRequest)
    65  			return
    66  		}
    67  		err = failpoints.EnableWith(key, string(v), func() error {
    68  			w.WriteHeader(http.StatusNoContent)
    69  			if f, ok := w.(http.Flusher); ok {
    70  				// flush before unlocking so a panic failpoint won't
    71  				// take down the http server before it sends the response
    72  				f.Flush()
    73  			}
    74  			return nil
    75  		})
    76  		if err != nil {
    77  			http.Error(w, "failed to set failpoint "+string(key), http.StatusBadRequest)
    78  			return
    79  		}
    80  	case r.Method == "GET":
    81  		if len(key) == 0 {
    82  			fps := List()
    83  			sort.Strings(fps)
    84  			lines := make([]string, len(fps))
    85  			for i := range lines {
    86  				s, _ := Status(fps[i])
    87  				lines[i] = fps[i] + "=" + s
    88  			}
    89  			w.Write([]byte(strings.Join(lines, "\n") + "\n"))
    90  		} else {
    91  			status, err := Status(key)
    92  			if err != nil {
    93  				http.Error(w, "failed to GET: "+err.Error(), http.StatusNotFound)
    94  			}
    95  			w.Write([]byte(status + "\n"))
    96  		}
    97  	// deactivates a failpoint
    98  	case r.Method == "DELETE":
    99  		if err := Disable(key); err != nil {
   100  			http.Error(w, "failed to delete failpoint "+err.Error(), http.StatusBadRequest)
   101  			return
   102  		}
   103  		w.WriteHeader(http.StatusNoContent)
   104  	default:
   105  		w.Header().Add("Allow", "DELETE")
   106  		w.Header().Add("Allow", "GET")
   107  		w.Header().Set("Allow", "PUT")
   108  		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
   109  	}
   110  }