diff --git a/.travis.yml b/.travis.yml
index 5143ab5..ffcf684 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,6 +1,6 @@
language: go
go:
- - 1.10.2
+ - 1.10.3
- tip
matrix:
allow_failures:
@@ -22,6 +22,7 @@ before_install:
- ln -s $GOPATH/src/github.com/$TRAVIS_REPO_SLUG $GOPATH/src/gopkg.in/webhooks.v2
- ln -s $GOPATH/src/github.com/$TRAVIS_REPO_SLUG $GOPATH/src/gopkg.in/webhooks.v3
- ln -s $GOPATH/src/github.com/$TRAVIS_REPO_SLUG $GOPATH/src/gopkg.in/webhooks.v4
+ - ln -s $GOPATH/src/github.com/$TRAVIS_REPO_SLUG $GOPATH/src/gopkg.in/webhooks.v5
before_script:
- go vet ./...
@@ -34,6 +35,6 @@ script:
- go test -race
after_success: |
- [ $TRAVIS_GO_VERSION = 1.10.2 ] &&
+ [ $TRAVIS_GO_VERSION = 1.10.3 ] &&
overalls -project="github.com/go-playground/webhooks" -covermode=count -ignore=.git,examples -debug &&
goveralls -coverprofile=overalls.coverprofile -service travis-ci -repotoken $COVERALLS_TOKEN
diff --git a/README.md b/README.md
index ea4d564..870da95 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,8 @@
Library webhooks
================
-

