SlideShare a Scribd company logo
1 of 42
net/http and the
http.Handler interface
(or simply put, building web servers in Go)
Who am I?
● Work at
● Go developer since 2013
● Currently building cloud based energy
optimization system for commercial buildings
(using Go)
● Joakim Gustin
Agenda
● The basics
● Routing
● Middleware
● Accessing “globals”
● Testing
● Tips and tricks
HTTP in Go
● Built in web server (net/http)
● Core piece of the Go standard library
● Can be run facing clients (even Cloudflare does)
● Handles TLS
● Automatic support for HTTP/2
net/http - ListenAndServe(this blue means
official GoDoc)
The http.Handler interface
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
type ResponseWriter interface {
Header() Header
WriteHeader(statusCode int)
Write([]byte) (int, error)
}
type Request struct {
Method string
URL *url.URL
Body io.ReadCloser
// … rest omitted
}
The naive approach
import “net/http”
type myServer struct {}
func (ms *myServer) ServeHTTP(rw ResponseWriter, req *Request) {
if req.method == “GET” {
rw.Write([]byte(“hello, Go West”)
}
}
func main() {
http.ListenAndServe(":8080", myServer)
}
How about some routing?
How about some routing?
import “net/http”
type myServer struct {}
func (ms *myServer) ServeHTTP(rw ResponseWriter, req *Request) {
if req.method == “GET” {
rw.Write([]byte(“hello, Go West”)
}
}
func main() {
http.Handle("/hello", myHandler)
http.ListenAndServe(":8080", nil) // nil, wut?
}
The http.ServeMux
import “net/http”
type myHandler struct {}
func (mh *myHandler) ServeHTTP(rw ResponseWriter, req *Request) {
if req.method == “GET” {
rw.Write([]byte(“hello”)
}
}
func main() {
mux := http.NewServeMux()
mux.Handle("/hello", myHandler)
http.ListenAndServe(":8080", mux) // phew, much better
}
The http.ServeMux
Can we make this part simpler?
type myServer struct {}
// implementing the http.Handler interface
func (ms *myServer) ServeHTTP(rw ResponseWriter, req *Request) {
if req.method == “GET” {
rw.Write([]byte(“hello”)
}
}
Revisiting the go type system
type myServer struct {} // Define a type
func (s myServer) DoSomething() {} // Define a method on it
type serverList []myServer // A type can be many things
func (sl serverList) DoSomething() {} // yep, valid
// wait a minute, functions are first class citizens in Go, right?
type mathFunc func(int) int
func (mf mathFunc) DoSomething() {} // OMG, yes it’s valid
A function implementing an interface?!
type HandlerFunc func(http.ResponseWriter, *http.Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
SURPRISE!
Revisiting our example
import “net/http”
// Look ma, no struct!
func myHandler(rw ResponseWriter, req *Request) {
if req.method == “GET” {
rw.Write([]byte(“hello”)
}
}
func main() {
mux := http.NewServeMux()
mux.Handle("/hello", http.HandlerFunc(myHandler))
log.Fatal(http.ListenAndServe(":8080", mux))
}
Revisiting our example
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/hello", myHandler)
log.Fatal(http.ListenAndServe(":8080", mux))}
}
Lets talk about
routing
Routing the std lib way
func editHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[len("/edit/"):]
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/edit/", editHandler)
http.ListenAndServe(":8080", mux)
}
$ curl localhost:8080/edit/my-page
$ # yeah, alright, but what about
$ curl localhost:8080/edit/my-page/revision/2
Third party to the rescue!
func editHandler(w http.ResponseWriter, r *http.Request) {
title := chi.URLParam(r, “title”)
}
func main() {
mux := chi.NewRouter()
mux.Get("/edit/{title}", editHandler)
mux.Patch("/{month}-{day}-{year}/update", fancyPatchHandler)
http.ListenAndServe(":8080", mux)
}
$ go get github.com/go-chi/chi
Third party to the rescue!
func editHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[len("/edit/"):]
}
func main() {
mux := chi.NewRouter()
mux.Get("/edit/", editHandler)
mux.Patch("/{month}-{day}-{year}/update", fancyPatchHandler)
http.ListenAndServe(":8080", mux)
}
$ go get github.com/go-chi/chi
What about
middleware?
type middleware func(next http.Handler) http.Handler
The middleware signature
Simple logging middleware
func logger(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Println("Before")
h.ServeHTTP(w, r) // call wrapped handler
log.Println("After")
})
}
func main() {
mux := chi.NewRouter()
mux.Get("/edit/", logger(editHandler))
http.ListenAndServe(":8080", mux)
}
Control flow
func authenticated(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if (isAuthenticated) { // Perform application auth
h.ServeHTTP(w, r) // call wrapped handler
} else {
w.WriteHeader(http.StatusUnauthorized)
}
})
}
func main() {
mux := chi.NewRouter()
mux.Get("/edit/", authenticated(editHandler))
http.ListenAndServe(":8080", mux)
}
In go-chi
func main() {
mux := chi.NewRouter()
// Base middleware
mux.use(logger)
mux.use(rateLimiter)
// Instead of logger(ratelimiter(editHandler))
mux.Get("/edit/", editHandler)
r.Route("/admin", func(r chi.Router) {
r.use(authenticated)
// Instead of logger(ratelimiter(authenticated(adminHandler))
r.Get(“/”, adminHandler)
}
http.ListenAndServe(":8080", mux)
}
Accessing
“globals”
Please no package level variables
var (
db *sql.DB
cache redis.Client
)
func updateHandler(w http.ResponseWriter, r *http.Request) {
lastSeen := time.Now()
db.Query(`UPDATE users SET last_seen = ? WHERE id = ?`, lastSeen, 1)
cache.Set(fmt.Sprintf(“%d_last_seen”, 1), lastSeen)
}
func main() {
mux := chi.NewRouter()
mux.Get(“/users/update”, updateHandler)
http.ListenAndServe(":8080", mux)
}
Inject the dependencies instead!
type HTTPEnv struct {
db *sql.DB
cache redis.Client
logger log.Logger
}
func makeUpdateHandler(env *HTTPEnv) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
lastSeen := time.Now()
env.db.Query(`UPDATE users SET last_seen = ? WHERE id = ?`, lastSeen, 1)
env.cache.Set(fmt.Sprintf(“%d_last_seen”, 1), lastSeen)
})
}
func main() {
mux := chi.NewRouter()
env := HTTPEnv{} // Setup a global env here
mux.Get(“/users/update”, makeUpdateHandler(env))
http.ListenAndServe(":8080", mux)
}
Testing http
Creating an HTTP request
req, err := http.NewRequest(“GET”, “localhost:3000/my-route”, nil)
Testing an individual handler
import “net/http/httptest”
func TestMyHandler(t *testing.T) {
req := httptest.NewRequest("GET", "/2018-03-07", nil)
w := httptest.NewRecorder()
updateHandler(w, req)
// Assert stuff on w
}
Testing an entire server
import “net/http/httptest”
func TestMyHandler(t *testing.T) {
mux := myapp.NewRootMux() // Let’s pretend this exists
// Now you will get the entire chain of middleware etc
ts := httptest.NewServer(mux)
defer ts.Close()
res, _:= http.Get(ts.URL + “/edit/2018-03-07”)
// Assert stuff on res
}
Tips and tricks
Anonymous struct
type userRequest struct {
username string
password string
}
func createUserHandler(rw http.ResponseWriter, r *http.Request) {
var user userRequest
if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
w.write([]byte(“an error occured”))
}
data := struct {
Message string
Username string
}{
Message: “Success!”,
Username: user.username,
}
json.NewEncoder(rw).Encode(data)
}
A small gotcha...
func createUserHandler(rw http.ResponseWriter, r *http.Request) {
var user userRequest
if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
w.write([]byte(“an error occured”))
// forgot to return, panic!
}
data := struct {
Message string
Username string
}{
Message: “Success!”,
Username: user.username,
}
json.NewEncoder(rw).Encode(data)
}
An altered (better?) interface
type HandlerFunc func(http.ResponseWriter, *http.Request) error
Clearer now?
func createUserHandler(w http.ResponseWriter, r *http.Request) error {
var user userRequest
if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
return errors.New(“an error occured!”)
}
data := struct {
Message string
Token string
}{
title,
users,
}
json.NewEncoder(w).Encode(data)
return nil
}
Pluggin’ it in
type ErrorHandlerFunc func(http.ResponseWriter, *http.Request) error
func ErrorHandler(h ErrorHandlerFunc) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if err := f(w, r); err != nil {
// bonus, centralized http error handling
w.Write([]byte(“an error occured”)
}
})
}
func main() {
mux := chi.NewRouter()
mux.Get(“/users/update”, ErrorHandler(updateHandler))
http.ListenAndServe(":8080", mux)
}
What about HTTP status codes?
type HTTPError struct {
err error // The actual error
statusCode int
}
func (h HTTPError) Error() string {
return h.err.Error() // Just pass along the original errors message
}
type error interface {
Error() string
}
Handling HTTP Error
type ErrorHandlerFunc func(http.ResponseWriter, *http.Request) error
func ErrorHandler(h ErrorHandlerFunc) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if err := f(w, r); err != nil { // an error occurred
switch e := err.(type) {
case *HTTPError:
w.WriteHeader(e.statusCode)
default:
w.WriteHeader(http.StatusInternalServerError) // aka 500
}
}
})
}

