Skip to content

Commit

Permalink
add configurable session-id-key
Browse files Browse the repository at this point in the history
  • Loading branch information
EddeCCC committed Sep 14, 2023
1 parent 54826e2 commit b19edac
Show file tree
Hide file tree
Showing 9 changed files with 164 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,9 @@ public class HttpExporterSettings {
* How long the data should be stored in the server
*/
private int timeToLive;

/**
* Key, which should be read during browser-propagation to receive the session-ID
*/
private String sessionIdKey;
}
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,5 @@ inspectit:
session-limit: 100
# how long the data should be stored in the server in seconds
time-to-live: 300
# key, which should be read during browser-propagation to receive the session-ID
session-id-key: "Cookie"
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,6 @@ public class BrowserPropagationHttpExporterService extends DynamicallyActivatabl
private BrowserPropagationSessionStorage sessionStorage;
private BrowserPropagationServlet httpServlet;

/**
* Stores a reference of the InspectITConfig to enable runtime updates of the session limit
*/
private InspectitConfig inspectitConfig;

/**
* Delay to rerun the scheduled method after the method finished in milliseconds
*/
Expand Down Expand Up @@ -65,12 +60,14 @@ protected boolean doEnable(InspectitConfig configuration) {
String host = settings.getHost();
int port = settings.getPort();
String path = settings.getPath();
int sessionLimit = settings.getSessionLimit();
timeToLive = settings.getTimeToLive();

int sessionLimit = settings.getSessionLimit();
sessionStorage = BrowserPropagationSessionStorage.getInstance();
sessionStorage.setSessionLimit(sessionLimit);
httpServlet = new BrowserPropagationServlet();
inspectitConfig = configuration;

String sessionIdKey = settings.getSessionIdKey();
httpServlet = new BrowserPropagationServlet(sessionIdKey);

return startServer(host, port, path, httpServlet);
}
Expand Down Expand Up @@ -107,19 +104,13 @@ protected boolean startServer(String host, int port, String path, HttpServlet se
}

/**
* Updates the session storage:
* 1. Browser propagation data is cached for a specific amount of time (timeToLive)
* If the time expires, clean up the storage
* 2. Update the session limit
* Note that this will not delete any active sessions, if the new session limit is exceeded
* Updates the session storage
* Browser propagation data is cached for a specific amount of time (timeToLive)
* If the time expires, clean up the storage
*/
@Scheduled(fixedDelay = FIXED_DELAY)
public void updateSessionStorage() {
if(httpServlet == null) return;
sessionStorage.cleanUpData(timeToLive);

if(inspectitConfig == null) return;
int sessionLimit = inspectitConfig.getExporters().getTags().getHttp().getSessionLimit();
sessionStorage.setSessionLimit(sessionLimit);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,24 @@
@Slf4j
public class BrowserPropagationServlet extends HttpServlet {

/**
* Key, which should be used to store the session-Ids
* Default-key: "Cookie"
*/
private final String sessionIDKey;
private final ObjectMapper mapper;
private final BrowserPropagationSessionStorage sessionStorage;

public BrowserPropagationServlet() {
mapper = new ObjectMapper();
sessionStorage = BrowserPropagationSessionStorage.getInstance();
public BrowserPropagationServlet(String sessionIDKey) {
this.sessionIDKey = sessionIDKey;
this.mapper = new ObjectMapper();
this.sessionStorage = BrowserPropagationSessionStorage.getInstance();
}

@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
log.debug("Tags HTTP-server received GET-request");
String sessionID = request.getHeader("cookie");
String sessionID = request.getHeader(sessionIDKey);
if(sessionID == null) {
log.warn("Request misses session ID");
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
Expand All @@ -63,7 +69,7 @@ public void doGet(HttpServletRequest request, HttpServletResponse response) thro
@Override
protected void doPut(HttpServletRequest request, HttpServletResponse response) {
log.debug("Tags HTTP-server received PUT-request");
String sessionID = request.getHeader("cookie");
String sessionID = request.getHeader(sessionIDKey);
if(sessionID == null) {
log.warn("Request misses session ID");
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package rocks.inspectit.ocelot.core.instrumentation.browser;

import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

Expand All @@ -16,6 +17,7 @@ public class BrowserPropagationSessionStorage {
private static final int KEY_MIN_SIZE = 64;
private static final int KEY_MAX_SIZE = 512;

@Getter
@Setter
private int sessionLimit = 100;
private static BrowserPropagationSessionStorage instance;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
import rocks.inspectit.ocelot.config.model.tracing.PropagationFormat;
import rocks.inspectit.ocelot.core.instrumentation.context.propagation.BrowserPropagationUtil;
import rocks.inspectit.ocelot.core.instrumentation.context.propagation.DatadogFormat;
import rocks.inspectit.ocelot.core.opentelemetry.trace.CustomIdGenerator;

Expand Down Expand Up @@ -47,6 +48,11 @@ public class ContextPropagationUtil {

public static final String CORRELATION_CONTEXT_HEADER = "Correlation-Context";

/**
* Session-ID-key to allow browser propagation
*/
public static String SESSION_ID_KEY = BrowserPropagationUtil.getSessionIdKey();

private static final String B3_HEADER_PREFIX = "X-B3-";

private static final Pattern COMMA_WITH_WHITESPACES = Pattern.compile(" *, *");
Expand Down Expand Up @@ -83,6 +89,7 @@ public String get(Map<String, String> carrier, String key) {

static {
PROPAGATION_FIELDS.add(CORRELATION_CONTEXT_HEADER);
PROPAGATION_FIELDS.add(SESSION_ID_KEY);
PROPAGATION_FIELDS.addAll(B3Propagator.injectingSingleHeader().fields());
PROPAGATION_FIELDS.addAll(B3Propagator.injectingMultiHeaders().fields());
PROPAGATION_FIELDS.addAll(W3CTraceContextPropagator.getInstance().fields());
Expand Down Expand Up @@ -361,4 +368,14 @@ public static void setPropagationFormat(PropagationFormat format) {
propagationFormat = B3Propagator.injectingMultiHeaders();
}
}

/**
* Updates the current session-id-key used for browser propagation
* @param sessionIdKey new session-id-key
*/
public static void setSessionIdKey(String sessionIdKey) {
PROPAGATION_FIELDS.remove(SESSION_ID_KEY);
SESSION_ID_KEY = sessionIdKey;
PROPAGATION_FIELDS.add(SESSION_ID_KEY);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package rocks.inspectit.ocelot.core.instrumentation.context.propagation;

import com.google.common.annotations.VisibleForTesting;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import rocks.inspectit.ocelot.core.config.InspectitConfigChangedEvent;
import rocks.inspectit.ocelot.core.config.InspectitEnvironment;
import rocks.inspectit.ocelot.core.instrumentation.context.ContextPropagationUtil;

import javax.annotation.PostConstruct;

/**
* Class to regulate the currently used session-id-key.
* The session-id-key is used to extract session-ids from http-request-headers to allow browser propagation.
* The session-id-key can change during runtime and needs to updated inside the PROPAGATION_FIELDS in ContextPropagationUtil.
*/
@Slf4j
@Component
public class BrowserPropagationUtil {

@Autowired
private InspectitEnvironment env;
@Getter
private static String sessionIdKey;

@PostConstruct
public void initialize() {
setSessionIdKey(env.getCurrentConfig().getExporters().getTags().getHttp().getSessionIdKey());
}

@EventListener
private void configEventListener(InspectitConfigChangedEvent event) {
String oldSessionIdKey = event.getOldConfig().getExporters().getTags().getHttp().getSessionIdKey();
String newSessionIdKey = event.getNewConfig().getExporters().getTags().getHttp().getSessionIdKey();

if(!oldSessionIdKey.equals(newSessionIdKey)) setSessionIdKey(newSessionIdKey);
}

@VisibleForTesting
void setSessionIdKey(String key) {
sessionIdKey = key;
ContextPropagationUtil.setSessionIdKey(sessionIdKey);
log.info("Use of new session-id-key: " + sessionIdKey);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,13 @@
import rocks.inspectit.ocelot.core.SpringTestBase;
import rocks.inspectit.ocelot.core.instrumentation.browser.BrowserPropagationSessionStorage;

import javax.servlet.http.HttpServlet;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static org.assertj.core.api.Assertions.assertThat;

@TestPropertySource(properties = {"inspectit.exporters.tags.http.time-to-live=8"})
@TestPropertySource(properties = {"inspectit.exporters.tags.http.time-to-live=10"})
@DirtiesContext
public class BrowserPropagationHttpExporterServiceIntTest extends SpringTestBase {

Expand All @@ -39,6 +37,8 @@ public class BrowserPropagationHttpExporterServiceIntTest extends SpringTestBase
private static int port;
private static final String path = "/inspectit";

private static final String sessionIDKey = "Cookie";

@BeforeEach
void prepareTest() throws IOException {
startServer();
Expand All @@ -53,7 +53,7 @@ void clearDataStorage() {

void startServer() throws IOException {
port = Network.getFreeServerPort();
HttpServlet servlet = new BrowserPropagationServlet();
BrowserPropagationServlet servlet = new BrowserPropagationServlet(sessionIDKey);
exporterService.startServer(host, port, path, servlet);
}

Expand All @@ -77,7 +77,7 @@ class GetEndpoint {
void verifyGetEndpoint() throws IOException {
String url = "http://" + host + ":" + port + path;
HttpGet getRequest = new HttpGet(url);
getRequest.setHeader("cookie", sessionID);
getRequest.setHeader(sessionIDKey, sessionID);
CloseableHttpResponse response = testClient.execute(getRequest);

int statusCode = response.getStatusLine().getStatusCode();
Expand Down Expand Up @@ -106,13 +106,25 @@ void verifyGetEndpointWithoutSessionID() throws IOException {
void verifyGetEndpointWithWrongSessionID() throws IOException {
String url = "http://" + host + ":" + port + path;
HttpGet getRequest = new HttpGet(url);
getRequest.setHeader("cookie", "###WrongSessionID###");
getRequest.setHeader(sessionIDKey, "###WrongSessionID###");
CloseableHttpResponse response = testClient.execute(getRequest);

int statusCode = response.getStatusLine().getStatusCode();
assertThat(statusCode).isEqualTo(404);
response.close();
}

@Test
void verifyGetEndpointWithoutCorrectSessionIDKey() throws IOException {
String url = "http://" + host + ":" + port + path;
HttpGet getRequest = new HttpGet(url);
getRequest.setHeader(sessionIDKey + "-1", sessionID);
CloseableHttpResponse response = testClient.execute(getRequest);

int statusCode = response.getStatusLine().getStatusCode();
assertThat(statusCode).isEqualTo(400);
response.close();
}
}

@Nested
Expand All @@ -125,7 +137,7 @@ void verifyPutEndpointWithCorrectData() throws IOException {
String requestBody = "[{\"newKey\":\"newValue\"}]";
StringEntity requestEntity = new StringEntity(requestBody);
putRequest.setEntity(requestEntity);
putRequest.setHeader("cookie", sessionID);
putRequest.setHeader(sessionIDKey, sessionID);

CloseableHttpResponse response = testClient.execute(putRequest);
int statusCode = response.getStatusLine().getStatusCode();
Expand All @@ -142,7 +154,7 @@ void verifyPutEndpointWithIncorrectData() throws IOException {
String requestBody = "##WrongDataFormat##";
StringEntity requestEntity = new StringEntity(requestBody);
putRequest.setEntity(requestEntity);
putRequest.setHeader("cookie", sessionID);
putRequest.setHeader(sessionIDKey, sessionID);

CloseableHttpResponse response = testClient.execute(putRequest);
int statusCode = response.getStatusLine().getStatusCode();
Expand Down Expand Up @@ -174,7 +186,7 @@ void verifyPutEndpointWithWrongSessionID() throws IOException {
String requestBody = "[{\"newKey\":\"newValue\"}]";
StringEntity requestEntity = new StringEntity(requestBody);
putRequest.setEntity(requestEntity);
putRequest.setHeader("cookie", "###WrongSessionID###");
putRequest.setHeader(sessionIDKey, "###WrongSessionID###");

CloseableHttpResponse response = testClient.execute(putRequest);
int statusCode = response.getStatusLine().getStatusCode();
Expand All @@ -185,21 +197,19 @@ void verifyPutEndpointWithWrongSessionID() throws IOException {
}

@Test
@Disabled("Fails in Github Actions")
void verifyPutEndpointTimeToLive() throws IOException, InterruptedException {
void verifyPutEndpointWithoutCorrectSessionIDKey() throws IOException {
String url = "http://" + host + ":" + port + path;
HttpPut putRequest = new HttpPut(url);
String requestBody = "[{\"newKey\":\"newValue\"}]";
StringEntity requestEntity = new StringEntity(requestBody);
putRequest.setEntity(requestEntity);
putRequest.setHeader("cookie", sessionID);
putRequest.setHeader(sessionIDKey + "-1", sessionID);

CloseableHttpResponse response = testClient.execute(putRequest);
assertThat(sessionStorage.getOrCreateDataStorage(sessionID).readData()).containsEntry("newKey", "newValue");
int statusCode = response.getStatusLine().getStatusCode();

// 10s (Clean-Up-Frequency) + 10s (Buffer)
TimeUnit.SECONDS.sleep(20);
assertThat(sessionStorage.getOrCreateDataStorage(sessionID).readData()).isEmpty();
assertThat(statusCode).isEqualTo(400);
assertThat(sessionStorage.getOrCreateDataStorage(sessionID).readData()).doesNotContainEntry("newKey", "newValue");
response.close();
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package rocks.inspectit.ocelot.core.instrumentation.context.propagation;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.junit.jupiter.MockitoExtension;
import rocks.inspectit.ocelot.core.instrumentation.context.ContextPropagationUtil;

import java.util.Set;

import static org.assertj.core.api.Assertions.assertThat;

@ExtendWith(MockitoExtension.class)
public class BrowserPropagationUtilTest {

@InjectMocks
BrowserPropagationUtil browserPropagationUtil;

final static String key = "Cookie";

@BeforeEach
void setUp() {
browserPropagationUtil.setSessionIdKey(key);
}

@Test
void verifySessionIdKeyExists() {
Set<String> headers = ContextPropagationUtil.getPropagationHeaderNames();

assertThat(headers.contains(key)).isTrue();
}

@Test
void verifySessionIdKeyIsUpdated() {
Set<String> headers = ContextPropagationUtil.getPropagationHeaderNames();
assertThat(headers.contains(key)).isTrue();

String newKey = "NewCookie";
browserPropagationUtil.setSessionIdKey(newKey);

assertThat(headers.contains(key)).isFalse();
assertThat(headers.contains(newKey)).isTrue();
}
}

0 comments on commit b19edac

Please sign in to comment.