diff --git a/adapters/vastbidder/bidder_macro.go b/adapters/vastbidder/bidder_macro.go index 2c35b4fe27d..dc6d1c6a2c1 100644 --- a/adapters/vastbidder/bidder_macro.go +++ b/adapters/vastbidder/bidder_macro.go @@ -32,6 +32,9 @@ type BidderMacro struct { Content *openrtb.Content UserExt *openrtb_ext.ExtUser RegsExt *openrtb_ext.ExtRegs + + // Impression level Request Headers + ImpReqHeaders http.Header } //NewBidderMacro contains definition for all openrtb macro's @@ -90,7 +93,6 @@ func (tag *BidderMacro) LoadImpression(imp *openrtb.Imp) error { if err := json.Unmarshal(bidderExt.Bidder, &tag.ImpBidderExt); err != nil { return err } - return nil } @@ -118,7 +120,8 @@ func (tag *BidderMacro) GetURI() string { return tag.Conf.Endpoint } -//GetHeaders GetHeaders +//GetHeaders returns list of custom request headers +//Override this method if your Vast bidder needs custom request headers func (tag *BidderMacro) GetHeaders() http.Header { return http.Header{} } @@ -480,7 +483,7 @@ func (tag *BidderMacro) MacroSiteName(key string) string { //MacroSitePage contains definition for SitePage Parameter func (tag *BidderMacro) MacroSitePage(key string) string { - if !tag.IsApp { + if !tag.IsApp && nil != tag.Request && nil != tag.Request.Site { return tag.Request.Site.Page } return "" @@ -850,7 +853,7 @@ func (tag *BidderMacro) MacroProducerName(key string) string { //MacroUserAgent contains definition for UserAgent Parameter func (tag *BidderMacro) MacroUserAgent(key string) string { - if nil != tag.Request.Device { + if nil != tag.Request && nil != tag.Request.Device { return tag.Request.Device.UA } return "" @@ -874,7 +877,7 @@ func (tag *BidderMacro) MacroLMT(key string) string { //MacroIP contains definition for IP Parameter func (tag *BidderMacro) MacroIP(key string) string { - if nil != tag.Request.Device { + if nil != tag.Request && nil != tag.Request.Device { if len(tag.Request.Device.IP) > 0 { return tag.Request.Device.IP } else if len(tag.Request.Device.IPv6) > 0 { @@ -950,7 +953,7 @@ func (tag *BidderMacro) MacroDeviceJS(key string) string { //MacroDeviceLanguage contains definition for DeviceLanguage Parameter func (tag *BidderMacro) MacroDeviceLanguage(key string) string { - if nil != tag.Request.Device { + if nil != tag.Request && nil != tag.Request.Device { return tag.Request.Device.Language } return "" @@ -1129,3 +1132,80 @@ func (tag *BidderMacro) MacroCacheBuster(key string) string { //change implementation return strconv.FormatInt(time.Now().UnixNano(), intBase) } + +/********************* Request Headers *********************/ + +// setDefaultHeaders sets following default headers based on VAST protocol version +// X-device-IP; end users IP address, per VAST 4.x +// X-Forwarded-For; end users IP address, prior VAST versions +// X-Device-User-Agent; End users user agent, per VAST 4.x +// User-Agent; End users user agent, prior VAST versions +// X-Device-Referer; Referer value from the original request, per VAST 4.x +// X-device-Accept-Language, Accept-language value from the original request, per VAST 4.x +func setDefaultHeaders(tag *BidderMacro) { + // openrtb2. auction.go setDeviceImplicitly + // already populates OpenRTB bid request based on http request headers + // reusing the same information to set these headers via Macro* methods + headers := http.Header{} + ip := tag.IBidderMacro.MacroIP("") + userAgent := tag.IBidderMacro.MacroUserAgent("") + referer := tag.IBidderMacro.MacroSitePage("") + language := tag.IBidderMacro.MacroDeviceLanguage("") + + // 1 - vast 1 - 3 expected, 2 - vast 4 expected + expectedVastTags := 0 + if nil != tag.Imp && nil != tag.Imp.Video && nil != tag.Imp.Video.Protocols && len(tag.Imp.Video.Protocols) > 0 { + for _, protocol := range tag.Imp.Video.Protocols { + if protocol == openrtb.ProtocolVAST40 || protocol == openrtb.ProtocolVAST40Wrapper { + expectedVastTags |= 1 << 1 + } + if protocol <= openrtb.ProtocolVAST30Wrapper { + expectedVastTags |= 1 << 0 + } + } + } else { + // not able to detect protocols. set all headers + expectedVastTags = 3 + } + + if expectedVastTags == 1 || expectedVastTags == 3 { + // vast prior to version 3 headers + setHeaders(headers, "X-Forwarded-For", ip) + setHeaders(headers, "User-Agent", userAgent) + } + + if expectedVastTags == 2 || expectedVastTags == 3 { + // vast 4 specific headers + setHeaders(headers, "X-device-Ip", ip) + setHeaders(headers, "X-Device-User-Agent", userAgent) + setHeaders(headers, "X-Device-Referer", referer) + setHeaders(headers, "X-Device-Accept-Language", language) + } + tag.ImpReqHeaders = headers +} + +func setHeaders(headers http.Header, key, value string) { + if "" != value { + headers.Set(key, value) + } +} + +//getAllHeaders combines default and custom headers and returns common list +//It internally calls GetHeaders() method for obtaining list of custom headers +func (tag *BidderMacro) getAllHeaders() http.Header { + setDefaultHeaders(tag) + customHeaders := tag.IBidderMacro.GetHeaders() + if nil != customHeaders { + for k, v := range customHeaders { + // custom header may contains default header key with value + // in such case custom value will be prefered + if nil != v && len(v) > 0 { + tag.ImpReqHeaders.Set(k, v[0]) + for i := 1; i < len(v); i++ { + tag.ImpReqHeaders.Add(k, v[i]) + } + } + } + } + return tag.ImpReqHeaders +} diff --git a/adapters/vastbidder/bidder_macro_test.go b/adapters/vastbidder/bidder_macro_test.go index 45555e579fa..fbdd90d84ee 100644 --- a/adapters/vastbidder/bidder_macro_test.go +++ b/adapters/vastbidder/bidder_macro_test.go @@ -2,13 +2,335 @@ package vastbidder import ( "fmt" + "net/http" "testing" + "github.com/stretchr/testify/assert" + "github.com/PubMatic-OpenWrap/openrtb" "github.com/PubMatic-OpenWrap/prebid-server/config" - "github.com/stretchr/testify/assert" ) +//TestSetDefaultHeaders verifies SetDefaultHeaders +func TestSetDefaultHeaders(t *testing.T) { + type args struct { + req *openrtb.BidRequest + } + type want struct { + headers http.Header + } + tests := []struct { + name string + args args + want want + }{ + { + name: "check all default headers", + args: args{req: &openrtb.BidRequest{ + Device: &openrtb.Device{ + IP: "1.1.1.1", + UA: "user-agent", + Language: "en", + }, + Site: &openrtb.Site{ + Page: "http://test.com/", + }, + }}, + want: want{ + headers: http.Header{ + "X-Device-Ip": []string{"1.1.1.1"}, + "X-Forwarded-For": []string{"1.1.1.1"}, + "X-Device-User-Agent": []string{"user-agent"}, + "User-Agent": []string{"user-agent"}, + "X-Device-Referer": []string{"http://test.com/"}, + "X-Device-Accept-Language": []string{"en"}, + }, + }, + }, + { + name: "nil bid request", + args: args{req: nil}, + want: want{ + headers: http.Header{}, + }, + }, + { + name: "no headers set", + args: args{req: &openrtb.BidRequest{}}, + want: want{ + headers: http.Header{}, + }, + }, { + name: "vast 4 protocol", + args: args{ + req: &openrtb.BidRequest{ + Device: &openrtb.Device{ + IP: "1.1.1.1", + UA: "user-agent", + Language: "en", + }, + Site: &openrtb.Site{ + Page: "http://test.com/", + }, + Imp: []openrtb.Imp{ + { + Video: &openrtb.Video{ + Protocols: []openrtb.Protocol{ + openrtb.ProtocolVAST40, + openrtb.ProtocolDAAST10, + }, + }, + }, + }, + }, + }, + want: want{ + headers: http.Header{ + "X-Device-Ip": []string{"1.1.1.1"}, + "X-Device-User-Agent": []string{"user-agent"}, + "X-Device-Referer": []string{"http://test.com/"}, + "X-Device-Accept-Language": []string{"en"}, + }, + }, + }, { + name: "< vast 4", + args: args{ + req: &openrtb.BidRequest{ + Device: &openrtb.Device{ + IP: "1.1.1.1", + UA: "user-agent", + Language: "en", + }, + Site: &openrtb.Site{ + Page: "http://test.com/", + }, + Imp: []openrtb.Imp{ + { + Video: &openrtb.Video{ + Protocols: []openrtb.Protocol{ + openrtb.ProtocolVAST20, + openrtb.ProtocolDAAST10, + }, + }, + }, + }, + }, + }, + want: want{ + headers: http.Header{ + "X-Forwarded-For": []string{"1.1.1.1"}, + "User-Agent": []string{"user-agent"}, + }, + }, + }, { + name: "vast 4.0 and 4.0 wrapper", + args: args{ + req: &openrtb.BidRequest{ + Device: &openrtb.Device{ + IP: "1.1.1.1", + UA: "user-agent", + Language: "en", + }, + Site: &openrtb.Site{ + Page: "http://test.com/", + }, + Imp: []openrtb.Imp{ + { + Video: &openrtb.Video{ + Protocols: []openrtb.Protocol{ + openrtb.ProtocolVAST40, + openrtb.ProtocolVAST40Wrapper, + }, + }, + }, + }, + }, + }, + want: want{ + headers: http.Header{ + "X-Device-Ip": []string{"1.1.1.1"}, + "X-Device-User-Agent": []string{"user-agent"}, + "X-Device-Referer": []string{"http://test.com/"}, + "X-Device-Accept-Language": []string{"en"}, + }, + }, + }, + { + name: "vast 2.0 and 4.0", + args: args{ + req: &openrtb.BidRequest{ + Device: &openrtb.Device{ + IP: "1.1.1.1", + UA: "user-agent", + Language: "en", + }, + Site: &openrtb.Site{ + Page: "http://test.com/", + }, + Imp: []openrtb.Imp{ + { + Video: &openrtb.Video{ + Protocols: []openrtb.Protocol{ + openrtb.ProtocolVAST40, + openrtb.ProtocolVAST20Wrapper, + }, + }, + }, + }, + }, + }, + want: want{ + headers: http.Header{ + "X-Device-Ip": []string{"1.1.1.1"}, + "X-Forwarded-For": []string{"1.1.1.1"}, + "X-Device-User-Agent": []string{"user-agent"}, + "User-Agent": []string{"user-agent"}, + "X-Device-Referer": []string{"http://test.com/"}, + "X-Device-Accept-Language": []string{"en"}, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tag := new(BidderMacro) + tag.IBidderMacro = tag + tag.IsApp = false + tag.Request = tt.args.req + if nil != tt.args.req && nil != tt.args.req.Imp && len(tt.args.req.Imp) > 0 { + tag.Imp = &tt.args.req.Imp[0] + } + setDefaultHeaders(tag) + assert.Equal(t, tt.want.headers, tag.ImpReqHeaders) + }) + } +} + +//TestGetAllHeaders verifies default and custom headers are returned +func TestGetAllHeaders(t *testing.T) { + type args struct { + req *openrtb.BidRequest + myBidder IBidderMacro + } + type want struct { + headers http.Header + } + + tests := []struct { + name string + args args + want want + }{ + { + name: "Default and custom headers check", + args: args{ + req: &openrtb.BidRequest{ + Device: &openrtb.Device{ + IP: "1.1.1.1", + UA: "user-agent", + Language: "en", + }, + Site: &openrtb.Site{ + Page: "http://test.com/", + }, + }, + myBidder: newMyVastBidderMacro(map[string]string{ + "my-custom-header": "some-value", + }), + }, + want: want{ + headers: http.Header{ + "X-Device-Ip": []string{"1.1.1.1"}, + "X-Forwarded-For": []string{"1.1.1.1"}, + "X-Device-User-Agent": []string{"user-agent"}, + "User-Agent": []string{"user-agent"}, + "X-Device-Referer": []string{"http://test.com/"}, + "X-Device-Accept-Language": []string{"en"}, + "My-Custom-Header": []string{"some-value"}, + }, + }, + }, + { + name: "override default header value", + args: args{ + req: &openrtb.BidRequest{ + Site: &openrtb.Site{ + Page: "http://test.com/", // default header value + }, + }, + myBidder: newMyVastBidderMacro(map[string]string{ + "X-Device-Referer": "my-custom-value", + }), + }, + want: want{ + headers: http.Header{ + // http://test.com/ is not expected here as value + "X-Device-Referer": []string{"my-custom-value"}, + }, + }, + }, + { + name: "no custom headers", + args: args{ + req: &openrtb.BidRequest{ + Device: &openrtb.Device{ + IP: "1.1.1.1", + UA: "user-agent", + Language: "en", + }, + Site: &openrtb.Site{ + Page: "http://test.com/", + }, + }, + myBidder: newMyVastBidderMacro(nil), // nil - no custom headers + }, + want: want{ + headers: http.Header{ // expect default headers + "X-Device-Ip": []string{"1.1.1.1"}, + "X-Forwarded-For": []string{"1.1.1.1"}, + "X-Device-User-Agent": []string{"user-agent"}, + "User-Agent": []string{"user-agent"}, + "X-Device-Referer": []string{"http://test.com/"}, + "X-Device-Accept-Language": []string{"en"}, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tag := tt.args.myBidder + tag.(*myVastBidderMacro).Request = tt.args.req + allHeaders := tag.getAllHeaders() + assert.Equal(t, tt.want.headers, allHeaders) + }) + } +} + +type myVastBidderMacro struct { + *BidderMacro + customHeaders map[string]string +} + +func newMyVastBidderMacro(customHeaders map[string]string) IBidderMacro { + obj := &myVastBidderMacro{ + BidderMacro: &BidderMacro{}, + customHeaders: customHeaders, + } + obj.IBidderMacro = obj + return obj +} + +func (tag *myVastBidderMacro) GetHeaders() http.Header { + if nil == tag.customHeaders { + return nil + } + h := http.Header{} + for k, v := range tag.customHeaders { + h.Set(k, v) + } + return h +} + type testBidderMacro struct { *BidderMacro } diff --git a/adapters/vastbidder/ibidder_macro.go b/adapters/vastbidder/ibidder_macro.go index 0c0189393f3..c17ce1bfaf6 100644 --- a/adapters/vastbidder/ibidder_macro.go +++ b/adapters/vastbidder/ibidder_macro.go @@ -23,6 +23,8 @@ type IBidderMacro interface { SetAdapterConfig(*config.Adapter) GetURI() string GetHeaders() http.Header + //getAllHeaders returns default and custom heades + getAllHeaders() http.Header //Request MacroTest(string) string diff --git a/adapters/vastbidder/tagbidder.go b/adapters/vastbidder/tagbidder.go index c66bcae0c42..2b8bb807441 100644 --- a/adapters/vastbidder/tagbidder.go +++ b/adapters/vastbidder/tagbidder.go @@ -38,11 +38,14 @@ func (a *TagBidder) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters. //uri := macroProcessor.ProcessURL(bidderMacro.GetURI(), a.bidderConfig.Flags) uri := macroProcessor.ProcessURL(bidderMacro.GetURI(), Flags{RemoveEmptyParam: true}) + // append custom headers if any + headers := bidderMacro.getAllHeaders() + requestData = append(requestData, &adapters.RequestData{ ImpIndex: i, Method: `GET`, Uri: uri, - Headers: bidderMacro.GetHeaders(), + Headers: headers, }) } diff --git a/adapters/vastbidder/tagbidder_test.go b/adapters/vastbidder/tagbidder_test.go new file mode 100644 index 00000000000..8b19c15f2b3 --- /dev/null +++ b/adapters/vastbidder/tagbidder_test.go @@ -0,0 +1,148 @@ +package vastbidder + +import ( + "net/http" + "testing" + + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" +) + +//TestMakeRequests verifies +// 1. default and custom headers are set +func TestMakeRequests(t *testing.T) { + + type args struct { + customHeaders map[string]string + req *openrtb.BidRequest + } + type want struct { + impIDReqHeaderMap map[string]http.Header + } + tests := []struct { + name string + args args + want want + }{ + { + name: "multi_impression_req", + args: args{ + customHeaders: map[string]string{ + "my-custom-header": "custom-value", + }, + req: &openrtb.BidRequest{ + Device: &openrtb.Device{ + IP: "1.1.1.1", + UA: "user-agent", + Language: "en", + }, + Site: &openrtb.Site{ + Page: "http://test.com/", + }, + Imp: []openrtb.Imp{ + { // vast 2.0 + ID: "vast_2_0_imp_req", + Video: &openrtb.Video{ + Protocols: []openrtb.Protocol{ + openrtb.ProtocolVAST20, + }, + }, + Ext: []byte(`{"bidder" :{}}`), + }, + { + ID: "vast_4_0_imp_req", + Video: &openrtb.Video{ // vast 4.0 + Protocols: []openrtb.Protocol{ + openrtb.ProtocolVAST40, + }, + }, + Ext: []byte(`{"bidder" :{}}`), + }, + { + ID: "vast_2_0_4_0_wrapper_imp_req", + Video: &openrtb.Video{ // vast 2 and 4.0 wrapper + Protocols: []openrtb.Protocol{ + openrtb.ProtocolVAST40Wrapper, + openrtb.ProtocolVAST20, + }, + }, + Ext: []byte(`{"bidder" :{}}`), + }, + { + ID: "other_non_vast_protocol", + Video: &openrtb.Video{ // DAAST 1.0 + Protocols: []openrtb.Protocol{ + openrtb.ProtocolDAAST10, + }, + }, + Ext: []byte(`{"bidder" :{}}`), + }, + { + ID: "no_protocol_field_set", + Video: &openrtb.Video{ // vast 2 and 4.0 wrapper + Protocols: []openrtb.Protocol{}, + }, + Ext: []byte(`{"bidder" :{}}`), + }, + }, + }, + }, + want: want{ + impIDReqHeaderMap: map[string]http.Header{ + "vast_2_0_imp_req": { + "X-Forwarded-For": []string{"1.1.1.1"}, + "User-Agent": []string{"user-agent"}, + "My-Custom-Header": []string{"custom-value"}, + }, + "vast_4_0_imp_req": { + "X-Device-Ip": []string{"1.1.1.1"}, + "X-Device-User-Agent": []string{"user-agent"}, + "X-Device-Referer": []string{"http://test.com/"}, + "X-Device-Accept-Language": []string{"en"}, + "My-Custom-Header": []string{"custom-value"}, + }, + "vast_2_0_4_0_wrapper_imp_req": { + "X-Device-Ip": []string{"1.1.1.1"}, + "X-Forwarded-For": []string{"1.1.1.1"}, + "X-Device-User-Agent": []string{"user-agent"}, + "User-Agent": []string{"user-agent"}, + "X-Device-Referer": []string{"http://test.com/"}, + "X-Device-Accept-Language": []string{"en"}, + "My-Custom-Header": []string{"custom-value"}, + }, + "other_non_vast_protocol": { + "My-Custom-Header": []string{"custom-value"}, + }, // no default headers expected + "no_protocol_field_set": { // set all default headers + "X-Device-Ip": []string{"1.1.1.1"}, + "X-Forwarded-For": []string{"1.1.1.1"}, + "X-Device-User-Agent": []string{"user-agent"}, + "User-Agent": []string{"user-agent"}, + "X-Device-Referer": []string{"http://test.com/"}, + "X-Device-Accept-Language": []string{"en"}, + "My-Custom-Header": []string{"custom-value"}, + }, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + bidderName := openrtb_ext.BidderName("myVastBidderMacro") + RegisterNewBidderMacro(bidderName, func() IBidderMacro { + return newMyVastBidderMacro(tt.args.customHeaders) + }) + bidder := NewTagBidder(bidderName, config.Adapter{}) + reqData, err := bidder.MakeRequests(tt.args.req, nil) + assert.Nil(t, err) + for _, req := range reqData { + impID := tt.args.req.Imp[req.ImpIndex].ID + expectedHeaders := tt.want.impIDReqHeaderMap[impID] + assert.Equal(t, expectedHeaders, req.Headers, "test for - "+impID) + } + }) + } +} diff --git a/exchange/bidder.go b/exchange/bidder.go index e5939f018ed..c0cbb7f3431 100644 --- a/exchange/bidder.go +++ b/exchange/bidder.go @@ -308,17 +308,19 @@ func getAssetByID(id int64, assets []nativeRequests.Asset) (nativeRequests.Asset func makeExt(httpInfo *httpCallInfo) *openrtb_ext.ExtHttpCall { if httpInfo.err == nil { return &openrtb_ext.ExtHttpCall{ - Uri: httpInfo.request.Uri, - RequestBody: string(httpInfo.request.Body), - ResponseBody: string(httpInfo.response.Body), - Status: httpInfo.response.StatusCode, + Uri: httpInfo.request.Uri, + RequestBody: string(httpInfo.request.Body), + ResponseBody: string(httpInfo.response.Body), + Status: httpInfo.response.StatusCode, + RequestHeaders: httpInfo.request.Headers, } } else if httpInfo.request == nil { return &openrtb_ext.ExtHttpCall{} } else { return &openrtb_ext.ExtHttpCall{ - Uri: httpInfo.request.Uri, - RequestBody: string(httpInfo.request.Body), + Uri: httpInfo.request.Uri, + RequestBody: string(httpInfo.request.Body), + RequestHeaders: httpInfo.request.Headers, } } } diff --git a/exchange/bidder_test.go b/exchange/bidder_test.go index 9e27bc41477..2e32801f76f 100644 --- a/exchange/bidder_test.go +++ b/exchange/bidder_test.go @@ -886,6 +886,9 @@ func TestBadRequestLogging(t *testing.T) { if ext.Status != 0 { t.Errorf("The Status code should be 0. Got %d", ext.Status) } + if nil != ext.RequestHeaders || len(ext.RequestHeaders) > 0 { + t.Errorf("The request headers should be empty. Got %s", ext.RequestHeaders) + } } // TestBadResponseLogging makes sure that openrtb_ext works properly if we don't get a sensible HTTP response. @@ -894,6 +897,9 @@ func TestBadResponseLogging(t *testing.T) { request: &adapters.RequestData{ Uri: "test.com", Body: []byte("request body"), + Headers: http.Header{ + "header-1": []string{"value-1"}, + }, }, err: errors.New("Bad response"), } @@ -910,6 +916,7 @@ func TestBadResponseLogging(t *testing.T) { if ext.Status != 0 { t.Errorf("The Status code should be 0. Got %d", ext.Status) } + assert.Equal(t, info.request.Headers, http.Header(ext.RequestHeaders), "The request headers should be \"header-1:value-1\"") } // TestSuccessfulResponseLogging makes sure that openrtb_ext works properly if the HTTP request is successful. @@ -918,6 +925,9 @@ func TestSuccessfulResponseLogging(t *testing.T) { request: &adapters.RequestData{ Uri: "test.com", Body: []byte("request body"), + Headers: http.Header{ + "header-1": []string{"value-1", "value-2"}, + }, }, response: &adapters.ResponseData{ StatusCode: 200, @@ -937,6 +947,7 @@ func TestSuccessfulResponseLogging(t *testing.T) { if ext.Status != info.response.StatusCode { t.Errorf("The Status code should be 0. Got %d", ext.Status) } + assert.Equal(t, info.request.Headers, http.Header(ext.RequestHeaders), "The request headers should be \"%s\". Got %s", info.request.Headers, ext.RequestHeaders) } func TestMobileNativeTypes(t *testing.T) { diff --git a/exchange/exchangetest/request-multi-bidders-debug-info.json b/exchange/exchangetest/request-multi-bidders-debug-info.json index ec174f75b36..f6950497fb3 100644 --- a/exchange/exchangetest/request-multi-bidders-debug-info.json +++ b/exchange/exchangetest/request-multi-bidders-debug-info.json @@ -66,7 +66,8 @@ "uri": "appnexusTest.com", "requestbody": "appnexusTestRequestBody", "responsebody": "appnexusTestResponseBody", - "status": 200 + "status": 200, + "requestheaders": null } ], "pbsSeatBid": { @@ -95,7 +96,8 @@ "uri": "audienceNetworkTest.com", "requestbody": "audienceNetworkTestRequestBody", "responsebody": "audienceNetworkTestResponseBody", - "status": 200 + "status": 200, + "requestheaders": null } ] } @@ -150,7 +152,8 @@ "uri": "appnexusTest.com", "requestbody": "appnexusTestRequestBody", "responsebody": "appnexusTestResponseBody", - "status": 200 + "status": 200, + "requestheaders": null } ], "audienceNetwork": [ @@ -158,7 +161,8 @@ "uri": "audienceNetworkTest.com", "requestbody": "audienceNetworkTestRequestBody", "responsebody": "audienceNetworkTestResponseBody", - "status": 200 + "status": 200, + "requestheaders": null } ] }, diff --git a/openrtb_ext/response.go b/openrtb_ext/response.go index ec0d2821c4e..eca3ec29088 100644 --- a/openrtb_ext/response.go +++ b/openrtb_ext/response.go @@ -48,10 +48,11 @@ type ExtBidderError struct { // ExtHttpCall defines the contract for a bidresponse.ext.debug.httpcalls.{bidder}[i] type ExtHttpCall struct { - Uri string `json:"uri"` - RequestBody string `json:"requestbody"` - ResponseBody string `json:"responsebody"` - Status int `json:"status"` + Uri string `json:"uri"` + RequestBody string `json:"requestbody"` + ResponseBody string `json:"responsebody"` + Status int `json:"status"` + RequestHeaders map[string][]string `json:"requestheaders"` } // CookieStatus describes the allowed values for bidresponse.ext.usersync.{bidder}.status