Skip to content

Commit

Permalink
AMP CCPA Fix (#1187)
Browse files Browse the repository at this point in the history
  • Loading branch information
SyntaxNode authored Mar 30, 2020
1 parent 7e706f4 commit e05369b
Show file tree
Hide file tree
Showing 19 changed files with 1,082 additions and 588 deletions.
Empty file.
90 changes: 51 additions & 39 deletions endpoints/openrtb2/amp_auction.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ import (
"github.com/prebid/prebid-server/openrtb_ext"
"github.com/prebid/prebid-server/pbsmetrics"
"github.com/prebid/prebid-server/privacy"
"github.com/prebid/prebid-server/privacy/ccpa"
"github.com/prebid/prebid-server/privacy/gdpr"
"github.com/prebid/prebid-server/stored_requests"
"github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher"
"github.com/prebid/prebid-server/usersync"
Expand All @@ -36,6 +34,7 @@ type AmpResponse struct {
Targeting map[string]string `json:"targeting"`
Debug *openrtb_ext.ExtResponseDebug `json:"debug,omitempty"`
Errors map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderError `json:"errors,omitempty"`
Warnings map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderError `json:"warnings,omitempty"`
}

// NewAmpEndpoint modifies the OpenRTB endpoint to handle AMP requests. This will basically modify the parsing
Expand Down Expand Up @@ -121,13 +120,13 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h
w.Header().Set("Access-Control-Expose-Headers", "AMP-Access-Control-Allow-Source-Origin")

req, errL := deps.parseAmpRequest(r)
ao.Errors = append(ao.Errors, errL...)

if fatalError(errL) {
if errortypes.ContainsFatalError(errL) {
w.WriteHeader(http.StatusBadRequest)
for _, err := range errL {
for _, err := range errortypes.FatalOnly(errL) {
w.Write([]byte(fmt.Sprintf("Invalid request format: %s\n", err.Error())))
}
ao.Errors = append(ao.Errors, errL...)
labels.RequestStatus = pbsmetrics.RequestStatusBadInput
return
}
Expand All @@ -151,18 +150,18 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h
// Blacklist account now that we have resolved the value
if acctIdErr := validateAccount(deps.cfg, labels.PubID); acctIdErr != nil {
errL = append(errL, acctIdErr)
erVal := errortypes.DecodeError(acctIdErr)
if erVal == errortypes.BlacklistedAppCode || erVal == errortypes.BlacklistedAcctCode {
errCode := errortypes.ReadCode(acctIdErr)
if errCode == errortypes.BlacklistedAppErrorCode || errCode == errortypes.BlacklistedAcctErrorCode {
w.WriteHeader(http.StatusServiceUnavailable)
labels.RequestStatus = pbsmetrics.RequestStatusBlacklisted
} else { //erVal == errortypes.AcctRequiredCode
} else {
w.WriteHeader(http.StatusBadRequest)
labels.RequestStatus = pbsmetrics.RequestStatusBadInput
}
for _, err := range errL {
for _, err := range errortypes.FatalOnly(errL) {
w.Write([]byte(fmt.Sprintf("Invalid request format: %s\n", err.Error())))
}
ao.Errors = append(ao.Errors, errL...)
ao.Errors = append(ao.Errors, acctIdErr)
return
}

Expand Down Expand Up @@ -206,17 +205,28 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h
}
}
}

// Extract any errors
var extResponse openrtb_ext.ExtBidResponse
eRErr := json.Unmarshal(response.Ext, &extResponse)
if eRErr != nil {
ao.Errors = append(ao.Errors, fmt.Errorf("AMP response: failed to unpack OpenRTB response.ext, debug info cannot be forwarded: %v", eRErr))
}

warnings := make(map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderError)
for _, v := range errortypes.WarningOnly(errL) {
bidderErr := openrtb_ext.ExtBidderError{
Code: errortypes.ReadCode(v),
Message: v.Error(),
}
warnings[openrtb_ext.BidderNameGeneral] = append(warnings[openrtb_ext.BidderNameGeneral], bidderErr)
}

// Now JSONify the targets for the AMP response.
ampResponse := AmpResponse{
Targeting: targets,
Errors: extResponse.Errors,
Warnings: warnings,
}

ao.AmpTargetingValues = targets
Expand Down Expand Up @@ -252,27 +262,24 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h
// If the errors list has at least one element, then no guarantees are made about the returned request.
func (deps *endpointDeps) parseAmpRequest(httpRequest *http.Request) (req *openrtb.BidRequest, errs []error) {
// Load the stored request for the AMP ID.
req, errs = deps.loadRequestJSONForAmp(httpRequest)
if len(errs) > 0 {
req, e := deps.loadRequestJSONForAmp(httpRequest)
if errs = append(errs, e...); errortypes.ContainsFatalError(errs) {
return
}

// Populate any "missing" OpenRTB fields with info from other sources, (e.g. HTTP request headers).
deps.setFieldsImplicitly(httpRequest, req)

// Need to ensure cache and targeting are turned on
errs = defaultRequestExt(req)
if len(errs) > 0 {
e = defaultRequestExt(req)
if errs = append(errs, e...); errortypes.ContainsFatalError(errs) {
return
}

// At this point, we should have a valid request that definitely has Targeting and Cache turned on

errL := deps.validateRequest(req)
if len(errL) > 0 {
errs = append(errs, errL...)
}

e = deps.validateRequest(req)
errs = append(errs, e...)
return
}

Expand All @@ -287,9 +294,6 @@ func (deps *endpointDeps) loadRequestJSONForAmp(httpRequest *http.Request) (req
return
}

debugParam := httpRequest.FormValue("debug")
debug := debugParam == "1"

ctx, cancel := context.WithTimeout(context.Background(), time.Duration(storedRequestTimeoutMillis)*time.Millisecond)
defer cancel()

Expand All @@ -309,7 +313,8 @@ func (deps *endpointDeps) loadRequestJSONForAmp(httpRequest *http.Request) (req
return
}

if debug {
debugParam := httpRequest.FormValue("debug")
if debugParam == "1" {
req.Test = 1
}

Expand All @@ -336,18 +341,15 @@ func (deps *endpointDeps) loadRequestJSONForAmp(httpRequest *http.Request) (req
*req.Imp[0].Secure = 1
}

err := deps.overrideWithParams(httpRequest, req)
if err != nil {
errs = []error{err}
}

errs = deps.overrideWithParams(httpRequest, req)
return
}

func (deps *endpointDeps) overrideWithParams(httpRequest *http.Request, req *openrtb.BidRequest) error {
func (deps *endpointDeps) overrideWithParams(httpRequest *http.Request, req *openrtb.BidRequest) []error {
if req.Site == nil {
req.Site = &openrtb.Site{}
}

// Override the stored request sizes with AMP ones, if they exist.
if req.Imp[0].Banner != nil {
width := parseFormInt(httpRequest, "w", 0)
Expand Down Expand Up @@ -383,16 +385,17 @@ func (deps *endpointDeps) overrideWithParams(httpRequest *http.Request, req *ope
req.Imp[0].TagID = slot
}

privacyPolicies := privacy.Policies{
GDPR: gdpr.Policy{
Consent: httpRequest.URL.Query().Get("gdpr_consent"),
},
CCPA: ccpa.Policy{
Value: httpRequest.URL.Query().Get("us_privacy"),
},
}
if err := privacyPolicies.Write(req); err != nil {
return err
consent := readConsent(httpRequest.URL)
if consent != "" {
if policies, ok := privacy.ReadPoliciesFromConsent(consent); ok {
if err := policies.Write(req); err != nil {
return []error{err}
}
} else {
return []error{&errortypes.InvalidPrivacyConsent{
Message: fmt.Sprintf("Consent '%s' is not recognized as either CCPA or GDPR TCF.", consent),
}}
}
}

if timeout, err := strconv.ParseInt(httpRequest.FormValue("timeout"), 10, 64); err == nil {
Expand Down Expand Up @@ -533,3 +536,12 @@ func setAmpExt(site *openrtb.Site, value string) {
site.Ext = json.RawMessage(`{"amp":` + value + `}`)
}
}

func readConsent(url *url.URL) string {
if v := url.Query().Get("consent_string"); v != "" {
return v
}

// Fallback to 'gdpr_consent' for compatability until it's no longer used by AMP.
return url.Query().Get("gdpr_consent")
}
Loading

0 comments on commit e05369b

Please sign in to comment.