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

Prototype blobs via workhorse

parent 9530b40d
...@@ -3,7 +3,6 @@ package api ...@@ -3,7 +3,6 @@ package api
import ( import (
"../badgateway" "../badgateway"
"../helper" "../helper"
"../proxy"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
...@@ -93,7 +92,7 @@ func (api *API) newRequest(r *http.Request, body io.Reader, suffix string) (*htt ...@@ -93,7 +92,7 @@ func (api *API) newRequest(r *http.Request, body io.Reader, suffix string) (*htt
authReq := &http.Request{ authReq := &http.Request{
Method: r.Method, Method: r.Method,
URL: rebaseUrl(r.URL, api.URL, suffix), URL: rebaseUrl(r.URL, api.URL, suffix),
Header: proxy.HeaderClone(r.Header), Header: helper.HeaderClone(r.Header),
} }
if body != nil { if body != nil {
authReq.Body = ioutil.NopCloser(body) 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) { ...@@ -69,3 +69,13 @@ func HTTPError(w http.ResponseWriter, r *http.Request, error string, code int) {
http.Error(w, error, code) 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 ...@@ -2,6 +2,8 @@ package proxy
import ( import (
"../badgateway" "../badgateway"
"../helper"
"../senddata"
"net/http" "net/http"
"net/http/httputil" "net/http/httputil"
"net/url" "net/url"
...@@ -25,24 +27,14 @@ func NewProxy(myURL *url.URL, version string, roundTripper *badgateway.RoundTrip ...@@ -25,24 +27,14 @@ func NewProxy(myURL *url.URL, version string, roundTripper *badgateway.RoundTrip
return &p 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) { func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Clone request // Clone request
req := *r req := *r
req.Header = HeaderClone(r.Header) req.Header = helper.HeaderClone(r.Header)
// Set Workhorse version // Set Workhorse version
req.Header.Set("Gitlab-Workhorse", p.Version) req.Header.Set("Gitlab-Workhorse", p.Version)
rw := newSendFileResponseWriter(w, &req) rw := senddata.NewSendFileResponseWriter(w, &req)
defer rw.Flush() defer rw.Flush()
p.reverseProxy.ServeHTTP(&rw, &req) p.reverseProxy.ServeHTTP(&rw, &req)
......
...@@ -4,9 +4,10 @@ via the X-Sendfile mechanism. All that is needed in the Rails code is the ...@@ -4,9 +4,10 @@ via the X-Sendfile mechanism. All that is needed in the Rails code is the
'send_file' method. 'send_file' method.
*/ */
package proxy package senddata
import ( import (
"../git"
"../helper" "../helper"
"log" "log"
"net/http" "net/http"
...@@ -19,7 +20,7 @@ type sendFileResponseWriter struct { ...@@ -19,7 +20,7 @@ type sendFileResponseWriter struct {
req *http.Request req *http.Request
} }
func newSendFileResponseWriter(rw http.ResponseWriter, req *http.Request) sendFileResponseWriter { func NewSendFileResponseWriter(rw http.ResponseWriter, req *http.Request) sendFileResponseWriter {
s := sendFileResponseWriter{ s := sendFileResponseWriter{
rw: rw, rw: rw,
req: req, req: req,
...@@ -48,30 +49,45 @@ func (s *sendFileResponseWriter) WriteHeader(status int) { ...@@ -48,30 +49,45 @@ func (s *sendFileResponseWriter) WriteHeader(status int) {
} }
s.status = status s.status = status
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
// Check X-Sendfile header // Serve the file
file := s.Header().Get("X-Sendfile") sendFileFromDisk(s.rw, s.req, file)
s.Header().Del("X-Sendfile") 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")
// If file is empty or status is not 200 pass through header git.SendGitBlob(s.rw, s.req, repoPath, sendBlob)
if file == "" || s.status != http.StatusOK { return
} else {
s.rw.WriteHeader(s.status) s.rw.WriteHeader(s.status)
return return
} }
}
// Mark this connection as hijacked func sendFileFromDisk(w http.ResponseWriter, r *http.Request, file string) {
s.hijacked = true log.Printf("Send file %q for %s %q", file, r.Method, r.RequestURI)
// Serve the file
log.Printf("Send file %q for %s %q", file, s.req.Method, s.req.RequestURI)
content, fi, err := helper.OpenFile(file) content, fi, err := helper.OpenFile(file)
if err != nil { if err != nil {
http.NotFound(s.rw, s.req) http.NotFound(w, r)
return return
} }
defer content.Close() defer content.Close()
http.ServeContent(s.rw, s.req, "", fi.ModTime(), content) http.ServeContent(w, r, "", fi.ModTime(), content)
} }
func (s *sendFileResponseWriter) Flush() { 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