More Related Content

What's hot

Javascript basics for automation testing
Javascript  basics for automation testingJavascript  basics for automation testing
Javascript basics for automation testingVikas Thange
 
jrubykaigi2010-lt-rubeus
jrubykaigi2010-lt-rubeusjrubykaigi2010-lt-rubeus
jrubykaigi2010-lt-rubeusTakeshi AKIMA
 
Intro to Javascript
Intro to JavascriptIntro to Javascript
Intro to JavascriptAnjan Banda
 
Reactive Access to MongoDB from Scala
Reactive Access to MongoDB from ScalaReactive Access to MongoDB from Scala
Reactive Access to MongoDB from ScalaHermann Hueck
 
運用Closure Compiler 打造高品質的JavaScript
運用Closure Compiler 打造高品質的JavaScript運用Closure Compiler 打造高品質的JavaScript
運用Closure Compiler 打造高品質的JavaScripttaobao.com
 
Venturing Into The Wild: A .NET Developer's Experience As A Ruby Developer
Venturing Into The Wild: A .NET Developer's Experience As A Ruby DeveloperVenturing Into The Wild: A .NET Developer's Experience As A Ruby Developer
Venturing Into The Wild: A .NET Developer's Experience As A Ruby DeveloperJon Kruger
 
Reactive Access to MongoDB from Java 8
Reactive Access to MongoDB from Java 8Reactive Access to MongoDB from Java 8
Reactive Access to MongoDB from Java 8Hermann Hueck
 
