import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.restconf.api.query.PrettyPrintParam;
import org.opendaylight.restconf.server.api.RestconfServer;
import org.opendaylight.restconf.server.spi.ErrorTagMapping;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-// FIXME: rename to APIResource
-final class RestconfRequestDispatcher extends AbstractResource {
- private static final Logger LOG = LoggerFactory.getLogger(RestconfRequestDispatcher.class);
+/**
+ * RESTCONF API resource, as defined in
+ * <a href="https://www.rfc-editor.org/rfc/rfc8040#section-3.3>RFC8040, section 3.3</a>.
+ */
+final class APIResource extends AbstractResource {
+ private static final Logger LOG = LoggerFactory.getLogger(APIResource.class);
private final Map<String, AbstractResource> resources;
- private final @NonNull PrincipalService principalService;
+ private final PrincipalService principalService;
private final @NonNull String firstSegment;
- private final @NonNull List<String> otherSegments;
+ private final List<String> otherSegments;
- RestconfRequestDispatcher(final RestconfServer server, final PrincipalService principalService,
- final List<String> segments, final String restconfPath, final ErrorTagMapping errorTagMapping,
- final MessageEncoding defaultEncoding, final PrettyPrintParam defaultPrettyPrint) {
+ @NonNullByDefault
+ APIResource(final RestconfServer server, final PrincipalService principalService, final List<String> segments,
+ final String restconfPath, final ErrorTagMapping errorTagMapping, final MessageEncoding defaultEncoding,
+ final PrettyPrintParam defaultPrettyPrint) {
super(new EndpointInvariants(server, defaultPrettyPrint, errorTagMapping, defaultEncoding,
URI.create(requireNonNull(restconfPath))));
this.principalService = requireNonNull(principalService);
}
if (!peeler.hasNext()) {
- // FIXME: we are rejecting requests to '{+restconf}', which matches JAX-RS server behaviour, but is not
- // correct: we should be reporting the entire API Resource, as described in
- // https://www.rfc-editor.org/rfc/rfc8040#section-3.3
- LOG.debug("Not servicing root request");
- return NOT_FOUND;
+ return prepare(method, targetUri, headers, principal);
}
final var segment = peeler.next();
return NOT_FOUND;
}
+ // FIXME: we are rejecting requests to '{+restconf}', which matches JAX-RS server behaviour, but is not correct:
+ // we should be reporting the entire API Resource, as described in
+ // https://www.rfc-editor.org/rfc/rfc8040#section-3.3
+ @NonNullByDefault
+ private static PreparedRequest prepare(final ImplementedMethod method, final URI targetUri,
+ final HttpHeaders headers, final @Nullable Principal principal) {
+ LOG.debug("Not servicing root request");
+ return NOT_FOUND;
+ }
+
String firstSegment() {
return firstSegment;
}
* point in the past.
*/
@NonNullByDefault
-abstract sealed class AbstractResource permits AbstractLeafResource, RestconfRequestDispatcher {
+abstract sealed class AbstractResource permits AbstractLeafResource, APIResource {
private static final Logger LOG = LoggerFactory.getLogger(AbstractResource.class);
static final CompletedRequest METHOD_NOT_ALLOWED_READ_ONLY =
package org.opendaylight.restconf.server;
/**
- * The result of
- * {@link RestconfRequestDispatcher#prepare(java.net.URI, SegmentPeeler, io.netty.handler.codec.http.HttpRequest)}. This
- * can either be a {@link CompletedRequest} or a {@link PendingRequest}.
+ * The result of {@link AbstractResource#prepare(java.net.URI, SegmentPeeler, io.netty.handler.codec.http.HttpRequest)}.
+ * This can either be a {@link CompletedRequest} or a {@link PendingRequest}.
*/
sealed interface PreparedRequest permits CompletedRequest, PendingRequest {
// Nothing else
Arrays.stream(ImplementedMethod.values())
.collect(Collectors.toUnmodifiableMap(ImplementedMethod::httpMethod, Function.identity()));
- private final RestconfRequestDispatcher dispatcher;
private final WellKnownResources wellKnown;
+ private final APIResource apiResource;
private final HttpScheme scheme;
- RestconfSession(final WellKnownResources wellKnown, final RestconfRequestDispatcher dispatcher,
- final HttpScheme scheme) {
+ RestconfSession(final WellKnownResources wellKnown, final APIResource apiResource, final HttpScheme scheme) {
super(FullHttpRequest.class, false);
this.wellKnown = requireNonNull(wellKnown);
- this.dispatcher = requireNonNull(dispatcher);
+ this.apiResource = requireNonNull(apiResource);
this.scheme = requireNonNull(scheme);
}
respond(ctx, streamId, wellKnown.request(version, method, peeler));
return;
}
- if (!segment.equals(dispatcher.firstSegment())) {
+ if (!segment.equals(apiResource.firstSegment())) {
// Does not match the dispatcher -- we are done now
LOG.debug("No resource for {}", requestUri);
msg.release();
// - invoke dispatcher.prepare() from here first
// - handle CompletedRequest to synchronous dispatch just like the above two cases, as it is that simple
- dispatcher.dispatch(peeler, method, targetUri, msg, new RestconfRequest() {
+ apiResource.dispatch(peeler, method, targetUri, msg, new RestconfRequest() {
@Override
public void onSuccess(final FullHttpResponse response) {
msg.release();
private final RestconfStream.Registry streamRegistry;
private final NettyEndpointConfiguration configuration;
- private final RestconfRequestDispatcher dispatcher;
private final WellKnownResources wellKnown;
+ private final APIResource apiResource;
private final String restconf;
RestconfTransportChannelListener(final RestconfServer server, final RestconfStream.Registry streamRegistry,
}
restconf = sb.toString();
wellKnown = new WellKnownResources(restconf);
- dispatcher = new RestconfRequestDispatcher(server, principalService, apiRootPath, sb.append('/').toString(),
+ apiResource = new APIResource(server, principalService, apiRootPath, sb.append('/').toString(),
configuration.errorTagMapping(), configuration.defaultEncoding(), configuration.prettyPrint());
LOG.info("Initialized with service {}", server.getClass());
new RestconfStreamService(streamRegistry, restconf, configuration.errorTagMapping(),
configuration.defaultEncoding(), configuration.prettyPrint()),
configuration.sseMaximumFragmentLength().toJava(), configuration.sseHeartbeatIntervalMillis().toJava()),
- new RestconfSession(wellKnown, dispatcher, channel.scheme()));
+ new RestconfSession(wellKnown, apiResource, channel.scheme()));
}
@Override
@Captor
private ArgumentCaptor<FullHttpResponse> responseCaptor;
- private RestconfRequestDispatcher dispatcher;
+ private APIResource apiResource;
@BeforeEach
void beforeEach() {
doReturn(null).when(principalService).acquirePrincipal(any());
- dispatcher = new RestconfRequestDispatcher(server, principalService, List.of("rests"), "/rests/",
- ERROR_TAG_MAPPING, MessageEncoding.JSON, PRETTY_PRINT);
+ apiResource = new APIResource(server, principalService, List.of("rests"), "/rests/", ERROR_TAG_MAPPING,
+ MessageEncoding.JSON, PRETTY_PRINT);
}
protected FullHttpResponse dispatch(final FullHttpRequest request) {
final var peeler = new SegmentPeeler(targetUri);
assertEquals("rests", peeler.next());
final var nettyMethod = request.method();
- dispatcher.dispatch(peeler, switch (nettyMethod.name()) {
+ apiResource.dispatch(peeler, switch (nettyMethod.name()) {
case "DELETE" -> ImplementedMethod.DELETE;
case "GET" -> ImplementedMethod.GET;
case "OPTIONS" -> ImplementedMethod.OPTIONS;