-[](https://travis-ci.org/go-playground/webhooks)
-[](https://coveralls.io/github/go-playground/webhooks?branch=v3)
+

+[](https://travis-ci.org/go-playground/webhooks)
+[](https://coveralls.io/github/go-playground/webhooks?branch=v5)
[](https://goreportcard.com/report/go-playground/webhooks)
[](https://godoc.org/gopkg.in/go-playground/webhooks.v5)

diff --git a/bitbucket/bitbucket.go b/bitbucket/bitbucket.go
index 4b430d7..cd4ff76 100644
--- a/bitbucket/bitbucket.go
+++ b/bitbucket/bitbucket.go
@@ -2,23 +2,27 @@ package bitbucket
import (
"encoding/json"
+ "errors"
"fmt"
+ "io"
"io/ioutil"
"net/http"
+)
- "gopkg.in/go-playground/webhooks.v5"
+// parse errros
+var (
+ ErrEventNotSpecifiedToParse = errors.New("No Event specified to parse")
+ ErrInvalidHTTPMethod = errors.New("Invalid HTTP Method")
+ ErrMissingHookUUIDHeader = errors.New("Missing X-Hook-UUID Header")
+ ErrMissingEventKeyHeader = errors.New("Missing X-Event-Key Header")
+ ErrEventNotFound = errors.New("Event not defined to be parsed")
+ ErrParsingPayload = errors.New("Error parsing payload")
+ ErrUUIDVerificationFailed = errors.New("UUID verification failed")
)
// Webhook instance contains all methods needed to process events
type Webhook struct {
- provider webhooks.Provider
- uuid string
- eventFuncs map[Event]webhooks.ProcessPayloadFunc
-}
-
-// Config defines the configuration to create a new Bitbucket Webhook instance
-type Config struct {
- UUID string
+ uuid string
}
// Event defines a Bitbucket hook event type
@@ -46,154 +50,154 @@ const (
PullRequestCommentDeletedEvent Event = "pullrequest:comment_deleted"
)
+// Option is a configuration option for the webhook
+type Option func(*Webhook) error
+
+// Options is a namespace var for configuration options
+var Options = WebhookOptions{}
+
+// WebhookOptions is a namespace for configuration option methods
+type WebhookOptions struct{}
+
+// UUID registers the BitBucket secret
+func (WebhookOptions) UUID(uuid string) Option {
+ return func(hook *Webhook) error {
+ hook.uuid = uuid
+ return nil
+ }
+}
+
// New creates and returns a WebHook instance denoted by the Provider type
-func New(config *Config) *Webhook {
- return &Webhook{
- provider: webhooks.Bitbucket,
- uuid: config.UUID,
- eventFuncs: map[Event]webhooks.ProcessPayloadFunc{},
+func New(options ...Option) (*Webhook, error) {
+ hook := new(Webhook)
+ for _, opt := range options {
+ if err := opt(hook); err != nil {
+ return nil, errors.New("Error applying Option")
+ }
}
+ return hook, nil
}
-// Provider returns the current hooks provider ID
-func (hook Webhook) Provider() webhooks.Provider {
- return hook.provider
-}
+// Parse verifies and parses the events specified and returns the payload object or an error
+func (hook Webhook) Parse(r *http.Request, events ...Event) (interface{}, error) {
+ defer func() {
+ _, _ = io.Copy(ioutil.Discard, r.Body)
+ _ = r.Body.Close()
+ }()
-// RegisterEvents registers the function to call when the specified event(s) are encountered
-func (hook Webhook) RegisterEvents(fn webhooks.ProcessPayloadFunc, events ...Event) {
-
- for _, event := range events {
- hook.eventFuncs[event] = fn
+ if len(events) == 0 {
+ return nil, ErrEventNotSpecifiedToParse
+ }
+ if r.Method != http.MethodPost {
+ return nil, ErrInvalidHTTPMethod
}
-}
-
-// ParsePayload parses and verifies the payload and fires off the mapped function, if it exists.
-func (hook Webhook) ParsePayload(w http.ResponseWriter, r *http.Request) {
- webhooks.DefaultLog.Info("Parsing Payload...")
uuid := r.Header.Get("X-Hook-UUID")
if uuid == "" {
- webhooks.DefaultLog.Error("Missing X-Hook-UUID Header")
- http.Error(w, "400 Bad Request - Missing X-Hook-UUID Header", http.StatusBadRequest)
- return
+ return nil, ErrMissingHookUUIDHeader
}
- webhooks.DefaultLog.Debug(fmt.Sprintf("X-Hook-UUID:%s", uuid))
-
- if len(hook.uuid) > 0 {
- if uuid != hook.uuid {
- webhooks.DefaultLog.Error(fmt.Sprintf("X-Hook-UUID %s does not match configured uuid of %s", uuid, hook.uuid))
- http.Error(w, "403 Forbidden - X-Hook-UUID does not match", http.StatusForbidden)
- return
- }
- } else {
- webhooks.DefaultLog.Debug("hook uuid not defined - recommend setting for improved security")
+ if len(hook.uuid) > 0 && uuid != hook.uuid {
+ return nil, ErrUUIDVerificationFailed
}
event := r.Header.Get("X-Event-Key")
if event == "" {
- webhooks.DefaultLog.Error("Missing X-Event-Key Header")
- http.Error(w, "400 Bad Request - Missing X-Event-Key Header", http.StatusBadRequest)
- return
+ return nil, ErrMissingEventKeyHeader
}
- webhooks.DefaultLog.Debug(fmt.Sprintf("X-Event-Key:%s", event))
bitbucketEvent := Event(event)
- fn, ok := hook.eventFuncs[bitbucketEvent]
- // if no event registered
- if !ok {
- webhooks.DefaultLog.Info(fmt.Sprintf("Webhook Event %s not registered, it is recommended to setup only events in bitbucket that will be registered in the webhook to avoid unnecessary traffic and reduce potential attack vectors.", event))
- return
+ var found bool
+ for _, evt := range events {
+ if evt == bitbucketEvent {
+ found = true
+ break
+ }
+ }
+ // event not defined to be parsed
+ if !found {
+ return nil, ErrEventNotFound
}
payload, err := ioutil.ReadAll(r.Body)
if err != nil || len(payload) == 0 {
- webhooks.DefaultLog.Error("Issue reading Payload")
- http.Error(w, "Issue reading Payload", http.StatusInternalServerError)
- return
+ return nil, ErrParsingPayload
}
- webhooks.DefaultLog.Debug(fmt.Sprintf("Payload:%s", string(payload)))
- hd := webhooks.Header(r.Header)
switch bitbucketEvent {
case RepoPushEvent:
var pl RepoPushPayload
- json.Unmarshal([]byte(payload), &pl)
- hook.runProcessPayloadFunc(fn, pl, hd)
+ err = json.Unmarshal([]byte(payload), &pl)
+ return pl, err
case RepoForkEvent:
var pl RepoForkPayload
- json.Unmarshal([]byte(payload), &pl)
- hook.runProcessPayloadFunc(fn, pl, hd)
+ err = json.Unmarshal([]byte(payload), &pl)
+ return pl, err
case RepoUpdatedEvent:
var pl RepoUpdatedPayload
- json.Unmarshal([]byte(payload), &pl)
- hook.runProcessPayloadFunc(fn, pl, hd)
+ err = json.Unmarshal([]byte(payload), &pl)
+ return pl, err
case RepoCommitCommentCreatedEvent:
var pl RepoCommitCommentCreatedPayload
- json.Unmarshal([]byte(payload), &pl)
- hook.runProcessPayloadFunc(fn, pl, hd)
+ err = json.Unmarshal([]byte(payload), &pl)
+ return pl, err
case RepoCommitStatusCreatedEvent:
var pl RepoCommitStatusCreatedPayload
- json.Unmarshal([]byte(payload), &pl)
- hook.runProcessPayloadFunc(fn, pl, hd)
+ err = json.Unmarshal([]byte(payload), &pl)
+ return pl, err
case RepoCommitStatusUpdatedEvent:
var pl RepoCommitStatusUpdatedPayload
- json.Unmarshal([]byte(payload), &pl)
- hook.runProcessPayloadFunc(fn, pl, hd)
+ err = json.Unmarshal([]byte(payload), &pl)
+ return pl, err
case IssueCreatedEvent:
var pl IssueCreatedPayload
- json.Unmarshal([]byte(payload), &pl)
- hook.runProcessPayloadFunc(fn, pl, hd)
+ err = json.Unmarshal([]byte(payload), &pl)
+ return pl, err
case IssueUpdatedEvent:
var pl IssueUpdatedPayload
- json.Unmarshal([]byte(payload), &pl)
- hook.runProcessPayloadFunc(fn, pl, hd)
+ err = json.Unmarshal([]byte(payload), &pl)
+ return pl, err
case IssueCommentCreatedEvent:
var pl IssueCommentCreatedPayload
- json.Unmarshal([]byte(payload), &pl)
- hook.runProcessPayloadFunc(fn, pl, hd)
+ err = json.Unmarshal([]byte(payload), &pl)
+ return pl, err
case PullRequestCreatedEvent:
var pl PullRequestCreatedPayload
- json.Unmarshal([]byte(payload), &pl)
- hook.runProcessPayloadFunc(fn, pl, hd)
+ err = json.Unmarshal([]byte(payload), &pl)
+ return pl, err
case PullRequestUpdatedEvent:
var pl PullRequestUpdatedPayload
- json.Unmarshal([]byte(payload), &pl)
- hook.runProcessPayloadFunc(fn, pl, hd)
+ err = json.Unmarshal([]byte(payload), &pl)
+ return pl, err
case PullRequestApprovedEvent:
var pl PullRequestApprovedPayload
- json.Unmarshal([]byte(payload), &pl)
- hook.runProcessPayloadFunc(fn, pl, hd)
+ err = json.Unmarshal([]byte(payload), &pl)
+ return pl, err
case PullRequestUnapprovedEvent:
var pl PullRequestUnapprovedPayload
- json.Unmarshal([]byte(payload), &pl)
- hook.runProcessPayloadFunc(fn, pl, hd)
+ err = json.Unmarshal([]byte(payload), &pl)
+ return pl, err
case PullRequestMergedEvent:
var pl PullRequestMergedPayload
- json.Unmarshal([]byte(payload), &pl)
- hook.runProcessPayloadFunc(fn, pl, hd)
+ err = json.Unmarshal([]byte(payload), &pl)
+ return pl, err
case PullRequestDeclinedEvent:
var pl PullRequestDeclinedPayload
- json.Unmarshal([]byte(payload), &pl)
- hook.runProcessPayloadFunc(fn, pl, hd)
+ err = json.Unmarshal([]byte(payload), &pl)
+ return pl, err
case PullRequestCommentCreatedEvent:
var pl PullRequestCommentCreatedPayload
- json.Unmarshal([]byte(payload), &pl)
- hook.runProcessPayloadFunc(fn, pl, hd)
+ err = json.Unmarshal([]byte(payload), &pl)
+ return pl, err
case PullRequestCommentUpdatedEvent:
var pl PullRequestCommentUpdatedPayload
- json.Unmarshal([]byte(payload), &pl)
- hook.runProcessPayloadFunc(fn, pl, hd)
+ err = json.Unmarshal([]byte(payload), &pl)
+ return pl, err
case PullRequestCommentDeletedEvent:
var pl PullRequestCommentDeletedPayload
- json.Unmarshal([]byte(payload), &pl)
- hook.runProcessPayloadFunc(fn, pl, hd)
+ err = json.Unmarshal([]byte(payload), &pl)
+ return pl, err
+ default:
+ return nil, fmt.Errorf("unknown event %s", bitbucketEvent)
}
}
-
-func (hook Webhook) runProcessPayloadFunc(fn webhooks.ProcessPayloadFunc, results interface{}, header webhooks.Header) {
- go func(fn webhooks.ProcessPayloadFunc, results interface{}, header webhooks.Header) {
- fn(results, header)
- }(fn, results, header)
-}
diff --git a/github/github.go b/github/github.go
index 800d2e3..e9733d9 100644
--- a/github/github.go
+++ b/github/github.go
@@ -17,9 +17,9 @@ var (
ErrEventNotSpecifiedToParse = errors.New("No Event specified to parse")
ErrInvalidHTTPMethod = errors.New("Invalid HTTP Method")
ErrMissingGithubEventHeader = errors.New("Missing X-GitHub-Event Header")
- ErrMissingHubSignatureHeader = errors.New("Missing X-Hub-Signature")
+ ErrMissingHubSignatureHeader = errors.New("Missing X-Hub-Signature Header")
ErrEventNotFound = errors.New("Event not defined to be parsed")
- ErrParsingPayload = errors.New("Error Reading Payload")
+ ErrParsingPayload = errors.New("Error parsing payload")
ErrHMACVerificationFailed = errors.New("HMAC verification failed")
)
@@ -123,7 +123,7 @@ func (hook Webhook) Parse(r *http.Request, events ...Event) (interface{}, error)
}
event := r.Header.Get("X-GitHub-Event")
- if len(event) == 0 {
+ if event == "" {
return nil, ErrMissingGithubEventHeader
}
gitHubEvent := Event(event)
diff --git a/gitlab/gitlab.go b/gitlab/gitlab.go
index 3784e1f..7fa4729 100644
--- a/gitlab/gitlab.go
+++ b/gitlab/gitlab.go
@@ -2,28 +2,13 @@ package gitlab
import (
"encoding/json"
+ "errors"
"fmt"
+ "io"
"io/ioutil"
"net/http"
-
- "gopkg.in/go-playground/webhooks.v5"
)
-// Webhook instance contains all methods needed to process events
-type Webhook struct {
- provider webhooks.Provider
- secret string
- eventFuncs map[Event]webhooks.ProcessPayloadFunc
-}
-
-// Config defines the configuration to create a new GitHub Webhook instance
-type Config struct {
- Secret string
-}
-
-// Event defines a GitHub hook event type
-type Event string
-
// GitLab hook types
const (
PushEvents Event = "Push Hook"
@@ -37,121 +22,145 @@ const (
BuildEvents Event = "Build Hook"
)
+// Option is a configuration option for the webhook
+type Option func(*Webhook) error
+
+// Options is a namespace var for configuration options
+var Options = WebhookOptions{}
+
+// WebhookOptions is a namespace for configuration option methods
+type WebhookOptions struct{}
+
+// Secret registers the GitLab secret
+func (WebhookOptions) Secret(secret string) Option {
+ return func(hook *Webhook) error {
+ hook.secret = secret
+ return nil
+ }
+}
+
+// Webhook instance contains all methods needed to process events
+type Webhook struct {
+ secret string
+}
+
+// Event defines a GitHub hook event type
+type Event string
+
// New creates and returns a WebHook instance denoted by the Provider type
-func New(config *Config) *Webhook {
- return &Webhook{
- provider: webhooks.GitLab,
- secret: config.Secret,
- eventFuncs: map[Event]webhooks.ProcessPayloadFunc{},
+func New(options ...Option) (*Webhook, error) {
+ hook := new(Webhook)
+ for _, opt := range options {
+ if err := opt(hook); err != nil {
+ return nil, errors.New("Error applying Option")
+ }
}
+ return hook, nil
}
-// Provider returns the current hooks provider ID
-func (hook Webhook) Provider() webhooks.Provider {
- return hook.provider
-}
+// parse errros
+var (
+ ErrEventNotSpecifiedToParse = errors.New("No Event specified to parse")
+ ErrInvalidHTTPMethod = errors.New("Invalid HTTP Method")
+ ErrMissingGitLabEventHeader = errors.New("Missing X-Gitlab-Event Header")
+ ErrMissingGitLabTokenHeader = errors.New("Missing X-Gitlab-Token Header")
+ ErrEventNotFound = errors.New("Event not defined to be parsed")
+ ErrParsingPayload = errors.New("Error parsing payload")
+ // ErrHMACVerificationFailed = errors.New("HMAC verification failed")
+)
-// RegisterEvents registers the function to call when the specified event(s) are encountered
-func (hook Webhook) RegisterEvents(fn webhooks.ProcessPayloadFunc, events ...Event) {
+// Parse verifies and parses the events specified and returns the payload object or an error
+func (hook Webhook) Parse(r *http.Request, events ...Event) (interface{}, error) {
+ defer func() {
+ _, _ = io.Copy(ioutil.Discard, r.Body)
+ _ = r.Body.Close()
+ }()
- for _, event := range events {
- hook.eventFuncs[event] = fn
+ if len(events) == 0 {
+ return nil, ErrEventNotSpecifiedToParse
+ }
+ if r.Method != http.MethodPost {
+ return nil, ErrInvalidHTTPMethod
}
-}
-
-// ParsePayload parses and verifies the payload and fires off the mapped function, if it exists.
-func (hook Webhook) ParsePayload(w http.ResponseWriter, r *http.Request) {
- webhooks.DefaultLog.Info("Parsing Payload...")
event := r.Header.Get("X-Gitlab-Event")
if len(event) == 0 {
- webhooks.DefaultLog.Error("Missing X-Gitlab-Event Header")
- http.Error(w, "400 Bad Request - Missing X-Gitlab-Event Header", http.StatusBadRequest)
- return
+ return nil, ErrMissingGitLabEventHeader
}
- webhooks.DefaultLog.Debug(fmt.Sprintf("X-Gitlab-Event:%s", event))
gitLabEvent := Event(event)
- fn, ok := hook.eventFuncs[gitLabEvent]
- // if no event registered
- if !ok {
- webhooks.DefaultLog.Info(fmt.Sprintf("Webhook Event %s not registered, it is recommended to setup only events in gitlab that will be registered in the webhook to avoid unnecessary traffic and reduce potential attack vectors.", event))
- return
+ var found bool
+ for _, evt := range events {
+ if evt == gitLabEvent {
+ found = true
+ break
+ }
+ }
+ // event not defined to be parsed
+ if !found {
+ return nil, ErrEventNotFound
}
payload, err := ioutil.ReadAll(r.Body)
if err != nil || len(payload) == 0 {
- webhooks.DefaultLog.Error("Issue reading Payload")
- http.Error(w, "Error reading Payload", http.StatusInternalServerError)
- return
+ return nil, ErrParsingPayload
}
- webhooks.DefaultLog.Debug(fmt.Sprintf("Payload:%s", string(payload)))
// If we have a Secret set, we should check the MAC
if len(hook.secret) > 0 {
- webhooks.DefaultLog.Info("Checking secret")
signature := r.Header.Get("X-Gitlab-Token")
if signature != hook.secret {
- webhooks.DefaultLog.Error(fmt.Sprintf("Invalid X-Gitlab-Token of '%s'", signature))
- http.Error(w, "403 Forbidden - Token missmatch", http.StatusForbidden)
- return
+ return nil, ErrMissingGitLabTokenHeader
}
}
- // Make headers available to ProcessPayloadFunc as a webhooks type
- hd := webhooks.Header(r.Header)
-
switch gitLabEvent {
case PushEvents:
- var pe PushEventPayload
- json.Unmarshal([]byte(payload), &pe)
- hook.runProcessPayloadFunc(fn, pe, hd)
+ var pl PushEventPayload
+ err = json.Unmarshal([]byte(payload), &pl)
+ return pl, err
case TagEvents:
- var te TagEventPayload
- json.Unmarshal([]byte(payload), &te)
- hook.runProcessPayloadFunc(fn, te, hd)
+ var pl TagEventPayload
+ err = json.Unmarshal([]byte(payload), &pl)
+ return pl, err
case ConfidentialIssuesEvents:
- var cie ConfidentialIssueEventPayload
- json.Unmarshal([]byte(payload), &cie)
- hook.runProcessPayloadFunc(fn, cie, hd)
+ var pl ConfidentialIssueEventPayload
+ err = json.Unmarshal([]byte(payload), &pl)
+ return pl, err
case IssuesEvents:
- var ie IssueEventPayload
- json.Unmarshal([]byte(payload), &ie)
- hook.runProcessPayloadFunc(fn, ie, hd)
+ var pl IssueEventPayload
+ err = json.Unmarshal([]byte(payload), &pl)
+ return pl, err
case CommentEvents:
- var ce CommentEventPayload
- json.Unmarshal([]byte(payload), &ce)
- hook.runProcessPayloadFunc(fn, ce, hd)
+ var pl CommentEventPayload
+ err = json.Unmarshal([]byte(payload), &pl)
+ return pl, err
case MergeRequestEvents:
- var mre MergeRequestEventPayload
- json.Unmarshal([]byte(payload), &mre)
- hook.runProcessPayloadFunc(fn, mre, hd)
+ var pl MergeRequestEventPayload
+ err = json.Unmarshal([]byte(payload), &pl)
+ return pl, err
case WikiPageEvents:
- var wpe WikiPageEventPayload
- json.Unmarshal([]byte(payload), &wpe)
- hook.runProcessPayloadFunc(fn, wpe, hd)
+ var pl WikiPageEventPayload
+ err = json.Unmarshal([]byte(payload), &pl)
+ return pl, err
case PipelineEvents:
- var pe PipelineEventPayload
- json.Unmarshal([]byte(payload), &pe)
- hook.runProcessPayloadFunc(fn, pe, hd)
+ var pl PipelineEventPayload
+ err = json.Unmarshal([]byte(payload), &pl)
+ return pl, err
case BuildEvents:
- var be BuildEventPayload
- json.Unmarshal([]byte(payload), &be)
- hook.runProcessPayloadFunc(fn, be, hd)
+ var pl BuildEventPayload
+ err = json.Unmarshal([]byte(payload), &pl)
+ return pl, err
+ default:
+ return nil, fmt.Errorf("unknown event %s", gitLabEvent)
}
}
-
-func (hook Webhook) runProcessPayloadFunc(fn webhooks.ProcessPayloadFunc, results interface{}, header webhooks.Header) {
- go func(fn webhooks.ProcessPayloadFunc, results interface{}, header webhooks.Header) {
- fn(results, header)
- }(fn, results, header)
-}
diff --git a/gogs/gogs.go b/gogs/gogs.go
index 28b9314..1a2d9b4 100644
--- a/gogs/gogs.go
+++ b/gogs/gogs.go
@@ -2,7 +2,9 @@ package gogs
import (
"encoding/json"
+ "errors"
"fmt"
+ "io"
"io/ioutil"
"net/http"
@@ -11,19 +13,28 @@ import (
"encoding/hex"
client "github.com/gogits/go-gogs-client"
- "gopkg.in/go-playground/webhooks.v5"
)
+// Option is a configuration option for the webhook
+type Option func(*Webhook) error
+
+// Options is a namespace var for configuration options
+var Options = WebhookOptions{}
+
+// WebhookOptions is a namespace for configuration option methods
+type WebhookOptions struct{}
+
+// Secret registers the GitLab secret
+func (WebhookOptions) Secret(secret string) Option {
+ return func(hook *Webhook) error {
+ hook.secret = secret
+ return nil
+ }
+}
+
// Webhook instance contains all methods needed to process events
type Webhook struct {
- provider webhooks.Provider
- secret string
- eventFuncs map[Event]webhooks.ProcessPayloadFunc
-}
-
-// Config defines the configuration to create a new Gogs Webhook instance
-type Config struct {
- Secret string
+ secret string
}
// Event defines a Gogs hook event type
@@ -42,66 +53,71 @@ const (
)
// New creates and returns a WebHook instance denoted by the Provider type
-func New(config *Config) *Webhook {
- return &Webhook{
- provider: webhooks.Gogs,
- secret: config.Secret,
- eventFuncs: map[Event]webhooks.ProcessPayloadFunc{},
+func New(options ...Option) (*Webhook, error) {
+ hook := new(Webhook)
+ for _, opt := range options {
+ if err := opt(hook); err != nil {
+ return nil, errors.New("Error applying Option")
+ }
}
+ return hook, nil
}
-// Provider returns the current hooks provider ID
-func (hook Webhook) Provider() webhooks.Provider {
- return hook.provider
-}
+// parse errros
+var (
+ ErrEventNotSpecifiedToParse = errors.New("No Event specified to parse")
+ ErrInvalidHTTPMethod = errors.New("Invalid HTTP Method")
+ ErrMissingGogsEventHeader = errors.New("Missing X-Gogs-Event Header")
+ ErrMissingGogsSignatureHeader = errors.New("Missing X-Gogs-Signature Header")
+ ErrEventNotFound = errors.New("Event not defined to be parsed")
+ ErrParsingPayload = errors.New("Error parsing payload")
+ ErrHMACVerificationFailed = errors.New("HMAC verification failed")
+)
-// RegisterEvents registers the function to call when the specified event(s) are encountered
-func (hook Webhook) RegisterEvents(fn webhooks.ProcessPayloadFunc, events ...Event) {
+// Parse verifies and parses the events specified and returns the payload object or an error
+func (hook Webhook) Parse(r *http.Request, events ...Event) (interface{}, error) {
+ defer func() {
+ _, _ = io.Copy(ioutil.Discard, r.Body)
+ _ = r.Body.Close()
+ }()
- for _, event := range events {
- hook.eventFuncs[event] = fn
+ if len(events) == 0 {
+ return nil, ErrEventNotSpecifiedToParse
+ }
+ if r.Method != http.MethodPost {
+ return nil, ErrInvalidHTTPMethod
}
-}
-
-// ParsePayload parses and verifies the payload and fires off the mapped function, if it exists.
-func (hook Webhook) ParsePayload(w http.ResponseWriter, r *http.Request) {
- webhooks.DefaultLog.Info("Parsing Payload...")
event := r.Header.Get("X-Gogs-Event")
if len(event) == 0 {
- webhooks.DefaultLog.Error("Missing X-Gogs-Event Header")
- http.Error(w, "400 Bad Request - Missing X-Gogs-Event Header", http.StatusBadRequest)
- return
+ return nil, ErrMissingGogsEventHeader
}
- webhooks.DefaultLog.Debug(fmt.Sprintf("X-Gogs-Event:%s", event))
gogsEvent := Event(event)
- fn, ok := hook.eventFuncs[gogsEvent]
- // if no event registered
- if !ok {
- webhooks.DefaultLog.Info(fmt.Sprintf("Webhook Event %s not registered, it is recommended to setup only events in gogs that will be registered in the webhook to avoid unnecessary traffic and reduce potential attack vectors.", event))
- return
+ var found bool
+ for _, evt := range events {
+ if evt == gogsEvent {
+ found = true
+ break
+ }
+ }
+ // event not defined to be parsed
+ if !found {
+ return nil, ErrEventNotFound
}
payload, err := ioutil.ReadAll(r.Body)
if err != nil || len(payload) == 0 {
- webhooks.DefaultLog.Error("Issue reading Payload")
- http.Error(w, "Issue reading Payload", http.StatusInternalServerError)
- return
+ return nil, ErrParsingPayload
}
- webhooks.DefaultLog.Debug(fmt.Sprintf("Payload:%s", string(payload)))
// If we have a Secret set, we should check the MAC
if len(hook.secret) > 0 {
- webhooks.DefaultLog.Info("Checking secret")
signature := r.Header.Get("X-Gogs-Signature")
if len(signature) == 0 {
- webhooks.DefaultLog.Error("Missing X-Gogs-Signature required for HMAC verification")
- http.Error(w, "403 Forbidden - Missing X-Gogs-Signature required for HMAC verification", http.StatusForbidden)
- return
+ return nil, ErrMissingGogsSignatureHeader
}
- webhooks.DefaultLog.Debug(fmt.Sprintf("X-Gogs-Signature:%s", signature))
mac := hmac.New(sha256.New, []byte(hook.secret))
mac.Write(payload)
@@ -109,60 +125,52 @@ func (hook Webhook) ParsePayload(w http.ResponseWriter, r *http.Request) {
expectedMAC := hex.EncodeToString(mac.Sum(nil))
if !hmac.Equal([]byte(signature), []byte(expectedMAC)) {
- webhooks.DefaultLog.Debug(string(payload))
- http.Error(w, "403 Forbidden - HMAC verification failed", http.StatusForbidden)
- return
+ return nil, ErrHMACVerificationFailed
}
}
- // Make headers available to ProcessPayloadFunc as a webhooks type
- hd := webhooks.Header(r.Header)
-
switch gogsEvent {
case CreateEvent:
- var pe client.CreatePayload
- json.Unmarshal([]byte(payload), &pe)
- hook.runProcessPayloadFunc(fn, pe, hd)
+ var pl client.CreatePayload
+ err = json.Unmarshal([]byte(payload), &pl)
+ return pl, err
case ReleaseEvent:
- var re client.ReleasePayload
- json.Unmarshal([]byte(payload), &re)
- hook.runProcessPayloadFunc(fn, re, hd)
+ var pl client.ReleasePayload
+ err = json.Unmarshal([]byte(payload), &pl)
+ return pl, err
case PushEvent:
- var pe client.PushPayload
- json.Unmarshal([]byte(payload), &pe)
- hook.runProcessPayloadFunc(fn, pe, hd)
+ var pl client.PushPayload
+ err = json.Unmarshal([]byte(payload), &pl)
+ return pl, err
case DeleteEvent:
- var de client.DeletePayload
- json.Unmarshal([]byte(payload), &de)
- hook.runProcessPayloadFunc(fn, de, hd)
+ var pl client.DeletePayload
+ err = json.Unmarshal([]byte(payload), &pl)
+ return pl, err
case ForkEvent:
- var fe client.ForkPayload
- json.Unmarshal([]byte(payload), &fe)
- hook.runProcessPayloadFunc(fn, fe, hd)
+ var pl client.ForkPayload
+ err = json.Unmarshal([]byte(payload), &pl)
+ return pl, err
case IssuesEvent:
- var ie client.IssuesPayload
- json.Unmarshal([]byte(payload), &ie)
- hook.runProcessPayloadFunc(fn, ie, hd)
+ var pl client.IssuesPayload
+ err = json.Unmarshal([]byte(payload), &pl)
+ return pl, err
case IssueCommentEvent:
- var ice client.IssueCommentPayload
- json.Unmarshal([]byte(payload), &ice)
- hook.runProcessPayloadFunc(fn, ice, hd)
+ var pl client.IssueCommentPayload
+ err = json.Unmarshal([]byte(payload), &pl)
+ return pl, err
case PullRequestEvent:
- var pre client.PullRequestPayload
- json.Unmarshal([]byte(payload), &pre)
- hook.runProcessPayloadFunc(fn, pre, hd)
+ var pl client.PullRequestPayload
+ err = json.Unmarshal([]byte(payload), &pl)
+ return pl, err
+
+ default:
+ return nil, fmt.Errorf("unknown event %s", gogsEvent)
}
}
-
-func (hook Webhook) runProcessPayloadFunc(fn webhooks.ProcessPayloadFunc, results interface{}, header webhooks.Header) {
- go func(fn webhooks.ProcessPayloadFunc, results interface{}, header webhooks.Header) {
- fn(results, header)
- }(fn, results, header)
-}
diff --git a/webhooks.go b/webhooks.go
index 55c85aa..516cdee 100644
--- a/webhooks.go
+++ b/webhooks.go
@@ -1,11 +1,7 @@
package webhooks
-import (
- "net/http"
-)
-
// Header provides http.Header to minimize imports
-type Header http.Header
+// type Header http.Header
// // Provider defines the type of webhook
// type Provider int
@@ -46,7 +42,7 @@ type Header http.Header
// }
// ProcessPayloadFunc is a common function for payload return values
-type ProcessPayloadFunc func(payload interface{}, header Header) error
+// type ProcessPayloadFunc func(payload interface{}, header Header) error
// // Handler returns the webhook http.Handler for use in your own Mux implementation
// func Handler(hook Webhook) http.Handler {