func Handler()

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)
	})
}