in kernels/kernels.go [150:294]
func Handler(localBackend *backends.Backend, remoteBackend *backends.Backend) http.Handler {
bs := []*backends.Backend{localBackend, remoteBackend}
kernelsRecords := &kernelsRecords{kernelsToBackendsMap: make(map[string]*backends.Backend)}
go func() {
kernelsRecords.fetchKernels(localBackend)
kernelsRecords.fetchKernels(remoteBackend)
}()
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
relativePath := strings.TrimPrefix(r.URL.Path, APIPath)
relativePath = strings.TrimPrefix(relativePath, "/")
if relativePath == "" && r.Method == http.MethodGet {
// List the kernels
unifiedKernels, err := kernelsRecords.combined(localBackend, remoteBackend)
if err != nil {
errorMsg := fmt.Sprintf("failure fetching the kernels: %v", err)
http.Error(w, errorMsg, util.HTTPStatusCode(err))
util.Log(r, fmt.Sprintf("Failed kernels API call: %q", errorMsg))
return
}
respBytes, err := json.Marshal(unifiedKernels)
if err != nil {
errorMsg := fmt.Sprintf("failure marshalling the kernels collection: %v", err)
http.Error(w, errorMsg, util.HTTPStatusCode(err))
util.Log(r, fmt.Sprintf("Failed kernels API call: %q", errorMsg))
return
}
w.Write(respBytes)
return
}
bodyBytes, err := ioutil.ReadAll(r.Body)
if err != nil {
errorMsg := fmt.Sprintf("failure reading in the request body: %v", err)
util.Log(r, errorMsg)
http.Error(w, errorMsg, util.HTTPStatusCode(err))
return
}
var backend *backends.Backend
if relativePath != "" {
// Forward the request directly to the backend
kernelID := strings.Split(relativePath, "/")[0]
backend, err = kernelsRecords.findBackend(kernelID)
if err != nil {
util.Log(r, err.Error())
http.Error(w, err.Error(), util.HTTPStatusCode(err))
return
}
}
var backendFromBody *backends.Backend
if len(bodyBytes) > 0 {
var backendKernel *resources.Kernel
var unifiedKernel resources.Kernel
if err := json.Unmarshal(bodyBytes, &unifiedKernel); err != nil {
errorMsg := fmt.Sprintf("failure parsing the body of a kernel request: %v", err)
util.Log(r, errorMsg)
http.Error(w, errorMsg, http.StatusBadRequest)
return
}
backendFromBody, backendKernel, err = BackendView(&unifiedKernel, bs)
if err != nil {
errorMsg := fmt.Sprintf("failure processing a kernel request: %v", err)
util.Log(r, errorMsg)
http.Error(w, errorMsg, http.StatusBadRequest)
return
}
backendReqBody, err := json.Marshal(backendKernel)
if err != nil {
errorMsg := fmt.Sprintf("failure marshalling the backend request: %v", err)
util.Log(r, errorMsg)
http.Error(w, errorMsg, util.HTTPStatusCode(err))
return
}
r.Body.Close()
r.Body = ioutil.NopCloser(bytes.NewReader(backendReqBody))
r.Header.Del("Content-Length")
r.ContentLength = int64(len(backendReqBody))
}
if backend != nil && backendFromBody != nil && backend != backendFromBody {
util.Log(r, fmt.Sprintf("Mismatch between backend from kernels API path and request body %q", string(bodyBytes)))
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
if backend == nil {
backend = backendFromBody
}
if backend == nil {
util.Log(r, "Programming error: backend is nil. This should not happen")
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
if websocket.IsWebSocketUpgrade(r) {
util.Log(r, fmt.Sprintf("Forwarding a websocket upgrade request: %+v", r))
backend.ServeHTTP(w, r)
return
}
r.Header.Del("Accept-Encoding")
rr := httptest.NewRecorder()
backend.ServeHTTP(rr, r)
backendResp := rr.Result()
for key, val := range backendResp.Header {
if key != "Content-Length" {
w.Header()[key] = val
}
}
backendRespBytes, err := ioutil.ReadAll(backendResp.Body)
backendResp.Body.Close()
if err != nil {
errorMsg := fmt.Sprintf("failure reading the backend response from %q: %v", backend.Name(), err)
util.Log(r, errorMsg)
http.Error(w, errorMsg, util.HTTPStatusCode(err))
return
}
if backendResp.StatusCode < http.StatusOK || backendResp.StatusCode >= http.StatusMultipleChoices {
// For anything other than a 2XX response to one of the Swagger URLs, we don't modify the response
w.WriteHeader(backendResp.StatusCode)
if backendResp.StatusCode >= http.StatusBadRequest {
util.Log(r, fmt.Sprintf("Error response %d from %q for %+v", backendResp.StatusCode, backend.Name(), r))
}
w.Write(backendRespBytes)
return
}
var respBytes []byte
if len(backendRespBytes) > 0 {
var kernel resources.Kernel
if err := json.Unmarshal(backendRespBytes, &kernel); err != nil {
errorMsg := fmt.Sprintf("failure parsing the backend response from %q for %+v: %v, %q", backend.Name(), r, err, string(backendRespBytes))
util.Log(r, errorMsg)
http.Error(w, errorMsg, util.HTTPStatusCode(err))
return
}
unifiedKernel := UnifiedView(&kernel, backend)
kernelsRecords.recordKernel(unifiedKernel.ID, backend)
var err error
respBytes, err = json.Marshal(unifiedKernel)
if err != nil {
errorMsg := fmt.Sprintf("failure marshalling the response: %v", err)
util.Log(r, errorMsg)
http.Error(w, errorMsg, util.HTTPStatusCode(err))
return
}
}
w.WriteHeader(backendResp.StatusCode)
w.Write(respBytes)
})
}