Path: blob/main/components/openvsx-proxy/pkg/modifyresponse.go
2498 views
// Copyright (c) 2021 Gitpod GmbH. All rights reserved.1// Licensed under the GNU Affero General Public License (AGPL).2// See License.AGPL.txt in the project root for license information.34package pkg56import (7"bytes"8"fmt"9"io/ioutil"10"net/http"11"strconv"12"time"13"unicode/utf8"1415"github.com/gitpod-io/gitpod/common-go/log"16"github.com/sirupsen/logrus"17)1819func (o *OpenVSXProxy) ModifyResponse(r *http.Response) error {20reqid := r.Request.Context().Value(REQUEST_ID_CTX).(string)21key, ok := r.Request.Context().Value(REQUEST_CACHE_KEY_CTX).(string)2223logFields := logrus.Fields{24LOG_FIELD_FUNC: "response_handler",25LOG_FIELD_REQUEST_ID: reqid,26LOG_FIELD_REQUEST: key,27LOG_FIELD_STATUS: strconv.Itoa(r.StatusCode),28"response_content_length": r.Header.Get("Content-Length"),29}3031start := time.Now()32defer func(ts time.Time) {33duration := time.Since(ts)34o.metrics.DurationResponseProcessingHistogram.Observe(duration.Seconds())35log.36WithFields(logFields).37WithFields(o.DurationLogFields(duration)).38Info("processing response finished")39}(start)4041log.WithFields(logFields).Debug("handling response")42o.metrics.IncStatusCounter(r.Request, strconv.Itoa(r.StatusCode))4344if !ok {45return nil46}4748if key == "" {49log.WithFields(logFields).Error("cache key header is missing - sending response as is")50return nil51}5253rawBody, err := ioutil.ReadAll(r.Body)54if err != nil {55log.WithFields(logFields).WithError(err).Error("error reading response raw body")56return err57}58r.Body.Close()5960if r.StatusCode >= 500 || r.StatusCode == http.StatusTooManyRequests || r.StatusCode == http.StatusRequestTimeout {61// use cache if exists62bodyLogField := "(binary)"63if utf8.Valid(rawBody) {64bodyStr := string(rawBody)65truncatedSuffix := ""66if len(bodyStr) > 500 {67truncatedSuffix = "... [truncated]"68}69bodyLogField = fmt.Sprintf("%.500s%s", bodyStr, truncatedSuffix)70}71log.72WithFields(logFields).73WithField("body", bodyLogField).74Warn("error from upstream server - trying to use cached response")75cached, ok, err := o.ReadCache(key)76if err != nil {77log.WithFields(logFields).WithError(err).Error("cannot read from cache")78return nil79}80if !ok {81log.WithFields(logFields).Debug("cache has no entry for key")82return nil83}84r.Header = cached.Header85if v := r.Header.Get("Access-Control-Allow-Origin"); v != "" && v != "*" {86r.Header.Set("Access-Control-Allow-Origin", r.Header.Get("Origin"))87}88r.Body = ioutil.NopCloser(bytes.NewBuffer(cached.Body))89r.ContentLength = int64(len(cached.Body))90r.StatusCode = cached.StatusCode91log.WithFields(logFields).Debug("used cache response due to an upstream error")92o.metrics.BackupCacheServeCounter.Inc()93return nil94}9596// no error (status code < 500)97cacheObj := &CacheObject{98Header: r.Header,99Body: rawBody,100StatusCode: r.StatusCode,101}102err = o.StoreCache(key, cacheObj)103if err != nil {104log.WithFields(logFields).WithError(err).Error("error storing response to cache")105} else {106log.WithFields(logFields).Debug("successfully stored response to cache")107}108109r.Body = ioutil.NopCloser(bytes.NewBuffer(rawBody))110r.ContentLength = int64(len(rawBody))111r.Header.Set("Content-Length", strconv.Itoa(len(rawBody)))112return nil113}114115116