Mongoose and MongoDB 101
Mongoose and MongoDB 101Mongoose and MongoDB 101
Mongoose and MongoDB 101Will Button
 
10. session 10 loops and arrays
10. session 10   loops and arrays10. session 10   loops and arrays
10. session 10 loops and arraysPhúc Đỗ
 
Javascript forloop-let
Javascript forloop-letJavascript forloop-let
Javascript forloop-letkang taehun
 
Reactive Web - Servlet & Async, Non-blocking I/O
Reactive Web - Servlet & Async, Non-blocking I/OReactive Web - Servlet & Async, Non-blocking I/O
Reactive Web - Servlet & Async, Non-blocking I/OArawn Park
 
jQuery - Chapter 5 - Ajax
jQuery - Chapter 5 -  AjaxjQuery - Chapter 5 -  Ajax
jQuery - Chapter 5 - AjaxWebStackAcademy
 
Selectors and normalizing state shape
Selectors and normalizing state shapeSelectors and normalizing state shape
Selectors and normalizing state shapeMuntasir Chowdhury
 
Introduction to Nodejs
Introduction to NodejsIntroduction to Nodejs
Introduction to NodejsGabriele Lana
 
Introducere in web
Introducere in webIntroducere in web
Introducere in webAlex Eftimie
 

