# 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 ?