github.com/gocaveman/caveman@v0.0.0-20191211162744-0ddf99dbdf6e/webutil/webutil.go (about) 1 // General utilities for web serving. 2 // 3 // To qualify for inclusion in this package, the thing in question must be: 4 // 5 // Not already solved by or included in the Go stdlib. 6 // 7 // Not already solved by or included in a third party package or cost of including that package outweighs the cost of including it here. 8 // 9 // Generally useful for web serving. Features which are intensely Caveman-specific belong elsewhere. Features which follow a pattern used or introduced in Caveman but don't directly depend on it may be okay. 10 // 11 // Don't fit cleanly into one of the other Caveman packages. 12 // 13 // Small enough that they don't deserve their own package. 14 // 15 // Simple enough to not bloat this package. 16 // 17 // Complex enough to bother putting in a separate package at all (avoid one-line functions). 18 // 19 // Interfaces which connect disparate packages without introducing dependencies may be okay. (e.g. DataSource, ReadSeekCloser) 20 // 21 // Avoid introducing non-stdlib dependencies, pretty much at all costs. 22 // 23 // Must be worth maintaining as part of this package (as opposed to e.g. just copying and pasting it around). 24 // 25 package webutil 26 27 import ( 28 "bufio" 29 "bytes" 30 "fmt" 31 "io" 32 "os" 33 "runtime" 34 "strings" 35 ) 36 37 // ErrNotFound is a generic "not found" error. Useful to communicate that generic concept 38 // across packages without introducing dependencies. For convenience it is an alias 39 // of os.ErrNotExist, which means os.IsNotExist() will return true for it. 40 var ErrNotFound = os.ErrNotExist 41 42 var ErrAlreadyExists = os.ErrExist 43 44 // MainOnly checks the call stack to ensure that the caller is in the main package. 45 // Used to defend against inexperienced developers trying to read from a registry anywhere 46 // but in the main package. The argument says how many levels of the stack to remove. 47 // Use 0 to indicate the function that called this one, 1 to indicate it's caller, etc. 48 func MainOnly(n int) { 49 50 stackbuf := make([]byte, 4096) 51 stackbuf = stackbuf[:runtime.Stack(stackbuf, false)] 52 53 // log.Printf("STACK\n%s", stackbuf) 54 55 // sample stack: 56 57 // goroutine 1 [running]: 58 // main.main.func1() 59 // /Volumes/Files/git/caveman/src/github.com/gocaveman/quickstart-full/main.go:60 +0x77 60 // main.main() 61 // /Volumes/Files/git/caveman/src/github.com/gocaveman/quickstart-full/main.go:62 +0x61d 62 63 r := bufio.NewReader(bytes.NewReader(stackbuf)) 64 65 r.ReadString('\n') // "goroutine N ..." 66 67 c := -2 68 for { 69 70 line, err := r.ReadString('\n') 71 if err == io.EOF { 72 break 73 } 74 75 if c < n { 76 c++ 77 continue 78 } 79 80 // lines starting with tabs are the filename:lineno ones, skip those 81 if strings.HasPrefix(line, "\t") { 82 continue 83 } 84 85 // first thing before the period should be the package name 86 lineParts := strings.SplitN(line, ".", 2) 87 88 if lineParts[0] != "main" { 89 panic(fmt.Errorf("call must be made from main package, found %q instead", line)) 90 } 91 92 break 93 94 } 95 96 }