-
Notifications
You must be signed in to change notification settings - Fork 2.1k
/
videoCache.js
158 lines (142 loc) · 5.02 KB
/
videoCache.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/**
* This module interacts with the server used to cache video ad content to be restored later.
* At a high level, the expected workflow goes like this:
*
* - Request video ads from Bidders
* - Generate IDs for each valid bid, and cache the key/value pair on the server.
* - Return these IDs so that publishers can use them to fetch the bids later.
*
* This trickery helps integrate with ad servers, which set character limits on request params.
*/
import {ajaxBuilder} from './ajax.js';
import {config} from './config.js';
import {auctionManager} from './auctionManager.js';
/**
* Might be useful to be configurable in the future
* Depending on publisher needs
*/
const ttlBufferInSeconds = 15;
/**
* @typedef {object} CacheableUrlBid
* @property {string} vastUrl A URL which loads some valid VAST XML.
*/
/**
* @typedef {object} CacheablePayloadBid
* @property {string} vastXml Some VAST XML which loads an ad in a video player.
*/
/**
* A CacheableBid describes the types which the videoCache can store.
*
* @typedef {CacheableUrlBid|CacheablePayloadBid} CacheableBid
*/
/**
* Function which wraps a URI that serves VAST XML, so that it can be loaded.
*
* @param {string} uri The URI where the VAST content can be found.
* @param {string} impUrl An impression tracker URL for the delivery of the video ad
* @return A VAST URL which loads XML from the given URI.
*/
function wrapURI(uri, impUrl) {
// Technically, this is vulnerable to cross-script injection by sketchy vastUrl bids.
// We could make sure it's a valid URI... but since we're loading VAST XML from the
// URL they provide anyway, that's probably not a big deal.
let vastImp = (impUrl) ? `<![CDATA[${impUrl}]]>` : ``;
return `<VAST version="3.0">
<Ad>
<Wrapper>
<AdSystem>prebid.org wrapper</AdSystem>
<VASTAdTagURI><![CDATA[${uri}]]></VASTAdTagURI>
<Impression>${vastImp}</Impression>
<Creatives></Creatives>
</Wrapper>
</Ad>
</VAST>`;
}
/**
* Wraps a bid in the format expected by the prebid-server endpoints, or returns null if
* the bid can't be converted cleanly.
*
* @param {CacheableBid} bid
* @param index
*/
function toStorageRequest(bid, {index = auctionManager.index} = {}) {
const vastValue = bid.vastXml ? bid.vastXml : wrapURI(bid.vastUrl, bid.vastImpUrl);
const auction = index.getAuction(bid);
const ttlWithBuffer = Number(bid.ttl) + ttlBufferInSeconds;
let payload = {
type: 'xml',
value: vastValue,
ttlseconds: ttlWithBuffer
};
if (config.getConfig('cache.vasttrack')) {
payload.bidder = bid.bidder;
payload.bidid = bid.requestId;
payload.aid = bid.auctionId;
}
if (auction != null) {
payload.timestamp = auction.getAuctionStart();
}
if (typeof bid.customCacheKey === 'string' && bid.customCacheKey !== '') {
payload.key = bid.customCacheKey;
}
return payload;
}
/**
* A function which should be called with the results of the storage operation.
*
* @callback videoCacheStoreCallback
*
* @param {Error} [error] The error, if one occurred.
* @param {?string[]} uuids An array of unique IDs. The array will have one element for each bid we were asked
* to store. It may include null elements if some of the bids were malformed, or an error occurred.
* Each non-null element in this array is a valid input into the retrieve function, which will fetch
* some VAST XML which can be used to render this bid's ad.
*/
/**
* A function which bridges the APIs between the videoCacheStoreCallback and our ajax function's API.
*
* @param {videoCacheStoreCallback} done A callback to the "store" function.
* @return {Function} A callback which interprets the cache server's responses, and makes up the right
* arguments for our callback.
*/
function shimStorageCallback(done) {
return {
success: function (responseBody) {
let ids;
try {
ids = JSON.parse(responseBody).responses
} catch (e) {
done(e, []);
return;
}
if (ids) {
done(null, ids);
} else {
done(new Error("The cache server didn't respond with a responses property."), []);
}
},
error: function (statusText, responseBody) {
done(new Error(`Error storing video ad in the cache: ${statusText}: ${JSON.stringify(responseBody)}`), []);
}
}
}
/**
* If the given bid is for a Video ad, generate a unique ID and cache it somewhere server-side.
*
* @param {CacheableBid[]} bids A list of bid objects which should be cached.
* @param {videoCacheStoreCallback} [done] An optional callback which should be executed after
* the data has been stored in the cache.
*/
export function store(bids, done, getAjax = ajaxBuilder) {
const requestData = {
puts: bids.map(toStorageRequest)
};
const ajax = getAjax(config.getConfig('cache.timeout'));
ajax(config.getConfig('cache.url'), shimStorageCallback(done), JSON.stringify(requestData), {
contentType: 'text/plain',
withCredentials: true
});
}
export function getCacheUrl(id) {
return `${config.getConfig('cache.url')}?uuid=${id}`;
}