Commit 86aaa133 authored by Jacob Vosmaer's avatar Jacob Vosmaer

Prototype blobs via workhorse

parent 9530b40d
......@@ -3,7 +3,6 @@ package api
import (
"../badgateway"
"../helper"
"../proxy"
"encoding/json"
"fmt"
"io"
......@@ -93,7 +92,7 @@ func (api *API) newRequest(r *http.Request, body io.Reader, suffix string) (*htt
authReq := &http.Request{
Method: r.Method,
URL: rebaseUrl(r.URL, api.URL, suffix),
Header: proxy.HeaderClone(r.Header),
Header: helper.HeaderClone(r.Header),
}
if body != nil {
authReq.Body = ioutil.NopCloser(body)
......
package git
import (
"../helper"
"bufio"
"encoding/base64"
"fmt"
"io"
"net/http"
)
const blobLine = `blob
`
func SendGitBlob(w http.ResponseWriter, r *http.Request, repoPath string, blobId string) {
blobSpec, err := base64.URLEncoding.DecodeString(blobId)
if err != nil {
helper.Fail500(w, fmt.Errorf("SendGitBlob: decode commit id + path: %v", err))
return
}
catFileCmd := gitCommand("", "git", "--git-dir="+repoPath, "cat-file", "--batch=%(objecttype)")
catFileStdin, err := catFileCmd.StdinPipe()
if err != nil {
helper.Fail500(w, fmt.Errorf("SendGitBlob: git cat-file stdin: %v", err))
return
}
catFileStdout, err := catFileCmd.StdoutPipe()
if err != nil {
helper.Fail500(w, fmt.Errorf("SendGitBlob: git cat-file stdout: %v", err))
return
}
if err := catFileCmd.Start(); err != nil {
helper.Fail500(w, fmt.Errorf("SendGitBlob: start %v: %v", catFileCmd, err))
return
}
defer cleanUpProcessGroup(catFileCmd)
if _, err := fmt.Fprintf(catFileStdin, "%s\n", blobSpec); err != nil {
helper.Fail500(w, fmt.Errorf("SendGitBlob: send command to git cat-file: %v", err))
return
}
if err := catFileStdin.Close(); err != nil {
helper.Fail500(w, fmt.Errorf("SendGitBlob: close git cat-file stdin: %v", err))
return
}
out := bufio.NewReader(catFileStdout)
if response, err := out.ReadString('\n'); err != nil || response != blobLine {
helper.Fail500(w, fmt.Errorf("SendGitBlob: git cat-file returned %q, error: %v", response, err))
return
}
if _, err := io.Copy(w, catFileStdout); err != nil {
helper.LogError(fmt.Errorf("SendGitBlob: copy git cat-file stdout: %v", err))
return
}
if err := catFileCmd.Wait(); err != nil {
helper.LogError(fmt.Errorf("SendGitBlob: wait for git cat-file: %v", err))
return
}
}
......@@ -69,3 +69,13 @@ func HTTPError(w http.ResponseWriter, r *http.Request, error string, code int) {
http.Error(w, error, code)
}
func HeaderClone(h http.Header) http.Header {
h2 := make(http.Header, len(h))
for k, vv := range h {
vv2 := make([]string, len(vv))
copy(vv2, vv)
h2[k] = vv2
}
return h2
}
......@@ -2,6 +2,8 @@ package proxy
import (
"../badgateway"
"../helper"
"../senddata"
"net/http"
"net/http/httputil"
"net/url"
......@@ -25,24 +27,14 @@ func NewProxy(myURL *url.URL, version string, roundTripper *badgateway.RoundTrip
return &p
}
func HeaderClone(h http.Header) http.Header {
h2 := make(http.Header, len(h))
for k, vv := range h {
vv2 := make([]string, len(vv))
copy(vv2, vv)
h2[k] = vv2
}
return h2
}
func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Clone request
req := *r
req.Header = HeaderClone(r.Header)
req.Header = helper.HeaderClone(r.Header)
// Set Workhorse version
req.Header.Set("Gitlab-Workhorse", p.Version)
rw := newSendFileResponseWriter(w, &req)
rw := senddata.NewSendFileResponseWriter(w, &req)
defer rw.Flush()
p.reverseProxy.ServeHTTP(&rw, &req)
......
......@@ -4,9 +4,10 @@ via the X-Sendfile mechanism. All that is needed in the Rails code is the
'send_file' method.
*/
package proxy
package senddata
import (
"../git"
"../helper"
"log"
"net/http"
......@@ -19,7 +20,7 @@ type sendFileResponseWriter struct {
req *http.Request
}
func newSendFileResponseWriter(rw http.ResponseWriter, req *http.Request) sendFileResponseWriter {
func NewSendFileResponseWriter(rw http.ResponseWriter, req *http.Request) sendFileResponseWriter {
s := sendFileResponseWriter{
rw: rw,
req: req,
......@@ -48,30 +49,45 @@ func (s *sendFileResponseWriter) WriteHeader(status int) {
}
s.status = status
// Check X-Sendfile header
file := s.Header().Get("X-Sendfile")
s.Header().Del("X-Sendfile")
// If file is empty or status is not 200 pass through header
if file == "" || s.status != http.StatusOK {
if s.status != http.StatusOK {
s.rw.WriteHeader(s.status)
return
}
if file := s.Header().Get("X-Sendfile"); file != "" {
s.Header().Del("X-Sendfile")
// Mark this connection as hijacked
s.hijacked = true
// Serve the file
log.Printf("Send file %q for %s %q", file, s.req.Method, s.req.RequestURI)
sendFileFromDisk(s.rw, s.req, file)
return
} else if repoPath := s.Header().Get("Gitlab-Workhorse-Repo-Path"); repoPath != "" {
s.hijacked = true
s.Header().Del("Gitlab-Workhorse-Repo-Path")
sendBlob := s.Header().Get("Gitlab-Workhorse-Send-Blob")
s.Header().Del("Gitlab-Workhorse-Send-Blob")
git.SendGitBlob(s.rw, s.req, repoPath, sendBlob)
return
} else {
s.rw.WriteHeader(s.status)
return
}
}
func sendFileFromDisk(w http.ResponseWriter, r *http.Request, file string) {
log.Printf("Send file %q for %s %q", file, r.Method, r.RequestURI)
content, fi, err := helper.OpenFile(file)
if err != nil {
http.NotFound(s.rw, s.req)
http.NotFound(w, r)
return
}
defer content.Close()
http.ServeContent(s.rw, s.req, "", fi.ModTime(), content)
http.ServeContent(w, r, "", fi.ModTime(), content)
}
func (s *sendFileResponseWriter) Flush() {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment