基于golang cgi 实现一个简单的git http server
昨天基于openssh 实现了一个简单的git ssh 协议实现,现在基于git 的cgi 服务实现一个http 协议支持
此方法实际上应该在好多开源git 项目系统中,直接使用了git 内置的http-backend (cgi) golang,
nginx,apache, 都是直接支持cgi 的
参考代码
package main
import (
"fmt"
"log"
"net/http"
"net/http/cgi"
"regexp"
)
type Handler struct{}
func (t Handler) ServeHTTP(response http.ResponseWriter, request *http.Request) {
fmt.Println("In comming: " + request.Method)
fmt.Println(request.URL)
// user agent check
userAgent := request.Header["User-Agent"][0]
userAgentPattern, _ := regexp.Compile(`^(git)(.*)$`)
userAgentSub := userAgentPattern.FindSubmatch([]byte(userAgent))
if len(userAgentSub) == 0 {
response.WriteHeader(405)
return
}
// ignore auth
// user, pass, ok := request.BasicAuth()
// if !ok {
// response.Header().Set("WWW-Authenticate", `Basic realm="Git User Login"`)
// response.WriteHeader(401)
// return
// }
handler := new(cgi.Handler)
handler.Path = "/usr/libexec/git-core/git-http-backend"
pattern, _ := regexp.Compile(`^([a-zA-z0-9\/\.-]+\.git)(.*)$`)
sub := pattern.FindSubmatch([]byte(request.URL.Path))
// from user
if len(sub) > 1 {
handler.Env = append(handler.Env, "GIT_PROJECT_ROOT="+"/opt/gitrepo")
handler.Env = append(handler.Env, "REMOTE_USER="+"dalong")
handler.Env = append(handler.Env, "REMOTE_ADDR="+"localhost")
handler.Env = append(handler.Env, "PGYER_UID="+"dalong")
} else {
response.WriteHeader(405)
return
}
handler.Env = append(handler.Env, "GIT_HTTP_EXPORT_ALL=")
request.Header.Del("REMOTE_USER")
request.Header.Del("REMOTE_ADDR")
request.Header.Del("PGYER-REPO")
request.Header.Del("PGYER-REPO-USER")
request.Header.Del("PGYER-REPO-ADDR")
handler.ServeHTTP(response, request)
}
var RequestHandler Handler
func main() {
http.HandleFunc("/", RequestHandler.ServeHTTP)
err := http.ListenAndServe("0.0.0.0:8080", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err.Error())
}
}
构建
Dockerfile
FROM golang:1.17-alpine AS build-env
WORKDIR /go/src/app
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
ENV GO111MODULE=on
ENV GOPROXY=https://goproxy.cn
COPY . .
RUN apk update && apk add git \
&& go build -o git-http
FROM alpine:latest
WORKDIR /app
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
RUN apk update && apk add ca-certificates git-daemon && rm -rf /var/cache/apk/*
COPY --from=build-env /go/src/app/git-http /app/git-http
ENTRYPOINT [ "/app/git-http" ]
使用
复用了以前的ssh 服务,具体参考 https://github.com/rongfengliang/write-one-git-ssh-server
- 创建一个git bare repo
git init --bare demoapp.git
- clone 代码
git clone http://localhost:8080/demoapp.git
效果
- push
touch rong.txt
git add --all
git commit -m "demo "
git push
效果
说明
以上处理是开源codefever 对于http 协议的处理,实际上gogs 关于git 操作部分也依赖了不少原生git cli 工具以及命令,只是没有直接使用git-http-backend 这个cgi 服务,而且基于git 的smart protocl 实现的处理,gogs route/repo/http.go 是值得学习参考的
参考资料
https://pkgs.alpinelinux.org/contents?branch=edge&name=git-daemon&arch=aarch64&repo=main
https://github.com/pasela/git-cgi-server
https://github.com/ynohat/git-http-backend
https://github.com/rongfengliang/write-one-git-ssh-server
https://github.com/rongfengliang/write-one-git-ssh-server/tree/http-server
https://github.com/gogs/git-module
https://github.com/gogs/gogs/blob/8a1a40ce6a2fcad2ca877c1c98dcf492c5f9fbed/internal/route/repo/http.go
https://git-scm.com/book/en/v2/Git-Internals-Transfer-Protocols
https://git-scm.com/docs/http-protocol