The net/http
package provides the ListenAndServer()
function that creates a default server. However, this function does not allow you to define timeouts to manage bad connections or server resources, so you should define custom server.
Create a custom server with the Server
type. The Server
type is a struct with the following fields:
type Server struct {
Addr string
Handler Handler
TLSConfig *tls.Config
ReadTimeout time.Duration
ReadHeaderTimeout time.Duration
WriteTimeout time.Duration
IdleTimeout time.Duration
MaxHeaderBytes int
TLSNextProto map[string]func(*Server, *tls.Conn, Handler)
ConnState func(net.Conn, ConnState)
ErrorLog *log.Logger
BaseContext func(net.Listener) context.Context
ConnContext func(ctx context.Context, c net.Conn) context.Context
}
Servers receive requests, dispatch those requests to the handler that is registered to the receiving path, and the handler sends a response. In Go, each incoming request is served in its own goroutine, which means each request is handled concurrently.
You create the server, then use HandleFunc
to register routes to handler functions. Then you use HandlerFunc
to define the handler function for the route.
http.Handler
is an interface that has the ServeHTTP(w ResponseWriter, r *Request)
signature.http.HandlerFunc
is an adapter type that lets you define ordinary functions as HTTP handlers. It adds a ServerHTTP(w, r)
method to whatever function you pass it.http.HandleFunc
registers a handler with a multiplexer server. It is syntactic sugar–it transforms a function to a handler and registers it in one step. It accepts two arguments: the path as a string
, and the handler function. It implements ServeHTTP()
.http.NewServeMux()
returns a custom server. It implements ServeHTTP()
.The following example registers the homeHandler
twice, but each method is functionally equivalent:
func homeHandler(w http.ResponseWriter, r *http.Request) {
// handler logic
}
func main() {
...
mux.Handle("/", http.HandlerFunc(homeHandler))
mux.HandleFunc("/", homeHandler)
}
Create handlers and helpers as methods on the application struct. This allows the handlers to access any application dependencies, like loggers:
// main.go
type application struct {
log *log.Logger,
...
}
// handlerName.go
func (app *application) handlerName(w, r) {...}
Sometimes requests pass information as key-value pairs in the URL:
http://localhost:4000/snippet/view?id=123
The values after the ?
are query strings, and they take the form of key
=value
. You can access the key
portion of the query string and retrieve the value
:
id, err := r.URL.Query().Get("id")
The proceeding example uses the Query()
method on the request’s URL
field. Query()
returns a Values
type, which is essenitally a map that holds all the query parameter key-value pairs. The Values
type has the Get()
method that returns a string
value associated with the key
provided to Get()
.
Responses are made with the http.ResponseWriter
interface, which is usually represented with a w
.
It is common to use fmt.Fprintf()
to write a response:
fmt.Fprintf(w, "Display a specific snippet with ID %d...", id)
http.StatusText
. For example:http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
404 Not Found
status code.Go provides the following headers automatically:
Date
Content-Length
Content-Type
(see w.Header().Set())header-name
, header-value
)header-name
:header-value
format. A common example is setting the Content-Type
header when you send JSON data:w.Header().Set("Content-Type", "application/json")
You must write the w.Header().Set()
method before w.WriteHeader()
or w.Write()
.
status-code
)If you do not call this method before w.Write([]bytes())
, then the Write
method returns a 200 OK
status code automatically.
w.WriteHeader
is not commonly used. You usually pass the writer (w
) value to an http
package helper function.
The http
package provides constants for request methods and status code. These constants improve readability and reduce runtime errors that result from typos. For example:
func snippetCreate(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
w.Header().Set("Allow", http.MethodPost)
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
return
}
w.Write([]byte("Create a new snippet..."))
}
You can create a router with Go’s standard library or a 3rd party package.
The Go standard library does not support naed parameters in URLs, so you might consider a 3rd-party solution for servers with complex routes.
By default, Go supports two types of URL patterns:
/path/endpoint
/
). A request must match a fixed path exactly./path/bucket/
/path/bucket/*
.Create a Go router with these components:
http.Handler
type to satisfy the server’s Handler
field.Server
struct that wraps the http.Handler
type.Server
that registers routes.The following example creates all required components:
const (
shorteningRoute = "/s"
resolveRoute = "/r/"
healthCheckRoute = "/health"
)
// mux is an unexported http.Handler
type mux http.Handler
// Server is a custom server type.
type Server struct {
mux // the server only exports ServeHTTP
}
// NewServer returns an instance of the custom Server type.
func NewServer() *Server {
var s Server
s.registerRoutes()
return &s
}
func (s *Server) registerRoutes() {
mux := http.NewServeMux()
mux.Handle(shorteningRoute, httpio.Handler(s.shorteningHandler))
mux.Handle(resolveRoute, httpio.Handler(s.resolveHandler))
mux.HandleFunc(healthCheckRoute, s.healthCheckHandler)
s.mux = mux
}
This example uses the julienschmidt router. This router has a few advantages over the Go standard library router:
"/"
) path exactly, so no need for manual checks.404
error handlers.To create a router, use the New()
method. You can register handlers with the Handler()
or HandlerFunc()
method:
router := httprouter.New()
router.HandlerFunc(http.MethodGet, "/snippet/view/:id", app.snippetView)
:id
is a named parameter–it acts as a wildcard for the path segment that it represents.
Always use the HTTP method constants when you register a route.
You can also use a parameter that matches everything, which is useful when registering static files:
fileServer := http.FileServer(http.Dir("./ui/static/"))
router.Handler(http.MethodGet, "/static/*filepath", http.StripPrefix("/static", fileServer))
The router.NotFound
handler is called when no matching route is found. You can assign it an http.HandlerFunc(func{w, r})
to handle 404
responses:
router.NotFound = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
app.notFound(w)
})
Use alice.New()
to chain middleware in a reader-friendly format:
standard := alice.New(app.recoverPanic, app.logRequest, secureHeaders)
Next, return the router:
return standard.Then(router)
When you create a router, it is a good practice to register placeholder handlers that return dummy plaintext responses. This helps you set up the routing infrastructure while managing the business logic at a later time.
In your handlers file, add the placeholder routes:
func (app *application) userSignup(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Display an HTML form for signing up a new user")
}
func (app *application) userSignupPost(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Create a new user...")
}
...
In your routes file (where you register your routes), register the routes:
router := httprouter.New()
...
router.Handler(http.MethodGet, "/user/signup", dynamic.ThenFunc(app.userSignup))
router.Handler(http.MethodPost, "/user/signup", dynamic.Then(app.userSignupPost()))
There are two important concepts that are central to creating a custom server in Go:
Server
type: listens for client connections and routes incoming requests in a goroutine to a type that implements the Handler
interface.Handler
interface: contains one method, ServeHTTP(ResponseWriter, *Request)
.You can create a server with any type as long as it implements Handler
interface. A server should have the following:
A suggested server directory structure:
.
├── cmd
│ └── server
│ └── server.go
├── internal
│ └── helpers
│ ├── handler.go
│ └── helpers.go
├── errors
│ └── errors.go
└── service-name
├── server.go
├── server_test.go
└── service.go
In the preceding example:
cmd/serverdir/server.go
contains the main
method. This is where you create a logger and execute the server’s ListenAndServe()
method.internal/helpers/*
contains private code such as JSON formatters and handlers for the handler chaining pattern.errors/errors.go
contains custom errors.server-name/server.go
contains the service’s server implementation, including the constructor, routes, etc.service-name/server_test.go
tests server.go
with the httptest.NewRecorder()
method.server-name/service.go
contains the service’s business logic.In the project root, create a subdirectory and file called json-server/server.go
:
mkdir json-server
touch json-server/server.go
The Server
type must implement the Handler
interface, so embed the Handler
interface in the Server type:
type mux http.Handler
type Server struct {
mux
}
Interface embedding elevates the interface methods to the containing type, so the Server
implements the Handler
’s ServeHTTP
method.
In the project root, create the main server.go
file:
mkdir -p cmd/server/server.go
In the main
method, define the address
that the server listens on in host:port
format. If you provide only the port
, then the server listens on all available network interfaces. This means that host
is important only if your machine has multiple network interfaces.
Additionally, define a timeout
duration. The timeout
makes the server return after timeout
seconds if the response is not complete:
const (
addr = "localhost:8080"
timeout = 10 * time.Second
)
In some circumstances, you will see
:http
or:http-alt
in place of a port number. This means the server looks up the port number in theetc/services
file.
Create a new multiplexer server:
server := server.NewServer()
Create the server. Assign the address
and timeout
variables. For Handler
, use TimeoutHandler
so you can assign the timeout value:
server := &http.Server{
Addr: addr,
Handler: http.TimeoutHandler(server, timeout, "timeout"), // runs server for timeout seconds
ReadTimeout: timeout, // max time for reading the entire request
}
Start the server. The normal behavior for stopping a server returns the http.ErrServerClosed
error, so check for that error and return a message:
if err := server.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
fmt.Fprintln(os.Stderr, "server closed unexpectedly:", err)
}
customServer := &http.Server{
Addr: fmt.Sprintf("%s:%d", *host, *port),
Handler: MuxFunc(*todoFile),
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
}
Start the server with ListenAndServe
:
if err := s.ListenAndServe(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
Add timeouts to the server instance. The following timeouts apply to all requests:
srv := &http.Server{
...
IdleTimeout: time.Minute,
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
In the preceding configuration:
IdleTimeout
closes all keep-alive connections after one minute. A keep-alive is HTTP connection reuse, where a single TCP connection is used to send and receive multiple HTTP requests and responses rather than opening new connections.
Always set an IdleTimeout
for the server.
ReadTimeout
sets the amount of time that the request headers and body are read after the request is first accepted. If the read operation exceeds the setting, the connection is closed.
By default, IdleTimeout
uses the same setting as ReadTimout
if it is not explicitly set. Again–always set IdleTimeout
on the server.
WriteTimeout
closes the connection if the server tries to write to the connection after the set length. This setting’s timeout period behavior depends upon the protocol:
This setting does not impact long-running handlers–it impacts how long the handler writes from its buffer to the connection when it returns.
To server static files (files that the server does not process), you have to create a Handler
with the FileServer()
function. The FileServer()
handler serves HTTP requests with the files stored in the path that you pass as a function argument:
staticFiles := http.FileServer(http.Dir("/public"))
When you register the file server handle, you have to strip the path pattern from the request URL, or the server attempts to serve files from an invalid path and returns a 404:
mux.Handle("/static", http.StripPrefix("/static", staticFiles))
The preceding examples create a Handler
named staticFiles
that serves HTTP requests with the contents of the /public
directory. When you register the staticFiles
, you are telling the multiplexer that when there is a request to the /static
path, strip /static
from the request URL, and then search the /public
directory for a file that matches the request.
After you create a file server, users can access the static files in a browser by going to /static/path/to/assets/
. To disable this, create a blank index.html
file in each subdirectory in the /static/
directory.
The following sections detail how to create, register, and chain middlewares with Go’s standard library. You can use the justinas/alice package to simplify middleware code.
Middleware is code that executes on a request before or after your handlers. It is functionality that you want to share among your HTTP request handlers. For example:
You chain middleware. After one middleware function executes, it calls the ServeHTTP()
method on the next handler until a response returns.
Middleware uses the following pattern:
func myMiddleware(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
// TODO: Execute our middleware logic here...
next.ServeHTTP(w, r)
}
return http.HandlerFunc(fn)
}
The pattern does the following:
myMiddleware
wraps the next
handler that you pass as an argument.fn
is a closure that has access to any values in the next
handler’s scope. It returns next.ServerHTTP()
.myMiddleware
converts the closure to a handler and returns.This pattern can be simplified as follows:
func myMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// TODO: Execute our middleware logic here...
next.ServeHTTP(w, r)
})
}
If you want the middleware to execute on all requests, place it before the server mux:
myMiddleware -> mux -> handler
If you want the middleware to execute on a specific request, place it after the server mux, but before the request handler:
mux -> myMiddleware -> handler
Create a new /cmd/web/middleware.go
file and add a middleware function that adds secure headers to each request. These headers satisfy OWASP standards:
func secureHeaders(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Security-Policy", "default-src 'self'; style-src 'self' fonts.googleapis.com; font-src fonts.gstatic.com")
w.Header().Set("Referrer-Policy", "origin-when-cross-origin")
w.Header().Set("X-Content-Type-Options", "nosniff")
w.Header().Set("X-Frame-Options", "deny")
w.Header().Set("X-XSS-Protections", "0")
next.ServeHTTP(w, r)
})
}
The previous example adds these headers:
During development, CSP headers are a common cause of blocked resource loads.
Referrer
header when users navigate away from your site.Log all requests before they are dispatched to a handler:
func (app *application) logRequest(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
app.infoLog.Printf("%s - %s %s %s", r.RemoteAddr, r.Proto, r.Method, r.URL.RequestURI())
next.ServeHTTP(w, r)
})
}
The preceding middleware is added as a method on the application so that it can access the application dependencies, like the infoLog
:
type application struct {
...
infoLog *log.Logger
...
}
If your middleware needs to execute on all routes, register it where you register routes. This example returns the security middleware from the logging middleware, and the security middleware returns the mux
.
// return a handler
func (app *application) routes() http.Handler {
mux := http.NewServeMux()
// register routes
return app.logRequest(secureHeaders(mux))
}
Think of the
mux
as the main handler that you are returning–it is the handler registered to your server struct inmain
–so the other handlers enclose it.
If there is a panic in a handler, the panic is isolated in the goroutine that runs the handler, so your application does not stop. However, you should handle panics to provide a good user experience.
The following middleware handles panics in a handler goroutine. It recovers from the panic, then sets a Connection: close
header so the server closes the panicked connection.
func (app *application) recoverPanic(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Create a deferred function (which will always be run in the event
// of a panic as Go unwinds the stack).
defer func() {
// Use the builtin recover function to check if there has been a
// panic or not. If there has...
if err := recover(); err != nil {
// Set a "Connection: close" header on the response.
w.Header().Set("Connection", "close")
// Call the app.serverError helper method to return a 500
// Internal Server response.
app.serverError(w, fmt.Errorf("%s", err))
}
}()
next.ServeHTTP(w, r)
})
}
Add this handler to the beginning of the middleware chain so that it recovers from any panic that occurs in any handler:
func (app *application) routes() http.Handler {
mux := http.NewServeMux()
// register routes
return app.recoverPanic(app.logRequest(secureHeaders(mux)))
}
The previous panic handlers recover only when the panic occurs in the same goroutine that runs the handler. If your handler spins off another goroutine that might panic, you can add the following anonymous function to do the work and handle the possible panic:
func myHandler(w http.ResponseWriter, r *http.Request) {
...
// Spin up a new goroutine to do some background processing.
go func() {
defer func() {
if err := recover(); err != nil {
log.Print(fmt.Errorf("%s\n%s", err, debug.Stack()))
}
}()
doSomeBackgroundProcessing()
}()
w.Write([]byte("OK"))
}
Parse a form with the Request’s .ParseForm()
method. This method stores data from the form in the requests PostForm
field as a map of Values
. In addition, the Request type has a Form
field. The two have the following differences:
PostForm
is populated for only POST
, PATCH
, and PUT
requests.Form
is populated for all requests, including query string parameters. Use the Get()
method to access query string parameters.The name
attribute of each HTML form element is the key in the map, and you access the value with the .Get()
method. For example:
<input type="text" name="title">
To get the value of this input, use the following:
title := r.PostForm.Get("title")
Consider the following form:
<form action="/snippet/create" method="post">
<div>
<label>Title:</label>
<input type="text" name="title">
</div>
<div>
<label>Content:</label>
<textarea name="content"></textarea>
</div>
<div>
<label>Delete in:</label>
<input type="radio" name="expires" value="365" checked> One Year
<input type="radio" name="expires" value="7"> One Week
<input type="radio" name="expires" value="1"> One Day
</div>
<div>
<input type="submit" value="Publish snippet">
</div>
</form>
The following method parses the form, extracts its values with error checking, then inserts the values in a table. At the end, it redirects the request to a new path with Redirect()
:
type snippetCreateForm struct {
Title string
Content string
Expires int
validator.Validator
}
func (app *application) snippetCreatePost(w http.ResponseWriter, r *http.Request) {
// First we call r.ParseForm() which adds any data in POST request bodies
// to the r.PostForm map. This also works in the same way for PUT and PATCH
// requests. If there are any errors, we use our app.ClientError() helper to
// send a 400 Bad Request response to the user.
err := r.ParseForm()
if err != nil {
app.clientError(w, http.StatusBadRequest)
return
}
// The r.PostForm.Get() method always returns the form data as a *string*.
// However, we're expecting our expires value to be a number, and want to
// represent it in our Go code as an integer. So we need to manually covert
// the form data to an integer using strconv.Atoi(), and we send a 400 Bad
// Request response if the conversion fails.
expires, err := strconv.Atoi(r.PostForm.Get("expires"))
if err != nil {
app.clientError(w, http.StatusBadRequest)
return
}
// Use the r.PostForm.Get() method to retrieve the title and content
// from the r.PostForm map.
form := snippetCreateForm{
Title: r.PostForm.Get("title"),
Content: r.PostForm.Get("content"),
Expires: expires,
}
form.CheckField(validator.NotBlank(form.Title), "title", "This field cannot be blank")
form.CheckField(validator.MaxChars(form.Title, 100), "title", "This field cannot be more than 100 characters long")
form.CheckField(validator.NotBlank(form.Content), "content", "This field cannot be blank")
form.CheckField(validator.PermittedInt(form.Expires, 1, 7, 365), "expires", "This field must equal 1, 7 or 365")
if !form.Valid() {
data := app.newTemplateData(r)
data.Form = form
app.render(w, http.StatusUnprocessableEntity, "create.tmpl.html", data)
return
}
id, err := app.snippets.Insert(form.Title, form.Content, form.Expires)
if err != nil {
app.serverError(w, err)
return
}
http.Redirect(w, r, fmt.Sprintf("/snippet/view/%d", id), http.StatusSeeOther)
}
If a form field sends multiple values, you have to loop through the PostForm
. For example:
<input type="checkbox" name="items" value="foo"> Foo
<input type="checkbox" name="items" value="bar"> Bar
<input type="checkbox" name="items" value="baz"> Baz
for i, item := range r.PostForm["items"] {
fmt.Fprintf(w, "%d: Item %s\n", i, item)
}
ParseForm
cannot parse request bodies that exceed 10MB, or it returns an error. You can change this amount using MaxBytesReader()
:
// Limit the request body size to 4096 bytes
r.Body = http.MaxBytesReader(w, r.Body, 4096)
err := r.ParseForm()
if err != nil {
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
When the limit is reached, the server closes the underlying TCP connection.
See Validation Snippets for Go for field validation patterns.
Validate form values with standard if
clauses and your validation criteria for best practices. A useful pattern is to create a map, and store the field and a discription of any invalid data in the map:
// Initialize a map to hold any validation errors for the form fields.
fieldErrors := make(map[string]string)
// validate fields
// If there are any errors, dump them in a plain text HTTP response and
// return from the handler.
if len(fieldErrors) > 0 {
fmt.Fprint(w, fieldErrors)
return
}
Create a session with the scs package with the following steps:
First, create a table to store the session data.
In the main
method:
New()
.In routes.go
:
5. Create a new middleware chain.
6. Update any routes with the middleware chain.
In the relevant handlers:
In the newTemplateData
helper method:
In templates.go
:
templateData
struct that stores the data that you want to pass to the template.Update the template to display the dynamic data from templateData
.
Now, the LoadAndSave()
middleware checks each incoming request for a cookie. If a cookie is present, it does the following:
Any changes to session data are updated in the handler request context, and then the middleware updates the changes when it returns.
You might have concurrent code that is still running when your server fails, and you need to make sure that the process completes before the server stops. You can add a WaitGroup to the application struct, and wait until all goroutines complete before you shut down the server.
Add the WaitGroup to the application struct:
// cmd/api/main.go
type application struct {
config config
logger *jsonlog.Logger
models data.Models
mailer mailer.Mailer
wg sync.WaitGroup
}
Wait for all goroutines to complete before you shutdown the server:
// cmd/api/server.go
func (app *application) serve() error {
srv := &http.Server{
Addr: fmt.Sprintf(":%d", app.config.port),
Handler: app.routes(),
IdleTimeout: time.Minute,
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
shutdownError := make(chan error)
go func() {
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
s := <-quit
app.logger.PrintInfo("caught signal", map[string]string{
"signal": s.String(),
})
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
// Call Shutdown() on the server like before, but now we only send on the
// shutdownError channel if it returns an error.
err := srv.Shutdown(ctx)
if err != nil {
shutdownError <- err
}
// Log a message to say that we're waiting for any background goroutines to
// complete their tasks.
app.logger.PrintInfo("completing background tasks", map[string]string{
"addr": srv.Addr,
})
// Call Wait() to block until our WaitGroup counter is zero --- essentially
// blocking until the background goroutines have finished. Then we return nil on
// the shutdownError channel, to indicate that the shutdown completed without
// any issues.
app.wg.Wait()
shutdownError <- nil
}()
app.logger.PrintInfo("starting server", map[string]string{
"addr": srv.Addr,
"env": app.config.env,
})
err := srv.ListenAndServe()
if !errors.Is(err, http.ErrServerClosed) {
return err
}
err = <-shutdownError
if err != nil {
return err
}
app.logger.PrintInfo("stopped server", map[string]string{
"addr": srv.Addr,
})
return nil
}
Implement the fields that you need when you define a custom server:
A multiplexer maps incoming requests to the proper handler functions using the request URL. net/http
provides the DefaultServeMux function that returns the default multiplexer, but you should define a custom multiplexer for the following reasons:
func todoRouter(todoFile string, l sync.Locker) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
list := &todo.List{}
l.Lock()
defer l.Unlock()
if err := list.Get(todoFile); err != nil {
replyError(w, r, http.StatusInternalServerError, err.Error())
return
}
if r.URL.Path == "" {
switch r.Method {
case http.MethodGet:
getAllHandler(w, r, list)
case http.MethodPost:
addHandler(w, r, list, todoFile)
default:
message := "Method not supported"
replyError(w, r, http.StatusMethodNotAllowed, message)
}
return
}
id, err := validateID(r.URL.Path, list)
if err != nil {
if errors.Is(err, ErrNotFound) {
replyError(w, r, http.StatusNotFound, err.Error())
return
}
replyError(w, r, http.StatusBadRequest, err.Error())
return
}
switch r.Method {
case http.MethodGet:
getOneHandler(w, r, list, id)
case http.MethodDelete:
deleteHandler(w, r, list, id, todoFile)
case http.MethodPatch:
patchHandler(w, r, list, id, todoFile)
default:
message := "Method not supported"
replyError(w, r, http.StatusMethodNotAllowed, message)
}
}
}
Multiplexer definition:
func newMux(todoFile string) http.Handler {
m := http.NewServeMux()
mu := &sync.Mutex{}
m.HandleFunc("/", rootHandler)
t := todoRouter(todoFile, mu)
m.Handle("/todo", http.StripPrefix("/todo", t))
m.Handle("/todo/", http.StripPrefix("/todo/", t))
return m
}