Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix download #18

Open
asu126 opened this issue Apr 21, 2019 · 0 comments
Open

fix download #18

asu126 opened this issue Apr 21, 2019 · 0 comments

Comments

@asu126
Copy link
Owner

asu126 commented Apr 21, 2019

package main

import (
	"compress/gzip"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path"
	"regexp"
	"strings"
)

type gitHandler struct {
	method      string
	regexp      *regexp.Regexp
	handle_func func(string, string, http.ResponseWriter, *http.Request)
	rpc         string
}

var repo_root string

var git_handlers = [...]gitHandler{
	gitHandler{"GET", regexp.MustCompile(`\A(/..*)/info/refs\z`), handle_get_info_refs, ""},
	gitHandler{"POST", regexp.MustCompile(`\A(/..*)/git-upload-pack\z`), handle_post_rpc, "git-upload-pack"},
	gitHandler{"POST", regexp.MustCompile(`\A(/..*)/git-receive-pack\z`), handle_post_rpc, "git-receive-pack"},
}

func main() {
	flag.Parse()
	repo_root = flag.Arg(0)
	log.Printf("repo_root: %s", repo_root)
	http.HandleFunc("/", git_handler)
	log.Fatal(http.ListenAndServe(":8080", nil))
}

func git_handler(w http.ResponseWriter, r *http.Request) {
	log.Print(r)
	for _, g := range git_handlers {
		m := g.regexp.FindStringSubmatch(r.URL.Path)
		if r.Method == g.method && m != nil {
			g.handle_func(g.rpc, path.Join(repo_root, m[1]), w, r)
			return
		}
	}
	log.Print("Reached end of dispatch for loop")
	w.WriteHeader(404)
}

func handle_get_info_refs(_ string, path string, w http.ResponseWriter, r *http.Request) {
	rpc := r.URL.Query().Get("service")
	switch rpc {
	case "git-upload-pack", "git-receive-pack":
		w.Header().Set("Content-Type", fmt.Sprintf("application/x-%s-advertisement", rpc))
		w.Header().Set("Cache-Control", "no-cache")

		if err := pktLine(w, fmt.Sprintf("# service=%s\n", rpc)); err != nil {
			fmt.Errorf("pktLine: %v", err)
			return
		}
		if err := pktFlush(w); err != nil {
			fmt.Errorf("pktFlush: %v", err)
			return
		}

		cmd := exec.Command("git", strings.TrimPrefix(rpc, "git-"), "--stateless-rpc", "--advertise-refs", path)
		log.Print(cmd.Args)
		cmd.Stdin = nil
		cmd.Stdout = w
		// stdout, err := cmd.StdoutPipe()
		// if err != nil {
		// 	fail_500(w, err)
		// 	return
		// }
		if err := cmd.Start(); err != nil {
			fail_500(w, err)
			return
		}
		// w.Header().Add("Content-Type", fmt.Sprintf("application/x-%s-advertisement", rpc))
		// no_cache(w)
		// fmt.Fprintf(w, "%s0000", pkt_line(fmt.Sprintf("# service=%s\n", rpc)))
		// if _, err := io.Copy(w, stdout); err != nil {
		// 	fail_500(w, err)
		// 	return
		// }
		
		if err := cmd.Wait(); err != nil {
			fail_500(w, err)
			return
		}
	case "":
		log.Print("dumb info refs")
	}
}


func handle_post_rpc(rpc string, path string, w http.ResponseWriter, r *http.Request) {
	var body io.Reader
	var err error
	if r.Header.Get("Content-Encoding") == "gzip" {
		body, err = gzip.NewReader(r.Body)
		if err != nil {
			fail_500(w, err)
			return
		}
	} else {
		body = r.Body
	}
	cmd := exec.Command("git", strings.TrimPrefix(rpc, "git-"), "--stateless-rpc", path)
	log.Print(cmd.Args)
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		fail_500(w, err)
		return
	}
	stdin, err := cmd.StdinPipe()
	if err != nil {
		fail_500(w, err)
		return
	}
	if err := cmd.Start(); err != nil {
		fail_500(w, err)
		return
	}
	w.Header().Add("Content-Type", fmt.Sprintf("application/x-%s-result", rpc))
	no_cache(w)
	if _, err := io.Copy(stdin, body); err != nil {
		fail_500(w, err)
		return
	}
	stdin.Close()
	if _, err := io.Copy(w, stdout); err != nil {
		fail_500(w, err)
		return
	}
	if err := cmd.Wait(); err != nil {
		fail_500(w, err)
		return
	}
}

func pkt_line(s string) string {
	return fmt.Sprintf("%04x%s", len(s)+4, s)
}

func fail_500(w http.ResponseWriter, err error) {
	w.WriteHeader(500)
	log.Print(err)
}

func no_cache(w http.ResponseWriter) {
	w.Header().Add("Cache-Control", "no-cache")
}

func pktLine(w io.Writer, s string) error {
	_, err := fmt.Fprintf(w, "%04x%s", len(s)+4, s)
	return err
}

func pktFlush(w io.Writer) error {
	_, err := fmt.Fprint(w, "0000")
	return err
}

// func CleanUpProcessGroup(cmd *exec.Cmd) {
// 	if cmd == nil {
// 		return
// 	}

// 	process := cmd.Process
// 	if process != nil && process.Pid > 0 {
// 		// Send SIGTERM to the process group of cmd
// 		syscall.Kill(-process.Pid, syscall.SIGTERM)
// 	}

// 	// reap our child process
// 	cmd.Wait()
// }

func ReadAllTempfile(r io.Reader) (tempfile *os.File, err error) {
	tempfile, err = ioutil.TempFile("", "gitlab-workhorse-read-all-tempfile")
	if err != nil {
		return nil, err
	}

	defer func() {
		// Avoid leaking an open file if the function returns with an error
		if err != nil {
			tempfile.Close()
		}
	}()

	if err := os.Remove(tempfile.Name()); err != nil {
		return nil, err
	}

	if _, err := io.Copy(tempfile, r); err != nil {
		return nil, err
	}

	if _, err := tempfile.Seek(0, 0); err != nil {
		return nil, err
	}

	return tempfile, nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant