Simulating Multiple Simultaneous Logins with Apache HttpClient
This guide shows how to disable HttpClient's automatic cookie handling, manually extract and store Set‑Cookie headers, and bind cookies to individual user objects so that multi‑user, multi‑threaded API tests can simulate concurrent logins reliably.
When using Apache HttpClient for API testing, the default cookie manager interferes with scenarios that require multiple users to log in at the same time, because cookies are shared globally.
1. Disable automatic cookie management
Configure a RequestConfig that sets CookieSpecs.IGNORE_COOKIES and disables redirects. This prevents HttpClient from automatically storing or rejecting cookies.
private static RequestConfig getRequestConfig() {
return RequestConfig.custom()
.setConnectionRequestTimeout(HttpClientConstant.CONNECT_REQUEST_TIMEOUT)
.setConnectTimeout(HttpClientConstant.CONNECT_TIMEOUT)
.setSocketTimeout(HttpClientConstant.SOCKET_TIMEOUT)
.setCookieSpec(CookieSpecs.IGNORE_COOKIES)
.setRedirectsEnabled(false)
.build();
}The same RequestConfig can be applied either to an HttpRequestBase instance or directly to a CloseableHttpClient. In practice the author injects it into a pooled CloseableHttpClient:
private static CloseableHttpClient getCloseableHttpsClients() {
// Create a custom HTTPS client with the connection manager and the request config above
CloseableHttpClient client = HttpClients.custom()
.setConnectionManager(connManager)
.setRetryHandler(httpRequestRetryHandler)
.setDefaultRequestConfig(requestConfig)
.build();
return client;
}2. Manually process Set‑Cookie headers
After receiving a response, the code extracts all Set-Cookie headers, splits each header value at the first semicolon and then at the first equals sign, and stores the name‑value pair in a JSONObject called cookies:
private static JSONObject afterResponse(CloseableHttpResponse response) {
JSONObject cookies = new JSONObject();
List<Header> headers = Arrays.asList(response.getHeaders("Set-Cookie"));
if (headers.size() == 0) return cookies;
headers.forEach(x -> {
String[] split = x.getValue().split(";")[0].split("=", 2);
cookies.put(split[0], split[1]);
});
return cookies;
}If the response body is not valid JSON, the method falls back to wrapping the raw content and adds an error code, while still attaching any extracted cookies:
private static JSONObject getJsonResponse(String content, JSONObject cookies) {
JSONObject jsonObject = new JSONObject();
try {
jsonObject = JSONObject.fromObject(content);
} catch (Exception e) {
jsonObject.put("content", content);
jsonObject.put("code", TEST_ERROR_CODE);
logger.warn("Response body is not JSON, automatically converted.");
} finally {
if (!cookies.isEmpty()) jsonObject.put(HttpClientConstant.COOKIE, cookies);
return jsonObject;
}
}3. Store and reuse cookies per user
Each test object's base class keeps the latest cookie map after a response. When sending a new request, the code adds the common request ID, a token header, and, if present, a header containing the stored cookies:
public JSONObject getResponse(HttpRequestBase httpRequestBase) {
setHeaders(httpRequestBase);
JSONObject response = FanLibrary.getHttpResponse(httpRequestBase);
handleResponseHeader(response);
return response;
}
public void setHeaders(HttpRequestBase httpRequestBase) {
httpRequestBase.addHeader(Common.REQUEST_ID);
httpRequestBase.addHeader(FanLibrary.getHeader("token", token));
if (!cookies.isEmpty()) httpRequestBase.addHeader(FanLibrary.getCookies(cookies));
}
public void handleResponseHeader(JSONObject response) {
if (!response.containsKey(HttpClientConstant.COOKIE)) return;
cookies.putAll(response.getJSONObject(HttpClientConstant.COOKIE));
response.remove(HttpClientConstant.COOKIE);
}Because the test framework runs many threads, the author observed occasional double‑initialisation of the base object, which could invalidate previously stored cookies. To avoid this, a thread‑bound user model is used: each thread represents a single user, ensuring that cookies are never mixed between users.
4. Practical considerations
The approach works for both HTTP and HTTPS protocols.
Disabling automatic cookie handling is essential; otherwise, HttpClient may reject cookies when the server sends a Set-Cookie header that does not match the default policy.
Storing cookies in a JSONObject keeps the data structure lightweight and easy to serialize.
Thread‑binding simplifies concurrency control without needing a complex notification system for cookie updates.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
