|  | @@ -165,18 +165,29 @@ func HTTP(ctx *context.Context) {
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	callback := func(rpc string, input []byte) {
 | 
	
		
			
				|  |  | +	callback := func(rpc string, input *os.File) {
 | 
	
		
			
				|  |  |  		if rpc != "receive-pack" || isWiki {
 | 
	
		
			
				|  |  |  			return
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		var lastLine int64 = 0
 | 
	
		
			
				|  |  | +		var (
 | 
	
		
			
				|  |  | +			head = make([]byte, 4) // 00+size
 | 
	
		
			
				|  |  | +			n    int
 | 
	
		
			
				|  |  | +			err  error
 | 
	
		
			
				|  |  | +		)
 | 
	
		
			
				|  |  |  		for {
 | 
	
		
			
				|  |  | -			head := input[lastLine : lastLine+2]
 | 
	
		
			
				|  |  | +			n, err = input.Read(head)
 | 
	
		
			
				|  |  | +			if err != nil && err != io.EOF {
 | 
	
		
			
				|  |  | +				log.Error(4, "read head: %v", err)
 | 
	
		
			
				|  |  | +				return
 | 
	
		
			
				|  |  | +			} else if n < 4 {
 | 
	
		
			
				|  |  | +				break
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  			if head[0] == '0' && head[1] == '0' {
 | 
	
		
			
				|  |  | -				size, err := strconv.ParseInt(string(input[lastLine+2:lastLine+4]), 16, 32)
 | 
	
		
			
				|  |  | +				size, err := strconv.ParseInt(string(head[2:4]), 16, 32)
 | 
	
		
			
				|  |  |  				if err != nil {
 | 
	
		
			
				|  |  | -					log.Error(4, "%v", err)
 | 
	
		
			
				|  |  | +					log.Error(4, "parse size: %v", err)
 | 
	
		
			
				|  |  |  					return
 | 
	
		
			
				|  |  |  				}
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -185,7 +196,16 @@ func HTTP(ctx *context.Context) {
 | 
	
		
			
				|  |  |  					break
 | 
	
		
			
				|  |  |  				}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -				line := input[lastLine : lastLine+size]
 | 
	
		
			
				|  |  | +				line := make([]byte, size)
 | 
	
		
			
				|  |  | +				n, err = input.Read(line)
 | 
	
		
			
				|  |  | +				if err != nil {
 | 
	
		
			
				|  |  | +					log.Error(4, "read line: %v", err)
 | 
	
		
			
				|  |  | +					return
 | 
	
		
			
				|  |  | +				} else if n < int(size) {
 | 
	
		
			
				|  |  | +					log.Error(4, "didn't read enough bytes: expect %d got %d", size, n)
 | 
	
		
			
				|  |  | +					break
 | 
	
		
			
				|  |  | +				}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  				idx := bytes.IndexRune(line, '\000')
 | 
	
		
			
				|  |  |  				if idx > -1 {
 | 
	
		
			
				|  |  |  					line = line[:idx]
 | 
	
	
		
			
				|  | @@ -193,7 +213,7 @@ func HTTP(ctx *context.Context) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  				fields := strings.Fields(string(line))
 | 
	
		
			
				|  |  |  				if len(fields) >= 3 {
 | 
	
		
			
				|  |  | -					oldCommitId := fields[0][4:]
 | 
	
		
			
				|  |  | +					oldCommitId := fields[0]
 | 
	
		
			
				|  |  |  					newCommitId := fields[1]
 | 
	
		
			
				|  |  |  					refFullName := fields[2]
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -211,7 +231,6 @@ func HTTP(ctx *context.Context) {
 | 
	
		
			
				|  |  |  					}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  				}
 | 
	
		
			
				|  |  | -				lastLine = lastLine + size
 | 
	
		
			
				|  |  |  			} else {
 | 
	
		
			
				|  |  |  				break
 | 
	
		
			
				|  |  |  			}
 | 
	
	
		
			
				|  | @@ -230,7 +249,7 @@ func HTTP(ctx *context.Context) {
 | 
	
		
			
				|  |  |  type serviceConfig struct {
 | 
	
		
			
				|  |  |  	UploadPack  bool
 | 
	
		
			
				|  |  |  	ReceivePack bool
 | 
	
		
			
				|  |  | -	OnSucceed   func(rpc string, input []byte)
 | 
	
		
			
				|  |  | +	OnSucceed   func(rpc string, input *os.File)
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  type serviceHandler struct {
 | 
	
	
		
			
				|  | @@ -347,10 +366,10 @@ func serviceRPC(h serviceHandler, service string) {
 | 
	
		
			
				|  |  |  	h.w.Header().Set("Content-Type", fmt.Sprintf("application/x-git-%s-result", service))
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	var (
 | 
	
		
			
				|  |  | -		reqBody = h.r.Body
 | 
	
		
			
				|  |  | -		input   []byte
 | 
	
		
			
				|  |  | -		br      io.Reader
 | 
	
		
			
				|  |  | -		err     error
 | 
	
		
			
				|  |  | +		reqBody     = h.r.Body
 | 
	
		
			
				|  |  | +		tmpFilename string
 | 
	
		
			
				|  |  | +		br          io.Reader
 | 
	
		
			
				|  |  | +		err         error
 | 
	
		
			
				|  |  |  	)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	// Handle GZIP.
 | 
	
	
		
			
				|  | @@ -371,7 +390,6 @@ func serviceRPC(h serviceHandler, service string) {
 | 
	
		
			
				|  |  |  			return
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  		defer os.Remove(tmpfile.Name())
 | 
	
		
			
				|  |  | -		defer tmpfile.Close()
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  		_, err = io.Copy(tmpfile, reqBody)
 | 
	
		
			
				|  |  |  		if err != nil {
 | 
	
	
		
			
				|  | @@ -379,23 +397,42 @@ func serviceRPC(h serviceHandler, service string) {
 | 
	
		
			
				|  |  |  			h.w.WriteHeader(http.StatusInternalServerError)
 | 
	
		
			
				|  |  |  			return
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  | +		tmpfile.Close()
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		tmpFilename = tmpfile.Name()
 | 
	
		
			
				|  |  | +		tmpfile, err = os.Open(tmpFilename)
 | 
	
		
			
				|  |  | +		if err != nil {
 | 
	
		
			
				|  |  | +			log.GitLogger.Error(2, "fail to open temporary file: %v", err)
 | 
	
		
			
				|  |  | +			h.w.WriteHeader(http.StatusInternalServerError)
 | 
	
		
			
				|  |  | +			return
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +		defer tmpfile.Close()
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  		br = tmpfile
 | 
	
		
			
				|  |  |  	} else {
 | 
	
		
			
				|  |  |  		br = reqBody
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +	var stderr bytes.Buffer
 | 
	
		
			
				|  |  |  	cmd := exec.Command("git", service, "--stateless-rpc", h.dir)
 | 
	
		
			
				|  |  |  	cmd.Dir = h.dir
 | 
	
		
			
				|  |  |  	cmd.Stdout = h.w
 | 
	
		
			
				|  |  | +	cmd.Stderr = &stderr
 | 
	
		
			
				|  |  |  	cmd.Stdin = br
 | 
	
		
			
				|  |  |  	if err := cmd.Run(); err != nil {
 | 
	
		
			
				|  |  | -		log.GitLogger.Error(2, "fail to serve RPC(%s): %v", service, err)
 | 
	
		
			
				|  |  | +		log.GitLogger.Error(2, "fail to serve RPC(%s): %v - %s", service, err, stderr)
 | 
	
		
			
				|  |  |  		h.w.WriteHeader(http.StatusInternalServerError)
 | 
	
		
			
				|  |  |  		return
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	if h.cfg.OnSucceed != nil {
 | 
	
		
			
				|  |  | +		input, err := os.Open(tmpFilename)
 | 
	
		
			
				|  |  | +		if err != nil {
 | 
	
		
			
				|  |  | +			log.GitLogger.Error(2, "fail to open temporary file: %v", err)
 | 
	
		
			
				|  |  | +			h.w.WriteHeader(http.StatusInternalServerError)
 | 
	
		
			
				|  |  | +			return
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +		defer input.Close()
 | 
	
		
			
				|  |  |  		h.cfg.OnSucceed(service, input)
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  }
 |