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  }