Skip to content

Commit 9ec8b72

Browse files
dsmileyiamsanjay
andauthored
SOLR-17069: HttpServletRequest; use eagerly to avoid errors (#3324)
HttpServletRequest should be used only by the thread dispatched receiving it, or else it _might_ produce errors (particularly in Jetty 12). Thus aspects of the request that are needed should be eagerly consumed instead of deferred, to avoid risks. * Cache Span in HttpSolrCall to avoid NPE from Jetty 12 async request loss * javadocs: warning for HttpSolrCall.getReq * Cache the security UserPrincipal in SolrRequestInfo, and not the request * CancellableQueryTracker: don't use HttpServletRequest * HttpSolrCall: eager parse the user agent * SolrRequestParsers: eagerly get the principal from the request --------- Co-authored-by: Sanjay Dutt <sanjaydutt@apache.org>
1 parent e908932 commit 9ec8b72

6 files changed

Lines changed: 31 additions & 22 deletions

File tree

solr/core/src/java/org/apache/solr/core/CancellableQueryTracker.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public String generateQueryID(SolrQueryRequest req) {
5656
}
5757
}
5858

59-
activeQueriesGenerated.put(queryID, req.getHttpSolrCall().getReq().getQueryString());
59+
activeQueriesGenerated.put(queryID, req.getParamString());
6060

6161
return queryID;
6262
}

