github.com/packtpublishing/learning-functional-programming-in-go@v0.0.0-20230130084745-8b849f6d58c4/Chapter07/func-param/src/server/server.go (about) 1 package server 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "github.com/pkg/errors" 7 "log" 8 "net/http" 9 "os" 10 "strconv" 11 ) 12 13 const ( 14 defaultServerMaxMessageSize = 1024 * 1024 * 4 15 defaultMaxNumber = 30 16 defaultMaxConcurrentConnections = 2 17 ) 18 19 var defaultServerOptions = options { 20 maxMessageSize: defaultServerMaxMessageSize, 21 maxNumber: defaultMaxNumber, 22 maxConcurrentConnections: defaultMaxConcurrentConnections, 23 } 24 25 type Server struct { 26 logger Logger 27 opts options 28 handler http.Handler 29 } 30 31 type options struct { 32 maxMessageSize int 33 maxNumber int 34 maxConcurrentConnections int 35 convertFn convert 36 useNumberHandler bool 37 } 38 39 type Logger interface { 40 Printf(format string, v ...interface{}) 41 } 42 43 func New(opt ...ServerOption) (*Server, error) { 44 opts := defaultServerOptions 45 for _, f := range opt { 46 err := f(&opts) 47 if err != nil { 48 return nil, errors.Wrap(err, "error setting option") 49 } 50 } 51 s := &Server{ 52 opts: opts, 53 logger: log.New(os.Stdout, "", 0), 54 } 55 s.register() 56 return s, nil 57 } 58 59 func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { 60 s.handler.ServeHTTP(w, r) 61 } 62 63 func (s *Server) register() { 64 mux := http.NewServeMux() 65 if s.opts.useNumberHandler { 66 mux.Handle("/", http.HandlerFunc(s.displayNumber)) 67 } else { 68 mux.Handle("/", http.FileServer(http.Dir("./"))) 69 } 70 s.handler = mux 71 } 72 73 func (s *Server) displayNumber(w http.ResponseWriter, r *http.Request) { 74 s.logger.Printf("displayNumber called with number=%s\n", r.URL.Query().Get("number")) 75 if numberParam := r.URL.Query().Get("number"); numberParam != "" { 76 number, err := strconv.Atoi(numberParam) 77 if err != nil { 78 writeJSON(w, map[string]interface{}{ 79 "error": fmt.Sprintf("invalid number (%v)", numberParam), 80 }, http.StatusBadRequest) 81 } 82 if number > s.opts.maxNumber { 83 writeJSON(w, map[string]interface{}{ 84 "error": fmt.Sprintf("number (%d) too big. Max number: %d", number, s.opts.maxNumber), 85 }, http.StatusBadRequest) 86 } else { 87 var displayNumber string 88 if s.opts.convertFn == nil { 89 displayNumber = numberParam 90 } else { 91 displayNumber, err = s.opts.convertFn(number) 92 } 93 if err != nil { 94 writeJSON(w, map[string]interface{}{ 95 "error": "error running convertFn number", 96 }, http.StatusBadRequest) 97 } else { 98 writeJSON(w, map[string]interface{}{ 99 "displayNumber": displayNumber, 100 }) 101 } 102 } 103 } else { 104 writeJSON(w, map[string]interface{}{ 105 "error": "missing number", 106 }, http.StatusBadRequest) 107 } 108 } 109 110 func writeJSON(w http.ResponseWriter, v interface{}, statuses ...int) { 111 w.Header().Set("Content-Type", "application/json;charset=utf-8") 112 if len(statuses) > 0 { 113 w.WriteHeader(statuses[0]) 114 } 115 json.NewEncoder(w).Encode(v) 116 }