基于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

posted on 2022-02-23 20:12  荣锋亮  阅读(463)  评论(0编辑  收藏  举报

导航