package httpbin import ( "bytes" "compress/gzip" "compress/zlib" "encoding/json" "fmt" "github.com/mccutchen/go-httpbin/models" "math/rand" "net/http" "strconv" "strings" "time" "github.com/mccutchen/go-httpbin/httpbin/assets" "github.com/mccutchen/go-httpbin/httpbin/digest" ) var acceptedMediaTypes = []string{ "image/webp", "image/svg+xml", "image/jpeg", "image/png", "image/", } func notImplementedHandler(w http.ResponseWriter, r *http.Request) { http.Error(w, "Not implemented", http.StatusNotImplemented) } // Index renders an HTML index page func (h *HTTPBin) Index(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/" { http.Error(w, "Not Found", http.StatusNotFound) return } w.Header().Set("Content-Security-Policy", "default-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' camo.githubusercontent.com") writeHTML(w, assets.MustAsset("index.html"), http.StatusOK) } // FormsPost renders an HTML form that submits a request to the /post endpoint func (h *HTTPBin) FormsPost(w http.ResponseWriter, r *http.Request) { writeHTML(w, assets.MustAsset("forms-post.html"), http.StatusOK) } // UTF8 renders an HTML encoding stress test func (h *HTTPBin) UTF8(w http.ResponseWriter, r *http.Request) { writeHTML(w, assets.MustAsset("utf8.html"), http.StatusOK) } // Get handles HTTP GET requests func (h *HTTPBin) Get(w http.ResponseWriter, r *http.Request) { resp := &getResponse{ Args: r.URL.Query(), Headers: getRequestHeaders(r), Origin: getOrigin(r), URL: getURL(r).String(), } body, _ := json.Marshal(resp) writeJSON(w, body, http.StatusOK) } // RequestWithBody handles POST, PUT, and PATCH requests func (h *HTTPBin) RequestWithBody(w http.ResponseWriter, r *http.Request) { resp := &bodyResponse{ Args: r.URL.Query(), Headers: getRequestHeaders(r), Origin: getOrigin(r), URL: getURL(r).String(), } err := parseBody(w, r, resp) if err != nil { http.Error(w, fmt.Sprintf("error parsing request body: %s", err), http.StatusBadRequest) return } body, _ := json.Marshal(resp) writeJSON(w, body, http.StatusOK) } // Gzip returns a gzipped response func (h *HTTPBin) Gzip(w http.ResponseWriter, r *http.Request) { resp := &gzipResponse{ Headers: getRequestHeaders(r), Origin: getOrigin(r), Gzipped: true, } body, _ := json.Marshal(resp) buf := &bytes.Buffer{} gzw := gzip.NewWriter(buf) gzw.Write(body) gzw.Close() gzBody := buf.Bytes() w.Header().Set("Content-Encoding", "gzip") writeJSON(w, gzBody, http.StatusOK) } // Deflate returns a gzipped response func (h *HTTPBin) Deflate(w http.ResponseWriter, r *http.Request) { resp := &deflateResponse{ Headers: getRequestHeaders(r), Origin: getOrigin(r), Deflated: true, } body, _ := json.Marshal(resp) buf := &bytes.Buffer{} w2 := zlib.NewWriter(buf) w2.Write(body) w2.Close() compressedBody := buf.Bytes() w.Header().Set("Content-Encoding", "deflate") writeJSON(w, compressedBody, http.StatusOK) } // IP echoes the IP address of the incoming request func (h *HTTPBin) IP(w http.ResponseWriter, r *http.Request) { body, _ := json.Marshal(&ipResponse{ Origin: getOrigin(r), }) writeJSON(w, body, http.StatusOK) } func (h *HTTPBin) IP1(w http.ResponseWriter, r *http.Request) { ret := models.Ret{Data: []string{}} ret.Ret = "ok" ret.Ip = "" origin := getOrigin(r) if len(origin) > 0 { ipArray := strings.Split(origin, ":") if len(ipArray) > 1 { ret.Ip = ipArray[0] } } body, _ := json.Marshal(ret) writeJSON(w, body, http.StatusOK) } func (h *HTTPBin) IP2(w http.ResponseWriter, r *http.Request) { ret := models.Ret{Data: []string{}} ret.Ret = "ok" ret.Ip = "" origin := getOrigin(r) if len(origin) > 0 { ipArray := strings.Split(origin, ":") if len(ipArray) > 1 { ret.Ip = ipArray[0] } } body, _ := json.Marshal(ret) writeJSON(w, body, http.StatusOK) } func (h *HTTPBin) IP10(w http.ResponseWriter, r *http.Request) { cmip := models.QueryIp10() ret := models.Ret{Data: []string{}} ret.Ret = "ok" ret.Ip = cmip.Ip body, _ := json.Marshal(ret) writeJSON(w, body, http.StatusOK) } func (h *HTTPBin) IP20(w http.ResponseWriter, r *http.Request) { cmip := models.QueryIp10() ret := models.Ret20{Data: []string{}} ret.Ret = "ok" ret.Ip = cmip.Ip ret.Updatetime = cmip.Updatetime body, _ := json.Marshal(ret) writeJSON(w, body, http.StatusOK) } func (h *HTTPBin) Ssh(w http.ResponseWriter, r *http.Request) { cmip := models.QueryIp10() result := fmt.Sprintf("ssh -p 221 root@%s",cmip.Ip) body := []byte(result) writeJSON(w, body, http.StatusOK) } func (h *HTTPBin) Sftp(w http.ResponseWriter, r *http.Request) { cmip := models.QueryIp10() result := fmt.Sprintf("sftp -P 221 root@%s",cmip.Ip) body := []byte(result) writeJSON(w, body, http.StatusOK) } // UserAgent echoes the incoming User-Agent header func (h *HTTPBin) UserAgent(w http.ResponseWriter, r *http.Request) { body, _ := json.Marshal(&userAgentResponse{ UserAgent: r.Header.Get("User-Agent"), }) writeJSON(w, body, http.StatusOK) } // Headers echoes the incoming request headers func (h *HTTPBin) Headers(w http.ResponseWriter, r *http.Request) { body, _ := json.Marshal(&headersResponse{ Headers: getRequestHeaders(r), }) writeJSON(w, body, http.StatusOK) } // Status responds with the specified status code. TODO: support random choice // from multiple, optionally weighted status codes. func (h *HTTPBin) Status(w http.ResponseWriter, r *http.Request) { parts := strings.Split(r.URL.Path, "/") if len(parts) != 3 { http.Error(w, "Not found", http.StatusNotFound) return } code, err := strconv.Atoi(parts[2]) if err != nil { http.Error(w, "Invalid status", http.StatusBadRequest) return } type statusCase struct { headers map[string]string body []byte } redirectHeaders := &statusCase{ headers: map[string]string{ "Location": "/redirect/1", }, } notAcceptableBody, _ := json.Marshal(map[string]interface{}{ "message": "Client did not request a supported media type", "accept": acceptedMediaTypes, }) http300body := []byte(`