mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 21:28:11 +09:00 
			
		
		
		
	Fix #98, support web hook
This commit is contained in:
		| @@ -5,7 +5,7 @@ Gogs(Go Git Service) is a Self Hosted Git Service in the Go Programming Language | |||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| ##### Current version: 0.3.2 Alpha | ##### Current version: 0.3.3 Alpha | ||||||
|  |  | ||||||
| ### NOTICES | ### NOTICES | ||||||
|  |  | ||||||
| @@ -35,7 +35,7 @@ More importantly, Gogs only needs one binary to setup your own project hosting o | |||||||
| - SSH/HTTP(S) protocol support. | - SSH/HTTP(S) protocol support. | ||||||
| - Register/delete/rename account. | - Register/delete/rename account. | ||||||
| - Create/migrate/mirror/delete/watch/rename/transfer public/private repository. | - Create/migrate/mirror/delete/watch/rename/transfer public/private repository. | ||||||
| - Repository viewer/release/issue tracker. | - Repository viewer/release/issue tracker/webhooks. | ||||||
| - Add/remove repository collaborators. | - Add/remove repository collaborators. | ||||||
| - Gravatar and cache support. | - Gravatar and cache support. | ||||||
| - Mail service(register, issue). | - Mail service(register, issue). | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ Gogs(Go Git Service) 是一个由 Go 语言编写的自助 Git 托管服务。 | |||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| ##### 当前版本:0.3.2 Alpha | ##### 当前版本:0.3.3 Alpha | ||||||
|  |  | ||||||
| ## 开发目的 | ## 开发目的 | ||||||
|  |  | ||||||
| @@ -26,7 +26,7 @@ Gogs 完全使用 Go 语言来实现对 Git 数据的操作,实现 **零** 依 | |||||||
| - SSH/HTTP(S) 协议支持 | - SSH/HTTP(S) 协议支持 | ||||||
| - 注册/删除/重命名用户 | - 注册/删除/重命名用户 | ||||||
| - 创建/迁移/镜像/删除/关注/重命名/转移 公开/私有 仓库 | - 创建/迁移/镜像/删除/关注/重命名/转移 公开/私有 仓库 | ||||||
| - 仓库 浏览器/发布/缺陷追踪 | - 仓库 浏览器/发布/缺陷管理/Web 钩子 | ||||||
| - 添加/删除 仓库协作者 | - 添加/删除 仓库协作者 | ||||||
| - Gravatar 以及缓存支持 | - Gravatar 以及缓存支持 | ||||||
| - 邮件服务(注册、Issue) | - 邮件服务(注册、Issue) | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								gogs.go
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								gogs.go
									
									
									
									
									
								
							| @@ -20,7 +20,7 @@ import ( | |||||||
| // Test that go1.2 tag above is included in builds. main.go refers to this definition. | // Test that go1.2 tag above is included in builds. main.go refers to this definition. | ||||||
| const go12tag = true | const go12tag = true | ||||||
|  |  | ||||||
| const APP_VER = "0.3.2.0505 Alpha" | const APP_VER = "0.3.3.0506 Alpha" | ||||||
|  |  | ||||||
| func init() { | func init() { | ||||||
| 	base.AppVer = APP_VER | 	base.AppVer = APP_VER | ||||||
|   | |||||||
| @@ -6,6 +6,8 @@ package models | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| @@ -13,6 +15,7 @@ import ( | |||||||
| 	qlog "github.com/qiniu/log" | 	qlog "github.com/qiniu/log" | ||||||
|  |  | ||||||
| 	"github.com/gogits/gogs/modules/base" | 	"github.com/gogits/gogs/modules/base" | ||||||
|  | 	"github.com/gogits/gogs/modules/hooks" | ||||||
| 	"github.com/gogits/gogs/modules/log" | 	"github.com/gogits/gogs/modules/log" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -73,45 +76,80 @@ func (a Action) GetContent() string { | |||||||
|  |  | ||||||
| // CommitRepoAction adds new action for committing repository. | // CommitRepoAction adds new action for committing repository. | ||||||
| func CommitRepoAction(userId, repoUserId int64, userName, actEmail string, | func CommitRepoAction(userId, repoUserId int64, userName, actEmail string, | ||||||
| 	repoId int64, repoUserName, repoName string, refName string, commit *base.PushCommits) error { | 	repoId int64, repoUserName, repoName string, refFullName string, commit *base.PushCommits) error { | ||||||
| 	// log.Trace("action.CommitRepoAction(start): %d/%s", userId, repoName) | 	// log.Trace("action.CommitRepoAction(start): %d/%s", userId, repoName) | ||||||
|  |  | ||||||
| 	opType := OP_COMMIT_REPO | 	opType := OP_COMMIT_REPO | ||||||
| 	// Check it's tag push or branch. | 	// Check it's tag push or branch. | ||||||
| 	if strings.HasPrefix(refName, "refs/tags/") { | 	if strings.HasPrefix(refFullName, "refs/tags/") { | ||||||
| 		opType = OP_PUSH_TAG | 		opType = OP_PUSH_TAG | ||||||
| 		commit = &base.PushCommits{} | 		commit = &base.PushCommits{} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	refName = git.RefEndName(refName) | 	refName := git.RefEndName(refFullName) | ||||||
|  |  | ||||||
| 	bs, err := json.Marshal(commit) | 	bs, err := json.Marshal(commit) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		qlog.Error("action.CommitRepoAction(json): %d/%s", repoUserId, repoName) | 		return errors.New("action.CommitRepoAction(json): " + err.Error()) | ||||||
| 		return err |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Change repository bare status and update last updated time. | 	// Change repository bare status and update last updated time. | ||||||
| 	repo, err := GetRepositoryByName(repoUserId, repoName) | 	repo, err := GetRepositoryByName(repoUserId, repoName) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		qlog.Error("action.CommitRepoAction(GetRepositoryByName): %d/%s", repoUserId, repoName) | 		return errors.New("action.CommitRepoAction(GetRepositoryByName): " + err.Error()) | ||||||
| 		return err |  | ||||||
| 	} | 	} | ||||||
| 	repo.IsBare = false | 	repo.IsBare = false | ||||||
| 	if err = UpdateRepository(repo); err != nil { | 	if err = UpdateRepository(repo); err != nil { | ||||||
| 		qlog.Error("action.CommitRepoAction(UpdateRepository): %d/%s", repoUserId, repoName) | 		return errors.New("action.CommitRepoAction(UpdateRepository): " + err.Error()) | ||||||
| 		return err |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if err = NotifyWatchers(&Action{ActUserId: userId, ActUserName: userName, ActEmail: actEmail, | 	if err = NotifyWatchers(&Action{ActUserId: userId, ActUserName: userName, ActEmail: actEmail, | ||||||
| 		OpType: opType, Content: string(bs), RepoId: repoId, RepoUserName: repoUserName, | 		OpType: opType, Content: string(bs), RepoId: repoId, RepoUserName: repoUserName, | ||||||
| 		RepoName: repoName, RefName: refName, | 		RepoName: repoName, RefName: refName, | ||||||
| 		IsPrivate: repo.IsPrivate}); err != nil { | 		IsPrivate: repo.IsPrivate}); err != nil { | ||||||
| 		qlog.Error("action.CommitRepoAction(notify watchers): %d/%s", userId, repoName) | 		return errors.New("action.CommitRepoAction(NotifyWatchers): " + err.Error()) | ||||||
| 		return err |  | ||||||
|  | 	} | ||||||
|  | 	qlog.Info("action.CommitRepoAction(end): %d/%s", repoUserId, repoName) | ||||||
|  |  | ||||||
|  | 	// New push event hook. | ||||||
|  | 	ws, err := GetActiveWebhooksByRepoId(repoId) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return errors.New("action.CommitRepoAction(GetWebhooksByRepoId): " + err.Error()) | ||||||
|  | 	} else if len(ws) == 0 { | ||||||
|  | 		return nil | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	qlog.Info("action.CommitRepoAction(end): %d/%s", repoUserId, repoName) | 	commits := make([]*hooks.PayloadCommit, len(commit.Commits)) | ||||||
|  | 	for i, cmt := range commit.Commits { | ||||||
|  | 		commits[i] = &hooks.PayloadCommit{ | ||||||
|  | 			Id:      cmt.Sha1, | ||||||
|  | 			Message: cmt.Message, | ||||||
|  | 			Url:     fmt.Sprintf("%s%s/%s/commit/%s", base.AppUrl, repoUserName, repoName, cmt.Sha1), | ||||||
|  | 			Author: &hooks.PayloadAuthor{ | ||||||
|  | 				Name:  cmt.AuthorName, | ||||||
|  | 				Email: cmt.AuthorEmail, | ||||||
|  | 			}, | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	p := &hooks.Payload{ | ||||||
|  | 		Ref:     refFullName, | ||||||
|  | 		Commits: commits, | ||||||
|  | 		Pusher: &hooks.PayloadAuthor{ | ||||||
|  | 			Name:  userName, | ||||||
|  | 			Email: actEmail, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, w := range ws { | ||||||
|  | 		w.GetEvent() | ||||||
|  | 		if !w.HasPushEvent() { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		p.Secret = w.Secret | ||||||
|  | 		hooks.AddHookTask(&hooks.HookTask{hooks.HTT_WEBHOOK, w.Url, p, w.ContentType, w.IsSsl}) | ||||||
|  | 	} | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -96,6 +96,6 @@ func Update(refName, oldCommitId, newCommitId, userName, repoUserName, repoName | |||||||
| 	//commits = append(commits, []string{lastCommit.Id().String(), lastCommit.Message()}) | 	//commits = append(commits, []string{lastCommit.Id().String(), lastCommit.Message()}) | ||||||
| 	if err = CommitRepoAction(userId, ru.Id, userName, actEmail, | 	if err = CommitRepoAction(userId, ru.Id, userName, actEmail, | ||||||
| 		repos.Id, repoUserName, repoName, refName, &base.PushCommits{l.Len(), commits}); err != nil { | 		repos.Id, repoUserName, repoName, refName, &base.PushCommits{l.Len(), commits}); err != nil { | ||||||
| 		qlog.Fatalf("runUpdate.models.CommitRepoAction: %v", err) | 		qlog.Fatalf("runUpdate.models.CommitRepoAction: %s/%s:%v", repoUserName, repoName, err) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -28,7 +28,7 @@ type HookEvent struct { | |||||||
| type Webhook struct { | type Webhook struct { | ||||||
| 	Id          int64 | 	Id          int64 | ||||||
| 	RepoId      int64 | 	RepoId      int64 | ||||||
| 	Payload     string `xorm:"TEXT"` | 	Url         string `xorm:"TEXT"` | ||||||
| 	ContentType int | 	ContentType int | ||||||
| 	Secret      string `xorm:"TEXT"` | 	Secret      string `xorm:"TEXT"` | ||||||
| 	Events      string `xorm:"TEXT"` | 	Events      string `xorm:"TEXT"` | ||||||
| @@ -50,6 +50,13 @@ func (w *Webhook) SaveEvent() error { | |||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (w *Webhook) HasPushEvent() bool { | ||||||
|  | 	if w.PushOnly { | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  |  | ||||||
| // CreateWebhook creates new webhook. | // CreateWebhook creates new webhook. | ||||||
| func CreateWebhook(w *Webhook) error { | func CreateWebhook(w *Webhook) error { | ||||||
| 	_, err := orm.Insert(w) | 	_, err := orm.Insert(w) | ||||||
| @@ -74,6 +81,12 @@ func GetWebhookById(hookId int64) (*Webhook, error) { | |||||||
| 	return w, nil | 	return w, nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // GetActiveWebhooksByRepoId returns all active webhooks of repository. | ||||||
|  | func GetActiveWebhooksByRepoId(repoId int64) (ws []*Webhook, err error) { | ||||||
|  | 	err = orm.Find(&ws, &Webhook{RepoId: repoId, IsActive: true}) | ||||||
|  | 	return ws, err | ||||||
|  | } | ||||||
|  |  | ||||||
| // GetWebhooksByRepoId returns all webhooks of repository. | // GetWebhooksByRepoId returns all webhooks of repository. | ||||||
| func GetWebhooksByRepoId(repoId int64) (ws []*Webhook, err error) { | func GetWebhooksByRepoId(repoId int64) (ws []*Webhook, err error) { | ||||||
| 	err = orm.Find(&ws, &Webhook{RepoId: repoId}) | 	err = orm.Find(&ws, &Webhook{RepoId: repoId}) | ||||||
|   | |||||||
							
								
								
									
										83
									
								
								modules/hooks/hooks.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								modules/hooks/hooks.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | |||||||
|  | // Copyright 2014 The Gogs Authors. All rights reserved. | ||||||
|  | // Use of this source code is governed by a MIT-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package hooks | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/gogits/gogs/modules/httplib" | ||||||
|  | 	"github.com/gogits/gogs/modules/log" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Hook task types. | ||||||
|  | const ( | ||||||
|  | 	HTT_WEBHOOK = iota + 1 | ||||||
|  | 	HTT_SERVICE | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type PayloadAuthor struct { | ||||||
|  | 	Name  string `json:"name"` | ||||||
|  | 	Email string `json:"email"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type PayloadCommit struct { | ||||||
|  | 	Id      string         `json:"id"` | ||||||
|  | 	Message string         `json:"message"` | ||||||
|  | 	Url     string         `json:"url"` | ||||||
|  | 	Author  *PayloadAuthor `json:"author"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Payload represents payload information of hook. | ||||||
|  | type Payload struct { | ||||||
|  | 	Secret  string           `json:"secret"` | ||||||
|  | 	Ref     string           `json:"ref"` | ||||||
|  | 	Commits []*PayloadCommit `json:"commits"` | ||||||
|  | 	Pusher  *PayloadAuthor   `json:"pusher"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // HookTask represents hook task. | ||||||
|  | type HookTask struct { | ||||||
|  | 	Type int | ||||||
|  | 	Url  string | ||||||
|  | 	*Payload | ||||||
|  | 	ContentType int | ||||||
|  | 	IsSsl       bool | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	taskQueue = make(chan *HookTask, 1000) | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // AddHookTask adds new hook task to task queue. | ||||||
|  | func AddHookTask(t *HookTask) { | ||||||
|  | 	taskQueue <- t | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func init() { | ||||||
|  | 	go handleQueue() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func handleQueue() { | ||||||
|  | 	for { | ||||||
|  | 		select { | ||||||
|  | 		case t := <-taskQueue: | ||||||
|  | 			// Only support JSON now. | ||||||
|  | 			data, err := json.MarshalIndent(t.Payload, "", "\t") | ||||||
|  | 			if err != nil { | ||||||
|  | 				log.Error("hooks.handleQueue(json): %v", err) | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			_, err = httplib.Post(t.Url).SetTimeout(5*time.Second, 5*time.Second). | ||||||
|  | 				Body(data).Response() | ||||||
|  | 			if err != nil { | ||||||
|  | 				log.Error("hooks.handleQueue: Fail to deliver hook: %v", err) | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			log.Info("Hook delivered") | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										62
									
								
								modules/httplib/README.md
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										62
									
								
								modules/httplib/README.md
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,62 @@ | |||||||
|  | # httplib | ||||||
|  | httplib is an libs help you to curl remote url. | ||||||
|  |  | ||||||
|  | # How to use? | ||||||
|  |  | ||||||
|  | ## GET | ||||||
|  | you can use Get to crawl data. | ||||||
|  |  | ||||||
|  | 	import "httplib" | ||||||
|  | 	 | ||||||
|  | 	str, err := httplib.Get("http://beego.me/").String() | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 	fmt.Println(str) | ||||||
|  | 	 | ||||||
|  | ## POST | ||||||
|  | POST data to remote url | ||||||
|  |  | ||||||
|  | 	b:=httplib.Post("http://beego.me/") | ||||||
|  | 	b.Param("username","astaxie") | ||||||
|  | 	b.Param("password","123456") | ||||||
|  | 	str, err := b.String() | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 	fmt.Println(str) | ||||||
|  |  | ||||||
|  | ## set timeout | ||||||
|  | you can set timeout in request.default is 60 seconds. | ||||||
|  |  | ||||||
|  | set Get timeout: | ||||||
|  |  | ||||||
|  | 	httplib.Get("http://beego.me/").SetTimeout(100 * time.Second, 30 * time.Second) | ||||||
|  | 	 | ||||||
|  | set post timeout:	 | ||||||
|  | 	 | ||||||
|  | 	httplib.Post("http://beego.me/").SetTimeout(100 * time.Second, 30 * time.Second) | ||||||
|  |  | ||||||
|  | - first param is connectTimeout. | ||||||
|  | - second param is readWriteTimeout | ||||||
|  |  | ||||||
|  | ## debug | ||||||
|  | if you want to debug the request info, set the debug on | ||||||
|  |  | ||||||
|  | 	httplib.Get("http://beego.me/").Debug(true) | ||||||
|  | 	 | ||||||
|  | ## support HTTPS client | ||||||
|  | if request url is https. You can set the client support TSL: | ||||||
|  |  | ||||||
|  | 	httplib.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true}) | ||||||
|  | 	 | ||||||
|  | more info about the tls.Config please visit http://golang.org/pkg/crypto/tls/#Config	 | ||||||
|  | 		 | ||||||
|  | ## set cookie | ||||||
|  | some http request need setcookie. So set it like this: | ||||||
|  |  | ||||||
|  | 	cookie := &http.Cookie{} | ||||||
|  | 	cookie.Name = "username" | ||||||
|  | 	cookie.Value  = "astaxie" | ||||||
|  | 	httplib.Get("http://beego.me/").SetCookie(cookie) | ||||||
|  |  | ||||||
							
								
								
									
										330
									
								
								modules/httplib/httplib.go
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										330
									
								
								modules/httplib/httplib.go
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,330 @@ | |||||||
|  | // Copyright 2013 The Beego Authors. All rights reserved. | ||||||
|  | // Copyright 2014 The Gogs Authors. All rights reserved. | ||||||
|  | // Use of this source code is governed by a MIT-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package httplib | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"crypto/tls" | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"encoding/xml" | ||||||
|  | 	"io" | ||||||
|  | 	"io/ioutil" | ||||||
|  | 	"net" | ||||||
|  | 	"net/http" | ||||||
|  | 	"net/http/httputil" | ||||||
|  | 	"net/url" | ||||||
|  | 	"os" | ||||||
|  | 	"strings" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var defaultUserAgent = "gogsServer" | ||||||
|  |  | ||||||
|  | // Get returns *BeegoHttpRequest with GET method. | ||||||
|  | func Get(url string) *BeegoHttpRequest { | ||||||
|  | 	var req http.Request | ||||||
|  | 	req.Method = "GET" | ||||||
|  | 	req.Header = http.Header{} | ||||||
|  | 	req.Header.Set("User-Agent", defaultUserAgent) | ||||||
|  | 	return &BeegoHttpRequest{url, &req, map[string]string{}, false, 60 * time.Second, 60 * time.Second, nil, nil, nil} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Post returns *BeegoHttpRequest with POST method. | ||||||
|  | func Post(url string) *BeegoHttpRequest { | ||||||
|  | 	var req http.Request | ||||||
|  | 	req.Method = "POST" | ||||||
|  | 	req.Header = http.Header{} | ||||||
|  | 	req.Header.Set("User-Agent", defaultUserAgent) | ||||||
|  | 	return &BeegoHttpRequest{url, &req, map[string]string{}, false, 60 * time.Second, 60 * time.Second, nil, nil, nil} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Put returns *BeegoHttpRequest with PUT method. | ||||||
|  | func Put(url string) *BeegoHttpRequest { | ||||||
|  | 	var req http.Request | ||||||
|  | 	req.Method = "PUT" | ||||||
|  | 	req.Header = http.Header{} | ||||||
|  | 	req.Header.Set("User-Agent", defaultUserAgent) | ||||||
|  | 	return &BeegoHttpRequest{url, &req, map[string]string{}, false, 60 * time.Second, 60 * time.Second, nil, nil, nil} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Delete returns *BeegoHttpRequest DELETE GET method. | ||||||
|  | func Delete(url string) *BeegoHttpRequest { | ||||||
|  | 	var req http.Request | ||||||
|  | 	req.Method = "DELETE" | ||||||
|  | 	req.Header = http.Header{} | ||||||
|  | 	req.Header.Set("User-Agent", defaultUserAgent) | ||||||
|  | 	return &BeegoHttpRequest{url, &req, map[string]string{}, false, 60 * time.Second, 60 * time.Second, nil, nil, nil} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Head returns *BeegoHttpRequest with HEAD method. | ||||||
|  | func Head(url string) *BeegoHttpRequest { | ||||||
|  | 	var req http.Request | ||||||
|  | 	req.Method = "HEAD" | ||||||
|  | 	req.Header = http.Header{} | ||||||
|  | 	req.Header.Set("User-Agent", defaultUserAgent) | ||||||
|  | 	return &BeegoHttpRequest{url, &req, map[string]string{}, false, 60 * time.Second, 60 * time.Second, nil, nil, nil} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // BeegoHttpRequest provides more useful methods for requesting one url than http.Request. | ||||||
|  | type BeegoHttpRequest struct { | ||||||
|  | 	url              string | ||||||
|  | 	req              *http.Request | ||||||
|  | 	params           map[string]string | ||||||
|  | 	showdebug        bool | ||||||
|  | 	connectTimeout   time.Duration | ||||||
|  | 	readWriteTimeout time.Duration | ||||||
|  | 	tlsClientConfig  *tls.Config | ||||||
|  | 	proxy            func(*http.Request) (*url.URL, error) | ||||||
|  | 	transport        http.RoundTripper | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Debug sets show debug or not when executing request. | ||||||
|  | func (b *BeegoHttpRequest) Debug(isdebug bool) *BeegoHttpRequest { | ||||||
|  | 	b.showdebug = isdebug | ||||||
|  | 	return b | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SetTimeout sets connect time out and read-write time out for BeegoRequest. | ||||||
|  | func (b *BeegoHttpRequest) SetTimeout(connectTimeout, readWriteTimeout time.Duration) *BeegoHttpRequest { | ||||||
|  | 	b.connectTimeout = connectTimeout | ||||||
|  | 	b.readWriteTimeout = readWriteTimeout | ||||||
|  | 	return b | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SetTLSClientConfig sets tls connection configurations if visiting https url. | ||||||
|  | func (b *BeegoHttpRequest) SetTLSClientConfig(config *tls.Config) *BeegoHttpRequest { | ||||||
|  | 	b.tlsClientConfig = config | ||||||
|  | 	return b | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Header add header item string in request. | ||||||
|  | func (b *BeegoHttpRequest) Header(key, value string) *BeegoHttpRequest { | ||||||
|  | 	b.req.Header.Set(key, value) | ||||||
|  | 	return b | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SetCookie add cookie into request. | ||||||
|  | func (b *BeegoHttpRequest) SetCookie(cookie *http.Cookie) *BeegoHttpRequest { | ||||||
|  | 	b.req.Header.Add("Cookie", cookie.String()) | ||||||
|  | 	return b | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Set transport to | ||||||
|  | func (b *BeegoHttpRequest) SetTransport(transport http.RoundTripper) *BeegoHttpRequest { | ||||||
|  | 	b.transport = transport | ||||||
|  | 	return b | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Set http proxy | ||||||
|  | // example: | ||||||
|  | // | ||||||
|  | //	func(req *http.Request) (*url.URL, error) { | ||||||
|  | // 		u, _ := url.ParseRequestURI("http://127.0.0.1:8118") | ||||||
|  | // 		return u, nil | ||||||
|  | // 	} | ||||||
|  | func (b *BeegoHttpRequest) SetProxy(proxy func(*http.Request) (*url.URL, error)) *BeegoHttpRequest { | ||||||
|  | 	b.proxy = proxy | ||||||
|  | 	return b | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Param adds query param in to request. | ||||||
|  | // params build query string as ?key1=value1&key2=value2... | ||||||
|  | func (b *BeegoHttpRequest) Param(key, value string) *BeegoHttpRequest { | ||||||
|  | 	b.params[key] = value | ||||||
|  | 	return b | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Body adds request raw body. | ||||||
|  | // it supports string and []byte. | ||||||
|  | func (b *BeegoHttpRequest) Body(data interface{}) *BeegoHttpRequest { | ||||||
|  | 	switch t := data.(type) { | ||||||
|  | 	case string: | ||||||
|  | 		bf := bytes.NewBufferString(t) | ||||||
|  | 		b.req.Body = ioutil.NopCloser(bf) | ||||||
|  | 		b.req.ContentLength = int64(len(t)) | ||||||
|  | 	case []byte: | ||||||
|  | 		bf := bytes.NewBuffer(t) | ||||||
|  | 		b.req.Body = ioutil.NopCloser(bf) | ||||||
|  | 		b.req.ContentLength = int64(len(t)) | ||||||
|  | 	} | ||||||
|  | 	return b | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *BeegoHttpRequest) getResponse() (*http.Response, error) { | ||||||
|  | 	var paramBody string | ||||||
|  | 	if len(b.params) > 0 { | ||||||
|  | 		var buf bytes.Buffer | ||||||
|  | 		for k, v := range b.params { | ||||||
|  | 			buf.WriteString(url.QueryEscape(k)) | ||||||
|  | 			buf.WriteByte('=') | ||||||
|  | 			buf.WriteString(url.QueryEscape(v)) | ||||||
|  | 			buf.WriteByte('&') | ||||||
|  | 		} | ||||||
|  | 		paramBody = buf.String() | ||||||
|  | 		paramBody = paramBody[0 : len(paramBody)-1] | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if b.req.Method == "GET" && len(paramBody) > 0 { | ||||||
|  | 		if strings.Index(b.url, "?") != -1 { | ||||||
|  | 			b.url += "&" + paramBody | ||||||
|  | 		} else { | ||||||
|  | 			b.url = b.url + "?" + paramBody | ||||||
|  | 		} | ||||||
|  | 	} else if b.req.Method == "POST" && b.req.Body == nil && len(paramBody) > 0 { | ||||||
|  | 		b.Header("Content-Type", "application/x-www-form-urlencoded") | ||||||
|  | 		b.Body(paramBody) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	url, err := url.Parse(b.url) | ||||||
|  | 	if url.Scheme == "" { | ||||||
|  | 		b.url = "http://" + b.url | ||||||
|  | 		url, err = url.Parse(b.url) | ||||||
|  | 	} | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	b.req.URL = url | ||||||
|  | 	if b.showdebug { | ||||||
|  | 		dump, err := httputil.DumpRequest(b.req, true) | ||||||
|  | 		if err != nil { | ||||||
|  | 			println(err.Error()) | ||||||
|  | 		} | ||||||
|  | 		println(string(dump)) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	trans := b.transport | ||||||
|  |  | ||||||
|  | 	if trans == nil { | ||||||
|  | 		// create default transport | ||||||
|  | 		trans = &http.Transport{ | ||||||
|  | 			TLSClientConfig: b.tlsClientConfig, | ||||||
|  | 			Proxy:           b.proxy, | ||||||
|  | 			Dial:            TimeoutDialer(b.connectTimeout, b.readWriteTimeout), | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		// if b.transport is *http.Transport then set the settings. | ||||||
|  | 		if t, ok := trans.(*http.Transport); ok { | ||||||
|  | 			if t.TLSClientConfig == nil { | ||||||
|  | 				t.TLSClientConfig = b.tlsClientConfig | ||||||
|  | 			} | ||||||
|  | 			if t.Proxy == nil { | ||||||
|  | 				t.Proxy = b.proxy | ||||||
|  | 			} | ||||||
|  | 			if t.Dial == nil { | ||||||
|  | 				t.Dial = TimeoutDialer(b.connectTimeout, b.readWriteTimeout) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	client := &http.Client{ | ||||||
|  | 		Transport: trans, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	resp, err := client.Do(b.req) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return resp, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // String returns the body string in response. | ||||||
|  | // it calls Response inner. | ||||||
|  | func (b *BeegoHttpRequest) String() (string, error) { | ||||||
|  | 	data, err := b.Bytes() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return string(data), nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Bytes returns the body []byte in response. | ||||||
|  | // it calls Response inner. | ||||||
|  | func (b *BeegoHttpRequest) Bytes() ([]byte, error) { | ||||||
|  | 	resp, err := b.getResponse() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	if resp.Body == nil { | ||||||
|  | 		return nil, nil | ||||||
|  | 	} | ||||||
|  | 	defer resp.Body.Close() | ||||||
|  | 	data, err := ioutil.ReadAll(resp.Body) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return data, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ToFile saves the body data in response to one file. | ||||||
|  | // it calls Response inner. | ||||||
|  | func (b *BeegoHttpRequest) ToFile(filename string) error { | ||||||
|  | 	f, err := os.Create(filename) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	defer f.Close() | ||||||
|  |  | ||||||
|  | 	resp, err := b.getResponse() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	if resp.Body == nil { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	defer resp.Body.Close() | ||||||
|  | 	_, err = io.Copy(f, resp.Body) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ToJson returns the map that marshals from the body bytes as json in response . | ||||||
|  | // it calls Response inner. | ||||||
|  | func (b *BeegoHttpRequest) ToJson(v interface{}) error { | ||||||
|  | 	data, err := b.Bytes() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	err = json.Unmarshal(data, v) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ToXml returns the map that marshals from the body bytes as xml in response . | ||||||
|  | // it calls Response inner. | ||||||
|  | func (b *BeegoHttpRequest) ToXML(v interface{}) error { | ||||||
|  | 	data, err := b.Bytes() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	err = xml.Unmarshal(data, v) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Response executes request client gets response mannually. | ||||||
|  | func (b *BeegoHttpRequest) Response() (*http.Response, error) { | ||||||
|  | 	return b.getResponse() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // TimeoutDialer returns functions of connection dialer with timeout settings for http.Transport Dial field. | ||||||
|  | func TimeoutDialer(cTimeout time.Duration, rwTimeout time.Duration) func(net, addr string) (c net.Conn, err error) { | ||||||
|  | 	return func(netw, addr string) (net.Conn, error) { | ||||||
|  | 		conn, err := net.DialTimeout(netw, addr, cTimeout) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 		conn.SetDeadline(time.Now().Add(rwTimeout)) | ||||||
|  | 		return conn, nil | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										32
									
								
								modules/httplib/httplib_test.go
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										32
									
								
								modules/httplib/httplib_test.go
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,32 @@ | |||||||
|  | package httplib | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"io/ioutil" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestGetUrl(t *testing.T) { | ||||||
|  | 	resp, err := Get("http://beego.me/").Debug(true).Response() | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 	if resp.Body == nil { | ||||||
|  | 		t.Fatal("body is nil") | ||||||
|  | 	} | ||||||
|  | 	data, err := ioutil.ReadAll(resp.Body) | ||||||
|  | 	defer resp.Body.Close() | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 	if len(data) == 0 { | ||||||
|  | 		t.Fatal("data is no") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	str, err := Get("http://beego.me/").String() | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 	if len(str) == 0 { | ||||||
|  | 		t.Fatal("has no info") | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -247,7 +247,7 @@ func WebHooksAddPost(ctx *middleware.Context, form auth.NewWebhookForm) { | |||||||
|  |  | ||||||
| 	w := &models.Webhook{ | 	w := &models.Webhook{ | ||||||
| 		RepoId:      ctx.Repo.Repository.Id, | 		RepoId:      ctx.Repo.Repository.Id, | ||||||
| 		Payload:     form.Url, | 		Url:         form.Url, | ||||||
| 		ContentType: ct, | 		ContentType: ct, | ||||||
| 		Secret:      form.Secret, | 		Secret:      form.Secret, | ||||||
| 		HookEvent: &models.HookEvent{ | 		HookEvent: &models.HookEvent{ | ||||||
| @@ -315,7 +315,7 @@ func WebHooksEditPost(ctx *middleware.Context, params martini.Params, form auth. | |||||||
| 	w := &models.Webhook{ | 	w := &models.Webhook{ | ||||||
| 		Id:          hookId, | 		Id:          hookId, | ||||||
| 		RepoId:      ctx.Repo.Repository.Id, | 		RepoId:      ctx.Repo.Repository.Id, | ||||||
| 		Payload:     form.Url, | 		Url:         form.Url, | ||||||
| 		ContentType: ct, | 		ContentType: ct, | ||||||
| 		Secret:      form.Secret, | 		Secret:      form.Secret, | ||||||
| 		HookEvent: &models.HookEvent{ | 		HookEvent: &models.HookEvent{ | ||||||
|   | |||||||
| @@ -16,7 +16,7 @@ | |||||||
|                     {{range .Webhooks}} |                     {{range .Webhooks}} | ||||||
|                     <li> |                     <li> | ||||||
|                         {{if .IsActive}}<span class="pull-left status text-success"><i class="fa fa-check"></i></span>{{else}}<span class="pull-left status"><i class="fa fa-times"></i></span>{{end}} |                         {{if .IsActive}}<span class="pull-left status text-success"><i class="fa fa-check"></i></span>{{else}}<span class="pull-left status"><i class="fa fa-times"></i></span>{{end}} | ||||||
|                         <a class="link" href="{{$.RepoLink}}/settings/hooks/{{.Id}}">{{.Payload}}</a> |                         <a class="link" href="{{$.RepoLink}}/settings/hooks/{{.Id}}">{{.Url}}</a> | ||||||
|                         <a href="{{$.RepoLink}}/settings/hooks?remove={{.Id}}" class="remove-hook pull-right"><i class="fa fa-times"></i></a> |                         <a href="{{$.RepoLink}}/settings/hooks?remove={{.Id}}" class="remove-hook pull-right"><i class="fa fa-times"></i></a> | ||||||
|                         <a href="{{$.RepoLink}}/settings/hooks/{{.Id}}" class="edit-hook pull-right"><i class="fa fa-pencil"></i></a> |                         <a href="{{$.RepoLink}}/settings/hooks/{{.Id}}" class="edit-hook pull-right"><i class="fa fa-pencil"></i></a> | ||||||
|                     </li> |                     </li> | ||||||
|   | |||||||
| @@ -19,7 +19,7 @@ | |||||||
|                         <hr/> |                         <hr/> | ||||||
|                         <div class="form-group"> |                         <div class="form-group"> | ||||||
|                             <label for="payload-url">Payload URL</label> |                             <label for="payload-url">Payload URL</label> | ||||||
|                             <input id="payload-url" name="url" class="form-control" type="url" required="required" value="{{.Webhook.Payload}}" /> |                             <input id="payload-url" name="url" class="form-control" type="url" required="required" value="{{.Webhook.Url}}" /> | ||||||
|                         </div> |                         </div> | ||||||
|  |  | ||||||
|                         <div class="form-group"> |                         <div class="form-group"> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user