Skip to content

Commit

Permalink
Pass Global Privacy Control header to bidders (prebid#1789)
Browse files Browse the repository at this point in the history
* Feature Request: Ability to pass Sec-GPC header to the bidder endpoints (prebid#1712)

* making Sec-GPC value check more strict

* minor syntax change

* gofmt fixes

* updates against draft-code-review:one, more to come soon.

* adding a unit test

* Adding a test and request header clone update

* modified one test and related logic

* modifying the last test added with slight more modification of the logic
  • Loading branch information
viveknarang authored and shunj-nb committed Nov 8, 2022
1 parent 1280978 commit a8f40a9
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 28 deletions.
3 changes: 2 additions & 1 deletion adapters/bidder.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,8 @@ func (r *RequestData) SetBasicAuth(username string, password string) {
}

type ExtraRequestInfo struct {
PbsEntryPoint metrics.RequestType
PbsEntryPoint metrics.RequestType
GlobalPrivacyControlHeader string
}

type Builder func(openrtb_ext.BidderName, config.Adapter) (Bidder, error)
15 changes: 9 additions & 6 deletions endpoints/openrtb2/amp_auction.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,13 +182,16 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h
return
}

secGPC := r.Header.Get("Sec-GPC")

auctionRequest := exchange.AuctionRequest{
BidRequest: req,
Account: *account,
UserSyncs: usersyncs,
RequestType: labels.RType,
StartTime: start,
LegacyLabels: labels,
BidRequest: req,
Account: *account,
UserSyncs: usersyncs,
RequestType: labels.RType,
StartTime: start,
LegacyLabels: labels,
GlobalPrivacyControlHeader: secGPC,
}

response, err := deps.ex.HoldAuction(ctx, auctionRequest, nil)
Expand Down
17 changes: 10 additions & 7 deletions endpoints/openrtb2/auction.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,14 +193,17 @@ func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ http
return
}

secGPC := r.Header.Get("Sec-GPC")

auctionRequest := exchange.AuctionRequest{
BidRequest: req,
Account: *account,
UserSyncs: usersyncs,
RequestType: labels.RType,
StartTime: start,
LegacyLabels: labels,
Warnings: warnings,
BidRequest: req,
Account: *account,
UserSyncs: usersyncs,
RequestType: labels.RType,
StartTime: start,
LegacyLabels: labels,
Warnings: warnings,
GlobalPrivacyControlHeader: secGPC,
}

response, err := deps.ex.HoldAuction(ctx, auctionRequest, nil)
Expand Down
15 changes: 9 additions & 6 deletions endpoints/openrtb2/video_auction.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,13 +274,16 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
return
}

secGPC := r.Header.Get("Sec-GPC")

auctionRequest := exchange.AuctionRequest{
BidRequest: bidReq,
Account: *account,
UserSyncs: usersyncs,
RequestType: labels.RType,
StartTime: start,
LegacyLabels: labels,
BidRequest: bidReq,
Account: *account,
UserSyncs: usersyncs,
RequestType: labels.RType,
StartTime: start,
LegacyLabels: labels,
GlobalPrivacyControlHeader: secGPC,
}

response, err := deps.ex.HoldAuction(ctx, auctionRequest, &debugLog)
Expand Down
13 changes: 13 additions & 0 deletions exchange/bidder.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,19 @@ func (bidder *bidderAdapter) requestBid(ctx context.Context, request *openrtb2.B
return nil, errs
}

if reqInfo.GlobalPrivacyControlHeader == "1" {
for i := 0; i < len(reqData); i++ {
if reqData[i].Headers != nil {
reqHeader := reqData[i].Headers.Clone()
reqHeader.Add("Sec-GPC", reqInfo.GlobalPrivacyControlHeader)
reqData[i].Headers = reqHeader
} else {
reqData[i].Headers = http.Header{}
reqData[i].Headers.Add("Sec-GPC", reqInfo.GlobalPrivacyControlHeader)
}
}
}

// Make any HTTP requests in parallel.
// If the bidder only needs to make one, save some cycles by just using the current one.
responseChannel := make(chan *httpCallInfo, len(reqData))
Expand Down
79 changes: 79 additions & 0 deletions exchange/bidder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,85 @@ func TestRequestBidRemovesSensitiveHeaders(t *testing.T) {
assert.ElementsMatch(t, seatBid.httpCalls, expectedHttpCalls)
}

func TestSetGPCHeader(t *testing.T) {
server := httptest.NewServer(mockHandler(200, "getBody", "responseJson"))
defer server.Close()

requestHeaders := http.Header{}
requestHeaders.Add("Content-Type", "application/json")

bidderImpl := &goodSingleBidder{
httpRequest: &adapters.RequestData{
Method: "POST",
Uri: server.URL,
Body: []byte("requestJson"),
Headers: requestHeaders,
},
bidResponse: &adapters.BidderResponse{
Bids: []*adapters.TypedBid{},
},
}

debugInfo := &config.DebugInfo{Allow: true}
ctx := context.Background()
ctx = context.WithValue(ctx, DebugContextKey, true)

bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, debugInfo)
currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
seatBid, errs := bidder.requestBid(ctx, &openrtb2.BidRequest{}, "test", 1, currencyConverter.Rates(), &adapters.ExtraRequestInfo{GlobalPrivacyControlHeader: "1"}, true)

