diff --git a/.gitignore b/.gitignore index 07a41df41..15a5282b1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,11 @@ .DS_Store Thumbs.db _gitignore/ +Vagrantfile +.vagrant/ + error.log access.log + /*.conf -Caddyfile \ No newline at end of file +Caddyfile diff --git a/config/dispenser.go b/config/dispenser.go index bfa7efaeb..beba59a33 100644 --- a/config/dispenser.go +++ b/config/dispenser.go @@ -141,11 +141,19 @@ func (d *dispenser) Err(msg string) middleware.Middleware { // Args is a convenience function that loads the next arguments // (tokens on the same line) into an arbitrary number of strings // pointed to in targets. If there are fewer tokens available -// than string pointers, the remaining strings will not be changed. -func (d *dispenser) Args(targets ...*string) { - for i := 0; i < len(targets) && d.NextArg(); i++ { +// than string pointers, the remaining strings will not be changed +// and false will be returned. If there were enough tokens available +// to fill the arguments, then true will be returned. +func (d *dispenser) Args(targets ...*string) bool { + enough := true + for i := 0; i < len(targets); i++ { + if !d.NextArg() { + enough = false + break + } *targets[i] = d.Val() } + return enough } // Startup registers a function to execute when the server starts. diff --git a/middleware/fastcgi.go b/middleware/fastcgi.go new file mode 100644 index 000000000..835ca3c93 --- /dev/null +++ b/middleware/fastcgi.go @@ -0,0 +1,111 @@ +package middleware + +import ( + "io" + "io/ioutil" + "log" + "net/http" + "path/filepath" + "strings" + + "bitbucket.org/PinIdea/fcgi_client" // TODO: Inline this dependency. It'll need some work. +) + +// FastCGI is middleware that acts as a FastCGI client. Requests +// that get forwarded to FastCGI stop the middleware execution +// chain. The most common use for this layer is to serve PHP +// websites with php-fpm. +func FastCGI(p parser) Middleware { + root := p.Root() + + var rules []fastCgi + for p.Next() { + rule := fastCgi{} + if !p.Args(&rule.path, &rule.address) { + return p.ArgErr() + } + rules = append(rules, rule) + } + + return func(next http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + + servedFcgi := false + for _, rule := range rules { + if Path(r.URL.Path).Matches(rule.path) { + servedFcgi = true + + // Get absolute file paths + absPath, err := filepath.Abs(root + r.URL.Path) + if err != nil { + // TODO! + log.Fatal(err) + } + + // Get absolute file paths + absRootPath, err := filepath.Abs(root) + if err != nil { + // TODO! + log.Fatal(err) + } + + // Separate remote IP and port + var ip, port string + if idx := strings.Index(r.RemoteAddr, ":"); idx > -1 { + ip = r.RemoteAddr[idx:] + port = r.RemoteAddr[:idx] + } else { + ip = r.RemoteAddr + } + + // TODO: Do we really have to make this map from scratch for each request? + env := make(map[string]string) + env["SERVER_SOFTWARE"] = "caddy" // TODO: Obtain version info... + env["SERVER_PROTOCOL"] = r.Proto + env["SCRIPT_FILENAME"] = absPath + env["REMOTE_ADDR"] = ip + env["REMOTE_PORT"] = port + env["REQUEST_METHOD"] = r.Method + env["QUERY_STRING"] = r.URL.RawQuery + env["DOCUMENT_URI"] = r.URL.Path + env["DOCUMENT_ROOT"] = absRootPath + + fcgi, err := fcgiclient.Dial("tcp", rule.address) + if err != nil { + // TODO! + } + + resp, err := fcgi.Get(env) + if err != nil && err != io.EOF { + // TODO! + } + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + // TODO! + } + + for key, vals := range resp.Header { + for _, val := range vals { + w.Header().Add(key, val) + } + } + + w.WriteHeader(resp.StatusCode) + w.Write(body) + + break + } + } + + if !servedFcgi { + next(w, r) + } + } + } +} + +type fastCgi struct { + path string + address string +} diff --git a/middleware/middleware.go b/middleware/middleware.go index 6db9a1d23..f4411f862 100644 --- a/middleware/middleware.go +++ b/middleware/middleware.go @@ -17,6 +17,7 @@ func init() { register("rewrite", Rewrite) register("redir", Redirect) register("ext", Extensionless) + register("fastcgi", FastCGI) } type ( @@ -37,7 +38,7 @@ type ( NextLine() bool NextBlock() bool Val() string - Args(...*string) + Args(...*string) bool ArgErr() Middleware Err(string) Middleware Startup(func() error)