├── LICENSE ├── Readme.md ├── ci.yml ├── headers.go └── headers_test.go /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2021 TJ Holowaychuk tj@tjholowaychuk.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Header 2 | 3 | Header format parser to match Netlify's [format](https://www.netlify.com/docs/headers-and-basic-auth/). 4 | 5 | ## Example 6 | 7 | ```go 8 | /* 9 | X-Frame-Options: DENY 10 | X-XSS-Protection: 1; mode=block 11 | 12 | ## A path: 13 | /templates/index.html 14 | # Headers for that path: 15 | X-Frame-Options: DENY 16 | X-XSS-Protection: 1; mode=block 17 | 18 | /templates/index2.html 19 | X-Frame-Options: SAMEORIGIN 20 | ``` 21 | 22 | yields 23 | 24 | ```json 25 | { 26 | "/*": { 27 | "X-Frame-Options": [ 28 | "DENY" 29 | ], 30 | "X-Xss-Protection": [ 31 | "1; mode=block" 32 | ] 33 | }, 34 | "/templates/index.html": { 35 | "X-Frame-Options": [ 36 | "DENY" 37 | ], 38 | "X-Xss-Protection": [ 39 | "1; mode=block" 40 | ] 41 | }, 42 | "/templates/index2.html": { 43 | "X-Frame-Options": [ 44 | "SAMEORIGIN" 45 | ] 46 | } 47 | } 48 | ``` 49 | 50 | --- 51 | 52 | [![GoDoc](https://godoc.org/github.com/tj/go-headers?status.svg)](https://godoc.org/github.com/tj/go-headers) 53 | ![](https://img.shields.io/badge/license-MIT-blue.svg) 54 | ![](https://img.shields.io/badge/status-stable-green.svg) 55 | 56 | 57 | -------------------------------------------------------------------------------- /ci.yml: -------------------------------------------------------------------------------- 1 | version: 0.2 2 | 3 | phases: 4 | install: 5 | commands: 6 | - go get -t ./... 7 | build: 8 | commands: 9 | - go test -cover -v ./... 10 | -------------------------------------------------------------------------------- /headers.go: -------------------------------------------------------------------------------- 1 | // Package headers provides Netlify style _headers file format parsing. 2 | package headers 3 | 4 | import ( 5 | "bufio" 6 | "io" 7 | "net/http" 8 | "strings" 9 | 10 | "github.com/pkg/errors" 11 | ) 12 | 13 | // Must parse utility. 14 | func Must(m map[string]http.Header, err error) map[string]http.Header { 15 | if err != nil { 16 | panic(err) 17 | } 18 | 19 | return m 20 | } 21 | 22 | // Parse the given reader. 23 | func Parse(r io.Reader) (map[string]http.Header, error) { 24 | rules := make(map[string]http.Header) 25 | var fields http.Header 26 | var path string 27 | 28 | s := bufio.NewScanner(r) 29 | 30 | for s.Scan() { 31 | line := strings.TrimSpace(s.Text()) 32 | 33 | // empty 34 | if line == "" { 35 | continue 36 | } 37 | 38 | // comment 39 | if strings.HasPrefix(line, "#") { 40 | continue 41 | } 42 | 43 | // field 44 | if strings.Contains(line, ":") { 45 | if fields == nil { 46 | return nil, errors.New("path must precede fields") 47 | } 48 | 49 | parts := strings.Split(line, ":") 50 | k := strings.TrimSpace(parts[0]) 51 | v := strings.TrimSpace(parts[1]) 52 | fields.Add(k, v) 53 | continue 54 | } 55 | 56 | // path 57 | if path != "" && fields != nil { 58 | rules[path] = fields 59 | } 60 | 61 | // new path 62 | path = line 63 | 64 | // already exists 65 | if f, ok := rules[path]; ok { 66 | fields = f 67 | continue 68 | } 69 | 70 | fields = make(http.Header) 71 | } 72 | 73 | if fields != nil { 74 | rules[path] = fields 75 | } 76 | 77 | return rules, s.Err() 78 | } 79 | 80 | // ParseString parses the given string. 81 | func ParseString(s string) (map[string]http.Header, error) { 82 | return Parse(strings.NewReader(s)) 83 | } 84 | -------------------------------------------------------------------------------- /headers_test.go: -------------------------------------------------------------------------------- 1 | package headers_test 2 | 3 | import ( 4 | "encoding/json" 5 | "os" 6 | 7 | "github.com/tj/go-headers" 8 | ) 9 | 10 | func Example() { 11 | h := headers.Must(headers.ParseString(` 12 | /* 13 | X-Frame-Options: DENY 14 | X-XSS-Protection: 1; mode=block 15 | 16 | ## A path: 17 | /templates/index.html 18 | # Headers for that path: 19 | X-Frame-Options: DENY 20 | X-XSS-Protection: 1; mode=block 21 | 22 | /templates/index2.html 23 | X-Frame-Options: SAMEORIGIN 24 | 25 | /* 26 | Link: ; rel=preload; as=stylesheet 27 | Link: ; rel=preload; as=script 28 | Link: ; rel=preload; as=image 29 | `)) 30 | 31 | enc := json.NewEncoder(os.Stdout) 32 | enc.SetIndent("", " ") 33 | enc.Encode(h) 34 | // Output: 35 | // { 36 | // "/*": { 37 | // "Link": [ 38 | // "\u003c/style.css\u003e; rel=preload; as=stylesheet", 39 | // "\u003c/main.js\u003e; rel=preload; as=script", 40 | // "\u003c/image.jpg\u003e; rel=preload; as=image" 41 | // ], 42 | // "X-Frame-Options": [ 43 | // "DENY" 44 | // ], 45 | // "X-Xss-Protection": [ 46 | // "1; mode=block" 47 | // ] 48 | // }, 49 | // "/templates/index.html": { 50 | // "X-Frame-Options": [ 51 | // "DENY" 52 | // ], 53 | // "X-Xss-Protection": [ 54 | // "1; mode=block" 55 | // ] 56 | // }, 57 | // "/templates/index2.html": { 58 | // "X-Frame-Options": [ 59 | // "SAMEORIGIN" 60 | // ] 61 | // } 62 | // } 63 | } 64 | --------------------------------------------------------------------------------