github.com/prebid/prebid-server@v0.275.0/endpoints/openrtb2/auction.go (about)

     1  package openrtb2
     2  
     3  import (
     4  	"compress/gzip"
     5  	"context"
     6  	"encoding/json"
     7  	"errors"
     8  	"fmt"
     9  	"io"
    10  	"net/http"
    11  	"net/url"
    12  	"regexp"
    13  	"strconv"
    14  	"strings"
    15  	"time"
    16  
    17  	"github.com/prebid/prebid-server/privacy"
    18  
    19  	"github.com/buger/jsonparser"
    20  	"github.com/gofrs/uuid"
    21  	"github.com/golang/glog"
    22  	"github.com/julienschmidt/httprouter"
    23  	gpplib "github.com/prebid/go-gpp"
    24  	"github.com/prebid/go-gpp/constants"
    25  	"github.com/prebid/openrtb/v19/adcom1"
    26  	"github.com/prebid/openrtb/v19/native1"
    27  	nativeRequests "github.com/prebid/openrtb/v19/native1/request"
    28  	"github.com/prebid/openrtb/v19/openrtb2"
    29  	"github.com/prebid/openrtb/v19/openrtb3"
    30  	"github.com/prebid/prebid-server/bidadjustment"
    31  	"github.com/prebid/prebid-server/hooks"
    32  	"github.com/prebid/prebid-server/ortb"
    33  	"golang.org/x/net/publicsuffix"
    34  	jsonpatch "gopkg.in/evanphx/json-patch.v4"
    35  
    36  	accountService "github.com/prebid/prebid-server/account"
    37  	"github.com/prebid/prebid-server/analytics"
    38  	"github.com/prebid/prebid-server/config"
    39  	"github.com/prebid/prebid-server/currency"
    40  	"github.com/prebid/prebid-server/errortypes"
    41  	"github.com/prebid/prebid-server/exchange"
    42  	"github.com/prebid/prebid-server/gdpr"
    43  	"github.com/prebid/prebid-server/hooks/hookexecution"
    44  	"github.com/prebid/prebid-server/metrics"
    45  	"github.com/prebid/prebid-server/openrtb_ext"
    46  	"github.com/prebid/prebid-server/prebid_cache_client"
    47  	"github.com/prebid/prebid-server/privacy/ccpa"
    48  	"github.com/prebid/prebid-server/privacy/lmt"
    49  	"github.com/prebid/prebid-server/schain"
    50  	"github.com/prebid/prebid-server/stored_requests"
    51  	"github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher"
    52  	"github.com/prebid/prebid-server/stored_responses"
    53  	"github.com/prebid/prebid-server/usersync"
    54  	"github.com/prebid/prebid-server/util/httputil"
    55  	"github.com/prebid/prebid-server/util/iputil"
    56  	"github.com/prebid/prebid-server/util/uuidutil"
    57  	"github.com/prebid/prebid-server/version"
    58  )
    59  
    60  const storedRequestTimeoutMillis = 50
    61  const ampChannel = "amp"
    62  const appChannel = "app"
    63  
    64  var (
    65  	dntKey      string = http.CanonicalHeaderKey("DNT")
    66  	dntDisabled int8   = 0
    67  	dntEnabled  int8   = 1
    68  	notAmp      int8   = 0
    69  )
    70  
    71  var accountIdSearchPath = [...]struct {
    72  	isApp bool
    73  	key   []string
    74  }{
    75  	{true, []string{"app", "publisher", "ext", openrtb_ext.PrebidExtKey, "parentAccount"}},
    76  	{true, []string{"app", "publisher", "id"}},
    77  	{false, []string{"site", "publisher", "ext", openrtb_ext.PrebidExtKey, "parentAccount"}},
    78  	{false, []string{"site", "publisher", "id"}},
    79  }
    80  
    81  func NewEndpoint(
    82  	uuidGenerator uuidutil.UUIDGenerator,
    83  	ex exchange.Exchange,
    84  	validator openrtb_ext.BidderParamValidator,
    85  	requestsById stored_requests.Fetcher,
    86  	accounts stored_requests.AccountFetcher,
    87  	cfg *config.Configuration,
    88  	metricsEngine metrics.MetricsEngine,
    89  	pbsAnalytics analytics.PBSAnalyticsModule,
    90  	disabledBidders map[string]string,
    91  	defReqJSON []byte,
    92  	bidderMap map[string]openrtb_ext.BidderName,
    93  	storedRespFetcher stored_requests.Fetcher,
    94  	hookExecutionPlanBuilder hooks.ExecutionPlanBuilder,
    95  	tmaxAdjustments *exchange.TmaxAdjustmentsPreprocessed,
    96  ) (httprouter.Handle, error) {
    97  	if ex == nil || validator == nil || requestsById == nil || accounts == nil || cfg == nil || metricsEngine == nil {
    98  		return nil, errors.New("NewEndpoint requires non-nil arguments.")
    99  	}
   100  
   101  	defRequest := defReqJSON != nil && len(defReqJSON) > 0
   102  
   103  	ipValidator := iputil.PublicNetworkIPValidator{
   104  		IPv4PrivateNetworks: cfg.RequestValidation.IPv4PrivateNetworksParsed,
   105  		IPv6PrivateNetworks: cfg.RequestValidation.IPv6PrivateNetworksParsed,
   106  	}
   107  
   108  	return httprouter.Handle((&endpointDeps{
   109  		uuidGenerator,
   110  		ex,
   111  		validator,
   112  		requestsById,
   113  		empty_fetcher.EmptyFetcher{},
   114  		accounts,
   115  		cfg,
   116  		metricsEngine,
   117  		pbsAnalytics,
   118  		disabledBidders,
   119  		defRequest,
   120  		defReqJSON,
   121  		bidderMap,
   122  		nil,
   123  		nil,
   124  		ipValidator,
   125  		storedRespFetcher,
   126  		hookExecutionPlanBuilder,
   127  		tmaxAdjustments}).Auction), nil
   128  }
   129  
   130  type endpointDeps struct {
   131  	uuidGenerator             uuidutil.UUIDGenerator
   132  	ex                        exchange.Exchange
   133  	paramsValidator           openrtb_ext.BidderParamValidator
   134  	storedReqFetcher          stored_requests.Fetcher
   135  	videoFetcher              stored_requests.Fetcher
   136  	accounts                  stored_requests.AccountFetcher
   137  	cfg                       *config.Configuration
   138  	metricsEngine             metrics.MetricsEngine
   139  	analytics                 analytics.PBSAnalyticsModule
   140  	disabledBidders           map[string]string
   141  	defaultRequest            bool
   142  	defReqJSON                []byte
   143  	bidderMap                 map[string]openrtb_ext.BidderName
   144  	cache                     prebid_cache_client.Client
   145  	debugLogRegexp            *regexp.Regexp
   146  	privateNetworkIPValidator iputil.IPValidator
   147  	storedRespFetcher         stored_requests.Fetcher
   148  	hookExecutionPlanBuilder  hooks.ExecutionPlanBuilder
   149  	tmaxAdjustments           *exchange.TmaxAdjustmentsPreprocessed
   150  }
   151  
   152  func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
   153  	// Prebid Server interprets request.tmax to be the maximum amount of time that a caller is willing
   154  	// to wait for bids. However, tmax may be defined in the Stored Request data.
   155  	//
   156  	// If so, then the trip to the backend might use a significant amount of this time.
   157  	// We can respect timeouts more accurately if we note the *real* start time, and use it
   158  	// to compute the auction timeout.
   159  	start := time.Now()
   160  
   161  	hookExecutor := hookexecution.NewHookExecutor(deps.hookExecutionPlanBuilder, hookexecution.EndpointAuction, deps.metricsEngine)
   162  
   163  	ao := analytics.AuctionObject{
   164  		Status:    http.StatusOK,
   165  		Errors:    make([]error, 0),
   166  		StartTime: start,
   167  	}
   168  
   169  	labels := metrics.Labels{
   170  		Source:        metrics.DemandUnknown,
   171  		RType:         metrics.ReqTypeORTB2Web,
   172  		PubID:         metrics.PublisherUnknown,
   173  		CookieFlag:    metrics.CookieFlagUnknown,
   174  		RequestStatus: metrics.RequestStatusOK,
   175  	}
   176  	defer func() {
   177  		deps.metricsEngine.RecordRequest(labels)
   178  		deps.metricsEngine.RecordRequestTime(labels, time.Since(start))
   179  		deps.analytics.LogAuctionObject(&ao)
   180  	}()
   181  
   182  	w.Header().Set("X-Prebid", version.BuildXPrebidHeader(version.Ver))
   183  
   184  	req, impExtInfoMap, storedAuctionResponses, storedBidResponses, bidderImpReplaceImp, account, errL := deps.parseRequest(r, &labels, hookExecutor)
   185  	if errortypes.ContainsFatalError(errL) && writeError(errL, w, &labels) {
   186  		return
   187  	}
   188  
   189  	if rejectErr := hookexecution.FindFirstRejectOrNil(errL); rejectErr != nil {
   190  		ao.RequestWrapper = req
   191  		labels, ao = rejectAuctionRequest(*rejectErr, w, hookExecutor, req.BidRequest, account, labels, ao)
   192  		return
   193  	}
   194  
   195  	tcf2Config := gdpr.NewTCF2Config(deps.cfg.GDPR.TCF2, account.GDPR)
   196  
   197  	activityControl := privacy.NewActivityControl(&account.Privacy)
   198  
   199  	ctx := context.Background()
   200  
   201  	timeout := deps.cfg.AuctionTimeouts.LimitAuctionTimeout(time.Duration(req.TMax) * time.Millisecond)
   202  	if timeout > 0 {
   203  		var cancel context.CancelFunc
   204  		ctx, cancel = context.WithDeadline(ctx, start.Add(timeout))
   205  		defer cancel()
   206  	}
   207  
   208  	// Read Usersyncs/Cookie
   209  	decoder := usersync.Base64Decoder{}
   210  	usersyncs := usersync.ReadCookie(r, decoder, &deps.cfg.HostCookie)
   211  	usersync.SyncHostCookie(r, usersyncs, &deps.cfg.HostCookie)
   212  
   213  	if req.Site != nil {
   214  		if usersyncs.HasAnyLiveSyncs() {
   215  			labels.CookieFlag = metrics.CookieFlagYes
   216  		} else {
   217  			labels.CookieFlag = metrics.CookieFlagNo
   218  		}
   219  	}
   220  
   221  	// Set Integration Information
   222  	err := deps.setIntegrationType(req, account)
   223  	if err != nil {
   224  		errL = append(errL, err)
   225  		writeError(errL, w, &labels)
   226  		return
   227  	}
   228  	secGPC := r.Header.Get("Sec-GPC")
   229  
   230  	warnings := errortypes.WarningOnly(errL)
   231  
   232  	auctionRequest := &exchange.AuctionRequest{
   233  		BidRequestWrapper:          req,
   234  		Account:                    *account,
   235  		UserSyncs:                  usersyncs,
   236  		RequestType:                labels.RType,
   237  		StartTime:                  start,
   238  		LegacyLabels:               labels,
   239  		Warnings:                   warnings,
   240  		GlobalPrivacyControlHeader: secGPC,
   241  		ImpExtInfoMap:              impExtInfoMap,
   242  		StoredAuctionResponses:     storedAuctionResponses,
   243  		StoredBidResponses:         storedBidResponses,
   244  		BidderImpReplaceImpID:      bidderImpReplaceImp,
   245  		PubID:                      labels.PubID,
   246  		HookExecutor:               hookExecutor,
   247  		TCF2Config:                 tcf2Config,
   248  		Activities:                 activityControl,
   249  		TmaxAdjustments:            deps.tmaxAdjustments,
   250  	}
   251  	auctionResponse, err := deps.ex.HoldAuction(ctx, auctionRequest, nil)
   252  	defer func() {
   253  		if !auctionRequest.BidderResponseStartTime.IsZero() {
   254  			deps.metricsEngine.RecordOverheadTime(metrics.MakeAuctionResponse, time.Since(auctionRequest.BidderResponseStartTime))
   255  		}
   256  	}()
   257  	ao.RequestWrapper = req
   258  	ao.Account = account
   259  	var response *openrtb2.BidResponse
   260  	if auctionResponse != nil {
   261  		response = auctionResponse.BidResponse
   262  	}
   263  	ao.Response = response
   264  	ao.SeatNonBid = auctionResponse.GetSeatNonBid()
   265  	rejectErr, isRejectErr := hookexecution.CastRejectErr(err)
   266  	if err != nil && !isRejectErr {
   267  		if errortypes.ReadCode(err) == errortypes.BadInputErrorCode {
   268  			writeError([]error{err}, w, &labels)
   269  			return
   270  		}
   271  		labels.RequestStatus = metrics.RequestStatusErr
   272  		w.WriteHeader(http.StatusInternalServerError)
   273  		fmt.Fprintf(w, "Critical error while running the auction: %v", err)
   274  		glog.Errorf("/openrtb2/auction Critical error: %v", err)
   275  		ao.Status = http.StatusInternalServerError
   276  		ao.Errors = append(ao.Errors, err)
   277  		return
   278  	} else if isRejectErr {
   279  		labels, ao = rejectAuctionRequest(*rejectErr, w, hookExecutor, req.BidRequest, account, labels, ao)
   280  		return
   281  	}
   282  
   283  	err = setSeatNonBidRaw(req, auctionResponse)
   284  	if err != nil {
   285  		glog.Errorf("Error setting seat non-bid: %v", err)
   286  	}
   287  	labels, ao = sendAuctionResponse(w, hookExecutor, response, req.BidRequest, account, labels, ao)
   288  }
   289  
   290  // setSeatNonBidRaw is transitional function for setting SeatNonBid inside bidResponse.Ext
   291  // Because,
   292  // 1. today exchange.HoldAuction prepares and marshals some piece of response.Ext which is then used by auction.go, amp_auction.go and video_auction.go
   293  // 2. As per discussion with Prebid Team we are planning to move away from - HoldAuction building openrtb2.BidResponse. instead respective auction modules will build this object
   294  // 3. So, we will need this method to do first,  unmarshalling of response.Ext
   295  func setSeatNonBidRaw(request *openrtb_ext.RequestWrapper, auctionResponse *exchange.AuctionResponse) error {
   296  	if auctionResponse == nil || auctionResponse.BidResponse == nil {
   297  		return nil
   298  	}
   299  	// unmarshalling is required here, until we are moving away from bidResponse.Ext, which is populated
   300  	// by HoldAuction
   301  	response := auctionResponse.BidResponse
   302  	respExt := &openrtb_ext.ExtBidResponse{}
   303  	if err := json.Unmarshal(response.Ext, &respExt); err != nil {
   304  		return err
   305  	}
   306  	if setSeatNonBid(respExt, request, auctionResponse) {
   307  		if respExtJson, err := json.Marshal(respExt); err == nil {
   308  			response.Ext = respExtJson
   309  			return nil
   310  		} else {
   311  			return err
   312  		}
   313  	}
   314  	return nil
   315  }
   316  
   317  func rejectAuctionRequest(
   318  	rejectErr hookexecution.RejectError,
   319  	w http.ResponseWriter,
   320  	hookExecutor hookexecution.HookStageExecutor,
   321  	request *openrtb2.BidRequest,
   322  	account *config.Account,
   323  	labels metrics.Labels,
   324  	ao analytics.AuctionObject,
   325  ) (metrics.Labels, analytics.AuctionObject) {
   326  	response := &openrtb2.BidResponse{NBR: openrtb3.NoBidReason(rejectErr.NBR).Ptr()}
   327  	if request != nil {
   328  		response.ID = request.ID
   329  	}
   330  
   331  	ao.Response = response
   332  	ao.Errors = append(ao.Errors, rejectErr)
   333  
   334  	return sendAuctionResponse(w, hookExecutor, response, request, account, labels, ao)
   335  }
   336  
   337  func sendAuctionResponse(
   338  	w http.ResponseWriter,
   339  	hookExecutor hookexecution.HookStageExecutor,
   340  	response *openrtb2.BidResponse,
   341  	request *openrtb2.BidRequest,
   342  	account *config.Account,
   343  	labels metrics.Labels,
   344  	ao analytics.AuctionObject,
   345  ) (metrics.Labels, analytics.AuctionObject) {
   346  	hookExecutor.ExecuteAuctionResponseStage(response)
   347  
   348  	if response != nil {
   349  		stageOutcomes := hookExecutor.GetOutcomes()
   350  		ao.HookExecutionOutcome = stageOutcomes
   351  
   352  		ext, warns, err := hookexecution.EnrichExtBidResponse(response.Ext, stageOutcomes, request, account)
   353  		if err != nil {
   354  			err = fmt.Errorf("Failed to enrich Bid Response with hook debug information: %s", err)
   355  			glog.Errorf(err.Error())
   356  			ao.Errors = append(ao.Errors, err)
   357  		} else {
   358  			response.Ext = ext
   359  		}
   360  
   361  		if len(warns) > 0 {
   362  			ao.Errors = append(ao.Errors, warns...)
   363  		}
   364  	}
   365  
   366  	// Fixes #231
   367  	enc := json.NewEncoder(w)
   368  	enc.SetEscapeHTML(false)
   369  
   370  	w.Header().Set("Content-Type", "application/json")
   371  
   372  	// If an error happens when encoding the response, there isn't much we can do.
   373  	// If we've sent _any_ bytes, then Go would have sent the 200 status code first.
   374  	// That status code can't be un-sent... so the best we can do is log the error.
   375  	if err := enc.Encode(response); err != nil {
   376  		labels.RequestStatus = metrics.RequestStatusNetworkErr
   377  		ao.Errors = append(ao.Errors, fmt.Errorf("/openrtb2/auction Failed to send response: %v", err))
   378  	}
   379  
   380  	return labels, ao
   381  }
   382  
   383  // parseRequest turns the HTTP request into an OpenRTB request. This is guaranteed to return:
   384  //
   385  //   - A context which times out appropriately, given the request.
   386  //   - A cancellation function which should be called if the auction finishes early.
   387  //
   388  // If the errors list is empty, then the returned request will be valid according to the OpenRTB 2.5 spec.
   389  // In case of "strong recommendations" in the spec, it tends to be restrictive. If a better workaround is
   390  // possible, it will return errors with messages that suggest improvements.
   391  //
   392  // If the errors list has at least one element, then no guarantees are made about the returned request.
   393  func (deps *endpointDeps) parseRequest(httpRequest *http.Request, labels *metrics.Labels, hookExecutor hookexecution.HookStageExecutor) (req *openrtb_ext.RequestWrapper, impExtInfoMap map[string]exchange.ImpExtInfo, storedAuctionResponses stored_responses.ImpsWithBidResponses, storedBidResponses stored_responses.ImpBidderStoredResp, bidderImpReplaceImpId stored_responses.BidderImpReplaceImpID, account *config.Account, errs []error) {
   394  	errs = nil
   395  	var err error
   396  	var r io.ReadCloser = httpRequest.Body
   397  	reqContentEncoding := httputil.ContentEncoding(httpRequest.Header.Get("Content-Encoding"))
   398  	if reqContentEncoding != "" {
   399  		if !deps.cfg.Compression.Request.IsSupported(reqContentEncoding) {
   400  			errs = []error{fmt.Errorf("Content-Encoding of type %s is not supported", reqContentEncoding)}
   401  			return
   402  		} else {
   403  			r, err = getCompressionEnabledReader(httpRequest.Body, reqContentEncoding)
   404  			if err != nil {
   405  				errs = []error{err}
   406  				return
   407  			}
   408  		}
   409  	}
   410  	defer r.Close()
   411  	limitedReqReader := &io.LimitedReader{
   412  		R: r,
   413  		N: deps.cfg.MaxRequestSize,
   414  	}
   415  
   416  	requestJson, err := io.ReadAll(limitedReqReader)
   417  	if err != nil {
   418  		errs = []error{err}
   419  		return
   420  	}
   421  
   422  	if limitedReqReader.N <= 0 {
   423  		// Limited Reader returns 0 if the request was exactly at the max size or over the limit.
   424  		// This is because it only reads up to N bytes. To check if the request was too large,
   425  		//  we need to look at the next byte of its underlying reader, limitedReader.R.
   426  		if _, err := limitedReqReader.R.Read(make([]byte, 1)); err != io.EOF {
   427  			// Discard the rest of the request body so that the connection can be reused.
   428  			io.Copy(io.Discard, httpRequest.Body)
   429  			errs = []error{fmt.Errorf("request size exceeded max size of %d bytes.", deps.cfg.MaxRequestSize)}
   430  			return
   431  		}
   432  	}
   433  
   434  	req = &openrtb_ext.RequestWrapper{}
   435  	req.BidRequest = &openrtb2.BidRequest{}
   436  
   437  	requestJson, rejectErr := hookExecutor.ExecuteEntrypointStage(httpRequest, requestJson)
   438  	if rejectErr != nil {
   439  		errs = []error{rejectErr}
   440  		if err = json.Unmarshal(requestJson, req.BidRequest); err != nil {
   441  			glog.Errorf("Failed to unmarshal BidRequest during entrypoint rejection: %s", err)
   442  		}
   443  		return
   444  	}
   445  
   446  	timeout := parseTimeout(requestJson, time.Duration(storedRequestTimeoutMillis)*time.Millisecond)
   447  	ctx, cancel := context.WithTimeout(context.Background(), timeout)
   448  	defer cancel()
   449  
   450  	impInfo, errs := parseImpInfo(requestJson)
   451  	if len(errs) > 0 {
   452  		return nil, nil, nil, nil, nil, nil, errs
   453  	}
   454  
   455  	storedBidRequestId, hasStoredBidRequest, storedRequests, storedImps, errs := deps.getStoredRequests(ctx, requestJson, impInfo)
   456  	if len(errs) > 0 {
   457  		return
   458  	}
   459  
   460  	accountId, isAppReq, errs := getAccountIdFromRawRequest(hasStoredBidRequest, storedRequests[storedBidRequestId], requestJson)
   461  	// fill labels here in order to pass correct metrics in case of errors
   462  	if isAppReq {
   463  		labels.Source = metrics.DemandApp
   464  		labels.RType = metrics.ReqTypeORTB2App
   465  		labels.PubID = accountId
   466  	} else { // is Site request
   467  		labels.Source = metrics.DemandWeb
   468  		labels.PubID = accountId
   469  	}
   470  	if errs != nil {
   471  		return
   472  	}
   473  
   474  	// Look up account
   475  	account, errs = accountService.GetAccount(ctx, deps.cfg, deps.accounts, accountId, deps.metricsEngine)
   476  	if len(errs) > 0 {
   477  		return
   478  	}
   479  
   480  	hookExecutor.SetAccount(account)
   481  	requestJson, rejectErr = hookExecutor.ExecuteRawAuctionStage(requestJson)
   482  	if rejectErr != nil {
   483  		errs = []error{rejectErr}
   484  		if err = json.Unmarshal(requestJson, req.BidRequest); err != nil {
   485  			glog.Errorf("Failed to unmarshal BidRequest during raw auction stage rejection: %s", err)
   486  		}
   487  		return
   488  	}
   489  
   490  	// retrieve storedRequests and storedImps once more in case stored data was changed by the raw auction hook
   491  	if hasPayloadUpdatesAt(hooks.StageRawAuctionRequest.String(), hookExecutor.GetOutcomes()) {
   492  		impInfo, errs = parseImpInfo(requestJson)
   493  		if len(errs) > 0 {
   494  			return nil, nil, nil, nil, nil, nil, errs
   495  		}
   496  		storedBidRequestId, hasStoredBidRequest, storedRequests, storedImps, errs = deps.getStoredRequests(ctx, requestJson, impInfo)
   497  		if len(errs) > 0 {
   498  			return
   499  		}
   500  	}
   501  
   502  	// Fetch the Stored Request data and merge it into the HTTP request.
   503  	if requestJson, impExtInfoMap, errs = deps.processStoredRequests(requestJson, impInfo, storedRequests, storedImps, storedBidRequestId, hasStoredBidRequest); len(errs) > 0 {
   504  		return
   505  	}
   506  
   507  	//Stored auction responses should be processed after stored requests due to possible impression modification
   508  	storedAuctionResponses, storedBidResponses, bidderImpReplaceImpId, errs = stored_responses.ProcessStoredResponses(ctx, requestJson, deps.storedRespFetcher, deps.bidderMap)
   509  	if len(errs) > 0 {
   510  		return nil, nil, nil, nil, nil, nil, errs
   511  	}
   512  
   513  	if err := json.Unmarshal(requestJson, req.BidRequest); err != nil {
   514  		errs = []error{err}
   515  		return
   516  	}
   517  
   518  	if err := mergeBidderParams(req); err != nil {
   519  		errs = []error{err}
   520  		return
   521  	}
   522  
   523  	// Populate any "missing" OpenRTB fields with info from other sources, (e.g. HTTP request headers).
   524  	deps.setFieldsImplicitly(httpRequest, req)
   525  
   526  	if err := ortb.SetDefaults(req); err != nil {
   527  		errs = []error{err}
   528  		return
   529  	}
   530  
   531  	if err := processInterstitials(req); err != nil {
   532  		errs = []error{err}
   533  		return
   534  	}
   535  
   536  	lmt.ModifyForIOS(req.BidRequest)
   537  
   538  	hasStoredResponses := len(storedAuctionResponses) > 0
   539  	errL := deps.validateRequest(req, false, hasStoredResponses, storedBidResponses, hasStoredBidRequest)
   540  	if len(errL) > 0 {
   541  		errs = append(errs, errL...)
   542  	}
   543  
   544  	return
   545  }
   546  
   547  func getCompressionEnabledReader(body io.ReadCloser, contentEncoding httputil.ContentEncoding) (io.ReadCloser, error) {
   548  	switch contentEncoding {
   549  	case httputil.ContentEncodingGZIP:
   550  		return gzip.NewReader(body)
   551  	default:
   552  		return nil, fmt.Errorf("unsupported compression type '%s'", contentEncoding)
   553  	}
   554  }
   555  
   556  // hasPayloadUpdatesAt checks if there are any successful payload updates at given stage
   557  func hasPayloadUpdatesAt(stageName string, outcomes []hookexecution.StageOutcome) bool {
   558  	for _, outcome := range outcomes {
   559  		if stageName != outcome.Stage {
   560  			continue
   561  		}
   562  
   563  		for _, group := range outcome.Groups {
   564  			for _, invocationResult := range group.InvocationResults {
   565  				if invocationResult.Status == hookexecution.StatusSuccess &&
   566  					invocationResult.Action == hookexecution.ActionUpdate {
   567  					return true
   568  				}
   569  			}
   570  		}
   571  	}
   572  
   573  	return false
   574  }
   575  
   576  // parseTimeout returns parses tmax from the requestJson, or returns the default if it doesn't exist.
   577  //
   578  // requestJson should be the content of the POST body.
   579  //
   580  // If the request defines tmax explicitly, then this will return that duration in milliseconds.
   581  // If not, it will return the default timeout.
   582  func parseTimeout(requestJson []byte, defaultTimeout time.Duration) time.Duration {
   583  	if tmax, dataType, _, err := jsonparser.Get(requestJson, "tmax"); dataType != jsonparser.NotExist && err == nil {
   584  		if tmaxInt, err := strconv.Atoi(string(tmax)); err == nil && tmaxInt > 0 {
   585  			return time.Duration(tmaxInt) * time.Millisecond
   586  		}
   587  	}
   588  	return defaultTimeout
   589  }
   590  
   591  // mergeBidderParams merges bidder parameters in req.ext down to the imp[].ext level, with
   592  // priority given to imp[].ext in case of a conflict. No validation of bidder parameters or
   593  // of the ext json is performed. Unmarshal errors are not expected since the ext json was
   594  // validated during the bid request unmarshal.
   595  func mergeBidderParams(req *openrtb_ext.RequestWrapper) error {
   596  	reqExt, err := req.GetRequestExt()
   597  	if err != nil {
   598  		return nil
   599  	}
   600  
   601  	prebid := reqExt.GetPrebid()
   602  	if prebid == nil {
   603  		return nil
   604  	}
   605  
   606  	bidderParamsJson := prebid.BidderParams
   607  	if len(bidderParamsJson) == 0 {
   608  		return nil
   609  	}
   610  
   611  	bidderParams := map[string]map[string]json.RawMessage{}
   612  	if err := json.Unmarshal(bidderParamsJson, &bidderParams); err != nil {
   613  		return nil
   614  	}
   615  
   616  	for i, imp := range req.GetImp() {
   617  		impExt, err := imp.GetImpExt()
   618  		if err != nil {
   619  			continue
   620  		}
   621  
   622  		// merges bidder parameters passed at req.ext level with imp[].ext.BIDDER level
   623  		if err := mergeBidderParamsImpExt(impExt, bidderParams); err != nil {
   624  			return fmt.Errorf("error processing bidder parameters for imp[%d]: %s", i, err.Error())
   625  		}
   626  
   627  		// merges bidder parameters passed at req.ext level with imp[].ext.prebid.bidder.BIDDER level
   628  		if err := mergeBidderParamsImpExtPrebid(impExt, bidderParams); err != nil {
   629  			return fmt.Errorf("error processing bidder parameters for imp[%d]: %s", i, err.Error())
   630  		}
   631  	}
   632  
   633  	return nil
   634  }
   635  
   636  // mergeBidderParamsImpExt merges bidder parameters in req.ext down to the imp[].ext.BIDDER
   637  // level, giving priority to imp[].ext.BIDDER in case of a conflict. Unmarshal errors are not
   638  // expected since the ext json was validated during the bid request unmarshal.
   639  func mergeBidderParamsImpExt(impExt *openrtb_ext.ImpExt, reqExtParams map[string]map[string]json.RawMessage) error {
   640  	extMap := impExt.GetExt()
   641  	extMapModified := false
   642  
   643  	for bidder, params := range reqExtParams {
   644  		if !isPossibleBidder(bidder) {
   645  			continue
   646  		}
   647  
   648  		impExtBidder, impExtBidderExists := extMap[bidder]
   649  		if !impExtBidderExists || impExtBidder == nil {
   650  			continue
   651  		}
   652  
   653  		impExtBidderMap := map[string]json.RawMessage{}
   654  		if len(impExtBidder) > 0 {
   655  			if err := json.Unmarshal(impExtBidder, &impExtBidderMap); err != nil {
   656  				continue
   657  			}
   658  		}
   659  
   660  		modified := false
   661  		for key, value := range params {
   662  			if _, present := impExtBidderMap[key]; !present {
   663  				impExtBidderMap[key] = value
   664  				modified = true
   665  			}
   666  		}
   667  
   668  		if modified {
   669  			impExtBidderJson, err := json.Marshal(impExtBidderMap)
   670  			if err != nil {
   671  				return fmt.Errorf("error marshalling ext.BIDDER: %s", err.Error())
   672  			}
   673  			extMap[bidder] = impExtBidderJson
   674  			extMapModified = true
   675  		}
   676  	}
   677  
   678  	if extMapModified {
   679  		impExt.SetExt(extMap)
   680  	}
   681  
   682  	return nil
   683  }
   684  
   685  // mergeBidderParamsImpExtPrebid merges bidder parameters in req.ext down to the imp[].ext.prebid.bidder.BIDDER
   686  // level, giving priority to imp[].ext.prebid.bidder.BIDDER in case of a conflict.
   687  func mergeBidderParamsImpExtPrebid(impExt *openrtb_ext.ImpExt, reqExtParams map[string]map[string]json.RawMessage) error {
   688  	prebid := impExt.GetPrebid()
   689  	prebidModified := false
   690  
   691  	if prebid == nil || len(prebid.Bidder) == 0 {
   692  		return nil
   693  	}
   694  
   695  	for bidder, params := range reqExtParams {
   696  		impExtPrebidBidder, impExtPrebidBidderExists := prebid.Bidder[bidder]
   697  		if !impExtPrebidBidderExists || impExtPrebidBidder == nil {
   698  			continue
   699  		}
   700  
   701  		impExtPrebidBidderMap := map[string]json.RawMessage{}
   702  		if len(impExtPrebidBidder) > 0 {
   703  			if err := json.Unmarshal(impExtPrebidBidder, &impExtPrebidBidderMap); err != nil {
   704  				continue
   705  			}
   706  		}
   707  
   708  		modified := false
   709  		for key, value := range params {
   710  			if _, present := impExtPrebidBidderMap[key]; !present {
   711  				impExtPrebidBidderMap[key] = value
   712  				modified = true
   713  			}
   714  		}
   715  
   716  		if modified {
   717  			impExtPrebidBidderJson, err := json.Marshal(impExtPrebidBidderMap)
   718  			if err != nil {
   719  				return fmt.Errorf("error marshalling ext.prebid.bidder.BIDDER: %s", err.Error())
   720  			}
   721  			prebid.Bidder[bidder] = impExtPrebidBidderJson
   722  			prebidModified = true
   723  		}
   724  	}
   725  
   726  	if prebidModified {
   727  		impExt.SetPrebid(prebid)
   728  	}
   729  
   730  	return nil
   731  }
   732  
   733  func (deps *endpointDeps) validateRequest(req *openrtb_ext.RequestWrapper, isAmp bool, hasStoredResponses bool, storedBidResp stored_responses.ImpBidderStoredResp, hasStoredBidRequest bool) []error {
   734  	errL := []error{}
   735  	if req.ID == "" {
   736  		return []error{errors.New("request missing required field: \"id\"")}
   737  	}
   738  
   739  	if req.TMax < 0 {
   740  		return []error{fmt.Errorf("request.tmax must be nonnegative. Got %d", req.TMax)}
   741  	}
   742  
   743  	if req.LenImp() < 1 {
   744  		return []error{errors.New("request.imp must contain at least one element.")}
   745  	}
   746  
   747  	if len(req.Cur) > 1 {
   748  		req.Cur = req.Cur[0:1]
   749  		errL = append(errL, &errortypes.Warning{Message: fmt.Sprintf("A prebid request can only process one currency. Taking the first currency in the list, %s, as the active currency", req.Cur[0])})
   750  	}
   751  
   752  	// If automatically filling source TID is enabled then validate that
   753  	// source.TID exists and If it doesn't, fill it with a randomly generated UUID
   754  	if deps.cfg.AutoGenSourceTID {
   755  		if err := validateAndFillSourceTID(req, deps.cfg.GenerateRequestID, hasStoredBidRequest, isAmp); err != nil {
   756  			return []error{err}
   757  		}
   758  	}
   759  
   760  	var aliases map[string]string
   761  	reqExt, err := req.GetRequestExt()
   762  	if err != nil {
   763  		return []error{fmt.Errorf("request.ext is invalid: %v", err)}
   764  	}
   765  
   766  	reqPrebid := reqExt.GetPrebid()
   767  	if err := deps.parseBidExt(req); err != nil {
   768  		return []error{err}
   769  	}
   770  
   771  	if reqPrebid != nil {
   772  		aliases = reqPrebid.Aliases
   773  
   774  		if err := deps.validateAliases(aliases); err != nil {
   775  			return []error{err}
   776  		}
   777  
   778  		if err := deps.validateAliasesGVLIDs(reqPrebid.AliasGVLIDs, aliases); err != nil {
   779  			return []error{err}
   780  		}
   781  
   782  		if err := deps.validateBidAdjustmentFactors(reqPrebid.BidAdjustmentFactors, aliases); err != nil {
   783  			return []error{err}
   784  		}
   785  
   786  		if err := validateSChains(reqPrebid.SChains); err != nil {
   787  			return []error{err}
   788  		}
   789  
   790  		if err := deps.validateEidPermissions(reqPrebid.Data, aliases); err != nil {
   791  			return []error{err}
   792  		}
   793  
   794  		if err := currency.ValidateCustomRates(reqPrebid.CurrencyConversions); err != nil {
   795  			return []error{err}
   796  		}
   797  	}
   798  
   799  	if err := mapSChains(req); err != nil {
   800  		return []error{err}
   801  	}
   802  
   803  	if err := validateOrFillChannel(req, isAmp); err != nil {
   804  		return []error{err}
   805  	}
   806  
   807  	if (req.Site == nil && req.App == nil) || (req.Site != nil && req.App != nil) {
   808  		return append(errL, errors.New("request.site or request.app must be defined, but not both."))
   809  	}
   810  
   811  	if errs := validateRequestExt(req); len(errs) != 0 {
   812  		if errortypes.ContainsFatalError(errs) {
   813  			return append(errL, errs...)
   814  		}
   815  		errL = append(errL, errs...)
   816  	}
   817  
   818  	if err := deps.validateSite(req); err != nil {
   819  		return append(errL, err)
   820  	}
   821  
   822  	if err := deps.validateApp(req); err != nil {
   823  		return append(errL, err)
   824  	}
   825  
   826  	var gpp gpplib.GppContainer
   827  	if req.BidRequest.Regs != nil && len(req.BidRequest.Regs.GPP) > 0 {
   828  		gpp, err = gpplib.Parse(req.BidRequest.Regs.GPP)
   829  		if err != nil {
   830  			errL = append(errL, &errortypes.Warning{
   831  				Message:     fmt.Sprintf("GPP consent string is invalid and will be ignored. (%v)", err),
   832  				WarningCode: errortypes.InvalidPrivacyConsentWarningCode})
   833  		}
   834  	}
   835  
   836  	if errs := deps.validateUser(req, aliases, gpp); errs != nil {
   837  		if len(errs) > 0 {
   838  			errL = append(errL, errs...)
   839  		}
   840  		if errortypes.ContainsFatalError(errs) {
   841  			return errL
   842  		}
   843  	}
   844  
   845  	if errs := validateRegs(req, gpp); errs != nil {
   846  		if len(errs) > 0 {
   847  			errL = append(errL, errs...)
   848  		}
   849  		if errortypes.ContainsFatalError(errs) {
   850  			return errL
   851  		}
   852  	}
   853  
   854  	if err := validateDevice(req.Device); err != nil {
   855  		return append(errL, err)
   856  	}
   857  
   858  	if ccpaPolicy, err := ccpa.ReadFromRequestWrapper(req, gpp); err != nil {
   859  		errL = append(errL, err)
   860  		if errortypes.ContainsFatalError([]error{err}) {
   861  			return errL
   862  		}
   863  	} else if _, err := ccpaPolicy.Parse(exchange.GetValidBidders(aliases)); err != nil {
   864  		if _, invalidConsent := err.(*errortypes.Warning); invalidConsent {
   865  			errL = append(errL, &errortypes.Warning{
   866  				Message:     fmt.Sprintf("CCPA consent is invalid and will be ignored. (%v)", err),
   867  				WarningCode: errortypes.InvalidPrivacyConsentWarningCode})
   868  			regsExt, err := req.GetRegExt()
   869  			if err != nil {
   870  				return append(errL, err)
   871  			}
   872  			regsExt.SetUSPrivacy("")
   873  		} else {
   874  			return append(errL, err)
   875  		}
   876  	}
   877  
   878  	impIDs := make(map[string]int, req.LenImp())
   879  	for i, imp := range req.GetImp() {
   880  		// check for unique imp id
   881  		if firstIndex, ok := impIDs[imp.ID]; ok {
   882  			errL = append(errL, fmt.Errorf(`request.imp[%d].id and request.imp[%d].id are both "%s". Imp IDs must be unique.`, firstIndex, i, imp.ID))
   883  		}
   884  		impIDs[imp.ID] = i
   885  
   886  		errs := deps.validateImp(imp, aliases, i, hasStoredResponses, storedBidResp)
   887  		if len(errs) > 0 {
   888  			errL = append(errL, errs...)
   889  		}
   890  		if errortypes.ContainsFatalError(errs) {
   891  			return errL
   892  		}
   893  	}
   894  
   895  	return errL
   896  }
   897  
   898  // mapSChains maps an schain defined in an ORTB 2.4 location (req.ext.schain) to the ORTB 2.5 location
   899  // (req.source.ext.schain) if no ORTB 2.5 schain (req.source.ext.schain, req.ext.prebid.schains) exists.
   900  // An ORTB 2.4 schain is always deleted from the 2.4 location regardless of whether an ORTB 2.5 schain exists.
   901  func mapSChains(req *openrtb_ext.RequestWrapper) error {
   902  	reqExt, err := req.GetRequestExt()
   903  	if err != nil {
   904  		return fmt.Errorf("req.ext is invalid: %v", err)
   905  	}
   906  	sourceExt, err := req.GetSourceExt()
   907  	if err != nil {
   908  		return fmt.Errorf("source.ext is invalid: %v", err)
   909  	}
   910  
   911  	reqExtSChain := reqExt.GetSChain()
   912  	reqExt.SetSChain(nil)
   913  
   914  	if reqPrebid := reqExt.GetPrebid(); reqPrebid != nil && reqPrebid.SChains != nil {
   915  		return nil
   916  	} else if sourceExt.GetSChain() != nil {
   917  		return nil
   918  	} else if reqExtSChain != nil {
   919  		sourceExt.SetSChain(reqExtSChain)
   920  	}
   921  	return nil
   922  }
   923  
   924  func validateAndFillSourceTID(req *openrtb_ext.RequestWrapper, generateRequestID bool, hasStoredBidRequest bool, isAmp bool) error {
   925  	if req.Source == nil {
   926  		req.Source = &openrtb2.Source{}
   927  	}
   928  
   929  	if req.Source.TID == "" || req.Source.TID == "{{UUID}}" || (generateRequestID && (isAmp || hasStoredBidRequest)) {
   930  		rawUUID, err := uuid.NewV4()
   931  		if err != nil {
   932  			return errors.New("error creating a random UUID for source.tid")
   933  		}
   934  		req.Source.TID = rawUUID.String()
   935  	}
   936  
   937  	for _, impWrapper := range req.GetImp() {
   938  		ie, _ := impWrapper.GetImpExt()
   939  		if ie.GetTid() == "" || ie.GetTid() == "{{UUID}}" || (generateRequestID && (isAmp || hasStoredBidRequest)) {
   940  			rawUUID, err := uuid.NewV4()
   941  			if err != nil {
   942  				return errors.New("imp.ext.tid missing in the imp and error creating a random UID")
   943  			}
   944  			ie.SetTid(rawUUID.String())
   945  			impWrapper.RebuildImp()
   946  		}
   947  	}
   948  
   949  	return nil
   950  }
   951  
   952  func (deps *endpointDeps) validateBidAdjustmentFactors(adjustmentFactors map[string]float64, aliases map[string]string) error {
   953  	for bidderToAdjust, adjustmentFactor := range adjustmentFactors {
   954  		if adjustmentFactor <= 0 {
   955  			return fmt.Errorf("request.ext.prebid.bidadjustmentfactors.%s must be a positive number. Got %f", bidderToAdjust, adjustmentFactor)
   956  		}
   957  		if _, isBidder := deps.bidderMap[bidderToAdjust]; !isBidder {
   958  			if _, isAlias := aliases[bidderToAdjust]; !isAlias {
   959  				return fmt.Errorf("request.ext.prebid.bidadjustmentfactors.%s is not a known bidder or alias", bidderToAdjust)
   960  			}
   961  		}
   962  	}
   963  	return nil
   964  }
   965  
   966  func validateSChains(sChains []*openrtb_ext.ExtRequestPrebidSChain) error {
   967  	_, err := schain.BidderToPrebidSChains(sChains)
   968  	return err
   969  }
   970  
   971  func (deps *endpointDeps) validateEidPermissions(prebid *openrtb_ext.ExtRequestPrebidData, aliases map[string]string) error {
   972  	if prebid == nil {
   973  		return nil
   974  	}
   975  
   976  	uniqueSources := make(map[string]struct{}, len(prebid.EidPermissions))
   977  	for i, eid := range prebid.EidPermissions {
   978  		if len(eid.Source) == 0 {
   979  			return fmt.Errorf(`request.ext.prebid.data.eidpermissions[%d] missing required field: "source"`, i)
   980  		}
   981  
   982  		if _, exists := uniqueSources[eid.Source]; exists {
   983  			return fmt.Errorf(`request.ext.prebid.data.eidpermissions[%d] duplicate entry with field: "source"`, i)
   984  		}
   985  		uniqueSources[eid.Source] = struct{}{}
   986  
   987  		if len(eid.Bidders) == 0 {
   988  			return fmt.Errorf(`request.ext.prebid.data.eidpermissions[%d] missing or empty required field: "bidders"`, i)
   989  		}
   990  
   991  		if err := validateBidders(eid.Bidders, deps.bidderMap, aliases); err != nil {
   992  			return fmt.Errorf(`request.ext.prebid.data.eidpermissions[%d] contains %v`, i, err)
   993  		}
   994  	}
   995  
   996  	return nil
   997  }
   998  
   999  func validateBidders(bidders []string, knownBidders map[string]openrtb_ext.BidderName, knownAliases map[string]string) error {
  1000  	for _, bidder := range bidders {
  1001  		if bidder == "*" {
  1002  			if len(bidders) > 1 {
  1003  				return errors.New(`bidder wildcard "*" mixed with specific bidders`)
  1004  			}
  1005  		} else {
  1006  			_, isCoreBidder := knownBidders[bidder]
  1007  			_, isAlias := knownAliases[bidder]
  1008  			if !isCoreBidder && !isAlias {
  1009  				return fmt.Errorf(`unrecognized bidder "%v"`, bidder)
  1010  			}
  1011  		}
  1012  	}
  1013  	return nil
  1014  }
  1015  
  1016  func (deps *endpointDeps) validateImp(imp *openrtb_ext.ImpWrapper, aliases map[string]string, index int, hasStoredResponses bool, storedBidResp stored_responses.ImpBidderStoredResp) []error {
  1017  	if imp.ID == "" {
  1018  		return []error{fmt.Errorf("request.imp[%d] missing required field: \"id\"", index)}
  1019  	}
  1020  
  1021  	if len(imp.Metric) != 0 {
  1022  		return []error{fmt.Errorf("request.imp[%d].metric is not yet supported by prebid-server. Support may be added in the future", index)}
  1023  	}
  1024  
  1025  	if imp.Banner == nil && imp.Video == nil && imp.Audio == nil && imp.Native == nil {
  1026  		return []error{fmt.Errorf("request.imp[%d] must contain at least one of \"banner\", \"video\", \"audio\", or \"native\"", index)}
  1027  	}
  1028  
  1029  	if err := validateBanner(imp.Banner, index, isInterstitial(imp)); err != nil {
  1030  		return []error{err}
  1031  	}
  1032  
  1033  	if err := validateVideo(imp.Video, index); err != nil {
  1034  		return []error{err}
  1035  	}
  1036  
  1037  	if err := validateAudio(imp.Audio, index); err != nil {
  1038  		return []error{err}
  1039  	}
  1040  
  1041  	if err := fillAndValidateNative(imp.Native, index); err != nil {
  1042  		return []error{err}
  1043  	}
  1044  
  1045  	if err := validatePmp(imp.PMP, index); err != nil {
  1046  		return []error{err}
  1047  	}
  1048  
  1049  	errL := deps.validateImpExt(imp, aliases, index, hasStoredResponses, storedBidResp)
  1050  	if len(errL) != 0 {
  1051  		return errL
  1052  	}
  1053  
  1054  	return nil
  1055  }
  1056  
  1057  func isInterstitial(imp *openrtb_ext.ImpWrapper) bool {
  1058  	return imp.Instl == 1
  1059  }
  1060  
  1061  func validateBanner(banner *openrtb2.Banner, impIndex int, isInterstitial bool) error {
  1062  	if banner == nil {
  1063  		return nil
  1064  	}
  1065  
  1066  	// The following fields were previously uints in the OpenRTB library we use, but have
  1067  	// since been changed to ints. We decided to maintain the non-negative check.
  1068  	if banner.W != nil && *banner.W < 0 {
  1069  		return fmt.Errorf("request.imp[%d].banner.w must be a positive number", impIndex)
  1070  	}
  1071  	if banner.H != nil && *banner.H < 0 {
  1072  		return fmt.Errorf("request.imp[%d].banner.h must be a positive number", impIndex)
  1073  	}
  1074  
  1075  	// The following fields are deprecated in the OpenRTB 2.5 spec but are still present
  1076  	// in the OpenRTB library we use. Enforce they are not specified.
  1077  	if banner.WMin != 0 {
  1078  		return fmt.Errorf("request.imp[%d].banner uses unsupported property: \"wmin\". Use the \"format\" array instead.", impIndex)
  1079  	}
  1080  	if banner.WMax != 0 {
  1081  		return fmt.Errorf("request.imp[%d].banner uses unsupported property: \"wmax\". Use the \"format\" array instead.", impIndex)
  1082  	}
  1083  	if banner.HMin != 0 {
  1084  		return fmt.Errorf("request.imp[%d].banner uses unsupported property: \"hmin\". Use the \"format\" array instead.", impIndex)
  1085  	}
  1086  	if banner.HMax != 0 {
  1087  		return fmt.Errorf("request.imp[%d].banner uses unsupported property: \"hmax\". Use the \"format\" array instead.", impIndex)
  1088  	}
  1089  
  1090  	hasRootSize := banner.H != nil && banner.W != nil && *banner.H > 0 && *banner.W > 0
  1091  	if !hasRootSize && len(banner.Format) == 0 && !isInterstitial {
  1092  		return fmt.Errorf("request.imp[%d].banner has no sizes. Define \"w\" and \"h\", or include \"format\" elements.", impIndex)
  1093  	}
  1094  
  1095  	for i, format := range banner.Format {
  1096  		if err := validateFormat(&format, impIndex, i); err != nil {
  1097  			return err
  1098  		}
  1099  	}
  1100  
  1101  	return nil
  1102  }
  1103  
  1104  func validateVideo(video *openrtb2.Video, impIndex int) error {
  1105  	if video == nil {
  1106  		return nil
  1107  	}
  1108  
  1109  	if len(video.MIMEs) < 1 {
  1110  		return fmt.Errorf("request.imp[%d].video.mimes must contain at least one supported MIME type", impIndex)
  1111  	}
  1112  
  1113  	// The following fields were previously uints in the OpenRTB library we use, but have
  1114  	// since been changed to ints. We decided to maintain the non-negative check.
  1115  	if video.W < 0 {
  1116  		return fmt.Errorf("request.imp[%d].video.w must be a positive number", impIndex)
  1117  	}
  1118  	if video.H < 0 {
  1119  		return fmt.Errorf("request.imp[%d].video.h must be a positive number", impIndex)
  1120  	}
  1121  	if video.MinBitRate < 0 {
  1122  		return fmt.Errorf("request.imp[%d].video.minbitrate must be a positive number", impIndex)
  1123  	}
  1124  	if video.MaxBitRate < 0 {
  1125  		return fmt.Errorf("request.imp[%d].video.maxbitrate must be a positive number", impIndex)
  1126  	}
  1127  
  1128  	return nil
  1129  }
  1130  
  1131  func validateAudio(audio *openrtb2.Audio, impIndex int) error {
  1132  	if audio == nil {
  1133  		return nil
  1134  	}
  1135  
  1136  	if len(audio.MIMEs) < 1 {
  1137  		return fmt.Errorf("request.imp[%d].audio.mimes must contain at least one supported MIME type", impIndex)
  1138  	}
  1139  
  1140  	// The following fields were previously uints in the OpenRTB library we use, but have
  1141  	// since been changed to ints. We decided to maintain the non-negative check.
  1142  	if audio.Sequence < 0 {
  1143  		return fmt.Errorf("request.imp[%d].audio.sequence must be a positive number", impIndex)
  1144  	}
  1145  	if audio.MaxSeq < 0 {
  1146  		return fmt.Errorf("request.imp[%d].audio.maxseq must be a positive number", impIndex)
  1147  	}
  1148  	if audio.MinBitrate < 0 {
  1149  		return fmt.Errorf("request.imp[%d].audio.minbitrate must be a positive number", impIndex)
  1150  	}
  1151  	if audio.MaxBitrate < 0 {
  1152  		return fmt.Errorf("request.imp[%d].audio.maxbitrate must be a positive number", impIndex)
  1153  	}
  1154  
  1155  	return nil
  1156  }
  1157  
  1158  // fillAndValidateNative validates the request, and assigns the Asset IDs as recommended by the Native v1.2 spec.
  1159  func fillAndValidateNative(n *openrtb2.Native, impIndex int) error {
  1160  	if n == nil {
  1161  		return nil
  1162  	}
  1163  
  1164  	if len(n.Request) == 0 {
  1165  		return fmt.Errorf("request.imp[%d].native missing required property \"request\"", impIndex)
  1166  	}
  1167  	var nativePayload nativeRequests.Request
  1168  	if err := json.Unmarshal(json.RawMessage(n.Request), &nativePayload); err != nil {
  1169  		return err
  1170  	}
  1171  
  1172  	if err := validateNativeContextTypes(nativePayload.Context, nativePayload.ContextSubType, impIndex); err != nil {
  1173  		return err
  1174  	}
  1175  	if err := validateNativePlacementType(nativePayload.PlcmtType, impIndex); err != nil {
  1176  		return err
  1177  	}
  1178  	if err := fillAndValidateNativeAssets(nativePayload.Assets, impIndex); err != nil {
  1179  		return err
  1180  	}
  1181  	if err := validateNativeEventTrackers(nativePayload.EventTrackers, impIndex); err != nil {
  1182  		return err
  1183  	}
  1184  
  1185  	serialized, err := json.Marshal(nativePayload)
  1186  	if err != nil {
  1187  		return err
  1188  	}
  1189  	n.Request = string(serialized)
  1190  	return nil
  1191  }
  1192  
  1193  func validateNativeContextTypes(cType native1.ContextType, cSubtype native1.ContextSubType, impIndex int) error {
  1194  	if cType == 0 {
  1195  		// Context is only recommended, so none is a valid type.
  1196  		return nil
  1197  	}
  1198  	if cType < native1.ContextTypeContent || (cType > native1.ContextTypeProduct && cType < openrtb_ext.NativeExchangeSpecificLowerBound) {
  1199  		return fmt.Errorf("request.imp[%d].native.request.context is invalid. See https://iabtechlab.com/wp-content/uploads/2016/07/OpenRTB-Native-Ads-Specification-Final-1.2.pdf#page=39", impIndex)
  1200  	}
  1201  	if cSubtype < 0 {
  1202  		return fmt.Errorf("request.imp[%d].native.request.contextsubtype value can't be less than 0. See https://iabtechlab.com/wp-content/uploads/2016/07/OpenRTB-Native-Ads-Specification-Final-1.2.pdf#page=39", impIndex)
  1203  	}
  1204  	if cSubtype == 0 {
  1205  		return nil
  1206  	}
  1207  	if cSubtype >= native1.ContextSubTypeGeneral && cSubtype <= native1.ContextSubTypeUserGenerated {
  1208  		if cType != native1.ContextTypeContent && cType < openrtb_ext.NativeExchangeSpecificLowerBound {
  1209  			return fmt.Errorf("request.imp[%d].native.request.context is %d, but contextsubtype is %d. This is an invalid combination. See https://iabtechlab.com/wp-content/uploads/2016/07/OpenRTB-Native-Ads-Specification-Final-1.2.pdf#page=39", impIndex, cType, cSubtype)
  1210  		}
  1211  		return nil
  1212  	}
  1213  	if cSubtype >= native1.ContextSubTypeSocial && cSubtype <= native1.ContextSubTypeChat {
  1214  		if cType != native1.ContextTypeSocial && cType < openrtb_ext.NativeExchangeSpecificLowerBound {
  1215  			return fmt.Errorf("request.imp[%d].native.request.context is %d, but contextsubtype is %d. This is an invalid combination. See https://iabtechlab.com/wp-content/uploads/2016/07/OpenRTB-Native-Ads-Specification-Final-1.2.pdf#page=39", impIndex, cType, cSubtype)
  1216  		}
  1217  		return nil
  1218  	}
  1219  	if cSubtype >= native1.ContextSubTypeSelling && cSubtype <= native1.ContextSubTypeProductReview {
  1220  		if cType != native1.ContextTypeProduct && cType < openrtb_ext.NativeExchangeSpecificLowerBound {
  1221  			return fmt.Errorf("request.imp[%d].native.request.context is %d, but contextsubtype is %d. This is an invalid combination. See https://iabtechlab.com/wp-content/uploads/2016/07/OpenRTB-Native-Ads-Specification-Final-1.2.pdf#page=39", impIndex, cType, cSubtype)
  1222  		}
  1223  		return nil
  1224  	}
  1225  	if cSubtype >= openrtb_ext.NativeExchangeSpecificLowerBound {
  1226  		return nil
  1227  	}
  1228  
  1229  	return fmt.Errorf("request.imp[%d].native.request.contextsubtype is invalid. See https://iabtechlab.com/wp-content/uploads/2016/07/OpenRTB-Native-Ads-Specification-Final-1.2.pdf#page=39", impIndex)
  1230  }
  1231  
  1232  func validateNativePlacementType(pt native1.PlacementType, impIndex int) error {
  1233  	if pt == 0 {
  1234  		// Placement Type is only recommended, not required.
  1235  		return nil
  1236  	}
  1237  	if pt < native1.PlacementTypeFeed || (pt > native1.PlacementTypeRecommendationWidget && pt < openrtb_ext.NativeExchangeSpecificLowerBound) {
  1238  		return fmt.Errorf("request.imp[%d].native.request.plcmttype is invalid. See https://iabtechlab.com/wp-content/uploads/2016/07/OpenRTB-Native-Ads-Specification-Final-1.2.pdf#page=40", impIndex)
  1239  	}
  1240  	return nil
  1241  }
  1242  
  1243  func fillAndValidateNativeAssets(assets []nativeRequests.Asset, impIndex int) error {
  1244  	if len(assets) < 1 {
  1245  		return fmt.Errorf("request.imp[%d].native.request.assets must be an array containing at least one object", impIndex)
  1246  	}
  1247  
  1248  	assetIDs := make(map[int64]struct{}, len(assets))
  1249  
  1250  	// If none of the asset IDs are defined by the caller, then prebid server should assign its own unique IDs. But
  1251  	// if the caller did assign its own asset IDs, then prebid server will respect those IDs
  1252  	assignAssetIDs := true
  1253  	for i := 0; i < len(assets); i++ {
  1254  		assignAssetIDs = assignAssetIDs && (assets[i].ID == 0)
  1255  	}
  1256  
  1257  	for i := 0; i < len(assets); i++ {
  1258  		if err := validateNativeAsset(assets[i], impIndex, i); err != nil {
  1259  			return err
  1260  		}
  1261  
  1262  		if assignAssetIDs {
  1263  			assets[i].ID = int64(i)
  1264  			continue
  1265  		}
  1266  
  1267  		// Each asset should have a unique ID thats assigned by the caller
  1268  		if _, ok := assetIDs[assets[i].ID]; ok {
  1269  			return fmt.Errorf("request.imp[%d].native.request.assets[%d].id is already being used by another asset. Each asset ID must be unique.", impIndex, i)
  1270  		}
  1271  
  1272  		assetIDs[assets[i].ID] = struct{}{}
  1273  	}
  1274  
  1275  	return nil
  1276  }
  1277  
  1278  func validateNativeAsset(asset nativeRequests.Asset, impIndex int, assetIndex int) error {
  1279  	assetErr := "request.imp[%d].native.request.assets[%d] must define exactly one of {title, img, video, data}"
  1280  	foundType := false
  1281  
  1282  	if asset.Title != nil {
  1283  		foundType = true
  1284  		if err := validateNativeAssetTitle(asset.Title, impIndex, assetIndex); err != nil {
  1285  			return err
  1286  		}
  1287  	}
  1288  
  1289  	if asset.Img != nil {
  1290  		if foundType {
  1291  			return fmt.Errorf(assetErr, impIndex, assetIndex)
  1292  		}
  1293  		foundType = true
  1294  		if err := validateNativeAssetImage(asset.Img, impIndex, assetIndex); err != nil {
  1295  			return err
  1296  		}
  1297  	}
  1298  
  1299  	if asset.Video != nil {
  1300  		if foundType {
  1301  			return fmt.Errorf(assetErr, impIndex, assetIndex)
  1302  		}
  1303  		foundType = true
  1304  		if err := validateNativeAssetVideo(asset.Video, impIndex, assetIndex); err != nil {
  1305  			return err
  1306  		}
  1307  	}
  1308  
  1309  	if asset.Data != nil {
  1310  		if foundType {
  1311  			return fmt.Errorf(assetErr, impIndex, assetIndex)
  1312  		}
  1313  		foundType = true
  1314  		if err := validateNativeAssetData(asset.Data, impIndex, assetIndex); err != nil {
  1315  			return err
  1316  		}
  1317  	}
  1318  
  1319  	if !foundType {
  1320  		return fmt.Errorf(assetErr, impIndex, assetIndex)
  1321  	}
  1322  
  1323  	return nil
  1324  }
  1325  
  1326  func validateNativeEventTrackers(trackers []nativeRequests.EventTracker, impIndex int) error {
  1327  	for i := 0; i < len(trackers); i++ {
  1328  		if err := validateNativeEventTracker(trackers[i], impIndex, i); err != nil {
  1329  			return err
  1330  		}
  1331  	}
  1332  	return nil
  1333  }
  1334  
  1335  func validateNativeAssetTitle(title *nativeRequests.Title, impIndex int, assetIndex int) error {
  1336  	if title.Len < 1 {
  1337  		return fmt.Errorf("request.imp[%d].native.request.assets[%d].title.len must be a positive number", impIndex, assetIndex)
  1338  	}
  1339  	return nil
  1340  }
  1341  
  1342  func validateNativeEventTracker(tracker nativeRequests.EventTracker, impIndex int, eventIndex int) error {
  1343  	if tracker.Event < native1.EventTypeImpression || (tracker.Event > native1.EventTypeViewableVideo50 && tracker.Event < openrtb_ext.NativeExchangeSpecificLowerBound) {
  1344  		return fmt.Errorf("request.imp[%d].native.request.eventtrackers[%d].event is invalid. See section 7.6: https://iabtechlab.com/wp-content/uploads/2016/07/OpenRTB-Native-Ads-Specification-Final-1.2.pdf#page=43", impIndex, eventIndex)
  1345  	}
  1346  	if len(tracker.Methods) < 1 {
  1347  		return fmt.Errorf("request.imp[%d].native.request.eventtrackers[%d].method is required. See section 7.7: https://iabtechlab.com/wp-content/uploads/2016/07/OpenRTB-Native-Ads-Specification-Final-1.2.pdf#page=43", impIndex, eventIndex)
  1348  	}
  1349  	for methodIndex, method := range tracker.Methods {
  1350  		if method < native1.EventTrackingMethodImage || (method > native1.EventTrackingMethodJS && method < openrtb_ext.NativeExchangeSpecificLowerBound) {
  1351  			return fmt.Errorf("request.imp[%d].native.request.eventtrackers[%d].methods[%d] is invalid. See section 7.7: https://iabtechlab.com/wp-content/uploads/2016/07/OpenRTB-Native-Ads-Specification-Final-1.2.pdf#page=43", impIndex, eventIndex, methodIndex)
  1352  		}
  1353  	}
  1354  
  1355  	return nil
  1356  }
  1357  
  1358  func validateNativeAssetImage(img *nativeRequests.Image, impIndex int, assetIndex int) error {
  1359  	if img.W < 0 {
  1360  		return fmt.Errorf("request.imp[%d].native.request.assets[%d].img.w must be a positive integer", impIndex, assetIndex)
  1361  	}
  1362  	if img.H < 0 {
  1363  		return fmt.Errorf("request.imp[%d].native.request.assets[%d].img.h must be a positive integer", impIndex, assetIndex)
  1364  	}
  1365  	if img.WMin < 0 {
  1366  		return fmt.Errorf("request.imp[%d].native.request.assets[%d].img.wmin must be a positive integer", impIndex, assetIndex)
  1367  	}
  1368  	if img.HMin < 0 {
  1369  		return fmt.Errorf("request.imp[%d].native.request.assets[%d].img.hmin must be a positive integer", impIndex, assetIndex)
  1370  	}
  1371  	return nil
  1372  }
  1373  
  1374  func validateNativeAssetVideo(video *nativeRequests.Video, impIndex int, assetIndex int) error {
  1375  	if len(video.MIMEs) < 1 {
  1376  		return fmt.Errorf("request.imp[%d].native.request.assets[%d].video.mimes must be an array with at least one MIME type", impIndex, assetIndex)
  1377  	}
  1378  	if video.MinDuration < 1 {
  1379  		return fmt.Errorf("request.imp[%d].native.request.assets[%d].video.minduration must be a positive integer", impIndex, assetIndex)
  1380  	}
  1381  	if video.MaxDuration < 1 {
  1382  		return fmt.Errorf("request.imp[%d].native.request.assets[%d].video.maxduration must be a positive integer", impIndex, assetIndex)
  1383  	}
  1384  	if err := validateNativeVideoProtocols(video.Protocols, impIndex, assetIndex); err != nil {
  1385  		return err
  1386  	}
  1387  
  1388  	return nil
  1389  }
  1390  
  1391  func validateNativeAssetData(data *nativeRequests.Data, impIndex int, assetIndex int) error {
  1392  	if data.Type < native1.DataAssetTypeSponsored || (data.Type > native1.DataAssetTypeCTAText && data.Type < 500) {
  1393  		return fmt.Errorf("request.imp[%d].native.request.assets[%d].data.type is invalid. See section 7.4: https://iabtechlab.com/wp-content/uploads/2016/07/OpenRTB-Native-Ads-Specification-Final-1.2.pdf#page=40", impIndex, assetIndex)
  1394  	}
  1395  
  1396  	return nil
  1397  }
  1398  
  1399  func validateNativeVideoProtocols(protocols []adcom1.MediaCreativeSubtype, impIndex int, assetIndex int) error {
  1400  	if len(protocols) < 1 {
  1401  		return fmt.Errorf("request.imp[%d].native.request.assets[%d].video.protocols must be an array with at least one element", impIndex, assetIndex)
  1402  	}
  1403  	for i := 0; i < len(protocols); i++ {
  1404  		if err := validateNativeVideoProtocol(protocols[i], impIndex, assetIndex, i); err != nil {
  1405  			return err
  1406  		}
  1407  	}
  1408  	return nil
  1409  }
  1410  
  1411  func validateNativeVideoProtocol(protocol adcom1.MediaCreativeSubtype, impIndex int, assetIndex int, protocolIndex int) error {
  1412  	if protocol < adcom1.CreativeVAST10 || protocol > adcom1.CreativeDAAST10Wrapper {
  1413  		return fmt.Errorf("request.imp[%d].native.request.assets[%d].video.protocols[%d] is invalid. See Section 5.8: https://www.iab.com/wp-content/uploads/2016/03/OpenRTB-API-Specification-Version-2-5-FINAL.pdf#page=52", impIndex, assetIndex, protocolIndex)
  1414  	}
  1415  	return nil
  1416  }
  1417  
  1418  func validateFormat(format *openrtb2.Format, impIndex, formatIndex int) error {
  1419  	usesHW := format.W != 0 || format.H != 0
  1420  	usesRatios := format.WMin != 0 || format.WRatio != 0 || format.HRatio != 0
  1421  
  1422  	// The following fields were previously uints in the OpenRTB library we use, but have
  1423  	// since been changed to ints. We decided to maintain the non-negative check.
  1424  	if format.W < 0 {
  1425  		return fmt.Errorf("request.imp[%d].banner.format[%d].w must be a positive number", impIndex, formatIndex)
  1426  	}
  1427  	if format.H < 0 {
  1428  		return fmt.Errorf("request.imp[%d].banner.format[%d].h must be a positive number", impIndex, formatIndex)
  1429  	}
  1430  	if format.WRatio < 0 {
  1431  		return fmt.Errorf("request.imp[%d].banner.format[%d].wratio must be a positive number", impIndex, formatIndex)
  1432  	}
  1433  	if format.HRatio < 0 {
  1434  		return fmt.Errorf("request.imp[%d].banner.format[%d].hratio must be a positive number", impIndex, formatIndex)
  1435  	}
  1436  	if format.WMin < 0 {
  1437  		return fmt.Errorf("request.imp[%d].banner.format[%d].wmin must be a positive number", impIndex, formatIndex)
  1438  	}
  1439  
  1440  	if usesHW && usesRatios {
  1441  		return fmt.Errorf("Request imp[%d].banner.format[%d] should define *either* {w, h} *or* {wmin, wratio, hratio}, but not both. If both are valid, send two \"format\" objects in the request.", impIndex, formatIndex)
  1442  	}
  1443  	if !usesHW && !usesRatios {
  1444  		return fmt.Errorf("Request imp[%d].banner.format[%d] should define *either* {w, h} (for static size requirements) *or* {wmin, wratio, hratio} (for flexible sizes) to be non-zero.", impIndex, formatIndex)
  1445  	}
  1446  	if usesHW && (format.W == 0 || format.H == 0) {
  1447  		return fmt.Errorf("Request imp[%d].banner.format[%d] must define non-zero \"h\" and \"w\" properties.", impIndex, formatIndex)
  1448  	}
  1449  	if usesRatios && (format.WMin == 0 || format.WRatio == 0 || format.HRatio == 0) {
  1450  		return fmt.Errorf("Request imp[%d].banner.format[%d] must define non-zero \"wmin\", \"wratio\", and \"hratio\" properties.", impIndex, formatIndex)
  1451  	}
  1452  	return nil
  1453  }
  1454  
  1455  func validatePmp(pmp *openrtb2.PMP, impIndex int) error {
  1456  	if pmp == nil {
  1457  		return nil
  1458  	}
  1459  
  1460  	for dealIndex, deal := range pmp.Deals {
  1461  		if deal.ID == "" {
  1462  			return fmt.Errorf("request.imp[%d].pmp.deals[%d] missing required field: \"id\"", impIndex, dealIndex)
  1463  		}
  1464  	}
  1465  	return nil
  1466  }
  1467  
  1468  func (deps *endpointDeps) validateImpExt(imp *openrtb_ext.ImpWrapper, aliases map[string]string, impIndex int, hasStoredResponses bool, storedBidResp stored_responses.ImpBidderStoredResp) []error {
  1469  	if len(imp.Ext) == 0 {
  1470  		return []error{fmt.Errorf("request.imp[%d].ext is required", impIndex)}
  1471  	}
  1472  
  1473  	impExt, err := imp.GetImpExt()
  1474  	if err != nil {
  1475  		return []error{err}
  1476  	}
  1477  
  1478  	prebid := impExt.GetOrCreatePrebid()
  1479  	prebidModified := false
  1480  
  1481  	if prebid.Bidder == nil {
  1482  		prebid.Bidder = make(map[string]json.RawMessage)
  1483  	}
  1484  
  1485  	ext := impExt.GetExt()
  1486  	extModified := false
  1487  
  1488  	// promote imp[].ext.BIDDER to newer imp[].ext.prebid.bidder.BIDDER location, with the later taking precedence
  1489  	for k, v := range ext {
  1490  		if isPossibleBidder(k) {
  1491  			if _, exists := prebid.Bidder[k]; !exists {
  1492  				prebid.Bidder[k] = v
  1493  				prebidModified = true
  1494  			}
  1495  			delete(ext, k)
  1496  			extModified = true
  1497  		}
  1498  	}
  1499  
  1500  	if hasStoredResponses && prebid.StoredAuctionResponse == nil {
  1501  		return []error{fmt.Errorf("request validation failed. The StoredAuctionResponse.ID field must be completely present with, or completely absent from, all impressions in request. No StoredAuctionResponse data found for request.imp[%d].ext.prebid \n", impIndex)}
  1502  	}
  1503  
  1504  	if len(storedBidResp) > 0 {
  1505  		if err := validateStoredBidRespAndImpExtBidders(prebid.Bidder, storedBidResp, imp.ID); err != nil {
  1506  			return []error{err}
  1507  		}
  1508  	}
  1509  
  1510  	errL := []error{}
  1511  
  1512  	for bidder, ext := range prebid.Bidder {
  1513  		coreBidder := bidder
  1514  		if tmp, isAlias := aliases[bidder]; isAlias {
  1515  			coreBidder = tmp
  1516  		}
  1517  
  1518  		if coreBidderNormalized, isValid := deps.bidderMap[coreBidder]; isValid {
  1519  			if err := deps.paramsValidator.Validate(coreBidderNormalized, ext); err != nil {
  1520  				return []error{fmt.Errorf("request.imp[%d].ext.prebid.bidder.%s failed validation.\n%v", impIndex, bidder, err)}
  1521  			}
  1522  		} else {
  1523  			if msg, isDisabled := deps.disabledBidders[bidder]; isDisabled {
  1524  				errL = append(errL, &errortypes.BidderTemporarilyDisabled{Message: msg})
  1525  				delete(prebid.Bidder, bidder)
  1526  				prebidModified = true
  1527  			} else {
  1528  				return []error{fmt.Errorf("request.imp[%d].ext.prebid.bidder contains unknown bidder: %s. Did you forget an alias in request.ext.prebid.aliases?", impIndex, bidder)}
  1529  			}
  1530  		}
  1531  	}
  1532  
  1533  	if len(prebid.Bidder) == 0 {
  1534  		errL = append(errL, fmt.Errorf("request.imp[%d].ext.prebid.bidder must contain at least one bidder", impIndex))
  1535  		return errL
  1536  	}
  1537  
  1538  	if prebidModified {
  1539  		impExt.SetPrebid(prebid)
  1540  	}
  1541  	if extModified {
  1542  		impExt.SetExt(ext)
  1543  	}
  1544  
  1545  	return errL
  1546  }
  1547  
  1548  // isPossibleBidder determines if a bidder name is a potential real bidder.
  1549  func isPossibleBidder(bidder string) bool {
  1550  	switch openrtb_ext.BidderName(bidder) {
  1551  	case openrtb_ext.BidderReservedContext:
  1552  		return false
  1553  	case openrtb_ext.BidderReservedData:
  1554  		return false
  1555  	case openrtb_ext.BidderReservedGPID:
  1556  		return false
  1557  	case openrtb_ext.BidderReservedPrebid:
  1558  		return false
  1559  	case openrtb_ext.BidderReservedSKAdN:
  1560  		return false
  1561  	case openrtb_ext.BidderReservedTID:
  1562  		return false
  1563  	case openrtb_ext.BidderReservedAE:
  1564  		return false
  1565  	default:
  1566  		return true
  1567  	}
  1568  }
  1569  
  1570  func (deps *endpointDeps) parseBidExt(req *openrtb_ext.RequestWrapper) error {
  1571  	if _, err := req.GetRequestExt(); err != nil {
  1572  		return fmt.Errorf("request.ext is invalid: %v", err)
  1573  	}
  1574  	return nil
  1575  }
  1576  
  1577  func (deps *endpointDeps) validateAliases(aliases map[string]string) error {
  1578  	for alias, coreBidder := range aliases {
  1579  		if _, isCoreBidderDisabled := deps.disabledBidders[coreBidder]; isCoreBidderDisabled {
  1580  			return fmt.Errorf("request.ext.prebid.aliases.%s refers to disabled bidder: %s", alias, coreBidder)
  1581  		}
  1582  
  1583  		if _, isCoreBidder := deps.bidderMap[coreBidder]; !isCoreBidder {
  1584  			return fmt.Errorf("request.ext.prebid.aliases.%s refers to unknown bidder: %s", alias, coreBidder)
  1585  		}
  1586  
  1587  		if alias == coreBidder {
  1588  			return fmt.Errorf("request.ext.prebid.aliases.%s defines a no-op alias. Choose a different alias, or remove this entry.", alias)
  1589  		}
  1590  	}
  1591  	return nil
  1592  }
  1593  
  1594  func (deps *endpointDeps) validateAliasesGVLIDs(aliasesGVLIDs map[string]uint16, aliases map[string]string) error {
  1595  	for alias, vendorId := range aliasesGVLIDs {
  1596  
  1597  		if _, aliasExist := aliases[alias]; !aliasExist {
  1598  			return fmt.Errorf("request.ext.prebid.aliasgvlids. vendorId %d refers to unknown bidder alias: %s", vendorId, alias)
  1599  		}
  1600  
  1601  		if vendorId < 1 {
  1602  			return fmt.Errorf("request.ext.prebid.aliasgvlids. Invalid vendorId %d for alias: %s. Choose a different vendorId, or remove this entry.", vendorId, alias)
  1603  		}
  1604  	}
  1605  	return nil
  1606  }
  1607  
  1608  func validateRequestExt(req *openrtb_ext.RequestWrapper) []error {
  1609  	reqExt, err := req.GetRequestExt()
  1610  	if err != nil {
  1611  		return []error{err}
  1612  	}
  1613  
  1614  	prebid := reqExt.GetPrebid()
  1615  	// exit early if there is no request.ext.prebid to validate
  1616  	if prebid == nil {
  1617  		return nil
  1618  	}
  1619  
  1620  	if prebid.Cache != nil {
  1621  		if prebid.Cache.Bids == nil && prebid.Cache.VastXML == nil {
  1622  			return []error{errors.New(`request.ext is invalid: request.ext.prebid.cache requires one of the "bids" or "vastxml" properties`)}
  1623  		}
  1624  	}
  1625  
  1626  	if err := validateTargeting(prebid.Targeting); err != nil {
  1627  		return []error{err}
  1628  	}
  1629  
  1630  	var errs []error
  1631  	if prebid.MultiBid != nil {
  1632  		validatedMultiBids, multBidErrs := openrtb_ext.ValidateAndBuildExtMultiBid(prebid)
  1633  
  1634  		for _, err := range multBidErrs {
  1635  			errs = append(errs, &errortypes.Warning{
  1636  				WarningCode: errortypes.MultiBidWarningCode,
  1637  				Message:     err.Error(),
  1638  			})
  1639  		}
  1640  
  1641  		// update the downstream multibid to avoid passing unvalidated ext to bidders, etc.
  1642  		prebid.MultiBid = validatedMultiBids
  1643  		reqExt.SetPrebid(prebid)
  1644  	}
  1645  
  1646  	if !bidadjustment.Validate(prebid.BidAdjustments) {
  1647  		prebid.BidAdjustments = nil
  1648  		reqExt.SetPrebid(prebid)
  1649  		errs = append(errs, &errortypes.Warning{
  1650  			WarningCode: errortypes.BidAdjustmentWarningCode,
  1651  			Message:     "bid adjustment from request was invalid",
  1652  		})
  1653  	}
  1654  
  1655  	return errs
  1656  }
  1657  
  1658  func validateTargeting(t *openrtb_ext.ExtRequestTargeting) error {
  1659  	if t == nil {
  1660  		return nil
  1661  	}
  1662  
  1663  	if (t.IncludeWinners == nil || !*t.IncludeWinners) && (t.IncludeBidderKeys == nil || !*t.IncludeBidderKeys) {
  1664  		return errors.New("ext.prebid.targeting: At least one of includewinners or includebidderkeys must be enabled to enable targeting support")
  1665  	}
  1666  
  1667  	if t.PriceGranularity != nil {
  1668  		if err := validatePriceGranularity(t.PriceGranularity); err != nil {
  1669  			return err
  1670  		}
  1671  	}
  1672  
  1673  	if t.MediaTypePriceGranularity.Video != nil {
  1674  		if err := validatePriceGranularity(t.MediaTypePriceGranularity.Video); err != nil {
  1675  			return err
  1676  		}
  1677  	}
  1678  	if t.MediaTypePriceGranularity.Banner != nil {
  1679  		if err := validatePriceGranularity(t.MediaTypePriceGranularity.Banner); err != nil {
  1680  			return err
  1681  		}
  1682  	}
  1683  	if t.MediaTypePriceGranularity.Native != nil {
  1684  		if err := validatePriceGranularity(t.MediaTypePriceGranularity.Native); err != nil {
  1685  			return err
  1686  		}
  1687  	}
  1688  
  1689  	return nil
  1690  }
  1691  
  1692  func validatePriceGranularity(pg *openrtb_ext.PriceGranularity) error {
  1693  	if pg.Precision == nil {
  1694  		return errors.New("Price granularity error: precision is required")
  1695  	} else if *pg.Precision < 0 {
  1696  		return errors.New("Price granularity error: precision must be non-negative")
  1697  	} else if *pg.Precision > openrtb_ext.MaxDecimalFigures {
  1698  		return fmt.Errorf("Price granularity error: precision of more than %d significant figures is not supported", openrtb_ext.MaxDecimalFigures)
  1699  	}
  1700  
  1701  	var prevMax float64 = 0
  1702  	for _, gr := range pg.Ranges {
  1703  		if gr.Max <= prevMax {
  1704  			return errors.New(`Price granularity error: range list must be ordered with increasing "max"`)
  1705  		}
  1706  
  1707  		if gr.Increment <= 0.0 {
  1708  			return errors.New("Price granularity error: increment must be a nonzero positive number")
  1709  		}
  1710  		prevMax = gr.Max
  1711  	}
  1712  	return nil
  1713  }
  1714  
  1715  func (deps *endpointDeps) validateSite(req *openrtb_ext.RequestWrapper) error {
  1716  	if req.Site == nil {
  1717  		return nil
  1718  	}
  1719  
  1720  	if req.Site.ID == "" && req.Site.Page == "" {
  1721  		return errors.New("request.site should include at least one of request.site.id or request.site.page.")
  1722  	}
  1723  	siteExt, err := req.GetSiteExt()
  1724  	if err != nil {
  1725  		return err
  1726  	}
  1727  	siteAmp := siteExt.GetAmp()
  1728  	if siteAmp != nil && (*siteAmp < 0 || *siteAmp > 1) {
  1729  		return errors.New(`request.site.ext.amp must be either 1, 0, or undefined`)
  1730  	}
  1731  
  1732  	return nil
  1733  }
  1734  
  1735  func (deps *endpointDeps) validateApp(req *openrtb_ext.RequestWrapper) error {
  1736  	if req.App == nil {
  1737  		return nil
  1738  	}
  1739  
  1740  	if req.App.ID != "" {
  1741  		if _, found := deps.cfg.BlacklistedAppMap[req.App.ID]; found {
  1742  			return &errortypes.BlacklistedApp{Message: fmt.Sprintf("Prebid-server does not process requests from App ID: %s", req.App.ID)}
  1743  		}
  1744  	}
  1745  
  1746  	_, err := req.GetAppExt()
  1747  	return err
  1748  }
  1749  
  1750  func (deps *endpointDeps) validateUser(req *openrtb_ext.RequestWrapper, aliases map[string]string, gpp gpplib.GppContainer) []error {
  1751  	var errL []error
  1752  
  1753  	if req == nil || req.BidRequest == nil || req.BidRequest.User == nil {
  1754  		return nil
  1755  	}
  1756  	// The following fields were previously uints in the OpenRTB library we use, but have
  1757  	// since been changed to ints. We decided to maintain the non-negative check.
  1758  	if req.User.Geo != nil && req.User.Geo.Accuracy < 0 {
  1759  		return append(errL, errors.New("request.user.geo.accuracy must be a positive number"))
  1760  	}
  1761  
  1762  	if req.User.Consent != "" {
  1763  		for _, section := range gpp.Sections {
  1764  			if section.GetID() == constants.SectionTCFEU2 && section.GetValue() != req.User.Consent {
  1765  				errL = append(errL, &errortypes.Warning{
  1766  					Message:     "user.consent GDPR string conflicts with GPP (regs.gpp) GDPR string, using regs.gpp",
  1767  					WarningCode: errortypes.InvalidPrivacyConsentWarningCode})
  1768  			}
  1769  		}
  1770  	}
  1771  	userExt, err := req.GetUserExt()
  1772  	if err != nil {
  1773  		return append(errL, fmt.Errorf("request.user.ext object is not valid: %v", err))
  1774  	}
  1775  	// Check if the buyeruids are valid
  1776  	prebid := userExt.GetPrebid()
  1777  	if prebid != nil {
  1778  		if len(prebid.BuyerUIDs) < 1 {
  1779  			return append(errL, errors.New(`request.user.ext.prebid requires a "buyeruids" property with at least one ID defined. If none exist, then request.user.ext.prebid should not be defined.`))
  1780  		}
  1781  		for bidderName := range prebid.BuyerUIDs {
  1782  			if _, ok := deps.bidderMap[bidderName]; !ok {
  1783  				if _, ok := aliases[bidderName]; !ok {
  1784  					return append(errL, fmt.Errorf("request.user.ext.%s is neither a known bidder name nor an alias in request.ext.prebid.aliases", bidderName))
  1785  				}
  1786  			}
  1787  		}
  1788  	}
  1789  	// Check Universal User ID
  1790  	eids := userExt.GetEid()
  1791  	if eids != nil {
  1792  		eidsValue := *eids
  1793  		uniqueSources := make(map[string]struct{}, len(eidsValue))
  1794  		for eidIndex, eid := range eidsValue {
  1795  			if eid.Source == "" {
  1796  				return append(errL, fmt.Errorf("request.user.ext.eids[%d] missing required field: \"source\"", eidIndex))
  1797  			}
  1798  			if _, ok := uniqueSources[eid.Source]; ok {
  1799  				return append(errL, errors.New("request.user.ext.eids must contain unique sources"))
  1800  			}
  1801  			uniqueSources[eid.Source] = struct{}{}
  1802  
  1803  			if len(eid.UIDs) == 0 {
  1804  				return append(errL, fmt.Errorf("request.user.ext.eids[%d].uids must contain at least one element or be undefined", eidIndex))
  1805  			}
  1806  
  1807  			for uidIndex, uid := range eid.UIDs {
  1808  				if uid.ID == "" {
  1809  					return append(errL, fmt.Errorf("request.user.ext.eids[%d].uids[%d] missing required field: \"id\"", eidIndex, uidIndex))
  1810  				}
  1811  			}
  1812  		}
  1813  	}
  1814  
  1815  	return errL
  1816  }
  1817  
  1818  func validateRegs(req *openrtb_ext.RequestWrapper, gpp gpplib.GppContainer) []error {
  1819  	var errL []error
  1820  
  1821  	if req == nil || req.BidRequest == nil || req.BidRequest.Regs == nil {
  1822  		return nil
  1823  	}
  1824  
  1825  	if req.BidRequest.Regs.GDPR != nil && req.BidRequest.Regs.GPPSID != nil {
  1826  		gdpr := int8(0)
  1827  		for _, id := range req.BidRequest.Regs.GPPSID {
  1828  			if id == int8(constants.SectionTCFEU2) {
  1829  				gdpr = 1
  1830  				break
  1831  			}
  1832  		}
  1833  		if gdpr != *req.BidRequest.Regs.GDPR {
  1834  			errL = append(errL, &errortypes.Warning{
  1835  				Message:     "regs.gdpr signal conflicts with GPP (regs.gpp_sid) and will be ignored",
  1836  				WarningCode: errortypes.InvalidPrivacyConsentWarningCode})
  1837  		}
  1838  	}
  1839  	regsExt, err := req.GetRegExt()
  1840  	if err != nil {
  1841  		return append(errL, fmt.Errorf("request.regs.ext is invalid: %v", err))
  1842  	}
  1843  
  1844  	gdpr := regsExt.GetGDPR()
  1845  	if gdpr != nil && *gdpr != 0 && *gdpr != 1 {
  1846  		return append(errL, errors.New("request.regs.ext.gdpr must be either 0 or 1"))
  1847  	}
  1848  
  1849  	return errL
  1850  }
  1851  
  1852  func validateDevice(device *openrtb2.Device) error {
  1853  	if device == nil {
  1854  		return nil
  1855  	}
  1856  
  1857  	// The following fields were previously uints in the OpenRTB library we use, but have
  1858  	// since been changed to ints. We decided to maintain the non-negative check.
  1859  	if device.W < 0 {
  1860  		return errors.New("request.device.w must be a positive number")
  1861  	}
  1862  	if device.H < 0 {
  1863  		return errors.New("request.device.h must be a positive number")
  1864  	}
  1865  	if device.PPI < 0 {
  1866  		return errors.New("request.device.ppi must be a positive number")
  1867  	}
  1868  	if device.Geo != nil && device.Geo.Accuracy < 0 {
  1869  		return errors.New("request.device.geo.accuracy must be a positive number")
  1870  	}
  1871  
  1872  	return nil
  1873  }
  1874  
  1875  func validateOrFillChannel(reqWrapper *openrtb_ext.RequestWrapper, isAmp bool) error {
  1876  	requestExt, err := reqWrapper.GetRequestExt()
  1877  	if err != nil {
  1878  		return err
  1879  	}
  1880  	requestPrebid := requestExt.GetPrebid()
  1881  
  1882  	if requestPrebid == nil || requestPrebid.Channel == nil {
  1883  		fillChannel(reqWrapper, isAmp)
  1884  	} else if requestPrebid.Channel.Name == "" {
  1885  		return errors.New("ext.prebid.channel.name can't be empty")
  1886  	}
  1887  	return nil
  1888  }
  1889  
  1890  func fillChannel(reqWrapper *openrtb_ext.RequestWrapper, isAmp bool) error {
  1891  	var channelName string
  1892  	requestExt, err := reqWrapper.GetRequestExt()
  1893  	if err != nil {
  1894  		return err
  1895  	}
  1896  	requestPrebid := requestExt.GetPrebid()
  1897  	if isAmp {
  1898  		channelName = ampChannel
  1899  	}
  1900  	if reqWrapper.App != nil {
  1901  		channelName = appChannel
  1902  	}
  1903  	if channelName != "" {
  1904  		if requestPrebid == nil {
  1905  			requestPrebid = &openrtb_ext.ExtRequestPrebid{}
  1906  		}
  1907  		requestPrebid.Channel = &openrtb_ext.ExtRequestPrebidChannel{Name: channelName}
  1908  		requestExt.SetPrebid(requestPrebid)
  1909  		reqWrapper.RebuildRequest()
  1910  	}
  1911  	return nil
  1912  
  1913  }
  1914  
  1915  func sanitizeRequest(r *openrtb_ext.RequestWrapper, ipValidator iputil.IPValidator) {
  1916  	if r.Device != nil {
  1917  		if ip, ver := iputil.ParseIP(r.Device.IP); ip == nil || ver != iputil.IPv4 || !ipValidator.IsValid(ip, ver) {
  1918  			r.Device.IP = ""
  1919  		}
  1920  
  1921  		if ip, ver := iputil.ParseIP(r.Device.IPv6); ip == nil || ver != iputil.IPv6 || !ipValidator.IsValid(ip, ver) {
  1922  			r.Device.IPv6 = ""
  1923  		}
  1924  	}
  1925  }
  1926  
  1927  // setFieldsImplicitly uses _implicit_ information from the httpReq to set values on bidReq.
  1928  // This function does not consume the request body, which was set explicitly, but infers certain
  1929  // OpenRTB properties from the headers and other implicit info.
  1930  //
  1931  // This function _should not_ override any fields which were defined explicitly by the caller in the request.
  1932  func (deps *endpointDeps) setFieldsImplicitly(httpReq *http.Request, r *openrtb_ext.RequestWrapper) {
  1933  	sanitizeRequest(r, deps.privateNetworkIPValidator)
  1934  
  1935  	setDeviceImplicitly(httpReq, r, deps.privateNetworkIPValidator)
  1936  
  1937  	// Per the OpenRTB spec: A bid request must not contain both a Site and an App object. If neither are
  1938  	// present, we'll assume it's a site request.
  1939  	if r.App == nil {
  1940  		setSiteImplicitly(httpReq, r)
  1941  	}
  1942  
  1943  	setAuctionTypeImplicitly(r)
  1944  }
  1945  
  1946  // setDeviceImplicitly uses implicit info from httpReq to populate bidReq.Device
  1947  func setDeviceImplicitly(httpReq *http.Request, r *openrtb_ext.RequestWrapper, ipValidtor iputil.IPValidator) {
  1948  	setIPImplicitly(httpReq, r, ipValidtor)
  1949  	setUAImplicitly(httpReq, r)
  1950  	setDoNotTrackImplicitly(httpReq, r)
  1951  
  1952  }
  1953  
  1954  // setAuctionTypeImplicitly sets the auction type to 1 if it wasn't on the request,
  1955  // since header bidding is generally a first-price auction.
  1956  func setAuctionTypeImplicitly(r *openrtb_ext.RequestWrapper) {
  1957  	if r.AT == 0 {
  1958  		r.AT = 1
  1959  	}
  1960  }
  1961  
  1962  func setSiteImplicitly(httpReq *http.Request, r *openrtb_ext.RequestWrapper) {
  1963  	if r.Site == nil {
  1964  		r.Site = &openrtb2.Site{}
  1965  	}
  1966  
  1967  	referrerCandidate := httpReq.Referer()
  1968  	if referrerCandidate == "" && r.Site.Page != "" {
  1969  		referrerCandidate = r.Site.Page // If http referer is disabled and thus has empty value - use site.page instead
  1970  	}
  1971  
  1972  	if referrerCandidate != "" {
  1973  		setSitePageIfEmpty(r.Site, referrerCandidate)
  1974  		if parsedUrl, err := url.Parse(referrerCandidate); err == nil {
  1975  			setSiteDomainIfEmpty(r.Site, parsedUrl.Host)
  1976  			if publisherDomain, err := publicsuffix.EffectiveTLDPlusOne(parsedUrl.Host); err == nil {
  1977  				setSitePublisherDomainIfEmpty(r.Site, publisherDomain)
  1978  			}
  1979  		}
  1980  	}
  1981  
  1982  	if siteExt, err := r.GetSiteExt(); err == nil && siteExt.GetAmp() == nil {
  1983  		siteExt.SetAmp(&notAmp)
  1984  	}
  1985  
  1986  }
  1987  
  1988  func setSitePageIfEmpty(site *openrtb2.Site, sitePage string) {
  1989  	if site.Page == "" {
  1990  		site.Page = sitePage
  1991  	}
  1992  }
  1993  
  1994  func setSiteDomainIfEmpty(site *openrtb2.Site, siteDomain string) {
  1995  	if site.Domain == "" {
  1996  		site.Domain = siteDomain
  1997  	}
  1998  }
  1999  
  2000  func setSitePublisherDomainIfEmpty(site *openrtb2.Site, publisherDomain string) {
  2001  	if site.Publisher == nil {
  2002  		site.Publisher = &openrtb2.Publisher{}
  2003  	}
  2004  	if site.Publisher.Domain == "" {
  2005  		site.Publisher.Domain = publisherDomain
  2006  	}
  2007  }
  2008  
  2009  func getJsonSyntaxError(testJSON []byte) (bool, string) {
  2010  	type JsonNode struct {
  2011  		raw   *json.RawMessage
  2012  		doc   map[string]*JsonNode
  2013  		ary   []*JsonNode
  2014  		which int
  2015  	}
  2016  	type jNode map[string]*JsonNode
  2017  	docErrdoc := &jNode{}
  2018  	docErr := json.Unmarshal(testJSON, docErrdoc)
  2019  	if uerror, ok := docErr.(*json.SyntaxError); ok {
  2020  		err := fmt.Sprintf("%s at offset %v", uerror.Error(), uerror.Offset)
  2021  		return true, err
  2022  	}
  2023  	return false, ""
  2024  }
  2025  
  2026  func (deps *endpointDeps) getStoredRequests(ctx context.Context, requestJson []byte, impInfo []ImpExtPrebidData) (string, bool, map[string]json.RawMessage, map[string]json.RawMessage, []error) {
  2027  	// Parse the Stored Request IDs from the BidRequest and Imps.
  2028  	storedBidRequestId, hasStoredBidRequest, err := getStoredRequestId(requestJson)
  2029  	if err != nil {
  2030  		return "", false, nil, nil, []error{err}
  2031  	}
  2032  
  2033  	// Fetch the Stored Request data
  2034  	var storedReqIds []string
  2035  	if hasStoredBidRequest {
  2036  		storedReqIds = []string{storedBidRequestId}
  2037  	}
  2038  
  2039  	impStoredReqIds := make([]string, 0, len(impInfo))
  2040  	impStoredReqIdsUniqueTracker := make(map[string]struct{}, len(impInfo))
  2041  	for _, impData := range impInfo {
  2042  		if impData.ImpExtPrebid.StoredRequest != nil && len(impData.ImpExtPrebid.StoredRequest.ID) > 0 {
  2043  			storedImpId := impData.ImpExtPrebid.StoredRequest.ID
  2044  			if _, present := impStoredReqIdsUniqueTracker[storedImpId]; !present {
  2045  				impStoredReqIds = append(impStoredReqIds, storedImpId)
  2046  				impStoredReqIdsUniqueTracker[storedImpId] = struct{}{}
  2047  			}
  2048  		}
  2049  	}
  2050  
  2051  	storedRequests, storedImps, errs := deps.storedReqFetcher.FetchRequests(ctx, storedReqIds, impStoredReqIds)
  2052  	if len(errs) != 0 {
  2053  		return "", false, nil, nil, errs
  2054  	}
  2055  
  2056  	return storedBidRequestId, hasStoredBidRequest, storedRequests, storedImps, errs
  2057  }
  2058  
  2059  func (deps *endpointDeps) processStoredRequests(requestJson []byte, impInfo []ImpExtPrebidData, storedRequests map[string]json.RawMessage, storedImps map[string]json.RawMessage, storedBidRequestId string, hasStoredBidRequest bool) ([]byte, map[string]exchange.ImpExtInfo, []error) {
  2060  	bidRequestID, err := getBidRequestID(storedRequests[storedBidRequestId])
  2061  	if err != nil {
  2062  		return nil, nil, []error{err}
  2063  	}
  2064  
  2065  	// Apply the Stored BidRequest, if it exists
  2066  	resolvedRequest := requestJson
  2067  
  2068  	if hasStoredBidRequest {
  2069  		isAppRequest, err := checkIfAppRequest(requestJson)
  2070  		if err != nil {
  2071  			return nil, nil, []error{err}
  2072  		}
  2073  		if (deps.cfg.GenerateRequestID && isAppRequest) || bidRequestID == "{{UUID}}" {
  2074  			uuidPatch, err := generateUuidForBidRequest(deps.uuidGenerator)
  2075  			if err != nil {
  2076  				return nil, nil, []error{err}
  2077  			}
  2078  			uuidPatch, err = jsonpatch.MergePatch(storedRequests[storedBidRequestId], uuidPatch)
  2079  			if err != nil {
  2080  				errL := storedRequestErrorChecker(requestJson, storedRequests, storedBidRequestId)
  2081  				return nil, nil, errL
  2082  			}
  2083  			resolvedRequest, err = jsonpatch.MergePatch(requestJson, uuidPatch)
  2084  			if err != nil {
  2085  				errL := storedRequestErrorChecker(requestJson, storedRequests, storedBidRequestId)
  2086  				return nil, nil, errL
  2087  			}
  2088  		} else {
  2089  			resolvedRequest, err = jsonpatch.MergePatch(storedRequests[storedBidRequestId], requestJson)
  2090  			if err != nil {
  2091  				errL := storedRequestErrorChecker(requestJson, storedRequests, storedBidRequestId)
  2092  				return nil, nil, errL
  2093  			}
  2094  		}
  2095  	}
  2096  
  2097  	// Apply default aliases, if they are provided
  2098  	if deps.defaultRequest {
  2099  		aliasedRequest, err := jsonpatch.MergePatch(deps.defReqJSON, resolvedRequest)
  2100  		if err != nil {
  2101  			hasErr, Err := getJsonSyntaxError(resolvedRequest)
  2102  			if hasErr {
  2103  				err = fmt.Errorf("Invalid JSON in Incoming Request: %s", Err)
  2104  			} else {
  2105  				hasErr, Err = getJsonSyntaxError(deps.defReqJSON)
  2106  				if hasErr {
  2107  					err = fmt.Errorf("Invalid JSON in Default Request Settings: %s", Err)
  2108  				}
  2109  			}
  2110  			return nil, nil, []error{err}
  2111  		}
  2112  		resolvedRequest = aliasedRequest
  2113  	}
  2114  
  2115  	// Apply any Stored Imps, if they exist. Since the JSON Merge Patch overrides arrays,
  2116  	// and Prebid Server defers to the HTTP Request to resolve conflicts, it's safe to
  2117  	// assume that the request.imp data did not change when applying the Stored BidRequest.
  2118  	impExtInfoMap := make(map[string]exchange.ImpExtInfo, len(impInfo))
  2119  	resolvedImps := make([]json.RawMessage, 0, len(impInfo))
  2120  	for i, impData := range impInfo {
  2121  		if impData.ImpExtPrebid.StoredRequest != nil && len(impData.ImpExtPrebid.StoredRequest.ID) > 0 {
  2122  			resolvedImp, err := jsonpatch.MergePatch(storedImps[impData.ImpExtPrebid.StoredRequest.ID], impData.Imp)
  2123  
  2124  			if err != nil {
  2125  				hasErr, errMessage := getJsonSyntaxError(impData.Imp)
  2126  				if hasErr {
  2127  					err = fmt.Errorf("Invalid JSON in Imp[%d] of Incoming Request: %s", i, errMessage)
  2128  				} else {
  2129  					hasErr, errMessage = getJsonSyntaxError(storedImps[impData.ImpExtPrebid.StoredRequest.ID])
  2130  					if hasErr {
  2131  						err = fmt.Errorf("imp.ext.prebid.storedrequest.id %s: Stored Imp has Invalid JSON: %s", impData.ImpExtPrebid.StoredRequest.ID, errMessage)
  2132  					}
  2133  				}
  2134  				return nil, nil, []error{err}
  2135  			}
  2136  			resolvedImps = append(resolvedImps, resolvedImp)
  2137  			impId, err := jsonparser.GetString(resolvedImp, "id")
  2138  			if err != nil {
  2139  				return nil, nil, []error{err}
  2140  			}
  2141  
  2142  			echoVideoAttributes := false
  2143  			if impData.ImpExtPrebid.Options != nil {
  2144  				echoVideoAttributes = impData.ImpExtPrebid.Options.EchoVideoAttrs
  2145  			}
  2146  
  2147  			// Extract Passthrough from Merged Imp
  2148  			passthrough, _, _, err := jsonparser.Get(resolvedImp, "ext", "prebid", "passthrough")
  2149  			if err != nil && err != jsonparser.KeyPathNotFoundError {
  2150  				return nil, nil, []error{err}
  2151  			}
  2152  			impExtInfoMap[impId] = exchange.ImpExtInfo{EchoVideoAttrs: echoVideoAttributes, StoredImp: storedImps[impData.ImpExtPrebid.StoredRequest.ID], Passthrough: passthrough}
  2153  
  2154  		} else {
  2155  			resolvedImps = append(resolvedImps, impData.Imp)
  2156  			impId, err := jsonparser.GetString(impData.Imp, "id")
  2157  			if err != nil {
  2158  				if err == jsonparser.KeyPathNotFoundError {
  2159  					err = fmt.Errorf("request.imp[%d] missing required field: \"id\"\n", i)
  2160  				}
  2161  				return nil, nil, []error{err}
  2162  			}
  2163  			impExtInfoMap[impId] = exchange.ImpExtInfo{Passthrough: impData.ImpExtPrebid.Passthrough}
  2164  		}
  2165  	}
  2166  	if len(resolvedImps) > 0 {
  2167  		newImpJson, err := json.Marshal(resolvedImps)
  2168  		if err != nil {
  2169  			return nil, nil, []error{err}
  2170  		}
  2171  		resolvedRequest, err = jsonparser.Set(resolvedRequest, newImpJson, "imp")
  2172  		if err != nil {
  2173  			return nil, nil, []error{err}
  2174  		}
  2175  	}
  2176  
  2177  	return resolvedRequest, impExtInfoMap, nil
  2178  }
  2179  
  2180  // parseImpInfo parses the request JSON and returns impression and unmarshalled imp.ext.prebid
  2181  func parseImpInfo(requestJson []byte) (impData []ImpExtPrebidData, errs []error) {
  2182  	if impArray, dataType, _, err := jsonparser.Get(requestJson, "imp"); err == nil && dataType == jsonparser.Array {
  2183  		_, err = jsonparser.ArrayEach(impArray, func(imp []byte, _ jsonparser.ValueType, _ int, err error) {
  2184  			impExtData, _, _, err := jsonparser.Get(imp, "ext", "prebid")
  2185  			var impExtPrebid openrtb_ext.ExtImpPrebid
  2186  			if impExtData != nil {
  2187  				if err := json.Unmarshal(impExtData, &impExtPrebid); err != nil {
  2188  					errs = append(errs, err)
  2189  				}
  2190  			}
  2191  			newImpData := ImpExtPrebidData{imp, impExtPrebid}
  2192  			impData = append(impData, newImpData)
  2193  		})
  2194  	}
  2195  	return
  2196  }
  2197  
  2198  type ImpExtPrebidData struct {
  2199  	Imp          json.RawMessage
  2200  	ImpExtPrebid openrtb_ext.ExtImpPrebid
  2201  }
  2202  
  2203  // getStoredRequestId parses a Stored Request ID from some json, without doing a full (slow) unmarshal.
  2204  // It returns the ID, true/false whether a stored request key existed, and an error if anything went wrong
  2205  // (e.g. malformed json, id not a string, etc).
  2206  func getStoredRequestId(data []byte) (string, bool, error) {
  2207  	// These keys must be kept in sync with openrtb_ext.ExtStoredRequest
  2208  	storedRequestId, dataType, _, err := jsonparser.Get(data, "ext", openrtb_ext.PrebidExtKey, "storedrequest", "id")
  2209  
  2210  	if dataType == jsonparser.NotExist {
  2211  		return "", false, nil
  2212  	}
  2213  	if err != nil {
  2214  		return "", false, err
  2215  	}
  2216  	if dataType != jsonparser.String {
  2217  		return "", true, errors.New("ext.prebid.storedrequest.id must be a string")
  2218  	}
  2219  	return string(storedRequestId), true, nil
  2220  }
  2221  
  2222  func getBidRequestID(data json.RawMessage) (string, error) {
  2223  	bidRequestID, dataType, _, err := jsonparser.Get(data, "id")
  2224  	if dataType == jsonparser.NotExist {
  2225  		return "", nil
  2226  	}
  2227  	if err != nil {
  2228  		return "", err
  2229  	}
  2230  	return string(bidRequestID), nil
  2231  }
  2232  
  2233  // setIPImplicitly sets the IP address on bidReq, if it's not explicitly defined and we can figure it out.
  2234  func setIPImplicitly(httpReq *http.Request, r *openrtb_ext.RequestWrapper, ipValidator iputil.IPValidator) {
  2235  	if r.Device == nil || (r.Device.IP == "" && r.Device.IPv6 == "") {
  2236  		if ip, ver := httputil.FindIP(httpReq, ipValidator); ip != nil {
  2237  			switch ver {
  2238  			case iputil.IPv4:
  2239  				if r.Device == nil {
  2240  					r.Device = &openrtb2.Device{}
  2241  				}
  2242  				r.Device.IP = ip.String()
  2243  			case iputil.IPv6:
  2244  				if r.Device == nil {
  2245  					r.Device = &openrtb2.Device{}
  2246  				}
  2247  				r.Device.IPv6 = ip.String()
  2248  			}
  2249  		}
  2250  	}
  2251  }
  2252  
  2253  // setUAImplicitly sets the User Agent on bidReq, if it's not explicitly defined and it's defined on the request.
  2254  func setUAImplicitly(httpReq *http.Request, r *openrtb_ext.RequestWrapper) {
  2255  	if r.Device == nil || r.Device.UA == "" {
  2256  		if ua := httpReq.UserAgent(); ua != "" {
  2257  			if r.Device == nil {
  2258  				r.Device = &openrtb2.Device{}
  2259  			}
  2260  			r.Device.UA = ua
  2261  		}
  2262  	}
  2263  }
  2264  
  2265  func setDoNotTrackImplicitly(httpReq *http.Request, r *openrtb_ext.RequestWrapper) {
  2266  	if r.Device == nil || r.Device.DNT == nil {
  2267  		dnt := httpReq.Header.Get(dntKey)
  2268  		if dnt == "0" || dnt == "1" {
  2269  			if r.Device == nil {
  2270  				r.Device = &openrtb2.Device{}
  2271  			}
  2272  
  2273  			switch dnt {
  2274  			case "0":
  2275  				r.Device.DNT = &dntDisabled
  2276  			case "1":
  2277  				r.Device.DNT = &dntEnabled
  2278  			}
  2279  		}
  2280  	}
  2281  }
  2282  
  2283  // Write(return) errors to the client, if any. Returns true if errors were found.
  2284  func writeError(errs []error, w http.ResponseWriter, labels *metrics.Labels) bool {
  2285  	var rc bool = false
  2286  	if len(errs) > 0 {
  2287  		httpStatus := http.StatusBadRequest
  2288  		metricsStatus := metrics.RequestStatusBadInput
  2289  		for _, err := range errs {
  2290  			erVal := errortypes.ReadCode(err)
  2291  			if erVal == errortypes.BlacklistedAppErrorCode || erVal == errortypes.BlacklistedAcctErrorCode {
  2292  				httpStatus = http.StatusServiceUnavailable
  2293  				metricsStatus = metrics.RequestStatusBlacklisted
  2294  				break
  2295  			} else if erVal == errortypes.MalformedAcctErrorCode {
  2296  				httpStatus = http.StatusInternalServerError
  2297  				metricsStatus = metrics.RequestStatusAccountConfigErr
  2298  				break
  2299  			}
  2300  		}
  2301  		w.WriteHeader(httpStatus)
  2302  		labels.RequestStatus = metricsStatus
  2303  		for _, err := range errs {
  2304  			w.Write([]byte(fmt.Sprintf("Invalid request: %s\n", err.Error())))
  2305  		}
  2306  		rc = true
  2307  	}
  2308  	return rc
  2309  }
  2310  
  2311  // Returns the account ID for the request
  2312  func getAccountID(pub *openrtb2.Publisher) string {
  2313  	if pub != nil {
  2314  		if pub.Ext != nil {
  2315  			var pubExt openrtb_ext.ExtPublisher
  2316  			err := json.Unmarshal(pub.Ext, &pubExt)
  2317  			if err == nil && pubExt.Prebid != nil && pubExt.Prebid.ParentAccount != nil && *pubExt.Prebid.ParentAccount != "" {
  2318  				return *pubExt.Prebid.ParentAccount
  2319  			}
  2320  		}
  2321  		if pub.ID != "" {
  2322  			return pub.ID
  2323  		}
  2324  	}
  2325  	return metrics.PublisherUnknown
  2326  }
  2327  
  2328  func getAccountIdFromRawRequest(hasStoredRequest bool, storedRequest json.RawMessage, originalRequest []byte) (string, bool, []error) {
  2329  	request := originalRequest
  2330  	if hasStoredRequest {
  2331  		request = storedRequest
  2332  	}
  2333  
  2334  	accountId, isAppReq, err := searchAccountId(request)
  2335  	if err != nil {
  2336  		return "", isAppReq, []error{err}
  2337  	}
  2338  
  2339  	// In case the stored request did not have account data we specifically search it in the original request
  2340  	if accountId == "" && hasStoredRequest {
  2341  		accountId, _, err = searchAccountId(originalRequest)
  2342  		if err != nil {
  2343  			return "", isAppReq, []error{err}
  2344  		}
  2345  	}
  2346  
  2347  	if accountId == "" {
  2348  		return metrics.PublisherUnknown, isAppReq, nil
  2349  	}
  2350  
  2351  	return accountId, isAppReq, nil
  2352  }
  2353  
  2354  func searchAccountId(request []byte) (string, bool, error) {
  2355  	for _, path := range accountIdSearchPath {
  2356  		accountId, exists, err := getStringValueFromRequest(request, path.key)
  2357  		if err != nil {
  2358  			return "", path.isApp, err
  2359  		}
  2360  		if exists {
  2361  			return accountId, path.isApp, nil
  2362  		}
  2363  	}
  2364  	return "", false, nil
  2365  }
  2366  
  2367  func getStringValueFromRequest(request []byte, key []string) (string, bool, error) {
  2368  	val, dataType, _, err := jsonparser.Get(request, key...)
  2369  	if dataType == jsonparser.NotExist {
  2370  		return "", false, nil
  2371  	}
  2372  	if err != nil {
  2373  		return "", false, err
  2374  	}
  2375  	if dataType != jsonparser.String {
  2376  		return "", true, fmt.Errorf("%s must be a string", strings.Join(key, "."))
  2377  	}
  2378  	return string(val), true, nil
  2379  }
  2380  
  2381  func storedRequestErrorChecker(requestJson []byte, storedRequests map[string]json.RawMessage, storedBidRequestId string) []error {
  2382  	if hasErr, syntaxErr := getJsonSyntaxError(requestJson); hasErr {
  2383  		return []error{fmt.Errorf("Invalid JSON in Incoming Request: %s", syntaxErr)}
  2384  	}
  2385  	if hasErr, syntaxErr := getJsonSyntaxError(storedRequests[storedBidRequestId]); hasErr {
  2386  		return []error{fmt.Errorf("ext.prebid.storedrequest.id refers to Stored Request %s which contains Invalid JSON: %s", storedBidRequestId, syntaxErr)}
  2387  	}
  2388  	return nil
  2389  }
  2390  
  2391  func generateUuidForBidRequest(uuidGenerator uuidutil.UUIDGenerator) ([]byte, error) {
  2392  	newBidRequestID, err := uuidGenerator.Generate()
  2393  	if err != nil {
  2394  		return nil, err
  2395  	}
  2396  	return []byte(`{"id":"` + newBidRequestID + `"}`), nil
  2397  }
  2398  
  2399  func (deps *endpointDeps) setIntegrationType(req *openrtb_ext.RequestWrapper, account *config.Account) error {
  2400  	reqExt, err := req.GetRequestExt()
  2401  	if err != nil {
  2402  		return err
  2403  	}
  2404  	reqPrebid := reqExt.GetPrebid()
  2405  
  2406  	if account == nil || account.DefaultIntegration == "" {
  2407  		return nil
  2408  	}
  2409  	if reqPrebid == nil {
  2410  		reqPrebid = &openrtb_ext.ExtRequestPrebid{Integration: account.DefaultIntegration}
  2411  		reqExt.SetPrebid(reqPrebid)
  2412  	} else if reqPrebid.Integration == "" {
  2413  		reqPrebid.Integration = account.DefaultIntegration
  2414  		reqExt.SetPrebid(reqPrebid)
  2415  	}
  2416  	return nil
  2417  }
  2418  
  2419  func checkIfAppRequest(request []byte) (bool, error) {
  2420  	requestApp, dataType, _, err := jsonparser.Get(request, "app")
  2421  	if dataType == jsonparser.NotExist {
  2422  		return false, nil
  2423  	}
  2424  	if err != nil {
  2425  		return false, err
  2426  	}
  2427  	if requestApp != nil {
  2428  		return true, nil
  2429  	}
  2430  	return false, nil
  2431  }
  2432  
  2433  func validateStoredBidRespAndImpExtBidders(bidderExts map[string]json.RawMessage, storedBidResp stored_responses.ImpBidderStoredResp, impId string) error {
  2434  	if bidResponses, ok := storedBidResp[impId]; ok {
  2435  		if len(bidResponses) != len(bidderExts) {
  2436  			return generateStoredBidResponseValidationError(impId)
  2437  		}
  2438  
  2439  		for bidderName := range bidResponses {
  2440  			if _, present := bidderExts[bidderName]; !present {
  2441  				return generateStoredBidResponseValidationError(impId)
  2442  			}
  2443  		}
  2444  	}
  2445  	return nil
  2446  }
  2447  
  2448  func generateStoredBidResponseValidationError(impID string) error {
  2449  	return fmt.Errorf("request validation failed. Stored bid responses are specified for imp %s. Bidders specified in imp.ext should match with bidders specified in imp.ext.prebid.storedbidresponse", impID)
  2450  }