golang.org/x/playground@v0.0.0-20230418134305-14ebe15bcd59/share.go (about) 1 // Copyright 2011 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package main 6 7 import ( 8 "bytes" 9 "crypto/sha256" 10 "encoding/base64" 11 "fmt" 12 "io" 13 "net/http" 14 ) 15 16 const ( 17 // This salt is not meant to be kept secret (it’s checked in after all). It’s 18 // a tiny bit of paranoia to avoid whatever problems a collision may cause. 19 salt = "Go playground salt\n" 20 21 maxSnippetSize = 64 * 1024 22 ) 23 24 type snippet struct { 25 Body []byte `datastore:",noindex"` // golang.org/issues/23253 26 } 27 28 func (s *snippet) ID() string { 29 h := sha256.New() 30 io.WriteString(h, salt) 31 h.Write(s.Body) 32 sum := h.Sum(nil) 33 b := make([]byte, base64.URLEncoding.EncodedLen(len(sum))) 34 base64.URLEncoding.Encode(b, sum) 35 // Web sites don’t always linkify a trailing underscore, making it seem like 36 // the link is broken. If there is an underscore at the end of the substring, 37 // extend it until there is not. 38 hashLen := 11 39 for hashLen <= len(b) && b[hashLen-1] == '_' { 40 hashLen++ 41 } 42 return string(b)[:hashLen] 43 } 44 45 func (s *server) handleShare(w http.ResponseWriter, r *http.Request) { 46 w.Header().Set("Access-Control-Allow-Origin", "*") 47 if r.Method == "OPTIONS" { 48 // This is likely a pre-flight CORS request. 49 return 50 } 51 if r.Method != "POST" { 52 http.Error(w, "Requires POST", http.StatusMethodNotAllowed) 53 return 54 } 55 if !allowShare(r) { 56 http.Error(w, "Either this isn't available in your country due to legal reasons, or our IP geolocation is wrong.", 57 http.StatusUnavailableForLegalReasons) 58 return 59 } 60 61 var body bytes.Buffer 62 _, err := io.Copy(&body, io.LimitReader(r.Body, maxSnippetSize+1)) 63 r.Body.Close() 64 if err != nil { 65 s.log.Errorf("reading Body: %v", err) 66 http.Error(w, "Server Error", http.StatusInternalServerError) 67 return 68 } 69 if body.Len() > maxSnippetSize { 70 http.Error(w, "Snippet is too large", http.StatusRequestEntityTooLarge) 71 return 72 } 73 74 snip := &snippet{Body: body.Bytes()} 75 id := snip.ID() 76 if err := s.db.PutSnippet(r.Context(), id, snip); err != nil { 77 s.log.Errorf("putting Snippet: %v", err) 78 http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) 79 return 80 } 81 82 fmt.Fprint(w, id) 83 } 84 85 func allowShare(r *http.Request) bool { 86 if r.Header.Get("X-AppEngine-Country") == "CN" { 87 return false 88 } 89 return true 90 }