diff --git a/modules/prebidServerBidAdapter.js b/modules/prebidServerBidAdapter.js index 06e5cff6503..d60cf5e1025 100644 --- a/modules/prebidServerBidAdapter.js +++ b/modules/prebidServerBidAdapter.js @@ -325,6 +325,7 @@ const LEGACY_PROTOCOL = { interpretResponse(result, bidRequests, requestedBidders) { const bids = []; + let responseTimes = {}; if (result.status === 'OK' || result.status === 'no_cookie') { if (result.bidder_status) { @@ -335,6 +336,8 @@ const LEGACY_PROTOCOL = { if (bidder.error) { utils.logWarn(`Prebid Server returned error: '${bidder.error}' for ${bidder.bidder}`); } + + responseTimes[bidder.bidder] = bidder.response_time_ms; }); } @@ -357,6 +360,9 @@ const LEGACY_PROTOCOL = { bidObject.creative_id = bidObj.creative_id; bidObject.bidderCode = bidObj.bidder; bidObject.cpm = cpm; + if (responseTimes[bidObj.bidder]) { + bidObject.serverResponseTimeMs = responseTimes[bidObj.bidder]; + } if (bidObj.cache_id) { bidObject.cache_id = bidObj.cache_id; } @@ -523,6 +529,11 @@ const OPEN_RTB_PROTOCOL = { bidObject.bidderCode = seatbid.seat; bidObject.cpm = cpm; + let serverResponseTimeMs = utils.deepAccess(response, ['ext', 'responsetimemillis', seatbid.seat].join('.')); + if (serverResponseTimeMs) { + bidObject.serverResponseTimeMs = serverResponseTimeMs; + } + if (utils.deepAccess(bid, 'ext.prebid.type') === VIDEO) { bidObject.mediaType = VIDEO; if (bid.adm) { bidObject.vastXml = bid.adm; } diff --git a/modules/rubiconAnalyticsAdapter.js b/modules/rubiconAnalyticsAdapter.js index 32d1a79af5c..dd111efe03c 100644 --- a/modules/rubiconAnalyticsAdapter.js +++ b/modules/rubiconAnalyticsAdapter.js @@ -111,6 +111,7 @@ function sendMessage(auctionId, bidWonId) { ? 'server' : 'client' }, 'clientLatencyMillis', + 'serverLatencyMillis', 'params', 'bidResponse', bidResponse => bidResponse ? _pick(bidResponse, [ 'bidPriceUSD', @@ -386,6 +387,9 @@ let rubiconAdapter = Object.assign({}, baseAdapter, { }; } bid.clientLatencyMillis = Date.now() - cache.auctions[args.auctionId].timestamp; + if (typeof args.serverResponseTimeMs !== 'undefined') { + bid.serverLatencyMillis = args.serverResponseTimeMs; + } bid.bidResponse = parseBidResponse(args); break; case BIDDER_DONE: diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index d525db857bc..427083beb17 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -595,6 +595,7 @@ describe('S2S Adapter', () => { expect(response).to.have.property('cache_id', '7654321'); expect(response).to.have.property('cache_url', 'http://www.test.com/cache?uuid=7654321'); expect(response).to.not.have.property('vastUrl'); + expect(response).to.have.property('serverResponseTimeMs', 52); }); it('registers video bids', () => { @@ -787,6 +788,7 @@ describe('S2S Adapter', () => { expect(response).to.have.property('bidderCode', 'appnexus'); expect(response).to.have.property('adId', '123'); expect(response).to.have.property('cpm', 0.5); + expect(response).to.have.property('serverResponseTimeMs', 8); }); it('handles OpenRTB video responses', () => { @@ -807,6 +809,7 @@ describe('S2S Adapter', () => { expect(response).to.have.property('bidderCode', 'appnexus'); expect(response).to.have.property('adId', '123'); expect(response).to.have.property('cpm', 10); + expect(response).to.have.property('serverResponseTimeMs', 81); }); it('should log warning for unsupported bidder', () => { diff --git a/test/spec/modules/rubiconAnalyticsAdapter_spec.js b/test/spec/modules/rubiconAnalyticsAdapter_spec.js index d8f0811e81c..38f8b726cd1 100644 --- a/test/spec/modules/rubiconAnalyticsAdapter_spec.js +++ b/test/spec/modules/rubiconAnalyticsAdapter_spec.js @@ -84,6 +84,8 @@ const BID2 = Object.assign({}, BID, { height: 90, mediaType: 'banner', cpm: 1.52, + source: 'server', + serverResponseTimeMs: 42, rubiconTargeting: { 'rpfl_elemid': '/19968336/header-bid-tag1', 'rpfl_14062': '2_tier0100' @@ -93,7 +95,7 @@ const BID2 = Object.assign({}, BID, { 'hb_adid': '3bd4ebb1c900e2', 'hb_pb': '1.500', 'hb_size': '728x90', - 'hb_source': 'client' + 'hb_source': 'server' } }); @@ -103,7 +105,7 @@ const MOCK = { [BID2.adUnitCode]: BID2.adserverTargeting }, AUCTION_INIT: { - 'timestamp': 1519149536560, + 'timestamp': 1519767010567, 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', 'timeout': 3000 }, @@ -237,7 +239,7 @@ const ANALYTICS_MESSAGE = { 'bidId': '2ecff0db240757', 'status': 'success', 'source': 'client', - 'clientLatencyMillis': 617477221, + 'clientLatencyMillis': 3214, 'params': { 'accountId': '14062', 'siteId': '70608', @@ -280,15 +282,16 @@ const ANALYTICS_MESSAGE = { 'hb_adid': '3bd4ebb1c900e2', 'hb_pb': '1.500', 'hb_size': '728x90', - 'hb_source': 'client' + 'hb_source': 'server' }, 'bids': [ { 'bidder': 'rubicon', 'bidId': '3bd4ebb1c900e2', 'status': 'success', - 'source': 'client', - 'clientLatencyMillis': 617477221, + 'source': 'server', + 'clientLatencyMillis': 3214, + 'serverLatencyMillis': 42, 'params': { 'accountId': '14062', 'siteId': '70608', @@ -316,7 +319,7 @@ const ANALYTICS_MESSAGE = { 'bidId': '2ecff0db240757', 'status': 'success', 'source': 'client', - 'clientLatencyMillis': 617477221, + 'clientLatencyMillis': 3214, 'samplingFactor': 1, 'accountId': 1001, 'params': { @@ -351,8 +354,9 @@ const ANALYTICS_MESSAGE = { 'adUnitCode': '/19968336/header-bid-tag1', 'bidId': '3bd4ebb1c900e2', 'status': 'success', - 'source': 'client', - 'clientLatencyMillis': 617477221, + 'source': 'server', + 'clientLatencyMillis': 3214, + 'serverLatencyMillis': 42, 'samplingFactor': 1, 'accountId': 1001, 'params': { @@ -368,7 +372,7 @@ const ANALYTICS_MESSAGE = { 'hb_adid': '3bd4ebb1c900e2', 'hb_pb': '1.500', 'hb_size': '728x90', - 'hb_source': 'client' + 'hb_source': 'server' }, 'bidResponse': { 'bidPriceUSD': 1.52,