Skip to content

Commit d7866e8

Browse files
committed
Templatize synthetic
1 parent adfbb16 commit d7866e8

File tree

8 files changed

+164
-34
lines changed

8 files changed

+164
-34
lines changed

Cargo.lock

Lines changed: 110 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,4 @@ futures = "0.3"
2626
tokio = { version = "1.0", features = ["sync", "macros", "io-util", "rt", "time"] }
2727
url = "2.4.1"
2828
config = "0.15.11"
29+
handlebars = "6.3.2"

potsi.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,11 @@ server_url = "http://68.183.113.79:8000/openrtb2/auction"
99
counter_store = "jevans_synth_id_counter"
1010
opid_store = "jevans_synth_id_opid"
1111
secret_key = "potsi"
12+
# Possible values
13+
# - "client_ip"
14+
# - "user_agent"
15+
# - "first_party_id"
16+
# - "auth_user_id"
17+
# - "publisher_domain"
18+
# - "accept_language"
19+
template = "{{ client_ip }}:{{ user_agent }}:{{ first_party_id }}:{{ auth_user_id }}:{{ publisher_domain }}:{{ accept_language }}"

src/constants.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1-
pub const SYNTH_HEADER_FRESH: &str = "X-Synthetic-Fresh";
2-
pub const SYNTH_HEADER_POTSI: &str = "X-Synthetic-Potsi";
1+
pub const SYNTHETIC_HEADER_FRESH: &str = "X-Synthetic-Fresh";
2+
pub const SYNTHETIC_HEADER_POTSI: &str = "X-Synthetic-Potsi";
3+
pub const SYNTHETIC_HEADER_PUB_USER_ID: &str = "X-Pub-User-ID";

src/main.rs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::env;
77

88
mod constants;
99
mod cookies;
10-
use constants::{SYNTH_HEADER_FRESH, SYNTH_HEADER_POTSI};
10+
use constants::{SYNTHETIC_HEADER_FRESH, SYNTHETIC_HEADER_POTSI};
1111
mod models;
1212
use models::AdResponse;
1313
mod prebid;
@@ -60,7 +60,7 @@ fn handle_main_page(settings: &Settings, req: Request) -> Result<Response, Error
6060

6161
println!(
6262
"Existing POTSI header: {:?}",
63-
req.get_header(SYNTH_HEADER_POTSI)
63+
req.get_header(SYNTHETIC_HEADER_POTSI)
6464
);
6565
println!("Generated Fresh ID: {}", fresh_id);
6666
println!("Using POTSI ID: {}", synthetic_id);
@@ -69,8 +69,8 @@ fn handle_main_page(settings: &Settings, req: Request) -> Result<Response, Error
6969
let mut response = Response::from_status(StatusCode::OK)
7070
.with_body(HTML_TEMPLATE)
7171
.with_header(header::CONTENT_TYPE, "text/html")
72-
.with_header(SYNTH_HEADER_FRESH, &fresh_id) // Fresh ID always changes
73-
.with_header(SYNTH_HEADER_POTSI, &synthetic_id); // POTSI ID remains stable
72+
.with_header(SYNTHETIC_HEADER_FRESH, &fresh_id) // Fresh ID always changes
73+
.with_header(SYNTHETIC_HEADER_POTSI, &synthetic_id); // POTSI ID remains stable
7474

