-
Notifications
You must be signed in to change notification settings - Fork 748
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
GPC: Set extension based on header #3895
Changes from 5 commits
8971a36
58aab7c
b7692d7
a2b7cf1
c7fbfe1
f713ff1
5fba3db
f8e7977
b5acce3
bff49e2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -63,10 +63,11 @@ const observeBrowsingTopics = "Observe-Browsing-Topics" | |
const observeBrowsingTopicsValue = "?1" | ||
|
||
var ( | ||
dntKey string = http.CanonicalHeaderKey("DNT") | ||
dntDisabled int8 = 0 | ||
dntEnabled int8 = 1 | ||
notAmp int8 = 0 | ||
dntKey string = http.CanonicalHeaderKey("DNT") | ||
secGPCHdrKey string = http.CanonicalHeaderKey("Sec-GPC") | ||
dntDisabled int8 = 0 | ||
dntEnabled int8 = 1 | ||
notAmp int8 = 0 | ||
) | ||
|
||
var accountIdSearchPath = [...]struct { | ||
|
@@ -1497,6 +1498,11 @@ func (deps *endpointDeps) setFieldsImplicitly(httpReq *http.Request, r *openrtb_ | |
|
||
setAuctionTypeImplicitly(r) | ||
|
||
err := setGPCImplicitly(httpReq, r) | ||
if err != nil { | ||
return []error{err} | ||
} | ||
|
||
errs := setSecBrowsingTopicsImplicitly(httpReq, r, account) | ||
return errs | ||
} | ||
|
@@ -1516,6 +1522,25 @@ func setAuctionTypeImplicitly(r *openrtb_ext.RequestWrapper) { | |
} | ||
} | ||
|
||
func setGPCImplicitly(httpReq *http.Request, r *openrtb_ext.RequestWrapper) error { | ||
secGPC := httpReq.Header.Get(secGPCHdrKey) | ||
fmt.Printf("Sec-GPC Header: %s\n", secGPC) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove print statement |
||
|
||
if secGPC != "1" { | ||
return nil | ||
} | ||
|
||
regExt, err := r.GetRegExt() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
gpc := "1" | ||
regExt.SetGPC(&gpc) | ||
Comment on lines
+1540
to
+1542
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suggest adding the following early return just before the set:
This way we won't perform an unnecessary write which sets the dirty flag and causes extra work when rebuilding the request downstream. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi @przemkaczmarek, here is a super nitpick to this function.
The code here does the right thing, however it's a little confusing and difficult to read. I had to read it couple of times to understand it. func setGPCImplicitly(httpReq *http.Request, r *openrtb_ext.RequestWrapper) error {
regExt, err := r.GetRegExt()
if err != nil {
return err
}
if regExt.GetGPC() != nil {
return nil
}
secGPC := httpReq.Header.Get(secGPCKey)
if secGPC == "1" {
gpc := "1"
regExt.SetGPC(&gpc)
}
return nil
} The unit test passes as is. What do you think? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In my version of the code, I first check the value of secGPC, and if it doesn't meet the condition, the function ends immediately with return nil. In your version of the code, this happens later, which leads to unnecessary retrieval of regExt, even when it's not needed. I avoid unnecessary operations. I avoid performing costly operations (such as r.GetRegExt()) if the secGPC header does not have the value '1'. In the first version, you retrieve the regExt extension regardless of the secGPC value, which is less optimal There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I understand this. I am only concerned about the readability. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I prefer to keep the code as is but thanks for the comment |
||
|
||
return nil | ||
} | ||
|
||
// setSecBrowsingTopicsImplicitly updates user.data with data from request header 'Sec-Browsing-Topics' | ||
func setSecBrowsingTopicsImplicitly(httpReq *http.Request, r *openrtb_ext.RequestWrapper, account *config.Account) []error { | ||
secBrowsingTopics := httpReq.Header.Get(secBrowsingTopics) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5618,6 +5618,92 @@ func TestValidateOrFillCookieDeprecation(t *testing.T) { | |
} | ||
} | ||
|
||
func TestSetGPCImplicitly(t *testing.T) { | ||
testCases := []struct { | ||
description string | ||
header string | ||
expectedGPC string | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can remove this because |
||
expectError bool | ||
regs *openrtb2.Regs | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nitpick: move |
||
expectedRegs *openrtb2.Regs | ||
}{ | ||
{ | ||
description: "regs_ext_gpc_not_set_and_header_is_1", | ||
header: "1", | ||
expectedGPC: "1", | ||
expectError: false, | ||
regs: &openrtb2.Regs{ | ||
Ext: []byte(`{}`), | ||
}, | ||
expectedRegs: &openrtb2.Regs{ | ||
Ext: []byte(`{"gpc":"1"}`), | ||
}, | ||
}, | ||
{ | ||
description: "sec_gpc_header_not_set_gpc_should_not_be_modified", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suggest covering the following test cases: |
||
header: "", | ||
expectedGPC: "", | ||
expectError: false, | ||
regs: &openrtb2.Regs{ | ||
Ext: []byte(`{}`), | ||
}, | ||
expectedRegs: &openrtb2.Regs{ | ||
Ext: []byte(`{}`), | ||
}, | ||
}, | ||
{ | ||
description: "sec_gpc_header_set_to_2_gpc_should_not_be_modified", | ||
header: "2", | ||
expectedGPC: "", | ||
expectError: false, | ||
regs: &openrtb2.Regs{ | ||
Ext: []byte(`{}`), | ||
}, | ||
expectedRegs: &openrtb2.Regs{ | ||
Ext: []byte(`{}`), | ||
}, | ||
}, | ||
{ | ||
description: "sec_gpc_header_set_to_1_and_regs_ext_contains_other_data", | ||
header: "1", | ||
expectedGPC: "1", | ||
expectError: false, | ||
regs: &openrtb2.Regs{ | ||
Ext: []byte(`{"some_other_field":"some_value"}`), | ||
}, | ||
expectedRegs: &openrtb2.Regs{ | ||
Ext: []byte(`{"some_other_field":"some_value","gpc":"1"}`), | ||
}, | ||
}, | ||
} | ||
|
||
for _, test := range testCases { | ||
t.Run(test.description, func(t *testing.T) { | ||
httpReq := &http.Request{ | ||
Header: http.Header{ | ||
http.CanonicalHeaderKey("Sec-GPC"): []string{test.header}, | ||
}, | ||
} | ||
|
||
r := &openrtb_ext.RequestWrapper{ | ||
BidRequest: &openrtb2.BidRequest{ | ||
Regs: test.regs, | ||
}, | ||
} | ||
|
||
err := setGPCImplicitly(httpReq, r) | ||
|
||
if test.expectError { | ||
assert.Error(t, err) | ||
} else { | ||
assert.NoError(t, err) | ||
} | ||
assert.NoError(t, r.RebuildRequest()) | ||
assert.JSONEq(t, string(test.expectedRegs.Ext), string(r.BidRequest.Regs.Ext)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In order to handle the two new test cases I requested you can modify this as such:
|
||
}) | ||
} | ||
} | ||
|
||
func TestValidateRequestCookieDeprecation(t *testing.T) { | ||
testCases := | ||
[]struct { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The code looks good. Please update There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure if you missed this during your last iteration but please add/update the wrapper tests to cover changes. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -61,6 +61,7 @@ const ( | |
schainKey = "schain" | ||
us_privacyKey = "us_privacy" | ||
cdepKey = "cdep" | ||
gpcKey = "gpc" | ||
) | ||
|
||
// LenImp returns the number of impressions without causing the creation of ImpWrapper objects. | ||
|
@@ -1201,6 +1202,8 @@ type RegExt struct { | |
dsaDirty bool | ||
gdpr *int8 | ||
gdprDirty bool | ||
gpc *string | ||
gpcDirty bool | ||
usPrivacy string | ||
usPrivacyDirty bool | ||
} | ||
|
@@ -1244,6 +1247,13 @@ func (re *RegExt) unmarshal(extJson json.RawMessage) error { | |
} | ||
} | ||
|
||
gpcJson, hasGPC := re.ext[gpcKey] | ||
if hasGPC && gpcJson != nil { | ||
if err := jsonutil.Unmarshal(gpcJson, &re.gpc); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
|
@@ -1287,6 +1297,19 @@ func (re *RegExt) marshal() (json.RawMessage, error) { | |
re.usPrivacyDirty = false | ||
} | ||
|
||
if re.gpcDirty { | ||
if re.gpc != nil { | ||
rawjson, err := jsonutil.Marshal(re.gpc) | ||
if err != nil { | ||
return nil, err | ||
} | ||
re.ext[gpcKey] = rawjson | ||
} else { | ||
delete(re.ext, gpcKey) | ||
} | ||
re.gpcDirty = false | ||
} | ||
|
||
re.extDirty = false | ||
if len(re.ext) == 0 { | ||
return nil, nil | ||
|
@@ -1295,7 +1318,7 @@ func (re *RegExt) marshal() (json.RawMessage, error) { | |
} | ||
|
||
func (re *RegExt) Dirty() bool { | ||
return re.extDirty || re.dsaDirty || re.gdprDirty || re.usPrivacyDirty | ||
return re.extDirty || re.dsaDirty || re.gdprDirty || re.usPrivacyDirty || re.gpcDirty | ||
} | ||
|
||
func (re *RegExt) GetExt() map[string]json.RawMessage { | ||
|
@@ -1337,6 +1360,19 @@ func (re *RegExt) SetGDPR(gdpr *int8) { | |
re.gdprDirty = true | ||
} | ||
|
||
func (re *RegExt) GetGPC() *string { | ||
if re.gpc == nil { | ||
return nil | ||
} | ||
GPC := *re.gpc | ||
return &GPC | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nitpick: |
||
} | ||
|
||
func (re *RegExt) SetGPC(gpc *string) { | ||
re.gpc = gpc | ||
re.gpcDirty = true | ||
} | ||
|
||
func (re *RegExt) GetUSPrivacy() string { | ||
uSPrivacy := re.usPrivacy | ||
return uSPrivacy | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick:
secGPCKey
suffices