// ServeHTTP implements the httpserver.Handler interface. func (i Internal) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { // Internal location requested? -> Not found. for _, prefix := range i.Paths { if httpserver.Path(r.URL.Path).Matches(prefix) { return http.StatusNotFound, nil } } // Use internal response writer to ignore responses that will be // redirected to internal locations iw := internalResponseWriter{ResponseWriter: w} status, err := i.Next.ServeHTTP(iw, r) for c := 0; c < maxRedirectCount && isInternalRedirect(iw); c++ { // Redirect - adapt request URL path and send it again // "down the chain" r.URL.Path = iw.Header().Get(redirectHeader) iw.ClearHeader() status, err = i.Next.ServeHTTP(iw, r) } if isInternalRedirect(iw) { // Too many redirect cycles iw.ClearHeader() return http.StatusInternalServerError, nil } return status, err }
// ServeHTTP implements the httpserver.Handler interface and serves requests, // setting headers on the response according to the configured rules. func (h Headers) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { replacer := httpserver.NewReplacer(r, nil, "") rww := &responseWriterWrapper{w: w} for _, rule := range h.Rules { if httpserver.Path(r.URL.Path).Matches(rule.Path) { for name := range rule.Headers { // One can either delete a header, add multiple values to a header, or simply // set a header. if strings.HasPrefix(name, "-") { rww.delHeader(strings.TrimLeft(name, "-")) } else if strings.HasPrefix(name, "+") { for _, value := range rule.Headers[name] { rww.Header().Add(strings.TrimLeft(name, "+"), replacer.Replace(value)) } } else { for _, value := range rule.Headers[name] { rww.Header().Set(name, replacer.Replace(value)) } } } } } return h.Next.ServeHTTP(rww, r) }
// ServerHTTP is the HTTP handler for this middleware func (s *Search) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { if httpserver.Path(r.URL.Path).Matches(s.Config.Endpoint) { if r.Header.Get("Accept") == "application/json" || s.Config.Template == nil { return s.SearchJSON(w, r) } return s.SearchHTML(w, r) } record := s.Indexer.Record(r.URL.String()) status, err := s.Next.ServeHTTP(&searchResponseWriter{w, record}, r) modif := w.Header().Get("Last-Modified") if len(modif) > 0 { modTime, err := time.Parse(`Mon, 2 Jan 2006 15:04:05 MST`, modif) if err == nil { record.SetModified(modTime) } } if status != http.StatusOK { record.Ignore() } go s.Pipeline.Pipe(record) return status, err }
// ServeHTTP handles requests to expvar's configured entry point with // expvar, or passes all other requests up the chain. func (e ExpVar) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { if httpserver.Path(r.URL.Path).Matches(string(e.Resource)) { expvarHandler(w, r) return 0, nil } return e.Next.ServeHTTP(w, r) }
func (jph JsonPHandlerType) ServeHTTP(www http.ResponseWriter, req *http.Request) (int, error) { if db1 { fmt.Printf("JsonPHandlerType.ServeHTTP called\n") } for _, prefix := range jph.Paths { if db1 { fmt.Printf("Path Matches\n") } if httpserver.Path(req.URL.Path).Matches(prefix) { if db1 { fmt.Printf("A\n") } ResponseBodyRecorder := bufferhtml.NewBufferHTML() if db1 { fmt.Printf("B\n") } status, err := jph.Next.ServeHTTP(ResponseBodyRecorder, req) if db1 { fmt.Printf("C status=%d err=%s\n", status, err) } if status == 200 || status == 0 { // if there is a "callback" argument then use that to format the JSONp response. if db1 { fmt.Printf("D Inside, status=%d\n", status) } Prefix := "" Postfix := "" u, err := url.ParseRequestURI(req.RequestURI) if err != nil { ResponseBodyRecorder.FlushAtEnd(www, "", "") return status, nil } m, err := url.ParseQuery(u.RawQuery) if err != nil { ResponseBodyRecorder.FlushAtEnd(www, "", "") return status, nil } callback := m.Get("callback") if callback != "" { if db1 { fmt.Printf("Callback = -[%s]-\n", callback) } www.Header().Set("Content-Type", "application/javascript") Prefix = callback + "(" Postfix = ");" } ResponseBodyRecorder.FlushAtEnd(www, Prefix, Postfix) } else { if db1 { fmt.Printf("E - error occured, %s\n", err) } log.Printf("Error (%s) status %d - from JSONP directive\n", err, status) } return status, err } } return jph.Next.ServeHTTP(www, req) }
// ServeHTTP handles requests to BasePath with pprof, or passes // all other requests up the chain. func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { if httpserver.Path(r.URL.Path).Matches(BasePath) { h.Mux.ServeHTTP(w, r) return 0, nil } return h.Next.ServeHTTP(w, r) }
// Rewrite rewrites the internal location of the current request. func (r *ComplexRule) Rewrite(fs http.FileSystem, req *http.Request) (re Result) { rPath := req.URL.Path replacer := newReplacer(req) // validate base if !httpserver.Path(rPath).Matches(r.Base) { return } // validate extensions if !r.matchExt(rPath) { return } // validate regexp if present if r.Regexp != nil { // include trailing slash in regexp if present start := len(r.Base) if strings.HasSuffix(r.Base, "/") { start-- } matches := r.FindStringSubmatch(rPath[start:]) switch len(matches) { case 0: // no match return default: // set regexp match variables {1}, {2} ... // url escaped values of ? and #. q, f := url.QueryEscape("?"), url.QueryEscape("#") for i := 1; i < len(matches); i++ { // Special case of unescaped # and ? by stdlib regexp. // Reverse the unescape. if strings.ContainsAny(matches[i], "?#") { matches[i] = strings.NewReplacer("?", q, "#", f).Replace(matches[i]) } replacer.Set(fmt.Sprint(i), matches[i]) } } } // validate rewrite conditions for _, i := range r.Ifs { if !i.True(req) { return } } // if status is present, stop rewrite and return it. if r.Status != 0 { return RewriteStatus } // attempt rewrite return To(fs, req, r.To, replacer) }
func (l Logger) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { for _, rule := range l.Rules { if httpserver.Path(r.URL.Path).Matches(rule.PathScope) { // Record the response responseRecorder := httpserver.NewResponseRecorder(w) // Attach the Replacer we'll use so that other middlewares can // set their own placeholders if they want to. rep := httpserver.NewReplacer(r, responseRecorder, CommonLogEmptyValue) responseRecorder.Replacer = rep // Bon voyage, request! status, err := l.Next.ServeHTTP(responseRecorder, r) if status >= 400 { // There was an error up the chain, but no response has been written yet. // The error must be handled here so the log entry will record the response size. if l.ErrorFunc != nil { l.ErrorFunc(responseRecorder, r, status) } else { // Default failover error handler responseRecorder.WriteHeader(status) fmt.Fprintf(responseRecorder, "%d %s", status, http.StatusText(status)) } status = 0 } // Write log entry rule.Log.Println(rep.Replace(rule.Format)) return status, err } } return l.Next.ServeHTTP(w, r) }
// ServeHTTP determines if the request is for this plugin, and if all prerequisites are met. // If so, control is handed over to ServeListing. func (b Browse) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { var bc *Config // See if there's a browse configuration to match the path for i := range b.Configs { if httpserver.Path(r.URL.Path).Matches(b.Configs[i].PathScope) { bc = &b.Configs[i] goto inScope } } return b.Next.ServeHTTP(w, r) inScope: // Browse works on existing directories; delegate everything else requestedFilepath, err := bc.Root.Open(r.URL.Path) if err != nil { switch { case os.IsPermission(err): return http.StatusForbidden, err case os.IsExist(err): return http.StatusNotFound, err default: return b.Next.ServeHTTP(w, r) } } defer requestedFilepath.Close() info, err := requestedFilepath.Stat() if err != nil { switch { case os.IsPermission(err): return http.StatusForbidden, err case os.IsExist(err): return http.StatusGone, err default: return b.Next.ServeHTTP(w, r) } } if !info.IsDir() { return b.Next.ServeHTTP(w, r) } // Do not reply to anything else because it might be nonsensical switch r.Method { case http.MethodGet, http.MethodHead: // proceed, noop case "PROPFIND", http.MethodOptions: return http.StatusNotImplemented, nil default: return b.Next.ServeHTTP(w, r) } // Browsing navigation gets messed up if browsing a directory // that doesn't end in "/" (which it should, anyway) if !strings.HasSuffix(r.URL.Path, "/") { staticfiles.Redirect(w, r, r.URL.Path+"/", http.StatusTemporaryRedirect) return 0, nil } return b.ServeListing(w, r, requestedFilepath, bc) }
// ShouldAllow takes a path and a request and decides if it should be allowed func (ipf IPFilter) ShouldAllow(path IPPath, r *http.Request) (bool, string, error) { allow := true scopeMatched := "" // check if we are in one of our scopes. for _, scope := range path.PathScopes { if httpserver.Path(r.URL.Path).Matches(scope) { // extract the client's IP and parse it. clientIP, err := getClientIP(r, path.Strict) if err != nil { return false, scope, err } // request status. var rs Status if len(path.CountryCodes) != 0 { // do the lookup. var result OnlyCountry if err = ipf.Config.DBHandler.Lookup(clientIP, &result); err != nil { return false, scope, err } // get only the ISOCode out of the lookup results. clientCountry := result.Country.ISOCode for _, c := range path.CountryCodes { if clientCountry == c { rs.countryMatch = true break } } } if len(path.Nets) != 0 { for _, rng := range path.Nets { if rng.Contains(clientIP) { rs.inRange = true break } } } scopeMatched = scope if rs.Any() { // Rule matched, if the rule has IsBlock = true then we have to deny access allow = !path.IsBlock } else { // Rule did not match, if the rule has IsBlock = true then we have to allow access allow = path.IsBlock } // We only have to test the first path that matches because it is the most specific break } } // no scope match, pass-through. return allow, scopeMatched, nil }
func (u *staticUpstream) AllowedPath(requestPath string) bool { for _, ignoredSubPath := range u.IgnoredSubPaths { if httpserver.Path(path.Clean(requestPath)).Matches(path.Join(u.From(), ignoredSubPath)) { return false } } return true }
// AllowedPath checks if requestPath is not an ignored path. func (r Rule) AllowedPath(requestPath string) bool { for _, ignoredSubPath := range r.IgnoredSubPaths { if httpserver.Path(path.Clean(requestPath)).Matches(path.Join(r.Path, ignoredSubPath)) { return false } } return true }
// ServeHTTP converts the HTTP request to a WebSocket connection and serves it up. func (ws WebSocket) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { for _, sockconfig := range ws.Sockets { if httpserver.Path(r.URL.Path).Matches(sockconfig.Path) { return serveWS(w, r, &sockconfig) } } // Didn't match a websocket path, so pass-through return ws.Next.ServeHTTP(w, r) }
// match finds the best match for a proxy config based // on r. func (p Proxy) match(r *http.Request) Upstream { var u Upstream var longestMatch int for _, upstream := range p.Upstreams { basePath := upstream.From() if !httpserver.Path(r.URL.Path).Matches(basePath) || !upstream.AllowedPath(r.URL.Path) { continue } if len(basePath) > longestMatch { longestMatch = len(basePath) u = upstream } } return u }
// ServeHTTP implements the httpserver.Handler interface and serves requests, // setting headers on the response according to the configured rules. func (h Headers) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { replacer := httpserver.NewReplacer(r, nil, "") for _, rule := range h.Rules { if httpserver.Path(r.URL.Path).Matches(rule.Path) { for _, header := range rule.Headers { if strings.HasPrefix(header.Name, "-") { w.Header().Del(strings.TrimLeft(header.Name, "-")) } else { w.Header().Set(header.Name, replacer.Replace(header.Value)) } } } } return h.Next.ServeHTTP(w, r) }
func (n *NLgids) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { if r.Method != "POST" { return n.Next.ServeHTTP(w, r) } path := httpserver.Path(r.URL.Path) if !path.Matches("/api") { return n.Next.ServeHTTP(w, r) } switch { case path.Matches("/api/auth/invoice"): return n.WebInvoice(w, r) case path.Matches("/api/open/contact"): return n.WebContact(w, r) case path.Matches("/api/open/booking"): return n.WebBooking(w, r) case path.Matches("/api/open/calendar"): return n.WebCalendar(w, r) } return http.StatusOK, nil }
// ServeHTTP implements the httpserver.Handler interface. func (a BasicAuth) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { var hasAuth bool var isAuthenticated bool for _, rule := range a.Rules { for _, res := range rule.Resources { if !httpserver.Path(r.URL.Path).Matches(res) { continue } // Path matches; parse auth header username, password, ok := r.BasicAuth() hasAuth = true // Check credentials if !ok || username != rule.Username || !rule.Password(password) { //subtle.ConstantTimeCompare([]byte(password), []byte(rule.Password)) != 1 { continue } // Flag set only on successful authentication isAuthenticated = true } } if hasAuth { if !isAuthenticated { w.Header().Set("WWW-Authenticate", "Basic") return http.StatusUnauthorized, nil } // "It's an older code, sir, but it checks out. I was about to clear them." return a.Next.ServeHTTP(w, r) } // Pass-thru when no paths match return a.Next.ServeHTTP(w, r) }
func setup(c *caddy.Controller) error { rules, err := parseRules(c) if err != nil { return err } siteConfig := httpserver.GetConfig(c) siteConfig.AddMiddleware(func(next httpserver.Handler) httpserver.Handler { return httpserver.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) { for _, rule := range rules { if httpserver.Path(r.URL.Path).Matches(rule.Path) { rule.Conf.HandleRequest(w, r) if cors.IsPreflight(r) { return 200, nil } break } } return next.ServeHTTP(w, r) }) }) return nil }
// ServeHTTP implements the httpserver.Handler interface. func (t Templates) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { for _, rule := range t.Rules { if !httpserver.Path(r.URL.Path).Matches(rule.Path) { continue } // Check for index files fpath := r.URL.Path if idx, ok := httpserver.IndexFile(t.FileSys, fpath, rule.IndexFiles); ok { fpath = idx } // Check the extension reqExt := path.Ext(fpath) for _, ext := range rule.Extensions { if reqExt == ext { // Create execution context ctx := httpserver.Context{Root: t.FileSys, Req: r, URL: r.URL} // New template templateName := filepath.Base(fpath) tpl := template.New(templateName) // Set delims if rule.Delims != [2]string{} { tpl.Delims(rule.Delims[0], rule.Delims[1]) } // Build the template templatePath := filepath.Join(t.Root, fpath) tpl, err := tpl.ParseFiles(templatePath) if err != nil { if os.IsNotExist(err) { return http.StatusNotFound, nil } else if os.IsPermission(err) { return http.StatusForbidden, nil } return http.StatusInternalServerError, err } // Execute it var buf bytes.Buffer err = tpl.Execute(&buf, ctx) if err != nil { return http.StatusInternalServerError, err } templateInfo, err := os.Stat(templatePath) if err == nil { // add the Last-Modified header if we were able to read the stamp httpserver.SetLastModifiedHeader(w, templateInfo.ModTime()) } buf.WriteTo(w) return http.StatusOK, nil } } } return t.Next.ServeHTTP(w, r) }
// ServeHTTP satisfies the httpserver.Handler interface. func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { for _, rule := range h.Rules { // First requirement: Base path must match and the path must be allowed. if !httpserver.Path(r.URL.Path).Matches(rule.Path) || !rule.AllowedPath(r.URL.Path) { continue } // In addition to matching the path, a request must meet some // other criteria before being proxied as FastCGI. For example, // we probably want to exclude static assets (CSS, JS, images...) // but we also want to be flexible for the script we proxy to. fpath := r.URL.Path if idx, ok := httpserver.IndexFile(h.FileSys, fpath, rule.IndexFiles); ok { fpath = idx // Index file present. // If request path cannot be split, return error. if !rule.canSplit(fpath) { return http.StatusInternalServerError, ErrIndexMissingSplit } } else { // No index file present. // If request path cannot be split, ignore request. if !rule.canSplit(fpath) { continue } } // These criteria work well in this order for PHP sites if !h.exists(fpath) || fpath[len(fpath)-1] == '/' || strings.HasSuffix(fpath, rule.Ext) { // Create environment for CGI script env, err := h.buildEnv(r, rule, fpath) if err != nil { return http.StatusInternalServerError, err } // Connect to FastCGI gateway fcgiBackend, err := rule.dialer.Dial() if err != nil { return http.StatusBadGateway, err } var resp *http.Response contentLength, _ := strconv.Atoi(r.Header.Get("Content-Length")) switch r.Method { case "HEAD": resp, err = fcgiBackend.Head(env) case "GET": resp, err = fcgiBackend.Get(env) case "OPTIONS": resp, err = fcgiBackend.Options(env) default: resp, err = fcgiBackend.Post(env, r.Method, r.Header.Get("Content-Type"), r.Body, contentLength) } if err != nil && err != io.EOF { return http.StatusBadGateway, err } // Write response header writeHeader(w, resp) // Write the response body _, err = io.Copy(w, resp.Body) if err != nil { return http.StatusBadGateway, err } defer rule.dialer.Close(fcgiBackend) // Log any stderr output from upstream if stderr := fcgiBackend.StdErr(); stderr.Len() != 0 { // Remove trailing newline, error logger already does this. err = LogError(strings.TrimSuffix(stderr.String(), "\n")) } // Normally we would return the status code if it is an error status (>= 400), // however, upstream FastCGI apps don't know about our contract and have // probably already written an error page. So we just return 0, indicating // that the response body is already written. However, we do return any // error value so it can be logged. // Note that the proxy middleware works the same way, returning status=0. return 0, err } } return h.Next.ServeHTTP(w, r) }
// ShouldCompress checks if the request path matches any of the // registered paths to ignore. It returns false if an ignored path // is found and true otherwise. func (p PathFilter) ShouldCompress(r *http.Request) bool { return !p.IgnoredPaths.ContainsFunc(func(value string) bool { return httpserver.Path(r.URL.Path).Matches(value) }) }
// ServeHTTP implements the http.Handler interface. func (md Markdown) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { var cfg *Config for _, c := range md.Configs { if httpserver.Path(r.URL.Path).Matches(c.PathScope) { // not negated cfg = c break // or goto } } if cfg == nil { return md.Next.ServeHTTP(w, r) // exit early } // We only deal with HEAD/GET switch r.Method { case http.MethodGet, http.MethodHead: default: return http.StatusMethodNotAllowed, nil } var dirents []os.FileInfo var lastModTime time.Time fpath := r.URL.Path if idx, ok := httpserver.IndexFile(md.FileSys, fpath, md.IndexFiles); ok { // We're serving a directory index file, which may be a markdown // file with a template. Let's grab a list of files this directory // URL points to, and pass that in to any possible template invocations, // so that templates can customize the look and feel of a directory. fdp, err := md.FileSys.Open(fpath) switch { case err == nil: // nop case os.IsPermission(err): return http.StatusForbidden, err case os.IsExist(err): return http.StatusNotFound, nil default: // did we run out of FD? return http.StatusInternalServerError, err } defer fdp.Close() // Grab a possible set of directory entries. Note, we do not check // for errors here (unreadable directory, for example). It may // still be useful to have a directory template file, without the // directory contents being present. Note, the directory's last // modification is also present here (entry "."). dirents, _ = fdp.Readdir(-1) for _, d := range dirents { lastModTime = latest(lastModTime, d.ModTime()) } // Set path to found index file fpath = idx } // If not supported extension, pass on it if _, ok := cfg.Extensions[path.Ext(fpath)]; !ok { return md.Next.ServeHTTP(w, r) } // At this point we have a supported extension/markdown f, err := md.FileSys.Open(fpath) switch { case err == nil: // nop case os.IsPermission(err): return http.StatusForbidden, err case os.IsExist(err): return http.StatusNotFound, nil default: // did we run out of FD? return http.StatusInternalServerError, err } defer f.Close() fs, err := f.Stat() if err != nil { return http.StatusGone, nil } lastModTime = latest(lastModTime, fs.ModTime()) ctx := httpserver.Context{ Root: md.FileSys, Req: r, URL: r.URL, } html, err := cfg.Markdown(title(fpath), f, dirents, ctx) if err != nil { return http.StatusInternalServerError, err } w.Header().Set("Content-Type", "text/html; charset=utf-8") w.Header().Set("Content-Length", strconv.FormatInt(int64(len(html)), 10)) httpserver.SetLastModifiedHeader(w, lastModTime) if r.Method == http.MethodGet { w.Write(html) } return http.StatusOK, nil }