HandlerFuncs returns the HTTP handlers that should be added to the REST API endpoint in order to handle database-related requests. There are two handlers, one for the /internal/raft endpoint and the other for /internal/db, which handle respectively raft and gRPC-SQL requests. These handlers might
(heartbeatHandler HeartbeatHandler, trustedCerts func() (map[certificate.Type]map[string]x509.Certificate, error))
| 154 | // non-clustered member not available over the network or because it is not a |
| 155 | // database node part of the cowsql cluster. |
| 156 | func (g *Gateway) HandlerFuncs(heartbeatHandler HeartbeatHandler, trustedCerts func() (map[certificate.Type]map[string]x509.Certificate, error)) map[string]http.HandlerFunc { |
| 157 | database := func(w http.ResponseWriter, r *http.Request) { |
| 158 | g.lock.RLock() |
| 159 | |
| 160 | certs, err := trustedCerts() |
| 161 | if err != nil { |
| 162 | g.lock.RUnlock() |
| 163 | http.Error(w, "403 invalid client certificate", http.StatusForbidden) |
| 164 | return |
| 165 | } |
| 166 | |
| 167 | if !tlsCheckCert(r, g.networkCert, g.state().ServerCert(), certs) { |
| 168 | g.lock.RUnlock() |
| 169 | http.Error(w, "403 invalid client certificate", http.StatusForbidden) |
| 170 | return |
| 171 | } |
| 172 | |
| 173 | g.lock.RUnlock() |
| 174 | |
| 175 | // Compare the cowsql version of the connecting client |
| 176 | // with our own one. |
| 177 | versionHeader := r.Header.Get("X-Dqlite-Version") |
| 178 | if versionHeader == "" { |
| 179 | // No version header means an old pre cowsql 1.0 client. |
| 180 | versionHeader = "0" |
| 181 | } |
| 182 | |
| 183 | version, err := strconv.Atoi(versionHeader) |
| 184 | if err != nil { |
| 185 | http.Error(w, "400 invalid cowsql version", http.StatusBadRequest) |
| 186 | return |
| 187 | } |
| 188 | |
| 189 | if version != cowsqlVersion { |
| 190 | if version > cowsqlVersion { |
| 191 | g.lock.Lock() |
| 192 | if !g.upgradeTriggered { |
| 193 | err = triggerUpdate(g.state()) |
| 194 | if err == nil { |
| 195 | g.upgradeTriggered = true |
| 196 | } |
| 197 | } |
| 198 | g.lock.Unlock() |
| 199 | http.Error(w, "503 unsupported cowsql version", http.StatusServiceUnavailable) |
| 200 | } else { |
| 201 | http.Error(w, "426 cowsql version too old ", http.StatusUpgradeRequired) |
| 202 | } |
| 203 | |
| 204 | return |
| 205 | } |
| 206 | |
| 207 | // Handle heartbeats (these normally come from leader, but can come from joining nodes too). |
| 208 | if r.Method == "PUT" { |
| 209 | if g.shutdownCtx.Err() != nil { |
| 210 | logger.Warn("Rejecting heartbeat request as shutting down") |
| 211 | http.Error(w, "503 Shutting down", http.StatusServiceUnavailable) |
| 212 | return |
| 213 | } |