(search) Hopefully fix race condition that leaves the response with no Content-type header

This commit is contained in:
Viktor Lofgren 2023-12-05 13:48:42 +01:00
parent 21abfc6424
commit 67195592c6
8 changed files with 35 additions and 50 deletions

View File

@ -33,17 +33,12 @@ public class CommandEvaluator {
public Object eval(Context ctx, Response response, SearchParameters parameters) {
for (var cmd : specialCommands) {
if (cmd.process(ctx, response, parameters)) {
// The commands will write directly to the response, so we don't need to do anything else
// but it's important we don't return null, as this signals to Spark that we haven't handled
// the request.
return "";
}
var maybe = cmd.process(ctx, response, parameters);
if (maybe.isPresent())
return maybe.get();
}
defaultCommand.process(ctx, response, parameters);
return "";
return defaultCommand.process(ctx, response, parameters).orElse("");
}
}

View File

@ -4,6 +4,8 @@ package nu.marginalia.search.command;
import nu.marginalia.client.Context;
import spark.Response;
import java.util.Optional;
public interface SearchCommandInterface {
boolean process(Context ctx, Response response, SearchParameters parameters);
Optional<Object> process(Context ctx, Response response, SearchParameters parameters);
}

View File

@ -24,7 +24,7 @@ public class BangCommand implements SearchCommandInterface {
}
@Override
public boolean process(Context ctx, Response response, SearchParameters parameters) {
public Optional<Object> process(Context ctx, Response response, SearchParameters parameters) {
for (var entry : bangsToPattern.entrySet()) {
String bangPattern = entry.getKey();
@ -38,7 +38,7 @@ public class BangCommand implements SearchCommandInterface {
}
}
return false;
return Optional.empty();
}
private Optional<String> matchBangPattern(String query, String bangKey) {

View File

@ -14,6 +14,7 @@ import spark.Response;
import java.io.IOException;
import java.util.Map;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.regex.Pattern;
@ -34,22 +35,21 @@ public class BrowseCommand implements SearchCommandInterface {
}
@Override
public boolean process(Context ctx, Response response, SearchParameters parameters) {
public Optional<Object> process(Context ctx, Response response, SearchParameters parameters) {
if (!queryPatternPredicate.test(parameters.query())) {
return false;
return Optional.empty();
}
var model = browseSite(ctx, parameters.query());
if (null == model)
return false;
return Optional.empty();
browseResultsRenderer.renderInto(response, model,
return Optional.of(browseResultsRenderer.render(model,
Map.of("query", parameters.query(),
"profile", parameters.profileStr(),
"focusDomain", model.focusDomain())
);
return true;
));
}

View File

@ -12,6 +12,7 @@ import spark.Response;
import java.io.IOException;
import java.util.Map;
import java.util.Optional;
public class ConvertCommand implements SearchCommandInterface {
private final SearchUnitConversionService searchUnitConversionService;
@ -26,18 +27,13 @@ public class ConvertCommand implements SearchCommandInterface {
@Override
@SneakyThrows
public boolean process(Context ctx, Response response, SearchParameters parameters) {
public Optional<Object> process(Context ctx, Response response, SearchParameters parameters) {
var conversion = searchUnitConversionService.tryConversion(ctx, parameters.query());
if (conversion.isEmpty()) {
return false;
}
conversionRenderer.renderInto(response, Map.of(
return conversion.map(s -> conversionRenderer.render(Map.of(
"query", parameters.query(),
"result", conversion.get(),
"result", s,
"profile", parameters.profileStr())
);
));
return true;
}
}

View File

@ -16,6 +16,7 @@ import spark.Response;
import java.io.IOException;
import java.util.Map;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.regex.Pattern;
@ -38,19 +39,17 @@ public class DefinitionCommand implements SearchCommandInterface {
}
@Override
public boolean process(Context ctx, Response response, SearchParameters parameters) {
public Optional<Object> process(Context ctx, Response response, SearchParameters parameters) {
if (!queryPatternPredicate.test(parameters.query())) {
return false;
return Optional.empty();
}
var results = lookupDefinition(ctx, parameters.query());
dictionaryRenderer.renderInto(response, results,
return Optional.of(dictionaryRenderer.render(results,
Map.of("query", parameters.query(),
"profile", parameters.profileStr())
);
return true;
));
}

View File

@ -13,6 +13,7 @@ import nu.marginalia.renderer.RendererFactory;
import spark.Response;
import java.io.IOException;
import java.util.Optional;
public class SearchCommand implements SearchCommandInterface {
private final DomainBlacklist blacklist;
@ -32,15 +33,9 @@ public class SearchCommand implements SearchCommandInterface {
}
@Override
public boolean process(Context ctx, Response response, SearchParameters parameters) {
public Optional<Object> process(Context ctx, Response response, SearchParameters parameters) {
DecoratedSearchResults results = searchOperator.doSearch(ctx, parameters);
searchResultsRenderer.renderInto(response, results);
return true;
}
private boolean isBlacklisted(UrlDetails details) {
return blacklist.isBlacklisted(details.domainId);
return Optional.of(searchResultsRenderer.render(results));
}
}

View File

@ -9,7 +9,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import spark.Response;
import java.io.IOException;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.regex.Pattern;
@ -17,7 +17,7 @@ public class SiteRedirectCommand implements SearchCommandInterface {
private final Logger logger = LoggerFactory.getLogger(getClass());
private final Predicate<String> queryPatternPredicate = Pattern.compile("^(site|links|similar):[.A-Za-z\\-0-9]+$").asPredicate();
private final Predicate<String> queryPatternPredicate = Pattern.compile("^(site|links):[.A-Za-z\\-0-9]+$").asPredicate();
@Inject
public SiteRedirectCommand() {
@ -25,9 +25,9 @@ public class SiteRedirectCommand implements SearchCommandInterface {
@SneakyThrows
@Override
public boolean process(Context ctx, Response response, SearchParameters parameters) {
public Optional<Object> process(Context ctx, Response response, SearchParameters parameters) {
if (!queryPatternPredicate.test(parameters.query())) {
return false;
return Optional.empty();
}
int idx = parameters.query().indexOf(':');
@ -37,19 +37,17 @@ public class SiteRedirectCommand implements SearchCommandInterface {
// Use an HTML redirect here, so we can use relative URLs
String view = switch (prefix) {
case "links" -> "links";
case "similar" -> "similar";
default -> "info";
};
response.raw().getOutputStream().println("""
return Optional.of("""
<!DOCTYPE html>
<html lang="en">
<meta charset="UTF-8">
<title>Redirecting...</title>
<meta http-equiv="refresh" content="0; url=/site/%s?view=%s">
""".formatted(domain, view));
return true;
""".formatted(domain, view)
);
}
}