mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-10-31 10:37:24 -04:00 
			
		
		
		
	I am not a lawyer, but according to the appendix of the license, these boilerplate notices should be included with every source file.
		
			
				
	
	
		
			146 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			146 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2015 Light Code Labs, LLC
 | |
| //
 | |
| // Licensed under the Apache License, Version 2.0 (the "License");
 | |
| // you may not use this file except in compliance with the License.
 | |
| // You may obtain a copy of the License at
 | |
| //
 | |
| //     http://www.apache.org/licenses/LICENSE-2.0
 | |
| //
 | |
| // Unless required by applicable law or agreed to in writing, software
 | |
| // distributed under the License is distributed on an "AS IS" BASIS,
 | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| // See the License for the specific language governing permissions and
 | |
| // limitations under the License.
 | |
| 
 | |
| package push
 | |
| 
 | |
| import (
 | |
| 	"net/http"
 | |
| 	"strings"
 | |
| 
 | |
| 	"github.com/mholt/caddy/caddyhttp/httpserver"
 | |
| 	"github.com/mholt/caddy/caddyhttp/staticfiles"
 | |
| )
 | |
| 
 | |
| func (h Middleware) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
 | |
| 	pusher, hasPusher := w.(http.Pusher)
 | |
| 
 | |
| 	// no push possible, carry on
 | |
| 	if !hasPusher {
 | |
| 		return h.Next.ServeHTTP(w, r)
 | |
| 	}
 | |
| 
 | |
| 	// check if this is a request for the pushed resource (avoid recursion)
 | |
| 	if _, exists := r.Header[pushHeader]; exists {
 | |
| 		return h.Next.ServeHTTP(w, r)
 | |
| 	}
 | |
| 
 | |
| 	headers := h.filterProxiedHeaders(r.Header)
 | |
| 
 | |
| 	// push first
 | |
| outer:
 | |
| 	for _, rule := range h.Rules {
 | |
| 		urlPath := r.URL.Path
 | |
| 		matches := httpserver.Path(urlPath).Matches(rule.Path)
 | |
| 		// Also check IndexPages when requesting a directory
 | |
| 		if !matches {
 | |
| 			indexFile, isIndexFile := httpserver.IndexFile(h.Root, urlPath, staticfiles.IndexPages)
 | |
| 			if isIndexFile {
 | |
| 				matches = httpserver.Path(indexFile).Matches(rule.Path)
 | |
| 			}
 | |
| 		}
 | |
| 		if matches {
 | |
| 			for _, resource := range rule.Resources {
 | |
| 				pushErr := pusher.Push(resource.Path, &http.PushOptions{
 | |
| 					Method: resource.Method,
 | |
| 					Header: h.mergeHeaders(headers, resource.Header),
 | |
| 				})
 | |
| 				if pushErr != nil {
 | |
| 					// if we cannot push (either not supported or concurrent streams are full - break)
 | |
| 					break outer
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// serve later
 | |
| 	code, err := h.Next.ServeHTTP(w, r)
 | |
| 
 | |
| 	// push resources returned in Link headers from upstream middlewares or proxied apps
 | |
| 	if links, exists := w.Header()["Link"]; exists {
 | |
| 		h.servePreloadLinks(pusher, headers, links)
 | |
| 	}
 | |
| 
 | |
| 	return code, err
 | |
| }
 | |
| 
 | |
| // servePreloadLinks parses Link headers from backend and pushes resources found in them.
 | |
| // For accepted header formats check parseLinkHeader function.
 | |
| //
 | |
| // If resource has 'nopush' attribute then it will be omitted.
 | |
| func (h Middleware) servePreloadLinks(pusher http.Pusher, headers http.Header, resources []string) {
 | |
| outer:
 | |
| 	for _, resource := range resources {
 | |
| 		for _, resource := range parseLinkHeader(resource) {
 | |
| 			if _, exists := resource.params["nopush"]; exists {
 | |
| 				continue
 | |
| 			}
 | |
| 
 | |
| 			if h.isRemoteResource(resource.uri) {
 | |
| 				continue
 | |
| 			}
 | |
| 
 | |
| 			err := pusher.Push(resource.uri, &http.PushOptions{
 | |
| 				Method: http.MethodGet,
 | |
| 				Header: headers,
 | |
| 			})
 | |
| 
 | |
| 			if err != nil {
 | |
| 				break outer
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (h Middleware) isRemoteResource(resource string) bool {
 | |
| 	return strings.HasPrefix(resource, "//") ||
 | |
| 		strings.HasPrefix(resource, "http://") ||
 | |
| 		strings.HasPrefix(resource, "https://")
 | |
| }
 | |
| 
 | |
| func (h Middleware) mergeHeaders(l, r http.Header) http.Header {
 | |
| 	out := http.Header{}
 | |
| 
 | |
| 	for k, v := range l {
 | |
| 		out[k] = v
 | |
| 	}
 | |
| 
 | |
| 	for k, vv := range r {
 | |
| 		for _, v := range vv {
 | |
| 			out.Add(k, v)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return out
 | |
| }
 | |
| 
 | |
| func (h Middleware) filterProxiedHeaders(headers http.Header) http.Header {
 | |
| 	filter := http.Header{}
 | |
| 
 | |
| 	for _, header := range proxiedHeaders {
 | |
| 		if val, ok := headers[header]; ok {
 | |
| 			filter[header] = val
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return filter
 | |
| }
 | |
| 
 | |
| var proxiedHeaders = []string{
 | |
| 	"Accept-Encoding",
 | |
| 	"Accept-Language",
 | |
| 	"Cache-Control",
 | |
| 	"Host",
 | |
| 	"User-Agent",
 | |
| }
 |