solr/core/src/java/org/apache/solr/handler/component/HttpShardHandler.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ private LBSolrClient.Req prepareLBRequest(
209209
req.setMethod(SolrRequest.METHOD.POST);
210210
SolrRequestInfo requestInfo = SolrRequestInfo.getRequestInfo();
211211
if (requestInfo != null) {
212-
req.setUserPrincipal(requestInfo.getReq().getUserPrincipal());
212+
req.setUserPrincipal(requestInfo.getUserPrincipal());
213213
}
214214

215215
return httpShardHandlerFactory.newLBHttpSolrClientReq(req, urls);

solr/core/src/java/org/apache/solr/request/SolrRequestInfo.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,12 @@ public class SolrRequestInfo {
5151
private SolrQueryRequest req;
5252
private SolrQueryResponse rsp;
5353
private Date now;
54-
public HttpServletRequest httpRequest;
5554
private TimeZone tz;
5655
private ResponseBuilder rb;
5756
private List<AutoCloseable> closeHooks;
5857
private SolrDispatchFilter.Action action;
5958
private boolean useServerToken = false;
59+
private Principal principal;
6060

6161
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
6262

@@ -151,6 +151,7 @@ private synchronized void close() {
151151
public SolrRequestInfo(SolrQueryRequest req, SolrQueryResponse rsp) {
152152
this.req = req;
153153
this.rsp = rsp;
154+
this.principal = req != null ? req.getUserPrincipal() : null;
154155
}
155156

156157
public SolrRequestInfo(
@@ -160,8 +161,8 @@ public SolrRequestInfo(
160161
}
161162

162163
public SolrRequestInfo(HttpServletRequest httpReq, SolrQueryResponse rsp) {
163-
this.httpRequest = httpReq;
164164
this.rsp = rsp;
165+
this.principal = httpReq != null ? httpReq.getUserPrincipal() : null;
165166
}
166167

167168
public SolrRequestInfo(
@@ -171,9 +172,7 @@ public SolrRequestInfo(
171172
}
172173

173174
public Principal getUserPrincipal() {
174-
if (req != null) return req.getUserPrincipal();
175-
if (httpRequest != null) return httpRequest.getUserPrincipal();
176-
return null;
175+
return principal;
177176
}
178177

179178
public Date getNOW() {

solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import java.util.Locale;
5151
import java.util.Map;
5252
import java.util.Objects;
53+
import java.util.Optional;
5354
import java.util.Random;
5455
import java.util.Set;
5556
import java.util.concurrent.TimeUnit;
@@ -145,6 +146,8 @@ public class HttpSolrCall {
145146
protected final HttpServletRequest req;
146147
protected final HttpServletResponse response;
147148
protected final boolean retry;
149+
private final SolrVersion userAgentSolrVersion; // of the client
150+
private final Span span;
148151
protected SolrCore core = null;
149152
protected SolrQueryRequest solrReq = null;
150153
private boolean mustClearSolrRequestInfo = false;
@@ -176,18 +179,30 @@ public HttpSolrCall(
176179
this.response = response;
177180
this.retry = retry;
178181
this.requestType = RequestType.UNKNOWN;
182+
this.userAgentSolrVersion = parseUserAgentSolrVersion();
183+
this.span = Optional.ofNullable(TraceUtils.getSpan(req)).orElse(Span.getInvalid());
184+
this.path = ServletUtils.getPathAfterContext(req);
185+
179186
req.setAttribute(HttpSolrCall.class.getName(), this);
180187
// set a request timer which can be reused by requests if needed
181188
req.setAttribute(SolrRequestParsers.REQUEST_TIMER_SERVLET_ATTRIBUTE, new RTimerTree());
182189
// put the core container in request attribute
183190
req.setAttribute("org.apache.solr.CoreContainer", cores);
184-
path = ServletUtils.getPathAfterContext(req);
185191
}
186192

187193
public String getPath() {
188194
return path;
189195
}
190196

197+
/**
198+
* WARNING: This method returns a non-null {@link HttpServletRequest}, but calling certain methods
199+
* on it — such as {@link HttpServletRequest#getAttribute(String)} — may throw {@link
200+
* NullPointerException} if accessed outside the original servlet thread (e.g., in asynchronous
201+
* tasks).
202+
*
203+
* <p>Always cache required request data early during request handling if it needs to be accessed
204+
* later.
205+
*/
191206
public HttpServletRequest getReq() {
192207
return req;
193208
}
@@ -639,12 +654,7 @@ protected void handleAdminOrRemoteRequest() throws IOException {
639654

640655
/** Get the Span for this request. Not null. */
641656
public Span getSpan() {
642-
var s = TraceUtils.getSpan(req);
643-
if (s != null) {
644-
return s;
645-
} else {
646-
return Span.getInvalid();
647-
}
657+
return span;
648658
}
649659

650660
// called after init().
@@ -1174,6 +1184,10 @@ protected Object _getHandler() {
11741184
* clients prior to 9.9 present themselves as 1.0 or 2.0.
11751185
*/
11761186
public SolrVersion getUserAgentSolrVersion() {
1187+
return userAgentSolrVersion;
1188+
}
1189+
1190+
private SolrVersion parseUserAgentSolrVersion() {
11771191
String header = req.getHeader("User-Agent");
11781192
if (header == null || !header.startsWith("Solr")) {
11791193
return null;

solr/core/src/java/org/apache/solr/servlet/SolrRequestParsers.java

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ public SolrQueryRequest parse(SolrCore core, String path, HttpServletRequest req
169169
SolrParams params = parser.parseParamsAndFillStreams(req, streams);
170170

171171
SolrQueryRequest sreq =
172-
buildRequestFrom(core, params, streams, getRequestTimer(req), req, null);
172+
buildRequestFrom(core, params, streams, getRequestTimer(req), req, req.getUserPrincipal());
173173

174174
// Handlers and login will want to know the path. If it contains a ':'
175175
// the handler could use it for RESTful URLs
@@ -200,7 +200,7 @@ private SolrQueryRequest buildRequestFrom(
200200
Collection<ContentStream> streams, // might be added to but caller shouldn't depend on it
201201
RTimerTree requestTimer,
202202
final HttpServletRequest req,
203-
final Principal principal)
203+
final Principal principal) // from req, if req was provided, otherwise from elsewhere
204204
throws Exception {
205205
// ensure streams is non-null and mutable so we can easily add to it
206206
if (streams == null) {
@@ -267,11 +267,7 @@ private SolrQueryRequest buildRequestFrom(
267267
new SolrQueryRequestBase(core, params, requestTimer) {
268268
@Override
269269
public Principal getUserPrincipal() {
270-
if (principal != null) {
271-
return principal;
272-
} else {
273-
return req == null ? null : req.getUserPrincipal();
274-
}
270+
return principal;
275271
}
276272

277273
@Override

solr/core/src/java/org/apache/solr/update/SolrCmdDistributor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ private void submit(final Req req, boolean isCommit) throws IOException {
308308
// Copy user principal from the original request to the new update request, for later
309309
// authentication interceptor use
310310
if (SolrRequestInfo.getRequestInfo() != null) {
311-
req.uReq.setUserPrincipal(SolrRequestInfo.getRequestInfo().getReq().getUserPrincipal());
311+
req.uReq.setUserPrincipal(SolrRequestInfo.getRequestInfo().getUserPrincipal());
312312
}
313313

314314
if (req.synchronous) {

0 commit comments

Comments
 (0)