Implement library support for Angular-style single page applications
This commit is contained in:
		
							parent
							
								
									f08165b0f1
								
							
						
					
					
						commit
						d5b4fcf0be
					
				
							
								
								
									
										32
									
								
								README.md
								
								
								
								
							
							
						
						
									
										32
									
								
								README.md
								
								
								
								
							| 
						 | 
					@ -28,3 +28,35 @@ else will be ignored.
 | 
				
			||||||
The interaction between range handling and compression also seems a little
 | 
					The interaction between range handling and compression also seems a little
 | 
				
			||||||
ill-defined; as we have pre-compressed data, however, we can consistently
 | 
					ill-defined; as we have pre-compressed data, however, we can consistently
 | 
				
			||||||
serve the exact same byte data for compressed files.
 | 
					serve the exact same byte data for compressed files.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Angular-style single-page application handling
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					If you wish to support an angular.js-style single page application, in which
 | 
				
			||||||
 | 
					a Javascript application uses the browser's history API to create a set of
 | 
				
			||||||
 | 
					virtual paths ("routes"), it is necessary to somehow intercept HTTP 404 errors
 | 
				
			||||||
 | 
					being returned from the handler and instead return an HTTP 200 with an HTML
 | 
				
			||||||
 | 
					document.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This can be achieved with a number of methods.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					If you have an nginx instance reverse proxying in front of `htpack`, then you
 | 
				
			||||||
 | 
					can use a couple of extra directives, for example:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						# prevent page loaded at "http://server.example/my-application" from
 | 
				
			||||||
 | 
						# requesting resources at "/*" when it should request them at
 | 
				
			||||||
 | 
						# "/my-application/*" instead
 | 
				
			||||||
 | 
						location = /my-application {
 | 
				
			||||||
 | 
							return 308 /my-application/;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						location /my-application/ {
 | 
				
			||||||
 | 
							proxy_to http://htpack-addr:8080/;
 | 
				
			||||||
 | 
							proxy_intercept_errors on;
 | 
				
			||||||
 | 
							error_page 404 =200 /my-application/;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					If you are using the handler as a library, then you may call
 | 
				
			||||||
 | 
					`handler.SetNotFound(filename)` to select a resource to return (with HTTP 200)
 | 
				
			||||||
 | 
					if a request is made for a resource that is not found. The filename must match
 | 
				
			||||||
 | 
					a packed resource, so it will be preceded with a `/` (for example it may be
 | 
				
			||||||
 | 
					`"/index.html"`).
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										26
									
								
								handler.go
								
								
								
								
							
							
						
						
									
										26
									
								
								handler.go
								
								
								
								
							| 
						 | 
					@ -72,6 +72,7 @@ type Handler struct {
 | 
				
			||||||
	dir       map[string]*packed.File
 | 
						dir       map[string]*packed.File
 | 
				
			||||||
	headers   map[string]string
 | 
						headers   map[string]string
 | 
				
			||||||
	startTime time.Time
 | 
						startTime time.Time
 | 
				
			||||||
 | 
						notFound  *packed.File
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SetHeader allows a custom header to be set on HTTP responses. These are
 | 
					// SetHeader allows a custom header to be set on HTTP responses. These are
 | 
				
			||||||
| 
						 | 
					@ -110,6 +111,28 @@ func (h *Handler) SetIndex(filename string) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SetNotFound allows overriding the returned resource when a request is made
 | 
				
			||||||
 | 
					// for a resource that does not exist. The default behaviour would be to return
 | 
				
			||||||
 | 
					// a standard HTTP 404 Not Found response; calling this function with an empty
 | 
				
			||||||
 | 
					// string will restore that behaviour.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This function will return an error if the named resource is not present in
 | 
				
			||||||
 | 
					// the packfile.
 | 
				
			||||||
 | 
					func (h *Handler) SetNotFound(notFound string) error {
 | 
				
			||||||
 | 
						if notFound == "" {
 | 
				
			||||||
 | 
							h.notFound = nil
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						notFound = path.Clean(notFound)
 | 
				
			||||||
 | 
						dir := h.dir[path.Clean(notFound)]
 | 
				
			||||||
 | 
						if dir == nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("no such resource %q", notFound)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						h.notFound = dir
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ServeHTTP handles requests for files. It supports GET and HEAD methods, with
 | 
					// ServeHTTP handles requests for files. It supports GET and HEAD methods, with
 | 
				
			||||||
// anything else returning a 405. Exact path matches are required, else a 404 is
 | 
					// anything else returning a 405. Exact path matches are required, else a 404 is
 | 
				
			||||||
// returned.
 | 
					// returned.
 | 
				
			||||||
| 
						 | 
					@ -130,9 +153,12 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	info := h.dir[path.Clean(req.URL.Path)]
 | 
						info := h.dir[path.Clean(req.URL.Path)]
 | 
				
			||||||
	if info == nil {
 | 
						if info == nil {
 | 
				
			||||||
 | 
							if h.notFound == nil {
 | 
				
			||||||
			http.NotFound(w, req)
 | 
								http.NotFound(w, req)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							info = h.notFound
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// set standard headers
 | 
						// set standard headers
 | 
				
			||||||
	w.Header().Set("Vary", "Accept-Encoding")
 | 
						w.Header().Set("Vary", "Accept-Encoding")
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue