Skip to content

Commit

Permalink
Merge pull request prebid#12 from monetization/allow-extending-extern…
Browse files Browse the repository at this point in the history
…ally

scr-oath/allow-extending-externally changes on top of upstream cb3fd3c
  • Loading branch information
scr-oath authored and GitHub Enterprise committed Feb 13, 2024
2 parents 929ba61 + 3a6cb00 commit a883b6e
Show file tree
Hide file tree
Showing 15 changed files with 577 additions and 143 deletions.
5 changes: 4 additions & 1 deletion endpoints/events/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,10 @@ func (e *eventEndpoint) Handle(w http.ResponseWriter, r *http.Request, _ httprou
return
}

ctx := context.Background()
ctx := r.Context()
if ctx == nil {
ctx = context.Background()
}
if e.Cfg.Event.TimeoutMS > 0 {
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(ctx, time.Duration(e.Cfg.Event.TimeoutMS)*time.Millisecond)
Expand Down
5 changes: 4 additions & 1 deletion endpoints/openrtb2/amp_auction.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,10 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h

ao.RequestWrapper = reqWrapper

ctx := context.Background()
ctx := r.Context()
if ctx == nil {
ctx = context.Background()
}
var cancel context.CancelFunc
if reqWrapper.TMax > 0 {
ctx, cancel = context.WithDeadline(ctx, start.Add(time.Duration(reqWrapper.TMax)*time.Millisecond))
Expand Down
5 changes: 4 additions & 1 deletion endpoints/openrtb2/auction.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,10 @@ func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ http
hookExecutor.SetActivityControl(activityControl)
hookExecutor.SetAccount(account)

ctx := context.Background()
ctx := r.Context()
if ctx == nil {
ctx = context.Background()
}

timeout := deps.cfg.AuctionTimeouts.LimitAuctionTimeout(time.Duration(req.TMax) * time.Millisecond)
if timeout > 0 {
Expand Down
5 changes: 4 additions & 1 deletion endpoints/openrtb2/video_auction.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,10 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
return
}

ctx := context.Background()
ctx := r.Context()
if ctx == nil {
ctx = context.Background()
}
timeout := deps.cfg.AuctionTimeouts.LimitAuctionTimeout(time.Duration(bidReqWrapper.TMax) * time.Millisecond)
if timeout > 0 {
var cancel context.CancelFunc
Expand Down
44 changes: 42 additions & 2 deletions exchange/adapter_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,49 @@ import (
"github.com/prebid/prebid-server/v2/openrtb_ext"
)

func BuildAdapters(client *http.Client, cfg *config.Configuration, infos config.BidderInfos, me metrics.MetricsEngine) (map[openrtb_ext.BidderName]AdaptedBidder, []error) {
type (
AdapterBuilderOpt func(*adapterBuilder)
AdapterBuilderMapOpt func(map[openrtb_ext.BidderName]adapters.Builder)
adapterBuilder struct {
adapterMapOps []AdapterBuilderMapOpt
}
)

var (
defaultAdapterBuilderOpts []AdapterBuilderOpt
)

func AddAdapterBuilderToMap(name openrtb_ext.BidderName, builder adapters.Builder) AdapterBuilderMapOpt {
return func(m map[openrtb_ext.BidderName]adapters.Builder) {
m[name] = builder
}
}

func WithAdapterBuilderMapOpts(opts ...AdapterBuilderMapOpt) AdapterBuilderOpt {
return func(ab *adapterBuilder) {
ab.adapterMapOps = append(ab.adapterMapOps, opts...)
}
}

func newAdapterBuilder(opts ...AdapterBuilderOpt) *adapterBuilder {
ret := &adapterBuilder{}
for _, opt := range defaultAdapterBuilderOpts {
opt(ret)
}
for _, opt := range opts {
opt(ret)
}
return ret
}

func BuildAdapters(client *http.Client, cfg *config.Configuration, infos config.BidderInfos, me metrics.MetricsEngine, opts ...AdapterBuilderOpt) (map[openrtb_ext.BidderName]AdaptedBidder, []error) {
ab := newAdapterBuilder(opts...)
server := config.Server{ExternalUrl: cfg.ExternalURL, GvlID: cfg.GDPR.HostVendorID, DataCenter: cfg.DataCenter}
bidders, errs := buildBidders(infos, newAdapterBuilders(), server)
adapterBuilders := newAdapterBuilders()
for _, opt := range ab.adapterMapOps {
opt(adapterBuilders)
}
bidders, errs := buildBidders(infos, adapterBuilders, server)

if len(errs) > 0 {
return nil, errs
Expand Down
14 changes: 8 additions & 6 deletions exchange/exchange.go
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,7 @@ func (e *exchange) HoldAuction(ctx context.Context, r *AuctionRequest, debugLog
e.bidValidationEnforcement.SetBannerCreativeMaxSize(r.Account.Validations)

// Build the response
bidResponse := e.buildBidResponse(ctx, liveAdapters, adapterBids, r.BidRequestWrapper.BidRequest, adapterExtra, auc, bidResponseExt, cacheInstructions.returnCreative, r.ImpExtInfoMap, r.PubID, errs)
bidResponse := e.buildBidResponse(ctx, liveAdapters, adapterBids, r.BidRequestWrapper.BidRequest, adapterExtra, auc, bidResponseExt, cacheInstructions.returnCreative, r.ImpExtInfoMap, r.PubID, errs, &seatNonBids)
bidResponse = adservertargeting.Apply(r.BidRequestWrapper, r.ResolvedBidRequest, bidResponse, r.QueryParams, bidResponseExt, r.Account.TruncateTargetAttribute)

bidResponse.Ext, err = encodeBidResponseExt(bidResponseExt)
Expand Down Expand Up @@ -890,7 +890,7 @@ func errsToBidderWarnings(errs []error) []openrtb_ext.ExtBidderMessage {
}

// This piece takes all the bids supplied by the adapters and crafts an openRTB response to send back to the requester
func (e *exchange) buildBidResponse(ctx context.Context, liveAdapters []openrtb_ext.BidderName, adapterSeatBids map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid, bidRequest *openrtb2.BidRequest, adapterExtra map[openrtb_ext.BidderName]*seatResponseExtra, auc *auction, bidResponseExt *openrtb_ext.ExtBidResponse, returnCreative bool, impExtInfoMap map[string]ImpExtInfo, pubID string, errList []error) *openrtb2.BidResponse {
func (e *exchange) buildBidResponse(ctx context.Context, liveAdapters []openrtb_ext.BidderName, adapterSeatBids map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid, bidRequest *openrtb2.BidRequest, adapterExtra map[openrtb_ext.BidderName]*seatResponseExtra, auc *auction, bidResponseExt *openrtb_ext.ExtBidResponse, returnCreative bool, impExtInfoMap map[string]ImpExtInfo, pubID string, errList []error, seatNonBids *nonBids) *openrtb2.BidResponse {
bidResponse := new(openrtb2.BidResponse)

bidResponse.ID = bidRequest.ID
Expand All @@ -905,7 +905,7 @@ func (e *exchange) buildBidResponse(ctx context.Context, liveAdapters []openrtb_
for a, adapterSeatBids := range adapterSeatBids {
//while processing every single bib, do we need to handle categories here?
if adapterSeatBids != nil && len(adapterSeatBids.Bids) > 0 {
sb := e.makeSeatBid(adapterSeatBids, a, adapterExtra, auc, returnCreative, impExtInfoMap, bidResponseExt, pubID)
sb := e.makeSeatBid(adapterSeatBids, a, adapterExtra, auc, returnCreative, impExtInfoMap, bidResponseExt, pubID, seatNonBids)
seatBids = append(seatBids, *sb)
bidResponse.Cur = adapterSeatBids.Currency
}
Expand Down Expand Up @@ -1224,28 +1224,29 @@ func (e *exchange) makeExtBidResponse(adapterBids map[openrtb_ext.BidderName]*en

// Return an openrtb seatBid for a bidder
// buildBidResponse is responsible for ensuring nil bid seatbids are not included
func (e *exchange) makeSeatBid(adapterBid *entities.PbsOrtbSeatBid, adapter openrtb_ext.BidderName, adapterExtra map[openrtb_ext.BidderName]*seatResponseExtra, auc *auction, returnCreative bool, impExtInfoMap map[string]ImpExtInfo, bidResponseExt *openrtb_ext.ExtBidResponse, pubID string) *openrtb2.SeatBid {
func (e *exchange) makeSeatBid(adapterBid *entities.PbsOrtbSeatBid, adapter openrtb_ext.BidderName, adapterExtra map[openrtb_ext.BidderName]*seatResponseExtra, auc *auction, returnCreative bool, impExtInfoMap map[string]ImpExtInfo, bidResponseExt *openrtb_ext.ExtBidResponse, pubID string, seatNonBids *nonBids) *openrtb2.SeatBid {
seatBid := &openrtb2.SeatBid{
Seat: adapter.String(),
Group: 0, // Prebid cannot support roadblocking
}

var errList []error
seatBid.Bid, errList = e.makeBid(adapterBid.Bids, auc, returnCreative, impExtInfoMap, bidResponseExt, adapter, pubID)
seatBid.Bid, errList = e.makeBid(adapterBid.Bids, auc, returnCreative, impExtInfoMap, bidResponseExt, adapter, pubID, seatNonBids)
if len(errList) > 0 {
adapterExtra[adapter].Errors = append(adapterExtra[adapter].Errors, errsToBidderErrors(errList)...)
}

return seatBid
}

func (e *exchange) makeBid(bids []*entities.PbsOrtbBid, auc *auction, returnCreative bool, impExtInfoMap map[string]ImpExtInfo, bidResponseExt *openrtb_ext.ExtBidResponse, adapter openrtb_ext.BidderName, pubID string) ([]openrtb2.Bid, []error) {
func (e *exchange) makeBid(bids []*entities.PbsOrtbBid, auc *auction, returnCreative bool, impExtInfoMap map[string]ImpExtInfo, bidResponseExt *openrtb_ext.ExtBidResponse, adapter openrtb_ext.BidderName, pubID string, seatNonBids *nonBids) ([]openrtb2.Bid, []error) {
result := make([]openrtb2.Bid, 0, len(bids))
errs := make([]error, 0, 1)

for _, bid := range bids {
if e.bidValidationEnforcement.BannerCreativeMaxSize == config.ValidationEnforce && bid.BidType == openrtb_ext.BidTypeBanner {
if !e.validateBannerCreativeSize(bid, bidResponseExt, adapter, pubID, e.bidValidationEnforcement.BannerCreativeMaxSize) {
seatNonBids.addBid(bid, int(ResponseRejectedCreativeSizeNotAllowed), adapter.String())
continue // Don't add bid to result
}
} else if e.bidValidationEnforcement.BannerCreativeMaxSize == config.ValidationWarn && bid.BidType == openrtb_ext.BidTypeBanner {
Expand All @@ -1254,6 +1255,7 @@ func (e *exchange) makeBid(bids []*entities.PbsOrtbBid, auc *auction, returnCrea
if _, ok := impExtInfoMap[bid.Bid.ImpID]; ok {
if e.bidValidationEnforcement.SecureMarkup == config.ValidationEnforce && (bid.BidType == openrtb_ext.BidTypeBanner || bid.BidType == openrtb_ext.BidTypeVideo) {
if !e.validateBidAdM(bid, bidResponseExt, adapter, pubID, e.bidValidationEnforcement.SecureMarkup) {
seatNonBids.addBid(bid, int(ResponseRejectedCreativeNotSecure), adapter.String())
continue // Don't add bid to result
}
} else if e.bidValidationEnforcement.SecureMarkup == config.ValidationWarn && (bid.BidType == openrtb_ext.BidTypeBanner || bid.BidType == openrtb_ext.BidTypeVideo) {
Expand Down
81 changes: 65 additions & 16 deletions exchange/exchange_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ func TestCharacterEscape(t *testing.T) {
var errList []error

// 4) Build bid response
bidResp := e.buildBidResponse(context.Background(), liveAdapters, adapterBids, bidRequest, adapterExtra, nil, nil, true, nil, "", errList)
bidResp := e.buildBidResponse(context.Background(), liveAdapters, adapterBids, bidRequest, adapterExtra, nil, nil, true, nil, "", errList, &nonBids{})

// 5) Assert we have no errors and one '&' character as we are supposed to
if len(errList) > 0 {
Expand Down Expand Up @@ -1336,7 +1336,7 @@ func TestGetBidCacheInfoEndToEnd(t *testing.T) {
var errList []error

// 4) Build bid response
bid_resp := e.buildBidResponse(context.Background(), liveAdapters, adapterBids, bidRequest, adapterExtra, auc, nil, true, nil, "", errList)
bid_resp := e.buildBidResponse(context.Background(), liveAdapters, adapterBids, bidRequest, adapterExtra, auc, nil, true, nil, "", errList, &nonBids{})

expectedBidResponse := &openrtb2.BidResponse{
SeatBid: []openrtb2.SeatBid{
Expand Down Expand Up @@ -1426,7 +1426,7 @@ func TestBidReturnsCreative(t *testing.T) {

//Run tests
for _, test := range testCases {
resultingBids, resultingErrs := e.makeBid(sampleBids, sampleAuction, test.inReturnCreative, nil, nil, "", "")
resultingBids, resultingErrs := e.makeBid(sampleBids, sampleAuction, test.inReturnCreative, nil, nil, "", "", &nonBids{})

assert.Equal(t, 0, len(resultingErrs), "%s. Test should not return errors \n", test.description)
assert.Equal(t, test.expectedCreativeMarkup, resultingBids[0].AdM, "%s. Ad markup string doesn't match expected \n", test.description)
Expand Down Expand Up @@ -1709,7 +1709,7 @@ func TestBidResponseCurrency(t *testing.T) {
}
// Run tests
for i := range testCases {
actualBidResp := e.buildBidResponse(context.Background(), liveAdapters, testCases[i].adapterBids, bidRequest, adapterExtra, nil, bidResponseExt, true, nil, "", errList)
actualBidResp := e.buildBidResponse(context.Background(), liveAdapters, testCases[i].adapterBids, bidRequest, adapterExtra, nil, bidResponseExt, true, nil, "", errList, &nonBids{})
assert.Equalf(t, testCases[i].expectedBidResponse, actualBidResp, fmt.Sprintf("[TEST_FAILED] Objects must be equal for test: %s \n Expected: >>%s<< \n Actual: >>%s<< ", testCases[i].description, testCases[i].expectedBidResponse.Ext, actualBidResp.Ext))
}
}
Expand Down Expand Up @@ -1775,7 +1775,7 @@ func TestBidResponseImpExtInfo(t *testing.T) {

expectedBidResponseExt := `{"origbidcpm":0,"prebid":{"meta":{"adaptercode":"appnexus"},"type":"video","passthrough":{"imp_passthrough_val":1}},"storedrequestattributes":{"h":480,"mimes":["video/mp4"]}}`

actualBidResp := e.buildBidResponse(context.Background(), liveAdapters, adapterBids, bidRequest, nil, nil, nil, true, impExtInfo, "", errList)
actualBidResp := e.buildBidResponse(context.Background(), liveAdapters, adapterBids, bidRequest, nil, nil, nil, true, impExtInfo, "", errList, &nonBids{})

resBidExt := string(actualBidResp.SeatBid[0].Bid[0].Ext)
assert.Equalf(t, expectedBidResponseExt, resBidExt, "Expected bid response extension is incorrect")
Expand Down Expand Up @@ -2212,8 +2212,10 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) {

aucResponse, err := ex.HoldAuction(ctx, auctionRequest, debugLog)
var bid *openrtb2.BidResponse
var bidExt *openrtb_ext.ExtBidResponse
if aucResponse != nil {
bid = aucResponse.BidResponse
bidExt = aucResponse.ExtBidResponse
}
if len(spec.Response.Error) > 0 && spec.Response.Bids == nil {
if err.Error() != spec.Response.Error {
Expand Down Expand Up @@ -2296,20 +2298,24 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) {
assert.JSONEq(t, string(spec.Response.Ext), string(bid.Ext), "ext mismatch")
}

expectedBidRespExt := &openrtb_ext.ExtBidResponse{}
if spec.Response.Ext != nil {
if err := jsonutil.UnmarshalValid(spec.Response.Ext, expectedBidRespExt); err != nil {
assert.NoError(t, err, fmt.Sprintf("Error when unmarshalling: %s", err))
}
}
if spec.HostConfigBidValidation.BannerCreativeMaxSize == config.ValidationEnforce || spec.HostConfigBidValidation.SecureMarkup == config.ValidationEnforce {
actualBidRespExt := &openrtb_ext.ExtBidResponse{}
expectedBidRespExt := &openrtb_ext.ExtBidResponse{}
if bid.Ext != nil {
if err := jsonutil.UnmarshalValid(bid.Ext, actualBidRespExt); err != nil {
assert.NoError(t, err, fmt.Sprintf("Error when unmarshalling: %s", err))
}
}
if err := jsonutil.UnmarshalValid(spec.Response.Ext, expectedBidRespExt); err != nil {
assert.NoError(t, err, fmt.Sprintf("Error when unmarshalling: %s", err))
}

assert.Equal(t, expectedBidRespExt.Errors, actualBidRespExt.Errors, "Expected errors from response ext do not match")
}
if expectedBidRespExt.Prebid != nil {
assert.ElementsMatch(t, expectedBidRespExt.Prebid.SeatNonBid, bidExt.Prebid.SeatNonBid, "Expected seatNonBids from response ext do not match")
}
}

func findBiddersInAuction(t *testing.T, context string, req *openrtb2.BidRequest) []string {
Expand Down Expand Up @@ -4734,43 +4740,82 @@ func TestMakeBidWithValidation(t *testing.T) {
description string
givenValidations config.Validations
givenBids []*entities.PbsOrtbBid
givenSeat openrtb_ext.BidderName
expectedNumOfBids int
expectedNonBids *nonBids
}{
{
description: "Validation is enforced, and one bid out of the two is invalid based on dimensions",
givenValidations: config.Validations{BannerCreativeMaxSize: config.ValidationEnforce, MaxCreativeWidth: 100, MaxCreativeHeight: 100},
givenBids: []*entities.PbsOrtbBid{{Bid: &openrtb2.Bid{W: 200, H: 200}, BidType: openrtb_ext.BidTypeBanner}, {Bid: &openrtb2.Bid{W: 50, H: 50}, BidType: openrtb_ext.BidTypeBanner}},
givenSeat: "pubmatic",
expectedNumOfBids: 1,
expectedNonBids: &nonBids{
seatNonBidsMap: map[string][]openrtb_ext.NonBid{
"pubmatic": {
{
StatusCode: 351,
Ext: openrtb_ext.NonBidExt{
Prebid: openrtb_ext.ExtResponseNonBidPrebid{
Bid: openrtb_ext.NonBidObject{
W: 200,
H: 200,
},
},
},
},
},
},
},
},
{
description: "Validation is warned, so no bids should be removed (Validating CreativeMaxSize) ",
givenValidations: config.Validations{BannerCreativeMaxSize: config.ValidationWarn, MaxCreativeWidth: 100, MaxCreativeHeight: 100},
givenBids: []*entities.PbsOrtbBid{{Bid: &openrtb2.Bid{W: 200, H: 200}, BidType: openrtb_ext.BidTypeBanner}, {Bid: &openrtb2.Bid{W: 50, H: 50}, BidType: openrtb_ext.BidTypeBanner}},
givenSeat: "pubmatic",
expectedNumOfBids: 2,
expectedNonBids: &nonBids{},
},
{
description: "Validation is enforced, and one bid out of the two is invalid based on AdM",
givenValidations: config.Validations{SecureMarkup: config.ValidationEnforce},
givenBids: []*entities.PbsOrtbBid{{Bid: &openrtb2.Bid{AdM: "http://domain.com/invalid", ImpID: "1"}, BidType: openrtb_ext.BidTypeBanner}, {Bid: &openrtb2.Bid{AdM: "https://domain.com/valid", ImpID: "2"}, BidType: openrtb_ext.BidTypeBanner}},
givenSeat: "pubmatic",
expectedNumOfBids: 1,
expectedNonBids: &nonBids{
seatNonBidsMap: map[string][]openrtb_ext.NonBid{
"pubmatic": {
{
ImpId: "1",
StatusCode: 352,
},
},
},
},
},
{
description: "Validation is warned so no bids should be removed (Validating SecureMarkup)",
givenValidations: config.Validations{SecureMarkup: config.ValidationWarn},
givenBids: []*entities.PbsOrtbBid{{Bid: &openrtb2.Bid{AdM: "http://domain.com/invalid", ImpID: "1"}, BidType: openrtb_ext.BidTypeBanner}, {Bid: &openrtb2.Bid{AdM: "https://domain.com/valid", ImpID: "2"}, BidType: openrtb_ext.BidTypeBanner}},
givenSeat: "pubmatic",
expectedNumOfBids: 2,
expectedNonBids: &nonBids{},
},
{
description: "Adm validation is skipped, creative size validation is enforced, one Adm is invalid, but because we skip, no bids should be removed",
givenValidations: config.Validations{SecureMarkup: config.ValidationSkip, BannerCreativeMaxSize: config.ValidationEnforce},
givenBids: []*entities.PbsOrtbBid{{Bid: &openrtb2.Bid{AdM: "http://domain.com/invalid"}, BidType: openrtb_ext.BidTypeBanner}, {Bid: &openrtb2.Bid{AdM: "https://domain.com/valid"}, BidType: openrtb_ext.BidTypeBanner}},
givenSeat: "pubmatic",
expectedNumOfBids: 2,
expectedNonBids: &nonBids{},
},
{
description: "Creative Size Validation is skipped, Adm Validation is enforced, one Creative Size is invalid, but because we skip, no bids should be removed",
givenValidations: config.Validations{BannerCreativeMaxSize: config.ValidationWarn, MaxCreativeWidth: 100, MaxCreativeHeight: 100},
givenBids: []*entities.PbsOrtbBid{{Bid: &openrtb2.Bid{W: 200, H: 200}, BidType: openrtb_ext.BidTypeBanner}, {Bid: &openrtb2.Bid{W: 50, H: 50}, BidType: openrtb_ext.BidTypeBanner}},
givenSeat: "pubmatic",
expectedNumOfBids: 2,
expectedNonBids: &nonBids{},
},
}

Expand Down Expand Up @@ -4807,12 +4852,16 @@ func TestMakeBidWithValidation(t *testing.T) {

//Run tests
for _, test := range testCases {
e.bidValidationEnforcement = test.givenValidations
sampleBids := test.givenBids
resultingBids, resultingErrs := e.makeBid(sampleBids, sampleAuction, true, ImpExtInfoMap, bidExtResponse, "", "")

assert.Equal(t, 0, len(resultingErrs), "%s. Test should not return errors \n", test.description)
assert.Equal(t, test.expectedNumOfBids, len(resultingBids), "%s. Test returns more valid bids than expected\n", test.description)
t.Run(test.description, func(t *testing.T) {
e.bidValidationEnforcement = test.givenValidations
sampleBids := test.givenBids
nonBids := &nonBids{}
resultingBids, resultingErrs := e.makeBid(sampleBids, sampleAuction, true, ImpExtInfoMap, bidExtResponse, test.givenSeat, "", nonBids)

assert.Equal(t, 0, len(resultingErrs), "%s. Test should not return errors \n", test.description)
assert.Equal(t, test.expectedNumOfBids, len(resultingBids), "%s. Test returns more valid bids than expected\n", test.description)
assert.Equal(t, test.expectedNonBids, nonBids, "%s. Test returns incorrect nonBids\n", test.description)
})
}
}

Expand Down
Loading

0 comments on commit a883b6e

Please sign in to comment.