Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,9 @@ func main() {
}
```
You can add optional parameters to constructor function.
* `WithoutUploadPack`: Disabled `git-upload-pack` command.
* `WithoutReceivePack`: Disabled `git-receive-pack` command.
* `WithoutDumbProto`: Disabled `dumb protocol` handling.
* `WithoutUploadPack` : Disable `git-upload-pack` command.
* `WithoutReceivePack`: Disable `git-receive-pack` command.
* `WithoutDumbProto` : Disable `dumb protocol` handling.
```go
ght, err := githttptransfer.New(
"/data/git",
Expand All @@ -88,13 +88,12 @@ func main() {
ght.Router.Add(githttptransfer.NewRoute(
http.MethodGet,
regexp.MustCompile("(.*?)/hello$"),
func(ctx githttptransfer.Context) error {
func(ctx githttptransfer.Context) {
resp, req := ctx.Response(), ctx.Request()
rp, fp := ctx.RepoPath(), ctx.FilePath()
fmt.Fprintf(resp.Writer,
"Hi there. URI: %s, RepoPath: %s, FilePath: %s",
req.URL.RequestURI(), rp, fp)
return nil
},
))

Expand Down
14 changes: 8 additions & 6 deletions addon/archivehandler/archivehandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ type ArchiveHandler struct {
*githttptransfer.GitHTTPTransfer
}

func (ght *ArchiveHandler) HandlerFunc(ctx githttptransfer.Context) error {
func (ght *ArchiveHandler) HandlerFunc(ctx githttptransfer.Context) {

res, repoPath, filePath := ctx.Response(), ctx.RepoPath(), ctx.FilePath()

Expand All @@ -38,23 +38,25 @@ func (ght *ArchiveHandler) HandlerFunc(ctx githttptransfer.Context) error {

stdout, err := cmd.StdoutPipe()
if err != nil {
return err
githttptransfer.RenderInternalServerError(res.Writer)
return
}
defer stdout.Close()

if err := cmd.Start(); err != nil {
return err
githttptransfer.RenderInternalServerError(res.Writer)
return
}

res.SetContentType("application/octet-stream")
res.Header().Add("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, fileName))
res.Header().Add("Content-Transfer-Encoding", "binary")

if _, err := res.Copy(stdout); err != nil {
return err
githttptransfer.RenderInternalServerError(res.Writer)
return
}
if err := cmd.Wait(); err != nil {
return err
githttptransfer.RenderInternalServerError(res.Writer)
}
return nil
}
12 changes: 4 additions & 8 deletions example/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,32 +25,28 @@ func main() {
return
}

ght.Event.On(githttptransfer.PrepareServiceRPCUpload, func(ctx githttptransfer.Context) error {
ght.Event.On(githttptransfer.PrepareServiceRPCUpload, func(ctx githttptransfer.Context) {
// prepare run service rpc upload.
return nil
})

ght.Event.On(githttptransfer.PrepareServiceRPCReceive, func(ctx githttptransfer.Context) error {
ght.Event.On(githttptransfer.PrepareServiceRPCReceive, func(ctx githttptransfer.Context) {
// prepare run service rpc receive.
return nil
})

ght.Event.On(githttptransfer.AfterMatchRouting, func(ctx githttptransfer.Context) error {
ght.Event.On(githttptransfer.AfterMatchRouting, func(ctx githttptransfer.Context) {
// after match routing.
return nil
})

// You can add some custom route.
ght.Router.Add(githttptransfer.NewRoute(
http.MethodGet,
regexp.MustCompile("(.*?)/hello$"),
func(ctx githttptransfer.Context) error {
func(ctx githttptransfer.Context) {
resp, req := ctx.Response(), ctx.Request()
rp, fp := ctx.RepoPath(), ctx.FilePath()
fmt.Fprintf(resp.Writer,
"Hi there. URI: %s, RepoPath: %s, FilePath: %s",
req.URL.RequestURI(), rp, fp)
return nil
},
))

Expand Down
7 changes: 0 additions & 7 deletions githttptransfer/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,3 @@ func (e *MethodNotAllowedError) Error() string {
return fmt.Sprintf("Method Not Allowed: Method %s, Path %s", e.Method, e.Path)
}

type NoAccessError struct {
Dir string
}

func (e *NoAccessError) Error() string {
return "No Access: " + e.Dir
}
138 changes: 68 additions & 70 deletions githttptransfer/githttptransfer.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,33 +25,33 @@ var (
getIdxFile = regexp.MustCompile("(.*?)/objects/pack/pack-[0-9a-f]{40}\\.idx$")
)

type gitHTTPTransferOptions struct {
uploadPack bool
type options struct {
uploadPack bool
receivePack bool
dumbProto bool
dumbProto bool
}

type GitHTTPTransferOption func(*gitHTTPTransferOptions)
type Option func(*options)

func WithoutUploadPack() GitHTTPTransferOption {
return func(o *gitHTTPTransferOptions) {
func WithoutUploadPack() Option {
return func(o *options) {
o.uploadPack = false
}
}

func WithoutReceivePack() GitHTTPTransferOption {
return func(o *gitHTTPTransferOptions) {
func WithoutReceivePack() Option {
return func(o *options) {
o.receivePack = false
}
}

func WithoutDumbProto() GitHTTPTransferOption {
return func(o *gitHTTPTransferOptions) {
func WithoutDumbProto() Option {
return func(o *options) {
o.dumbProto = false
}
}

func New(gitRootPath, gitBinPath string, opts ...GitHTTPTransferOption) (*GitHTTPTransfer, error) {
func New(gitRootPath, gitBinPath string, opts ...Option) (*GitHTTPTransfer, error) {

if gitRootPath == "" {
cwd, err := os.Getwd()
Expand All @@ -61,7 +61,7 @@ func New(gitRootPath, gitBinPath string, opts ...GitHTTPTransferOption) (*GitHTT
gitRootPath = cwd
}

ghtOpts := &gitHTTPTransferOptions{true, true, true}
ghtOpts := &options{true, true, true}

for _, opt := range opts {
opt(ghtOpts)
Expand Down Expand Up @@ -109,28 +109,14 @@ func (ght *GitHTTPTransfer) ServeHTTP(rw http.ResponseWriter, r *http.Request) {

ctx := NewContext(rw, r, repoPath, filePath)

if err := ght.Event.emit(AfterMatchRouting, ctx); err != nil {
RenderInternalServerError(ctx.Response().Writer)
return
}
ght.Event.emit(AfterMatchRouting, ctx)

if !ght.Git.Exists(ctx.RepoPath()) {
RenderNotFound(ctx.Response().Writer)
return
}

if err := handler(ctx); err != nil {
if os.IsNotExist(err) {
RenderNotFound(ctx.Response().Writer)
return
}
switch err.(type) {
case *NoAccessError:
RenderNoAccess(ctx.Response().Writer)
return
}
RenderInternalServerError(ctx.Response().Writer)
}
handler(ctx)
}

func (ght *GitHTTPTransfer) matchRouting(method, path string) (repoPath string, filePath string, handler HandlerFunc, err error) {
Expand All @@ -144,11 +130,11 @@ func (ght *GitHTTPTransfer) matchRouting(method, path string) (repoPath string,
}

const (
uploadPack string = "upload-pack"
receivePack string = "receive-pack"
uploadPack = "upload-pack"
receivePack = "receive-pack"
)

type HandlerFunc func(ctx Context) error
type HandlerFunc func(ctx Context)

func newEvent() *event {
return &event{map[EventKey]HandlerFunc{}}
Expand All @@ -166,38 +152,34 @@ type event struct {
listeners map[EventKey]HandlerFunc
}

func (e *event) emit(evt EventKey, ctx Context) error {
func (e *event) emit(evt EventKey, ctx Context) {
v, ok := e.listeners[evt]
if ok {
return v(ctx)
v(ctx)
}
return nil
}

func (e *event) On(evt EventKey, listener HandlerFunc) {
e.listeners[evt] = listener
}

func (ght *GitHTTPTransfer) serviceRPCUpload(ctx Context) error {
if err := ght.Event.emit(PrepareServiceRPCUpload, ctx); err != nil {
return err
}
return ght.serviceRPC(ctx, uploadPack)
func (ght *GitHTTPTransfer) serviceRPCUpload(ctx Context) {
ght.Event.emit(PrepareServiceRPCUpload, ctx)
ght.serviceRPC(ctx, uploadPack)
}

func (ght *GitHTTPTransfer) serviceRPCReceive(ctx Context) error {
if err := ght.Event.emit(PrepareServiceRPCReceive, ctx); err != nil {
return err
}
return ght.serviceRPC(ctx, receivePack)
func (ght *GitHTTPTransfer) serviceRPCReceive(ctx Context) {
ght.Event.emit(PrepareServiceRPCReceive, ctx)
ght.serviceRPC(ctx, receivePack)
}

func (ght *GitHTTPTransfer) serviceRPC(ctx Context, rpc string) error {
func (ght *GitHTTPTransfer) serviceRPC(ctx Context, rpc string) {

res, req, repoPath := ctx.Response(), ctx.Request(), ctx.RepoPath()

if !ght.Git.HasAccess(req, rpc, true) {
return &NoAccessError{Dir: ght.Git.GetAbsolutePath(repoPath)}
RenderNoAccess(ctx.Response().Writer)
return
}

var body io.ReadCloser
Expand All @@ -206,7 +188,8 @@ func (ght *GitHTTPTransfer) serviceRPC(ctx Context, rpc string) error {
if req.Header.Get("Content-Encoding") == "gzip" {
body, err = gzip.NewReader(req.Body)
if err != nil {
return err
RenderInternalServerError(ctx.Response().Writer)
return
}
} else {
body = req.Body
Expand All @@ -220,17 +203,19 @@ func (ght *GitHTTPTransfer) serviceRPC(ctx Context, rpc string) error {

stdin, err := cmd.StdinPipe()
if err != nil {
return err
RenderInternalServerError(ctx.Response().Writer)
return
}

stdout, err := cmd.StdoutPipe()
if err != nil {
return err
RenderInternalServerError(ctx.Response().Writer)
return
}

err = cmd.Start() // could be merged in one statement
if err != nil {
return err
if err = cmd.Start(); err != nil {
RenderInternalServerError(ctx.Response().Writer)
return
}

var wg sync.WaitGroup
Expand All @@ -250,23 +235,28 @@ func (ght *GitHTTPTransfer) serviceRPC(ctx Context, rpc string) error {

wg.Wait()

return cmd.Wait()

if err = cmd.Wait(); err != nil {
RenderInternalServerError(ctx.Response().Writer)
return
}
}

func (ght *GitHTTPTransfer) getInfoRefs(ctx Context) error {
func (ght *GitHTTPTransfer) getInfoRefs(ctx Context) {
res, req, repoPath := ctx.Response(), ctx.Request(), ctx.RepoPath()

serviceName := getServiceType(req)
if !ght.Git.HasAccess(req, serviceName, false) {
ght.Git.UpdateServerInfo(repoPath)
res.HdrNocache()
return ght.sendFile("text/plain; charset=utf-8", ctx)
if err := ght.sendFile("text/plain; charset=utf-8", ctx); err != nil {
RenderNotFound(ctx.Response().Writer)
}
}

refs, err := ght.Git.GetInfoRefs(repoPath, serviceName)
if err != nil {
return err
RenderNotFound(ctx.Response().Writer)
return
}

res.HdrNocache()
Expand All @@ -275,33 +265,42 @@ func (ght *GitHTTPTransfer) getInfoRefs(ctx Context) error {
res.PktWrite("# service=git-" + serviceName + "\n")
res.PktFlush()
res.Write(refs)

return nil
}

func (ght *GitHTTPTransfer) getInfoPacks(ctx Context) error {
func (ght *GitHTTPTransfer) getInfoPacks(ctx Context) {
ctx.Response().HdrCacheForever()
return ght.sendFile("text/plain; charset=utf-8", ctx)
if err := ght.sendFile("text/plain; charset=utf-8", ctx); err != nil {
RenderNotFound(ctx.Response().Writer)
}
}

func (ght *GitHTTPTransfer) getLooseObject(ctx Context) error {
func (ght *GitHTTPTransfer) getLooseObject(ctx Context) {
ctx.Response().HdrCacheForever()
return ght.sendFile("application/x-git-loose-object", ctx)
if err := ght.sendFile("application/x-git-loose-object", ctx); err != nil {
RenderNotFound(ctx.Response().Writer)
}
}

func (ght *GitHTTPTransfer) getPackFile(ctx Context) error {
func (ght *GitHTTPTransfer) getPackFile(ctx Context) {
ctx.Response().HdrCacheForever()
return ght.sendFile("application/x-git-packed-objects", ctx)
if err := ght.sendFile("application/x-git-packed-objects", ctx); err != nil {
RenderNotFound(ctx.Response().Writer)
}

}

func (ght *GitHTTPTransfer) getIdxFile(ctx Context) error {
func (ght *GitHTTPTransfer) getIdxFile(ctx Context) {
ctx.Response().HdrCacheForever()
return ght.sendFile("application/x-git-packed-objects-toc", ctx)
if err := ght.sendFile("application/x-git-packed-objects-toc", ctx); err != nil {
RenderNotFound(ctx.Response().Writer)
}
}

func (ght *GitHTTPTransfer) getTextFile(ctx Context) error {
func (ght *GitHTTPTransfer) getTextFile(ctx Context) {
ctx.Response().HdrNocache()
return ght.sendFile("text/plain", ctx)
if err := ght.sendFile("text/plain", ctx); err != nil {
RenderNotFound(ctx.Response().Writer)
}
}

func (ght *GitHTTPTransfer) sendFile(contentType string, ctx Context) error {
Expand All @@ -314,6 +313,5 @@ func (ght *GitHTTPTransfer) sendFile(contentType string, ctx Context) error {
res.SetContentLength(fmt.Sprintf("%d", fileInfo.Size()))
res.SetLastModified(fileInfo.ModTime().Format(http.TimeFormat))
http.ServeFile(res.Writer, req, fileInfo.AbsolutePath)

return nil
}
Loading