Commit 4d4c1f02 authored by Kamil Trzcinski's avatar Kamil Trzcinski

Don't use ServerMux due to URI normalization: we do it our own way

parent 3be3f01d
...@@ -12,6 +12,7 @@ import ( ...@@ -12,6 +12,7 @@ import (
"os" "os"
"os/exec" "os/exec"
"syscall" "syscall"
"path"
) )
func fail500(w http.ResponseWriter, err error) { func fail500(w http.ResponseWriter, err error) {
...@@ -23,6 +24,13 @@ func logError(err error) { ...@@ -23,6 +24,13 @@ func logError(err error) {
log.Printf("error: %v", err) log.Printf("error: %v", err)
} }
func httpError(w http.ResponseWriter, r *http.Request, error string, code int) {
if r.ProtoAtLeast(1, 1) {
w.Header().Set("Connection", "close")
}
http.Error(w, error, code)
}
// Git subprocess helpers // Git subprocess helpers
func gitCommand(gl_id string, name string, args ...string) *exec.Cmd { func gitCommand(gl_id string, name string, args ...string) *exec.Cmd {
cmd := exec.Command(name, args...) cmd := exec.Command(name, args...)
...@@ -90,3 +98,21 @@ func openFile(path string) (file *os.File, fi os.FileInfo, err error) { ...@@ -90,3 +98,21 @@ func openFile(path string) (file *os.File, fi os.FileInfo, err error) {
return return
} }
// Borrowed from: net/http/server.go
// Return the canonical path for p, eliminating . and .. elements.
func cleanURIPath(p string) string {
if p == "" {
return "/"
}
if p[0] != '/' {
p = "/" + p
}
np := path.Clean(p)
// path.Clean removes trailing slash except for root;
// put the trailing slash back if necessary.
if p[len(p)-1] == '/' && np != "/" {
np += "/"
}
return np
}
...@@ -171,10 +171,5 @@ func main() { ...@@ -171,10 +171,5 @@ func main() {
upstream := newUpstream(*authBackend, proxyTransport) upstream := newUpstream(*authBackend, proxyTransport)
upstream.SetRelativeURLRoot(*relativeURLRoot) upstream.SetRelativeURLRoot(*relativeURLRoot)
log.Fatal(http.Serve(listener, upstream))
// Because net/http/pprof installs itself in the DefaultServeMux
// we create a fresh one for the Git server.
serveMux := http.NewServeMux()
serveMux.Handle(upstream.relativeURLRoot, upstream)
log.Fatal(http.Serve(listener, serveMux))
} }
...@@ -12,6 +12,7 @@ import ( ...@@ -12,6 +12,7 @@ import (
"net/http/httputil" "net/http/httputil"
"net/url" "net/url"
"strings" "strings"
"fmt"
) )
type serviceHandleFunc func(w http.ResponseWriter, r *gitRequest) type serviceHandleFunc func(w http.ResponseWriter, r *gitRequest)
...@@ -26,29 +27,29 @@ type upstream struct { ...@@ -26,29 +27,29 @@ type upstream struct {
type authorizationResponse struct { type authorizationResponse struct {
// GL_ID is an environment variable used by gitlab-shell hooks during 'git // GL_ID is an environment variable used by gitlab-shell hooks during 'git
// push' and 'git pull' // push' and 'git pull'
GL_ID string GL_ID string
// RepoPath is the full path on disk to the Git repository the request is // RepoPath is the full path on disk to the Git repository the request is
// about // about
RepoPath string RepoPath string
// ArchivePath is the full path where we should find/create a cached copy // ArchivePath is the full path where we should find/create a cached copy
// of a requested archive // of a requested archive
ArchivePath string ArchivePath string
// ArchivePrefix is used to put extracted archive contents in a // ArchivePrefix is used to put extracted archive contents in a
// subdirectory // subdirectory
ArchivePrefix string ArchivePrefix string
// CommitId is used do prevent race conditions between the 'time of check' // CommitId is used do prevent race conditions between the 'time of check'
// in the GitLab Rails app and the 'time of use' in gitlab-workhorse. // in the GitLab Rails app and the 'time of use' in gitlab-workhorse.
CommitId string CommitId string
// StoreLFSPath is provided by the GitLab Rails application // StoreLFSPath is provided by the GitLab Rails application
// to mark where the tmp file should be placed // to mark where the tmp file should be placed
StoreLFSPath string StoreLFSPath string
// LFS object id // LFS object id
LfsOid string LfsOid string
// LFS object size // LFS object size
LfsSize int64 LfsSize int64
// TmpPath is the path where we should store temporary files // TmpPath is the path where we should store temporary files
// This is set by authorization middleware // This is set by authorization middleware
TempPath string TempPath string
} }
// A gitRequest is an *http.Request decorated with attributes returned by the // A gitRequest is an *http.Request decorated with attributes returned by the
...@@ -56,7 +57,7 @@ type authorizationResponse struct { ...@@ -56,7 +57,7 @@ type authorizationResponse struct {
type gitRequest struct { type gitRequest struct {
*http.Request *http.Request
authorizationResponse authorizationResponse
u *upstream u *upstream
// This field contains the URL.Path stripped from RelativeUrlRoot // This field contains the URL.Path stripped from RelativeUrlRoot
relativeURIPath string relativeURIPath string
...@@ -92,10 +93,29 @@ func (u *upstream) ServeHTTP(ow http.ResponseWriter, r *http.Request) { ...@@ -92,10 +93,29 @@ func (u *upstream) ServeHTTP(ow http.ResponseWriter, r *http.Request) {
w := newLoggingResponseWriter(ow) w := newLoggingResponseWriter(ow)
defer w.Log(r) defer w.Log(r)
// Drop WebSocket connection and CONNECT method
if r.RequestURI == "*" {
httpError(&w, r, "Connection upgrade not allowed", http.StatusBadRequest)
return
}
// Disallow connect
if r.Method == "CONNECT" {
httpError(&w, r, "CONNECT not allowed", http.StatusBadRequest)
return
}
// Check URL Root
URIPath := cleanURIPath(r.URL.Path)
if !strings.HasPrefix(URIPath, u.relativeURLRoot) {
httpError(&w, r, fmt.Sprintf("Not found %q", URIPath), http.StatusNotFound)
return
}
// Strip prefix and add "/" // Strip prefix and add "/"
// To match against non-relative URL // To match against non-relative URL
// Making it simpler for our matcher // Making it simpler for our matcher
relativeURIPath := "/" + strings.TrimPrefix(r.URL.Path, u.relativeURLRoot) relativeURIPath := cleanURIPath(strings.TrimPrefix(URIPath, u.relativeURLRoot))
// Look for a matching Git service // Look for a matching Git service
foundService := false foundService := false
...@@ -112,7 +132,7 @@ func (u *upstream) ServeHTTP(ow http.ResponseWriter, r *http.Request) { ...@@ -112,7 +132,7 @@ func (u *upstream) ServeHTTP(ow http.ResponseWriter, r *http.Request) {
if !foundService { if !foundService {
// The protocol spec in git/Documentation/technical/http-protocol.txt // The protocol spec in git/Documentation/technical/http-protocol.txt
// says we must return 403 if no matching service is found. // says we must return 403 if no matching service is found.
http.Error(&w, "Forbidden", 403) httpError(&w, r, "Forbidden", http.StatusForbidden)
return return
} }
......
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