expectedHttpCall := []*openrtb_ext.ExtHttpCall{
{
Uri: server.URL,
RequestBody: "requestJson",
RequestHeaders: map[string][]string{"Content-Type": {"application/json"}, "Sec-Gpc": {"1"}},
ResponseBody: "responseJson",
Status: 200,
},
}

assert.Empty(t, errs)
assert.ElementsMatch(t, seatBid.httpCalls, expectedHttpCall)
}

func TestSetGPCHeaderNil(t *testing.T) {
server := httptest.NewServer(mockHandler(200, "getBody", "responseJson"))
defer server.Close()

bidderImpl := &goodSingleBidder{
httpRequest: &adapters.RequestData{
Method: "POST",
Uri: server.URL,
Body: []byte("requestJson"),
Headers: nil,
},
bidResponse: &adapters.BidderResponse{
Bids: []*adapters.TypedBid{},
},
}

debugInfo := &config.DebugInfo{Allow: true}
ctx := context.Background()
ctx = context.WithValue(ctx, DebugContextKey, true)

bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, debugInfo)
currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
seatBid, errs := bidder.requestBid(ctx, &openrtb2.BidRequest{}, "test", 1, currencyConverter.Rates(), &adapters.ExtraRequestInfo{GlobalPrivacyControlHeader: "1"}, true)

expectedHttpCall := []*openrtb_ext.ExtHttpCall{
{
Uri: server.URL,
RequestBody: "requestJson",
RequestHeaders: map[string][]string{"Sec-Gpc": {"1"}},
ResponseBody: "responseJson",
Status: 200,
},
}

assert.Empty(t, errs)
assert.ElementsMatch(t, seatBid.httpCalls, expectedHttpCall)
}

// TestMultiBidder makes sure all the requests get sent, and the responses processed.
// Because this is done in parallel, it should be run under the race detector.
func TestMultiBidder(t *testing.T) {
Expand Down
19 changes: 11 additions & 8 deletions exchange/exchange.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,12 +123,13 @@ func NewExchange(adapters map[openrtb_ext.BidderName]adaptedBidder, cache prebid
// AuctionRequest holds the bid request for the auction
// and all other information needed to process that request
type AuctionRequest struct {
BidRequest *openrtb2.BidRequest
Account config.Account
UserSyncs IdFetcher
RequestType metrics.RequestType
StartTime time.Time
Warnings []error
BidRequest *openrtb2.BidRequest
Account config.Account
UserSyncs IdFetcher
RequestType metrics.RequestType
StartTime time.Time
Warnings []error
GlobalPrivacyControlHeader string

// LegacyLabels is included here for temporary compatability with cleanOpenRTBRequests
// in HoldAuction until we get to factoring it away. Do not use for anything new.
Expand Down Expand Up @@ -193,7 +194,7 @@ func (e *exchange) HoldAuction(ctx context.Context, r AuctionRequest, debugLog *
// Get currency rates conversions for the auction
conversions := e.currencyConverter.Rates()

adapterBids, adapterExtra, anyBidsReturned := e.getAllBids(auctionCtx, bidderRequests, bidAdjustmentFactors, conversions, r.Account.DebugAllow)
adapterBids, adapterExtra, anyBidsReturned := e.getAllBids(auctionCtx, bidderRequests, bidAdjustmentFactors, conversions, r.Account.DebugAllow, r.GlobalPrivacyControlHeader)

var auc *auction
var cacheErrs []error
Expand Down Expand Up @@ -405,7 +406,8 @@ func (e *exchange) getAllBids(
bidderRequests []BidderRequest,
bidAdjustments map[string]float64,
conversions currency.Conversions,
accountDebugAllowed bool) (
accountDebugAllowed bool,
globalPrivacyControlHeader string) (
map[openrtb_ext.BidderName]*pbsOrtbSeatBid,
map[openrtb_ext.BidderName]*seatResponseExtra, bool) {
// Set up pointers to the bid results
Expand Down Expand Up @@ -436,6 +438,7 @@ func (e *exchange) getAllBids(
}
var reqInfo adapters.ExtraRequestInfo
reqInfo.PbsEntryPoint = bidderRequest.BidderLabels.RType
reqInfo.GlobalPrivacyControlHeader = globalPrivacyControlHeader
bids, err := e.adapterMap[bidderRequest.BidderCoreName].requestBid(ctx, bidderRequest.BidRequest, bidderRequest.BidderName, adjustmentFactor, conversions, &reqInfo, accountDebugAllowed)

// Add in time reporting
Expand Down

0 comments on commit a8f40a9

Please sign in to comment.