Skip to content

Commit 76e6f3c

Browse files
Igor Macedo QuintanilhaIgor Macedo Quintanilha
authored andcommitted
fix: use posthog-java/server User-Agent for server-side runtime detection
Workaround: PostHog's runtime detection uses User-Agent patterns to determine if requests come from server-side or client-side SDKs. The pattern "posthog-server/" is not recognized, so we use "posthog-java/server/" instead until PostHog adds support for "posthog-server/" in their detection patterns. Also adds $lib and $lib_version to flags requests for SDK identification, and makes userAgent configurable in PostHogConfig.
1 parent 0a8beeb commit 76e6f3c

5 files changed

Lines changed: 44 additions & 12 deletions

File tree

posthog-server/src/main/java/com/posthog/server/PostHogConfig.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,9 @@ public open class PostHogConfig constructor(
194194
// Set SDK identification
195195
coreConfig.sdkName = BuildConfig.SDK_NAME
196196
coreConfig.sdkVersion = BuildConfig.VERSION_NAME
197+
// Workaround: Use "posthog-java/server" for User-Agent for server-side runtime detection
198+
// until PostHog supports "posthog-server/" in their runtime detection patterns.
199+
coreConfig.userAgent = "posthog-java/server/${BuildConfig.VERSION_NAME}"
197200
coreConfig.context = PostHogServerContext(coreConfig)
198201

199202
return coreConfig

posthog/src/main/java/com/posthog/PostHogConfig.kt

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -277,10 +277,13 @@ public open class PostHogConfig(
277277
@PostHogInternal
278278
public var sdkVersion: String = BuildConfig.VERSION_NAME
279279

280-
internal val userAgent: String
281-
get() {
282-
return "$sdkName/$sdkVersion"
283-
}
280+
// Can be overridden by SDK modules for custom User-Agent header
281+
@PostHogInternal
282+
public var userAgent: String? = null
283+
284+
internal fun getUserAgent(): String {
285+
return userAgent ?: "$sdkName/$sdkVersion"
286+
}
284287

285288
@PostHogInternal
286289
public var legacyStoragePrefix: String? = null

posthog/src/main/java/com/posthog/internal/PostHogApi.kt

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ public class PostHogApi(
113113

114114
return Request.Builder()
115115
.url(url)
116-
.header("User-Agent", config.userAgent)
116+
.header("User-Agent", config.getUserAgent())
117117
.post(requestBody)
118118
.build()
119119
}
@@ -135,6 +135,8 @@ public class PostHogApi(
135135
personProperties,
136136
groupProperties,
137137
config.evaluationContexts,
138+
lib = config.sdkName,
139+
libVersion = config.sdkVersion,
138140
)
139141

140142
val url = "$theHost/flags/?v=2&config=true"
@@ -178,7 +180,7 @@ public class PostHogApi(
178180
val request =
179181
Request.Builder()
180182
.url("$host/array/${config.apiKey}/config")
181-
.header("User-Agent", config.userAgent)
183+
.header("User-Agent", config.getUserAgent())
182184
.header("Content-Type", APP_JSON_UTF_8)
183185
.get()
184186
.build()
@@ -220,7 +222,7 @@ public class PostHogApi(
220222
val requestBuilder =
221223
Request.Builder()
222224
.url(url)
223-
.header("User-Agent", config.userAgent)
225+
.header("User-Agent", config.getUserAgent())
224226
.header("Content-Type", APP_JSON_UTF_8)
225227
.header("Authorization", "Bearer $personalApiKey")
226228

@@ -266,6 +268,16 @@ public class PostHogApi(
266268
private fun logResponse(response: Response): Response {
267269
if (config.debug) {
268270
try {
271+
// Log response headers
272+
val responseHeaders = response.headers
273+
val responseHeaderStrings = responseHeaders.names().map { name -> "$name: ${responseHeaders[name]}" }
274+
config.logger.log("Response headers for ${response.request.url}: ${responseHeaderStrings.joinToString(", ")}")
275+
276+
// Log the final request headers (after interceptors like gzip)
277+
val finalRequestHeaders = response.request.headers
278+
val finalRequestHeaderStrings = finalRequestHeaders.names().map { name -> "$name: ${finalRequestHeaders[name]}" }
279+
config.logger.log("Final request headers for ${response.request.url}: ${finalRequestHeaderStrings.joinToString(", ")}")
280+
269281
val responseBody = response.body ?: return response
270282
val mediaType = responseBody.contentType()
271283
val content =

posthog/src/main/java/com/posthog/internal/PostHogFlagsRequest.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,18 @@ internal class PostHogFlagsRequest(
1111
personProperties: Map<String, Any?>? = null,
1212
groupProperties: Map<String, Map<String, Any?>>? = null,
1313
evaluationContexts: List<String>? = null,
14+
lib: String? = null,
15+
libVersion: String? = null,
1416
) : HashMap<String, Any>() {
1517
init {
1618
this["api_key"] = apiKey
1719
this["distinct_id"] = distinctId
20+
if (!lib.isNullOrBlank()) {
21+
this["\$lib"] = lib
22+
}
23+
if (!libVersion.isNullOrBlank()) {
24+
this["\$lib_version"] = libVersion
25+
}
1826
if (!anonymousId.isNullOrBlank()) {
1927
this["\$anon_distinct_id"] = anonymousId
2028
}

posthog/src/test/java/com/posthog/PostHogConfigTest.kt

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,14 +86,20 @@ internal class PostHogConfigTest {
8686
}
8787

8888
@Test
89-
fun `user agent is returned correctly if changed`() {
90-
config.sdkName = "posthog-android"
91-
assertEquals("posthog-android/${BuildConfig.VERSION_NAME}", config.userAgent)
89+
fun `user agent is returned correctly if overridden`() {
90+
config.userAgent = "posthog-android/1.0.0"
91+
assertEquals("posthog-android/1.0.0", config.getUserAgent())
9292
}
9393

9494
@Test
95-
fun `user agent is set the java sdk by default`() {
96-
assertEquals("posthog-java/${BuildConfig.VERSION_NAME}", config.userAgent)
95+
fun `user agent falls back to sdkName and sdkVersion by default`() {
96+
assertEquals("posthog-java/${BuildConfig.VERSION_NAME}", config.getUserAgent())
97+
}
98+
99+
@Test
100+
fun `user agent falls back to sdkName when userAgent is null`() {
101+
config.sdkName = "posthog-android"
102+
assertEquals("posthog-android/${BuildConfig.VERSION_NAME}", config.getUserAgent())
97103
}
98104

99105
@Test

0 commit comments

Comments
 (0)