7575
// Always set the cookie with the synthetic ID
7676
response.set_header(
@@ -163,7 +163,10 @@ fn handle_ad_request(settings: &Settings, req: Request) -> Result<Response, Erro
163163
println!("Synthetic ID {} visit count: {}", synthetic_id, new_count);
164164

165165
// Construct URL with synthetic ID
166-
let ad_server_url = settings.ad_server.sync_url.replace("{{synthetic_id}}", &synthetic_id);
166+
let ad_server_url = settings
167+
.ad_server
168+
.sync_url
169+
.replace("{{synthetic_id}}", &synthetic_id);
167170

168171
println!("Sending request to backend: {}", ad_server_url);
169172

@@ -309,14 +312,14 @@ async fn handle_prebid_test(settings: &Settings, mut req: Request) -> Result<Res
309312

310313
println!(
311314
"Existing POTSI header: {:?}",
312-
req.get_header(SYNTH_HEADER_POTSI)
315+
req.get_header(SYNTHETIC_HEADER_POTSI)
313316
);
314317
println!("Generated Fresh ID: {}", fresh_id);
315318
println!("Using POTSI ID: {}", synthetic_id);
316319

317320
// Set both IDs as headers
318-
req.set_header(SYNTH_HEADER_FRESH, &fresh_id);
319-
req.set_header(SYNTH_HEADER_POTSI, &synthetic_id);
321+
req.set_header(SYNTHETIC_HEADER_FRESH, &fresh_id);
322+
req.set_header(SYNTHETIC_HEADER_POTSI, &synthetic_id);
320323

321324
println!("Using POTSI ID: {}, Fresh ID: {}", synthetic_id, fresh_id);
322325

@@ -341,7 +344,7 @@ async fn handle_prebid_test(settings: &Settings, mut req: Request) -> Result<Res
341344

342345
println!("Attempting to send bid request to Prebid Server at prebid_backend");
343346

344-
match prebid_req.send_bid_request(&req).await {
347+
match prebid_req.send_bid_request(settings, &req).await {
345348
// Pass the original request
346349
Ok(mut prebid_response) => {
347350
println!("Received response from Prebid Server");

src/prebid.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use fastly::{Error, Request, Response};
33
use serde_json::json;
44
use url;
55

6-
use crate::constants::{SYNTH_HEADER_FRESH, SYNTH_HEADER_POTSI};
6+
use crate::constants::{SYNTHETIC_HEADER_FRESH, SYNTHETIC_HEADER_POTSI};
77
use crate::settings::Settings;
88
use crate::synthetic::generate_synthetic_id;
99

@@ -32,7 +32,7 @@ impl PrebidRequest {
3232
pub fn new(settings: &Settings, req: &Request) -> Result<Self, Error> {
3333
// Get the POTSI ID from header (which we just set in handle_prebid_test)
3434
let synthetic_id = req
35-
.get_header(SYNTH_HEADER_POTSI)
35+
.get_header(SYNTHETIC_HEADER_POTSI)
3636
.and_then(|h| h.to_str().ok())
3737
.map(|s| s.to_string())
3838
.unwrap_or_else(|| generate_synthetic_id(settings, req));
@@ -90,12 +90,12 @@ impl PrebidRequest {
9090
///
9191
/// # Returns
9292
/// * `Result<Response, Error>` - Prebid Server response or error
93-
pub async fn send_bid_request(&self, incoming_req: &Request) -> Result<Response, Error> {
94-
let mut req = Request::new(Method::POST, "http://68.183.113.79:8000/openrtb2/auction");
93+
pub async fn send_bid_request(&self, settings: &Settings, incoming_req: &Request) -> Result<Response, Error> {
94+
let mut req = Request::new(Method::POST, settings.prebid.server_url.to_owned());
9595

9696
// Get and store the POTSI ID value from the incoming request
9797
let potsi_id = incoming_req
98-
.get_header(SYNTH_HEADER_POTSI)
98+
.get_header(SYNTHETIC_HEADER_POTSI)
9999
.and_then(|h| h.to_str().ok())
100100
.map(|s| s.to_string())
101101
.unwrap_or_else(|| self.synthetic_id.clone());
@@ -166,8 +166,8 @@ impl PrebidRequest {
166166
req.set_header(header::CONTENT_TYPE, "application/json");
167167
req.set_header("X-Forwarded-For", &self.client_ip);
168168
req.set_header(header::ORIGIN, &self.origin);
169-
req.set_header(SYNTH_HEADER_FRESH, &self.synthetic_id);
170-
req.set_header(SYNTH_HEADER_POTSI, &potsi_id);
169+
req.set_header(SYNTHETIC_HEADER_FRESH, &self.synthetic_id);
170+
req.set_header(SYNTHETIC_HEADER_POTSI, &potsi_id);
171171

172172
println!(
173173
"Sending prebid request with Fresh ID: {} and POTSI ID: {}",

src/settings.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ pub struct Synthetic {
2121
pub counter_store: String,
2222
pub opid_store: String,
2323
pub secret_key: String,
24+
pub template: String,
2425
}
2526

2627
#[derive(Debug, Deserialize)]

src/synthetic.rs

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ use fastly::Request;
33
use hmac::{Hmac, Mac};
44
use log;
55
use sha2::Sha256;
6+
use handlebars::Handlebars;
7+
use serde_json::json;
68

7-
use crate::constants::SYNTH_HEADER_POTSI;
9+
use crate::constants::{SYNTHETIC_HEADER_POTSI, SYNTHETIC_HEADER_PUB_USER_ID};
810
use crate::cookies::handle_request_cookies;
911
use crate::settings::Settings;
1012

@@ -14,34 +16,36 @@ type HmacSha256 = Hmac<Sha256>;
1416
pub fn generate_synthetic_id(settings: &Settings, req: &Request) -> String {
1517
let user_agent = req
1618
.get_header(header::USER_AGENT)
17-
.map(|h| h.to_str().unwrap_or("Unknown"));
19+
.map(|h| h.to_str().unwrap_or("unknown"));
1820
let first_party_id = handle_request_cookies(req).and_then(|jar| {
1921
jar.get("pub_userid")
2022
.map(|cookie| cookie.value().to_string())
2123
});
2224
let auth_user_id = req
23-
.get_header("X-Pub-User-ID")
25+
.get_header(SYNTHETIC_HEADER_PUB_USER_ID)
2426
.map(|h| h.to_str().unwrap_or("anonymous"));
2527
let publisher_domain = req
2628
.get_header(header::HOST)
27-
.map(|h| h.to_str().unwrap_or("unknown.com"));
29+
.map(|h| h.to_str().unwrap_or("unknown"));
2830
let client_ip = req.get_client_ip_addr().map(|ip| ip.to_string());
2931
let accept_language = req
3032
.get_header(header::ACCEPT_LANGUAGE)
3133
.and_then(|h| h.to_str().ok())
3234
.map(|lang| lang.split(',').next().unwrap_or("unknown"));
3335

34-
let input_string = format!(
35-
"{}:{}:{}:{}:{}:{}",
36-
client_ip.unwrap_or("unknown".to_string()),
37-
user_agent.unwrap_or("unknown"),
38-
first_party_id.unwrap_or("anonymous".to_string()),
39-
auth_user_id.unwrap_or("anonymous"),
40-
publisher_domain.unwrap_or("unknown.com"),
41-
accept_language.unwrap_or("unknown")
42-
);
36+
let handlebars = Handlebars::new();
37+
let data = &json!({
38+
"client_ip": client_ip.unwrap_or("unknown".to_string()),
39+
"user_agent": user_agent.unwrap_or("unknown"),
40+
"first_party_id": first_party_id.unwrap_or("anonymous".to_string()),
41+
"auth_user_id": auth_user_id.unwrap_or("anonymous"),
42+
"publisher_domain": publisher_domain.unwrap_or("unknown.com"),
43+
"accept_language": accept_language.unwrap_or("unknown")
44+
});
45+
46+
let input_string = handlebars.render_template(&settings.synthetic.template, data).unwrap();
47+
println!("Input string for fresh ID: {} {}", input_string, data);
4348

44-
log::info!("Input string for fresh ID: {}", input_string);
4549

4650
let mut mac = HmacSha256::new_from_slice(settings.synthetic.secret_key.as_bytes())
4751
.expect("HMAC can take key of any size");
@@ -57,7 +61,7 @@ pub fn generate_synthetic_id(settings: &Settings, req: &Request) -> String {
5761
pub fn get_or_generate_synthetic_id(settings: &Settings, req: &Request) -> String {
5862
// First try to get existing POTSI ID from header
5963
if let Some(potsi) = req
60-
.get_header(SYNTH_HEADER_POTSI)
64+
.get_header(SYNTHETIC_HEADER_POTSI)
6165
.and_then(|h| h.to_str().ok())
6266
.map(|s| s.to_string())
6367
{
@@ -113,6 +117,7 @@ mod tests {
113117
counter_store: "https://example.com".to_string(),
114118
opid_store: "https://example.com".to_string(),
115119
secret_key: "secret_key".to_string(),
120+
template: "{{ client_ip }}:{{ user_agent }}:{{ first_party_id }}:{{ auth_user_id }}:{{ publisher_domain }}:{{ accept_language }}".to_string(),
116121
},
117122
}
118123
}
@@ -129,6 +134,7 @@ mod tests {
129134
]);
130135

131136
let synthetic_id = generate_synthetic_id(&settings, &req);
137+
print!("Generated synthetic ID: {}", synthetic_id);
132138
assert_eq!(
133139
synthetic_id,
134140
"07cd73bb8c7db39753ab6b10198b10c3237a3f5a6d2232c6ce578f2c2a623e56"
@@ -138,7 +144,7 @@ mod tests {
138144
#[test]
139145
fn test_get_or_generate_synthetic_id_with_header() {
140146
let settings = create_settings();
141-
let req = create_test_request(vec![(SYNTH_HEADER_POTSI, "existing_potsi_id")]);
147+
let req = create_test_request(vec![(SYNTHETIC_HEADER_POTSI, "existing_potsi_id")]);
142148

143149
let synthetic_id = get_or_generate_synthetic_id(&settings, &req);
144150
assert_eq!(synthetic_id, "existing_potsi_id");

0 commit comments

Comments
 (0)