// https://gist.github.com/miguelmota/7b765edff00dc676215d6174f3f30216
package main
import (
"crypto/tls"
"errors"
"log"
"net"
"net/http"
"os"
"strconv"
"strings"
"time"
)
const (
defaultTimeOut = 10.
)
var (
errMalformedAdr = errors.New("malformed address")
errIPNotFound = errors.New("ip not found")
errBadFloatValue = errors.New("bad float value")
errBadConfig = errors.New("bad config")
)
func getDefaultOptions() map[string]float64 {
return map[string]float64{
"APP_CONTEXT_TIMEOUT": defaultTimeOut,
"APP_READ_HEADER_TIMEOUT": defaultTimeOut,
"APP_READ_TIMEOUT": defaultTimeOut,
"APP_WRITE_TIMEOUT": defaultTimeOut,
"APP_IDLE_TIMEOUT": defaultTimeOut,
}
}
func getIP(request *http.Request) (string, error) {
ips := request.Header.Get("X-Forwarded-For")
splitIps := strings.Split(ips, ",")
log.Println(request.RemoteAddr)
if len(splitIps) > 0 {
netIP := net.ParseIP(splitIps[len(splitIps)-1])
if netIP != nil {
return netIP.String(), nil
}
}
ip, _, err := net.SplitHostPort(request.RemoteAddr)
if err != nil {
return "", errMalformedAdr
}
if netIP := net.ParseIP(ip); netIP != nil {
ip := netIP.String()
if ip == "::1" {
return "127.0.0.1", nil
}
return ip, nil
}
return "", errIPNotFound
}
func ipHandler(writer http.ResponseWriter, request *http.Request) {
ipAddr, err := getIP(request)
if err != nil {
writer.WriteHeader(http.StatusInternalServerError)
return
}
writer.WriteHeader(http.StatusOK)
writtenLen, err := writer.Write([]byte(ipAddr))
if err != nil || writtenLen != len(ipAddr) {
writer.WriteHeader(http.StatusInternalServerError)
return
}
}
type Config struct {
Addr string
ContextTimeOut float64
ReadHeaderTimeout float64
ReadTimeout float64
WriteTimeout float64
IdleTimeout float64
}
func getConfigValue(envVarName string) (float64, error) {
defaultOptions := getDefaultOptions()
paramEnv := os.Getenv(envVarName)
if paramEnv != "" {
param, err := strconv.ParseFloat(paramEnv, 64)
if err != nil {
return defaultOptions[envVarName], errBadFloatValue
}
return param, nil
}
return defaultOptions[envVarName], nil
}
func getConfig() (Config, error) {
var config Config
var err error
appAddrEnv := os.Getenv("APP_ADDR")
if appAddrEnv != "" {
config.Addr = appAddrEnv
} else {
config.Addr = ":8080"
}
config.ContextTimeOut, err = getConfigValue("APP_CONTEXT_TIMEOUT")
if err != nil {
log.Println(err.Error())
}
config.ReadHeaderTimeout, err = getConfigValue("APP_READ_HEADER_TIMEOUT")
if err != nil {
log.Println(err.Error())
}
config.ReadTimeout, err = getConfigValue("APP_READ_TIMEOUT")
if err != nil {
log.Println(err.Error())
}
config.WriteTimeout, err = getConfigValue("APP_WRITE_TIMEOUT")
if err != nil {
log.Println(err.Error())
}
config.IdleTimeout, err = getConfigValue("APP_IDLE_TIMEOUT")
if err != nil {
log.Println(err.Error())
}
return config, err
}
func main() {
log.SetOutput(os.Stdout)
http.HandleFunc("/", ipHandler)
config, err := getConfig()
if err != nil {
log.Fatal(errBadConfig)
}
tlsConfig := &tls.Config{
MinVersion: tls.VersionTLS13,
CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256},
PreferServerCipherSuites: true,
}
server := http.Server{
Addr: config.Addr,
ReadHeaderTimeout: time.Duration(config.ReadHeaderTimeout) * time.Second,
ReadTimeout: time.Duration(config.ReadTimeout) * time.Second,
WriteTimeout: time.Duration(config.WriteTimeout) * time.Second,
IdleTimeout: time.Duration(config.IdleTimeout) * time.Second,
TLSNextProto: nil,
ErrorLog: nil,
Handler: nil,
TLSConfig: tlsConfig,
}
log.Fatal(server.ListenAndServeTLS("/certs/server.cert", "/certs/server.key"))
}