// DeliverHooks checks and delivers undelivered hooks. // FIXME: maybe can use goroutine to shoot a number of them at same time? func DeliverHooks() { if isShooting { return } isShooting = true defer func() { isShooting = false }() tasks := make([]*HookTask, 0, 10) timeout := time.Duration(setting.Webhook.DeliverTimeout) * time.Second x.Where("is_delivered=?", false).Iterate(new(HookTask), func(idx int, bean interface{}) error { t := bean.(*HookTask) req := httplib.Post(t.Url).SetTimeout(timeout, timeout). Header("X-Gogs-Delivery", t.Uuid). Header("X-Gogs-Event", string(t.EventType)). SetTLSClientConfig(&tls.Config{InsecureSkipVerify: setting.Webhook.SkipTLSVerify}) switch t.ContentType { case JSON: req = req.Header("Content-Type", "application/json").Body(t.PayloadContent) case FORM: req.Param("payload", t.PayloadContent) } t.IsDelivered = true // FIXME: record response. switch t.Type { case GOGS: { if _, err := req.Response(); err != nil { log.Error(5, "Delivery: %v", err) } else { t.IsSucceed = true } } case SLACK: { if res, err := req.Response(); err != nil { log.Error(5, "Delivery: %v", err) } else { defer res.Body.Close() contents, err := ioutil.ReadAll(res.Body) if err != nil { log.Error(5, "%s", err) } else { if string(contents) != "ok" { log.Error(5, "slack failed with: %s", string(contents)) } else { t.IsSucceed = true } } } } } tasks = append(tasks, t) if t.IsSucceed { log.Trace("Hook delivered(%s): %s", t.Uuid, t.PayloadContent) } return nil }) // Update hook task status. for _, t := range tasks { if err := UpdateHookTask(t); err != nil { log.Error(4, "UpdateHookTask(%d): %v", t.Id, err) } } }
func (t *HookTask) deliver() { t.IsDelivered = true timeout := time.Duration(setting.Webhook.DeliverTimeout) * time.Second req := httplib.Post(t.URL).SetTimeout(timeout, timeout). Header("X-Gogs-Delivery", t.UUID). Header("X-Gogs-Event", string(t.EventType)). SetTLSClientConfig(&tls.Config{InsecureSkipVerify: setting.Webhook.SkipTLSVerify}) switch t.ContentType { case JSON: req = req.Header("Content-Type", "application/json").Body(t.PayloadContent) case FORM: req.Param("payload", t.PayloadContent) } // Record delivery information. t.RequestInfo = &HookRequest{ Headers: map[string]string{}, } for k, vals := range req.Headers() { t.RequestInfo.Headers[k] = strings.Join(vals, ",") } t.ResponseInfo = &HookResponse{ Headers: map[string]string{}, } defer func() { t.Delivered = time.Now().UnixNano() if t.IsSucceed { log.Trace("Hook delivered: %s", t.UUID) } else { log.Trace("Hook delivery failed: %s", t.UUID) } // Update webhook last delivery status. w, err := GetWebhookByRepoID(t.RepoID, t.HookID) if err != nil { log.Error(5, "GetWebhookByID: %v", err) return } if t.IsSucceed { w.LastStatus = HOOK_STATUS_SUCCEED } else { w.LastStatus = HOOK_STATUS_FAILED } if err = UpdateWebhook(w); err != nil { log.Error(5, "UpdateWebhook: %v", err) return } }() resp, err := req.Response() if err != nil { t.ResponseInfo.Body = fmt.Sprintf("Delivery: %v", err) return } defer resp.Body.Close() // Status code is 20x can be seen as succeed. t.IsSucceed = resp.StatusCode/100 == 2 t.ResponseInfo.Status = resp.StatusCode for k, vals := range resp.Header { t.ResponseInfo.Headers[k] = strings.Join(vals, ",") } p, err := ioutil.ReadAll(resp.Body) if err != nil { t.ResponseInfo.Body = fmt.Sprintf("read body: %s", err) return } t.ResponseInfo.Body = string(p) }