# Introduction to reactor package ## Code Walkthrough :question: Why I choose to implement package reactor? * Because I need an object that I can pass it to the layer which needs to send / receive request, response. * I need to deal with internal server error in the same place, and do some logging. ```mermaid sequenceDiagram participant rc.HandlerWrapper participant api.Healthcheck rc.HandlerWrapper->>api.Healthcheck: reactor.Context Note right of api.Healthcheck: send response using reactor.Context api.Healthcheck->>rc.HandlerWrapper: internal server error Note left of rc.HandlerWrapper: log the error using logger stored in rc.Reactor ``` Example: At commit [`002524`](https://github.com/unknowntpo/todos/commit/0025248cec922dea9614e3e213f908d1bc8d9e4b) in file [`./internal/healthcheck/delivery/api/healthcheck_api.go`](https://github.com/unknowntpo/todos/blob/0025248cec922dea9614e3e213f908d1bc8d9e4b/internal/healthcheck/delivery/api/healthcheck_api.go) ```go // Put reactor at structure that holds all the handler type healthcheckAPI struct { version string env string rc *reactor.Reactor } type HealthcheckResponse struct { Status string `json: "status"` Environment string `json: "environment"` Version string `json: "version"` } // NewHealthcheckAPI registers all handlers in /v1/healcheck to the router. func NewHealthcheckAPI(router *httprouter.Router, version, env string, rc *reactor.Reactor) { api := &healthcheckAPI{version: version, env: env, rc: rc} router.Handler(http.MethodGet, "/v1/healthcheck", rc.HandlerWrapper(api.Healthcheck)) } // Healthcheck shows status of service. func (h *healthcheckAPI) Healthcheck(c *reactor.Context) error { return c.WriteJSON(http.StatusOK, &HealthcheckResponse{ Status: "available", Version: h.version, Environment: h.env, }) } ``` in [`internal/reactor/reactor.go`](https://github.com/unknowntpo/todos/blob/0025248cec922dea9614e3e213f908d1bc8d9e4b/internal/reactor/reactor.go#L7) ```go=44 func (rc *Reactor) HandlerWrapper(h HandlerFunc) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Use ctxPool to reduce allocation and gc. c := ctxPool.Get().(*Context) defer ctxPool.Put(c) c.w = w c.r = r err := h(c) // Only Internal Server Error will come to here. // TODO: Use kindIs to check , if failed, panic if err != nil { rc.Logger.PrintError(err, nil) if e := c.ServerErrorResponse(); e != nil { // Something goes wrong during sending server error response. // So we write the message directly. rc.Logger.PrintError(e, nil) c.w.WriteHeader(http.StatusInternalServerError) msg := `{"error":"the server encountered a problem and could not process your request"}` c.w.Write([]byte(msg)) return } return } }) } ``` :question: How does `*reactor.Reactor.HandlerWrapper` work with `Healthcheck` handler ? The callgraph: 1. `(*Reactor).HandlerWrapper.func1` is type `http.HandlerFunc`, so it has a method `ServeHTTP(w, r)` 2. httprouter select the URL `/v1/healthcheck` and calls `(*Reactor).HandlerWrapper.func1.ServeHTTP(w, r)` 3. `(*Reactor).HandlerWrapper.func1.ServeHTTP(w, r)` will call `(*Reactor).HandlerWrapper.func1` 4. line47: Attempt to get new `reactor.Context` instance, if reactor.Context is not in `sync.Pool`, `sync.Pool` will allocate a new one for us. 5. Store `w, r` inside reactor.Context 6. line52: call `api.Healthcheck(c)` 7. line55: if `api.Healthcheck` returns any error, we treat it as internal server error, log it and send response. ## Why I remove the `reactor.Context` mechanism now ?