Allow multiple formats for response, add html and json response

• If a request comes in to the root path, look at accept header and
   parse it. Check for hmtl, json, and text. If no known accept header
   comes in, return text/plain
 • Add route for /json that always returns json response
 • Add route for /text that always returns text response
 • Add route for /html that always returns html response
master
Buddy Sandidge 8 years ago
parent ba99b61aa0
commit b0b44a992f

20
ip.go

@ -1,6 +1,7 @@
package main package main
import ( import (
"encoding/json"
"errors" "errors"
"net" "net"
) )
@ -11,10 +12,6 @@ type IP struct {
version string version string
} }
func (ip *IP) isV6() bool {
return ip.To4() == nil
}
// NewIP returns a new instance of an IP address // NewIP returns a new instance of an IP address
func NewIP(addr string) (*IP, error) { func NewIP(addr string) (*IP, error) {
parsedIP := net.ParseIP(addr) parsedIP := net.ParseIP(addr)
@ -31,3 +28,18 @@ func NewIP(addr string) (*IP, error) {
return result, nil return result, nil
} }
// MarshalJSON for marshalling ip to json for response
func (ip *IP) MarshalJSON() ([]byte, error) {
return json.Marshal(&struct {
IP string `json:"ip"`
Version string `json:"version"`
}{
IP: ip.String(),
Version: ip.version,
})
}
func (ip *IP) isV6() bool {
return ip.To4() == nil
}

@ -1,18 +1,37 @@
package main package main
import ( import (
"bytes"
"html/template"
"io" "io"
"net" "net"
"net/http" "net/http"
"strings"
"github.com/pkg/errors"
) )
// Server that handles http responses // Server that handles http responses
type Server struct { type Server struct {
headerNames []string headerNames []string
logger LoggerHandler logger LoggerHandler
tmpl *template.Template
} }
func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) { type responseFormat int
const (
unknownResponse responseFormat = iota
jsonResponse
textResponse
htmlResponse
)
func (s *Server) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
s.handleHTTP(resp, req, s.getResponseType(req))
}
func (s *Server) handleHTTP(resp http.ResponseWriter, req *http.Request, responseType responseFormat) {
for _, headerName := range s.headerNames { for _, headerName := range s.headerNames {
possibleIP := req.Header.Get(headerName) possibleIP := req.Header.Get(headerName)
if possibleIP == "" { if possibleIP == "" {
@ -24,28 +43,89 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
continue continue
} }
s.sendResponse(w, ip) s.sendResponse(resp, ip, responseType)
return return
} }
addr, _, err := net.SplitHostPort(req.RemoteAddr) addr, _, err := net.SplitHostPort(req.RemoteAddr)
if err != nil { if err != nil {
s.handleError(w, http.StatusBadRequest, err, "Could not get IP address from request") s.handleError(resp, http.StatusBadRequest, err, "Could not get IP address from request")
return return
} }
ip, err := NewIP(addr) ip, err := NewIP(addr)
if err != nil { if err != nil {
s.handleError(w, http.StatusBadRequest, err, "Could not parse IP address"+addr) s.handleError(resp, http.StatusBadRequest, err, "Could not parse IP address"+addr)
return return
} }
s.sendResponse(w, ip) s.sendResponse(resp, ip, responseType)
}
func hasType(accepts, valid []string) bool {
for _, accept := range accepts {
value := strings.Trim(strings.Split(accept, ";")[0], " ")
for _, check := range valid {
if check == value {
return true
}
}
}
return false
}
func (s *Server) getResponseType(req *http.Request) responseFormat {
accepts := strings.Split(req.Header.Get("Accept"), ",")
if hasType(accepts, []string{"text/html", "application/xhtml+xml"}) {
return htmlResponse
}
if hasType(accepts, []string{"application/json", "text/json", "text/javascript"}) {
return jsonResponse
}
if hasType(accepts, []string{"text/plain"}) {
return textResponse
}
return unknownResponse
} }
func (s *Server) sendResponse(resp http.ResponseWriter, ip *IP) { func (s *Server) sendResponse(resp http.ResponseWriter, ip *IP, responseType responseFormat) {
io.WriteString(resp, ip.String()+"\n")
s.logger.Printf("Request from %s %s\n", ip.version, ip) s.logger.Printf("Request from %s %s\n", ip.version, ip)
var body []byte
var contentType = "text/plain"
var err error
switch responseType {
case jsonResponse:
jsonBody, marshalErr := ip.MarshalJSON()
if marshalErr != nil {
err = errors.Wrap(marshalErr, "could not marshal json")
}
contentType = "application/json"
body = jsonBody
case htmlResponse:
buffer := bytes.NewBuffer([]byte{})
exeErr := s.tmpl.Execute(buffer, map[string]string{"IP": ip.String()})
if exeErr != nil {
err = errors.Wrap(exeErr, "could not get html")
}
contentType = "text/html"
body = buffer.Bytes()
default:
body = []byte(ip.String())
}
if err != nil {
const errorMessage = "could not send response"
s.handleError(resp, http.StatusInternalServerError, errors.Wrap(err, errorMessage), errorMessage)
return
}
resp.Header().Set("Content-Type", contentType)
resp.Write(body)
} }
func (s *Server) handleError(resp http.ResponseWriter, status int, err error, message string) { func (s *Server) handleError(resp http.ResponseWriter, status int, err error, message string) {

@ -2,6 +2,7 @@ package main
import ( import (
"errors" "errors"
"html/template"
"net/http" "net/http"
"os" "os"
@ -18,7 +19,7 @@ const (
func main() { func main() {
app := cli.NewApp() app := cli.NewApp()
app.Name = appName app.Name = appName
app.Version = "0.3.0" app.Version = "0.4.0"
app.Usage = "Webapp that gives the IP address for incoming requests" app.Usage = "Webapp that gives the IP address for incoming requests"
app.Action = action app.Action = action
@ -67,6 +68,7 @@ func getHandler(headers []string) (http.Handler, *Server) {
service := &Server{ service := &Server{
headerNames: headers, headerNames: headers,
logger: NewLogger(), logger: NewLogger(),
tmpl: template.Must(template.New("html").Parse(htmlTemplate)),
} }
recover := negroni.NewRecovery() recover := negroni.NewRecovery()
@ -74,6 +76,18 @@ func getHandler(headers []string) (http.Handler, *Server) {
router := pat.New() router := pat.New()
router.Get("/json", func(resp http.ResponseWriter, req *http.Request) {
service.handleHTTP(resp, req, jsonResponse)
})
router.Get("/text", func(resp http.ResponseWriter, req *http.Request) {
service.handleHTTP(resp, req, textResponse)
})
router.Get("/html", func(resp http.ResponseWriter, req *http.Request) {
service.handleHTTP(resp, req, htmlResponse)
})
router.Get("/", func(resp http.ResponseWriter, req *http.Request) { router.Get("/", func(resp http.ResponseWriter, req *http.Request) {
if req.URL.Path == "/" { if req.URL.Path == "/" {
service.ServeHTTP(resp, req) service.ServeHTTP(resp, req)
@ -90,3 +104,15 @@ func getHandler(headers []string) (http.Handler, *Server) {
return n, service return n, service
} }
const htmlTemplate = `<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>What is My IP?</title>
<style> .ip { text-align: center; } </style>
</head>
<body>
<div class="ip">{{.IP}}</div>
</body>
</html>`

Loading…
Cancel
Save