What's hot (17)

Javascript basics for automation testing
Javascript  basics for automation testingJavascript  basics for automation testing
Javascript basics for automation testing
 
jrubykaigi2010-lt-rubeus
jrubykaigi2010-lt-rubeusjrubykaigi2010-lt-rubeus
jrubykaigi2010-lt-rubeus
 
Intro to Javascript
Intro to JavascriptIntro to Javascript
Intro to Javascript
 
Reactive Access to MongoDB from Scala
Reactive Access to MongoDB from ScalaReactive Access to MongoDB from Scala
Reactive Access to MongoDB from Scala
 
運用Closure Compiler 打造高品質的JavaScript
運用Closure Compiler 打造高品質的JavaScript運用Closure Compiler 打造高品質的JavaScript
運用Closure Compiler 打造高品質的JavaScript
 
Hack tutorial
Hack tutorialHack tutorial
Hack tutorial
 
Venturing Into The Wild: A .NET Developer's Experience As A Ruby Developer
Venturing Into The Wild: A .NET Developer's Experience As A Ruby DeveloperVenturing Into The Wild: A .NET Developer's Experience As A Ruby Developer
Venturing Into The Wild: A .NET Developer's Experience As A Ruby Developer
 
Reactive Access to MongoDB from Java 8
Reactive Access to MongoDB from Java 8Reactive Access to MongoDB from Java 8
Reactive Access to MongoDB from Java 8
 
Mongoose and MongoDB 101
Mongoose and MongoDB 101Mongoose and MongoDB 101
Mongoose and MongoDB 101
 
10. session 10 loops and arrays
10. session 10   loops and arrays10. session 10   loops and arrays
10. session 10 loops and arrays
 
Javascript forloop-let
Javascript forloop-letJavascript forloop-let
Javascript forloop-let
 
Introduction to Node.js
Introduction to Node.jsIntroduction to Node.js
Introduction to Node.js
 
Reactive Web - Servlet & Async, Non-blocking I/O
Reactive Web - Servlet & Async, Non-blocking I/OReactive Web - Servlet & Async, Non-blocking I/O
Reactive Web - Servlet & Async, Non-blocking I/O
 
jQuery - Chapter 5 - Ajax
jQuery - Chapter 5 -  AjaxjQuery - Chapter 5 -  Ajax
jQuery - Chapter 5 - Ajax
 
Selectors and normalizing state shape
Selectors and normalizing state shapeSelectors and normalizing state shape
Selectors and normalizing state shape
 
Introduction to Nodejs
Introduction to NodejsIntroduction to Nodejs
Introduction to Nodejs
 
Introducere in web
Introducere in webIntroducere in web
Introducere in web
 

Similar to Net/http and the http.handler interface

May 2010 - RestEasy
May 2010 - RestEasyMay 2010 - RestEasy
May 2010 - RestEasyJBug Italy
 
RESTful Web Applications with Google Go
RESTful Web Applications with Google GoRESTful Web Applications with Google Go
RESTful Web Applications with Google GoFrank Müller
 
Server Side Swift with Swag
Server Side Swift with SwagServer Side Swift with Swag
Server Side Swift with SwagJens Ravens
 
async/await in Swift
async/await in Swiftasync/await in Swift
async/await in SwiftPeter Friese
 
Protocol-Oriented Networking
Protocol-Oriented NetworkingProtocol-Oriented Networking
Protocol-Oriented NetworkingMostafa Amer
 
GDG Devfest 2019 - Build go kit microservices at kubernetes with ease
GDG Devfest 2019 - Build go kit microservices at kubernetes with easeGDG Devfest 2019 - Build go kit microservices at kubernetes with ease
GDG Devfest 2019 - Build go kit microservices at kubernetes with easeKAI CHU CHUNG
 
服务框架: Thrift & PasteScript
服务框架: Thrift & PasteScript服务框架: Thrift & PasteScript
服务框架: Thrift & PasteScriptQiangning Hong
 
Golang slidesaudrey
Golang slidesaudreyGolang slidesaudrey
Golang slidesaudreyAudrey Lim
 
Android Best Practices
Android Best PracticesAndroid Best Practices
Android Best PracticesYekmer Simsek
 
外部環境への依存をテストする
外部環境への依存をテストする外部環境への依存をテストする
外部環境への依存をテストするShunsuke Maeda
 
神に近づくx/net/context (Finding God with x/net/context)
神に近づくx/net/context (Finding God with x/net/context)神に近づくx/net/context (Finding God with x/net/context)
神に近づくx/net/context (Finding God with x/net/context)guregu
 
Cервер на Go для мобильной стратегии
Cервер на Go для мобильной стратегииCервер на Go для мобильной стратегии
Cервер на Go для мобильной стратегииArtem Kovardin
 
Building @Anywhere (for TXJS)
Building @Anywhere (for TXJS)Building @Anywhere (for TXJS)
Building @Anywhere (for TXJS)danwrong
 
Finagle and Java Service Framework at Pinterest
Finagle and Java Service Framework at PinterestFinagle and Java Service Framework at Pinterest
Finagle and Java Service Framework at PinterestPavan Chitumalla
 

Similar to Net/http and the http.handler interface (20)

May 2010 - RestEasy
May 2010 - RestEasyMay 2010 - RestEasy
May 2010 - RestEasy
 
RESTEasy
RESTEasyRESTEasy
RESTEasy
 
RESTful Web Applications with Google Go
RESTful Web Applications with Google GoRESTful Web Applications with Google Go
RESTful Web Applications with Google Go
 
Server Side Swift with Swag
Server Side Swift with SwagServer Side Swift with Swag
Server Side Swift with Swag
 
async/await in Swift
async/await in Swiftasync/await in Swift
async/await in Swift
 
Protocol-Oriented Networking
Protocol-Oriented NetworkingProtocol-Oriented Networking
Protocol-Oriented Networking
 
GDG Devfest 2019 - Build go kit microservices at kubernetes with ease
GDG Devfest 2019 - Build go kit microservices at kubernetes with easeGDG Devfest 2019 - Build go kit microservices at kubernetes with ease
GDG Devfest 2019 - Build go kit microservices at kubernetes with ease
 
Intro to Node
Intro to NodeIntro to Node
Intro to Node
 
服务框架: Thrift & PasteScript
服务框架: Thrift & PasteScript服务框架: Thrift & PasteScript
服务框架: Thrift & PasteScript
 
Go react codelab
Go react codelabGo react codelab
Go react codelab
 
Golang slidesaudrey
Golang slidesaudreyGolang slidesaudrey
Golang slidesaudrey
 
Android Best Practices
Android Best PracticesAndroid Best Practices
Android Best Practices
 
Server Side Swift: Vapor
Server Side Swift: VaporServer Side Swift: Vapor
Server Side Swift: Vapor
 
外部環境への依存をテストする
外部環境への依存をテストする外部環境への依存をテストする
外部環境への依存をテストする
 
Clean coding-practices
Clean coding-practicesClean coding-practices
Clean coding-practices
 
神に近づくx/net/context (Finding God with x/net/context)
神に近づくx/net/context (Finding God with x/net/context)神に近づくx/net/context (Finding God with x/net/context)
神に近づくx/net/context (Finding God with x/net/context)
 
Ajax
AjaxAjax
Ajax
 
Cервер на Go для мобильной стратегии
Cервер на Go для мобильной стратегииCервер на Go для мобильной стратегии
Cервер на Go для мобильной стратегии
 
Building @Anywhere (for TXJS)
Building @Anywhere (for TXJS)Building @Anywhere (for TXJS)
Building @Anywhere (for TXJS)
 
Finagle and Java Service Framework at Pinterest
Finagle and Java Service Framework at PinterestFinagle and Java Service Framework at Pinterest
Finagle and Java Service Framework at Pinterest
 

Recently uploaded

Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024BookNet Canada
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsRizwan Syed
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupFlorian Wilhelm
 
SIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge GraphSIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge GraphNeo4j
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationSlibray Presentation
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Scott Keck-Warren
 
AI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsAI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsMemoori
 
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Wonjun Hwang
 
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 3652toLead Limited
 
Key Features Of Token Development (1).pptx
Key  Features Of Token  Development (1).pptxKey  Features Of Token  Development (1).pptx
Key Features Of Token Development (1).pptxLBM Solutions
 
Snow Chain-Integrated Tire for a Safe Drive on Winter Roads
Snow Chain-Integrated Tire for a Safe Drive on Winter RoadsSnow Chain-Integrated Tire for a Safe Drive on Winter Roads
Snow Chain-Integrated Tire for a Safe Drive on Winter RoadsHyundai Motor Group
 
APIForce Zurich 5 April Automation LPDG
APIForce Zurich 5 April  Automation LPDGAPIForce Zurich 5 April  Automation LPDG
APIForce Zurich 5 April Automation LPDGMarianaLemus7
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitecturePixlogix Infotech
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticscarlostorres15106
 
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024BookNet Canada
 
Making_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptx
Making_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptxMaking_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptx
Making_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptxnull - The Open Security Community
 
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationBeyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationSafe Software
 
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Patryk Bandurski
 
Pigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions
 

Recently uploaded (20)

Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL Certs
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project Setup
 
SIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge GraphSIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge Graph
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck Presentation
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024
 
AI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsAI as an Interface for Commercial Buildings
AI as an Interface for Commercial Buildings
 
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
 
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
 
Key Features Of Token Development (1).pptx
Key  Features Of Token  Development (1).pptxKey  Features Of Token  Development (1).pptx
Key Features Of Token Development (1).pptx
 
Snow Chain-Integrated Tire for a Safe Drive on Winter Roads
Snow Chain-Integrated Tire for a Safe Drive on Winter RoadsSnow Chain-Integrated Tire for a Safe Drive on Winter Roads
Snow Chain-Integrated Tire for a Safe Drive on Winter Roads
 
APIForce Zurich 5 April Automation LPDG
APIForce Zurich 5 April  Automation LPDGAPIForce Zurich 5 April  Automation LPDG
APIForce Zurich 5 April Automation LPDG
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC Architecture
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
 
DMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special EditionDMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special Edition
 
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
 
Making_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptx
Making_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptxMaking_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptx
Making_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptx
 
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationBeyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
 
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
 
Pigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food Manufacturing
 

Net/http and the http.handler interface

  • 1. net/http and the http.Handler interface (or simply put, building web servers in Go)
  • 2. Who am I? ● Work at ● Go developer since 2013 ● Currently building cloud based energy optimization system for commercial buildings (using Go) ● Joakim Gustin
  • 3. Agenda ● The basics ● Routing ● Middleware ● Accessing “globals” ● Testing ● Tips and tricks
  • 4. HTTP in Go ● Built in web server (net/http) ● Core piece of the Go standard library ● Can be run facing clients (even Cloudflare does) ● Handles TLS ● Automatic support for HTTP/2
  • 5. net/http - ListenAndServe(this blue means official GoDoc)
  • 6. The http.Handler interface type Handler interface { ServeHTTP(ResponseWriter, *Request) } type ResponseWriter interface { Header() Header WriteHeader(statusCode int) Write([]byte) (int, error) } type Request struct { Method string URL *url.URL Body io.ReadCloser // … rest omitted }
  • 7. The naive approach import “net/http” type myServer struct {} func (ms *myServer) ServeHTTP(rw ResponseWriter, req *Request) { if req.method == “GET” { rw.Write([]byte(“hello, Go West”) } } func main() { http.ListenAndServe(":8080", myServer) }
  • 8. How about some routing?
  • 9. How about some routing? import “net/http” type myServer struct {} func (ms *myServer) ServeHTTP(rw ResponseWriter, req *Request) { if req.method == “GET” { rw.Write([]byte(“hello, Go West”) } } func main() { http.Handle("/hello", myHandler) http.ListenAndServe(":8080", nil) // nil, wut? }
  • 10. The http.ServeMux import “net/http” type myHandler struct {} func (mh *myHandler) ServeHTTP(rw ResponseWriter, req *Request) { if req.method == “GET” { rw.Write([]byte(“hello”) } } func main() { mux := http.NewServeMux() mux.Handle("/hello", myHandler) http.ListenAndServe(":8080", mux) // phew, much better }
  • 12. Can we make this part simpler? type myServer struct {} // implementing the http.Handler interface func (ms *myServer) ServeHTTP(rw ResponseWriter, req *Request) { if req.method == “GET” { rw.Write([]byte(“hello”) } }
  • 13. Revisiting the go type system type myServer struct {} // Define a type func (s myServer) DoSomething() {} // Define a method on it type serverList []myServer // A type can be many things func (sl serverList) DoSomething() {} // yep, valid // wait a minute, functions are first class citizens in Go, right? type mathFunc func(int) int func (mf mathFunc) DoSomething() {} // OMG, yes it’s valid
  • 14. A function implementing an interface?! type HandlerFunc func(http.ResponseWriter, *http.Request) func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r) } type Handler interface { ServeHTTP(ResponseWriter, *Request) }
  • 16. Revisiting our example import “net/http” // Look ma, no struct! func myHandler(rw ResponseWriter, req *Request) { if req.method == “GET” { rw.Write([]byte(“hello”) } } func main() { mux := http.NewServeMux() mux.Handle("/hello", http.HandlerFunc(myHandler)) log.Fatal(http.ListenAndServe(":8080", mux)) }
  • 17. Revisiting our example func main() { mux := http.NewServeMux() mux.HandleFunc("/hello", myHandler) log.Fatal(http.ListenAndServe(":8080", mux))} }
  • 19. Routing the std lib way func editHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[len("/edit/"):] } func main() { mux := http.NewServeMux() mux.HandleFunc("/edit/", editHandler) http.ListenAndServe(":8080", mux) } $ curl localhost:8080/edit/my-page $ # yeah, alright, but what about $ curl localhost:8080/edit/my-page/revision/2
  • 20. Third party to the rescue! func editHandler(w http.ResponseWriter, r *http.Request) { title := chi.URLParam(r, “title”) } func main() { mux := chi.NewRouter() mux.Get("/edit/{title}", editHandler) mux.Patch("/{month}-{day}-{year}/update", fancyPatchHandler) http.ListenAndServe(":8080", mux) } $ go get github.com/go-chi/chi
  • 21. Third party to the rescue! func editHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[len("/edit/"):] } func main() { mux := chi.NewRouter() mux.Get("/edit/", editHandler) mux.Patch("/{month}-{day}-{year}/update", fancyPatchHandler) http.ListenAndServe(":8080", mux) } $ go get github.com/go-chi/chi
  • 23.
  • 24. type middleware func(next http.Handler) http.Handler The middleware signature
  • 25. Simple logging middleware func logger(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { log.Println("Before") h.ServeHTTP(w, r) // call wrapped handler log.Println("After") }) } func main() { mux := chi.NewRouter() mux.Get("/edit/", logger(editHandler)) http.ListenAndServe(":8080", mux) }
  • 26. Control flow func authenticated(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if (isAuthenticated) { // Perform application auth h.ServeHTTP(w, r) // call wrapped handler } else { w.WriteHeader(http.StatusUnauthorized) } }) } func main() { mux := chi.NewRouter() mux.Get("/edit/", authenticated(editHandler)) http.ListenAndServe(":8080", mux) }
  • 27. In go-chi func main() { mux := chi.NewRouter() // Base middleware mux.use(logger) mux.use(rateLimiter) // Instead of logger(ratelimiter(editHandler)) mux.Get("/edit/", editHandler) r.Route("/admin", func(r chi.Router) { r.use(authenticated) // Instead of logger(ratelimiter(authenticated(adminHandler)) r.Get(“/”, adminHandler) } http.ListenAndServe(":8080", mux) }
  • 29. Please no package level variables var ( db *sql.DB cache redis.Client ) func updateHandler(w http.ResponseWriter, r *http.Request) { lastSeen := time.Now() db.Query(`UPDATE users SET last_seen = ? WHERE id = ?`, lastSeen, 1) cache.Set(fmt.Sprintf(“%d_last_seen”, 1), lastSeen) } func main() { mux := chi.NewRouter() mux.Get(“/users/update”, updateHandler) http.ListenAndServe(":8080", mux) }
  • 30. Inject the dependencies instead! type HTTPEnv struct { db *sql.DB cache redis.Client logger log.Logger } func makeUpdateHandler(env *HTTPEnv) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { lastSeen := time.Now() env.db.Query(`UPDATE users SET last_seen = ? WHERE id = ?`, lastSeen, 1) env.cache.Set(fmt.Sprintf(“%d_last_seen”, 1), lastSeen) }) } func main() { mux := chi.NewRouter() env := HTTPEnv{} // Setup a global env here mux.Get(“/users/update”, makeUpdateHandler(env)) http.ListenAndServe(":8080", mux) }
  • 32. Creating an HTTP request req, err := http.NewRequest(“GET”, “localhost:3000/my-route”, nil)
  • 33. Testing an individual handler import “net/http/httptest” func TestMyHandler(t *testing.T) { req := httptest.NewRequest("GET", "/2018-03-07", nil) w := httptest.NewRecorder() updateHandler(w, req) // Assert stuff on w }
  • 34. Testing an entire server import “net/http/httptest” func TestMyHandler(t *testing.T) { mux := myapp.NewRootMux() // Let’s pretend this exists // Now you will get the entire chain of middleware etc ts := httptest.NewServer(mux) defer ts.Close() res, _:= http.Get(ts.URL + “/edit/2018-03-07”) // Assert stuff on res }
  • 36. Anonymous struct type userRequest struct { username string password string } func createUserHandler(rw http.ResponseWriter, r *http.Request) { var user userRequest if err := json.NewDecoder(r.Body).Decode(&user); err != nil { w.write([]byte(“an error occured”)) } data := struct { Message string Username string }{ Message: “Success!”, Username: user.username, } json.NewEncoder(rw).Encode(data) }
  • 37. A small gotcha... func createUserHandler(rw http.ResponseWriter, r *http.Request) { var user userRequest if err := json.NewDecoder(r.Body).Decode(&user); err != nil { w.write([]byte(“an error occured”)) // forgot to return, panic! } data := struct { Message string Username string }{ Message: “Success!”, Username: user.username, } json.NewEncoder(rw).Encode(data) }
  • 38. An altered (better?) interface type HandlerFunc func(http.ResponseWriter, *http.Request) error
  • 39. Clearer now? func createUserHandler(w http.ResponseWriter, r *http.Request) error { var user userRequest if err := json.NewDecoder(r.Body).Decode(&user); err != nil { return errors.New(“an error occured!”) } data := struct { Message string Token string }{ title, users, } json.NewEncoder(w).Encode(data) return nil }
  • 40. Pluggin’ it in type ErrorHandlerFunc func(http.ResponseWriter, *http.Request) error func ErrorHandler(h ErrorHandlerFunc) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if err := f(w, r); err != nil { // bonus, centralized http error handling w.Write([]byte(“an error occured”) } }) } func main() { mux := chi.NewRouter() mux.Get(“/users/update”, ErrorHandler(updateHandler)) http.ListenAndServe(":8080", mux) }
  • 41. What about HTTP status codes? type HTTPError struct { err error // The actual error statusCode int } func (h HTTPError) Error() string { return h.err.Error() // Just pass along the original errors message } type error interface { Error() string }
  • 42. Handling HTTP Error type ErrorHandlerFunc func(http.ResponseWriter, *http.Request) error func ErrorHandler(h ErrorHandlerFunc) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if err := f(w, r); err != nil { // an error occurred switch e := err.(type) { case *HTTPError: w.WriteHeader(e.statusCode) default: w.WriteHeader(http.StatusInternalServerError) // aka 500 } } }) }