Move some request parsing into hook package

Trying to simplify hookHandler.  No functional changes introduced.
This commit is contained in:
Cameron Moore 2020-11-17 15:00:58 -06:00
parent 4fadb1171f
commit 22c8a1670b
3 changed files with 125 additions and 69 deletions

View file

@ -17,7 +17,6 @@ import (
"log"
"math"
"net"
"net/http"
"net/textproto"
"os"
"reflect"
@ -48,30 +47,6 @@ const (
EnvNamespace string = "HOOK_"
)
// Request represents a webhook request.
type Request struct {
// The request ID set by the RequestID middleware.
ID string
// The Content-Type of the request.
ContentType string
// The raw request body.
Body []byte
// Headers is a map of the parsed headers.
Headers map[string]interface{}
// Query is a map of the parsed URL query values.
Query map[string]interface{}
// Payload is a map of the parsed payload.
Payload map[string]interface{}
// The underlying HTTP request.
RawRequest *http.Request
}
// ParameterNodeError describes an error walking a parameter node.
type ParameterNodeError struct {
key string

116
internal/hook/request.go Normal file
View file

@ -0,0 +1,116 @@
package hook
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"net/url"
"unicode"
"github.com/clbanning/mxj"
)
// Request represents a webhook request.
type Request struct {
// The request ID set by the RequestID middleware.
ID string
// The Content-Type of the request.
ContentType string
// The raw request body.
Body []byte
// Headers is a map of the parsed headers.
Headers map[string]interface{}
// Query is a map of the parsed URL query values.
Query map[string]interface{}
// Payload is a map of the parsed payload.
Payload map[string]interface{}
// The underlying HTTP request.
RawRequest *http.Request
}
func (r *Request) ParseJSONPayload() error {
decoder := json.NewDecoder(bytes.NewReader(r.Body))
decoder.UseNumber()
var firstChar byte
for i := 0; i < len(r.Body); i++ {
if unicode.IsSpace(rune(r.Body[i])) {
continue
}
firstChar = r.Body[i]
break
}
if firstChar == byte('[') {
var arrayPayload interface{}
err := decoder.Decode(&arrayPayload)
if err != nil {
return fmt.Errorf("error parsing JSON array payload %+v", err)
}
r.Payload = make(map[string]interface{}, 1)
r.Payload["root"] = arrayPayload
} else {
err := decoder.Decode(&r.Payload)
if err != nil {
return fmt.Errorf("error parsing JSON payload %+v", err)
}
}
return nil
}
func (r *Request) ParseHeaders(headers map[string][]string) {
r.Headers = make(map[string]interface{}, len(headers))
for k, v := range headers {
if len(v) > 0 {
r.Headers[k] = v[0]
}
}
}
func (r *Request) ParseQuery(query map[string][]string) {
r.Query = make(map[string]interface{}, len(query))
for k, v := range query {
if len(v) > 0 {
r.Query[k] = v[0]
}
}
}
func (r *Request) ParseFormPayload() error {
fd, err := url.ParseQuery(string(r.Body))
if err != nil {
return fmt.Errorf("error parsing form payload %+v", err)
}
r.Payload = make(map[string]interface{}, len(fd))
for k, v := range fd {
if len(v) > 0 {
r.Payload[k] = v[0]
}
}
return nil
}
func (r *Request) ParseXMLPayload() error {
var err error
r.Payload, err = mxj.NewMapXmlReader(bytes.NewReader(r.Body))
if err != nil {
return fmt.Errorf("error parsing XML payload: %+v", err)
}
return nil
}

View file

@ -1,7 +1,6 @@
package main
import (
"bytes"
"crypto/tls"
"encoding/json"
"flag"
@ -10,19 +9,16 @@ import (
"log"
"net"
"net/http"
"net/url"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
"unicode"
"github.com/adnanh/webhook/internal/hook"
"github.com/adnanh/webhook/internal/middleware"
"github.com/adnanh/webhook/internal/pidfile"
"github.com/clbanning/mxj"
chimiddleware "github.com/go-chi/chi/middleware"
"github.com/gorilla/mux"
fsnotify "gopkg.in/fsnotify.v1"
@ -373,57 +369,26 @@ func hookHandler(w http.ResponseWriter, r *http.Request) {
}
}
// parse headers
req.Headers = valuesToMap(r.Header)
// parse query variables
req.Query = valuesToMap(r.URL.Query())
// parse body
// var payload map[string]interface{}
req.ParseHeaders(r.Header)
req.ParseQuery(r.URL.Query())
switch {
case strings.Contains(req.ContentType, "json"):
decoder := json.NewDecoder(bytes.NewReader(req.Body))
decoder.UseNumber()
var firstChar byte
for i := 0; i < len(req.Body); i++ {
if unicode.IsSpace(rune(req.Body[i])) {
continue
}
firstChar = req.Body[i]
break
}
if firstChar == byte('[') {
var arrayPayload interface{}
err := decoder.Decode(&arrayPayload)
if err != nil {
log.Printf("[%s] error parsing JSON array payload %+v\n", req.ID, err)
}
req.Payload = make(map[string]interface{}, 1)
req.Payload["root"] = arrayPayload
} else {
err := decoder.Decode(&req.Payload)
if err != nil {
log.Printf("[%s] error parsing JSON payload %+v\n", req.ID, err)
}
err = req.ParseJSONPayload()
if err != nil {
log.Printf("[%s] %s", req.ID, err)
}
case strings.Contains(req.ContentType, "x-www-form-urlencoded"):
fd, err := url.ParseQuery(string(req.Body))
err = req.ParseFormPayload()
if err != nil {
log.Printf("[%s] error parsing form payload %+v\n", req.ID, err)
} else {
req.Payload = valuesToMap(fd)
log.Printf("[%s] %s", req.ID, err)
}
case strings.Contains(req.ContentType, "xml"):
req.Payload, err = mxj.NewMapXmlReader(bytes.NewReader(req.Body))
err = req.ParseXMLPayload()
if err != nil {
log.Printf("[%s] error parsing XML payload: %+v\n", req.ID, err)
log.Printf("[%s] %s", req.ID, err)
}
case isMultipart: