mirror of
https://github.com/jimeh/casecmp.git
synced 2026-02-19 10:26:40 +00:00
216 lines
4.0 KiB
Go
216 lines
4.0 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"runtime"
|
|
"strconv"
|
|
"strings"
|
|
"text/template"
|
|
"time"
|
|
)
|
|
|
|
var (
|
|
version = "dev"
|
|
commit = "unknown"
|
|
)
|
|
|
|
const (
|
|
name = "casecmp"
|
|
defaultPort = 8080
|
|
defaultBind = "0.0.0.0"
|
|
)
|
|
|
|
// Argument parsing setup.
|
|
var (
|
|
portFlag = flag.Int("p", defaultPort, "Port to listen on")
|
|
bindFlag = flag.String("b", defaultBind, "Bind address")
|
|
forceHTTPSFlag = flag.Bool(
|
|
"f", false, "Use https:// in example curl commands",
|
|
)
|
|
versionFlag = flag.Bool("v", false, "Print version info")
|
|
)
|
|
|
|
type IndexData struct {
|
|
Name string
|
|
Version string
|
|
Scheme string
|
|
Host string
|
|
}
|
|
|
|
var indexTpl = template.Must(template.New("index").Parse(`{{.Name}} {{.Version}}
|
|
|
|
Case-insensitive string comparison, as an API. Because ¯\_(ツ)_/¯
|
|
|
|
Example usage:
|
|
curl -X POST -F "a=Foo Bar" -F "b=FOO BAR" {{.Scheme}}://{{.Host}}/
|
|
curl -X GET "{{.Scheme}}://{{.Host}}/?a=Foo+Bar&b=FOO+BAR"
|
|
curl -X GET -H "Accept: application/json" "{{.Scheme}}://{{.Host}}/?a=Foo+Bar&b=FOO+BAR"
|
|
curl -X POST -H "Content-Type: application/json" -d '{"a":"Foo Bar","b":"FOO BAR"}' {{.Scheme}}://{{.Host}}/
|
|
`))
|
|
|
|
func indexHandler(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != "GET" || r.URL.RawQuery != "" {
|
|
casecmpHandler(w, r)
|
|
return
|
|
}
|
|
|
|
scheme := "http"
|
|
if r.TLS != nil || *forceHTTPSFlag {
|
|
scheme = "https"
|
|
}
|
|
|
|
err := indexTpl.Execute(w, &IndexData{
|
|
Name: name,
|
|
Version: version,
|
|
Scheme: scheme,
|
|
Host: r.Host,
|
|
})
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func aboutHandler(w http.ResponseWriter, _ *http.Request) {
|
|
_, err := fmt.Fprintf(w,
|
|
`%s %s
|
|
|
|
https://github.com/jimeh/casecmp
|
|
`,
|
|
name, version)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
type JSONData struct {
|
|
A string `json:"a"`
|
|
B string `json:"b"`
|
|
}
|
|
|
|
func casecmpHandler(w http.ResponseWriter, r *http.Request) {
|
|
equal, err := casecmp(r)
|
|
if err != nil {
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
_, _ = fmt.Fprint(w, err.Error())
|
|
return
|
|
}
|
|
|
|
resp := "0"
|
|
if equal {
|
|
resp = "1"
|
|
}
|
|
|
|
accept := r.Header.Get("Accept")
|
|
if strings.Contains(accept, "application/json") {
|
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
|
_, _ = fmt.Fprintf(w, `{"result":%s}`, resp)
|
|
|
|
return
|
|
}
|
|
|
|
_, _ = fmt.Fprint(w, resp)
|
|
}
|
|
|
|
func casecmp(r *http.Request) (bool, error) {
|
|
var a, b string
|
|
|
|
contentType := r.Header.Get("Content-Type")
|
|
if strings.Contains(contentType, "application/json") {
|
|
body, err := io.ReadAll(r.Body)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
d := JSONData{}
|
|
err = json.Unmarshal(body, &d)
|
|
if err != nil {
|
|
return false, fmt.Errorf("invalid JSON request: %w", err)
|
|
}
|
|
|
|
a = d.A
|
|
b = d.B
|
|
} else {
|
|
a = r.FormValue("a")
|
|
b = r.FormValue("b")
|
|
}
|
|
|
|
return strings.EqualFold(string(a), string(b)), nil
|
|
}
|
|
|
|
func printVersion() {
|
|
var buffer bytes.Buffer
|
|
buffer.WriteString(fmt.Sprintf("%s %s", name, version))
|
|
|
|
if commit != "unknown" {
|
|
buffer.WriteString(fmt.Sprintf(" (%s)", commit))
|
|
}
|
|
|
|
buffer.WriteString(fmt.Sprintf(", built with %s", runtime.Version()))
|
|
|
|
fmt.Println(buffer.String())
|
|
}
|
|
|
|
func startServer() error {
|
|
if *portFlag == defaultPort {
|
|
envPort := os.Getenv("PORT")
|
|
if envPort != "" {
|
|
v, err := strconv.Atoi(envPort)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
*portFlag = v
|
|
}
|
|
}
|
|
|
|
if *bindFlag == defaultBind {
|
|
envBind := os.Getenv("BIND")
|
|
if envBind != "" {
|
|
*bindFlag = envBind
|
|
}
|
|
}
|
|
|
|
if !*forceHTTPSFlag && os.Getenv("FORCE_HTTPS") != "" {
|
|
*forceHTTPSFlag = true
|
|
}
|
|
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/{$}", indexHandler)
|
|
mux.HandleFunc("/about", aboutHandler)
|
|
mux.HandleFunc("/about/{$}", aboutHandler)
|
|
|
|
address := fmt.Sprintf("%s:%d", *bindFlag, *portFlag)
|
|
fmt.Printf("Listening on %s\n", address)
|
|
|
|
srv := &http.Server{
|
|
ReadTimeout: 5 * time.Second,
|
|
WriteTimeout: 5 * time.Second,
|
|
IdleTimeout: 30 * time.Second,
|
|
Handler: mux,
|
|
Addr: address,
|
|
}
|
|
|
|
return srv.ListenAndServe()
|
|
}
|
|
|
|
func main() {
|
|
flag.Parse()
|
|
|
|
if *versionFlag {
|
|
printVersion()
|
|
return
|
|
}
|
|
|
|
err := startServer()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|