The source code of the handler process for receiving Rest requests is built when Elasticsearch 8.9 starts.

  • 1. Main mode entrance
  • 2. The third phase of Elasticsearch initialization
    • 1. Construct restController when constructing node node object
    • 2. Perform the operation of initializing RestHanders at the end of the node construction object.
  • 3. Take the RestGetIndicesAction object registered in the handler as an example.
    • 1. Inherited BaseRestHandler, the routes method is used as routing rules, and the parent class calls the prepareRequest implementation of the subclass.
    • 2. BaseRestHandler implements the RestHandler interface

1. Main mode entrance

Path: org.elasticsearch.bootstrap.Elasticsearch

 /**
     * Start the main entry point for elasticsearch.
     */
    public static void main(final String[] args) {<!-- -->

        Bootstrap bootstrap = initPhase1();
        assert bootstrap != null;

        try {<!-- -->
            initPhase2(bootstrap);
            initPhase3(bootstrap);
        } catch (NodeValidationException e) {<!-- -->
            bootstrap.exitWithNodeValidationException(e);
        } catch (Throwable t) {<!-- -->
            bootstrap.exitWithUnknownException(t);
        }
    }

There are three initialization phases here. You can look directly at initPhase3

2. The third phase of Elasticsearch initialization

 /**
     *The third phase of initialization
     *Phase 3 includes everything after initializing the security manager. Until now, the system has been single-threaded. This phase can spawn threads, write logs, and is subject to security manager policies.
     * At the end of phase 3, the system is ready to accept requests and the main thread is ready to terminate. this means:
     * The node component has been built and started
     * Cleanup completed (e.g. security settings turned off)
     * At least one thread other than the main thread is active and will remain active after the main thread terminates
     * The parent CLI process has been notified that the system is ready
     */
    private static void initPhase3(Bootstrap bootstrap) throws IOException, NodeValidationException {<!-- -->
        //Call the checkLucene() function to check Lucene
        checkLucene();
        //Create a Node object and override the validateNodeBeforeAcceptingRequests method for node verification before accepting requests.
        Node node = new Node(bootstrap.environment()) {<!-- -->
            @Override
            protected void validateNodeBeforeAcceptingRequests(
                final BootstrapContext context,
                final BoundTransportAddress boundTransportAddress,
                List<BootstrapCheck> checks
            ) throws NodeValidationException {<!-- -->
                BootstrapChecks.check(context, boundTransportAddress, checks);
            }
        };
        //Use bootstrap.spawner() and the previously created node object to instantiate an Elasticsearch object and assign it to the INSTANCE variable.
        INSTANCE = new Elasticsearch(bootstrap.spawner(), node);
        //Turn off security settings
        IOUtils.close(bootstrap.secureSettings());
        //Start the INSTANCE object, node will start and maintain a living thread
        INSTANCE.start();
        //If the command line parameter specifies daemonize, remove the log configuration output by the console.
        if (bootstrap.args().daemonize()) {<!-- -->
            LogConfigurator.removeConsoleAppender();
        }
        //Send a CLI flag to indicate that the server is ready to accept requests.
        bootstrap.sendCliMarker(BootstrapInfo.SERVER_READY_MARKER);
        //If the command line parameter specifies daemonize, close the stream; otherwise, start the CLI monitoring thread.
        if (bootstrap.args().daemonize()) {<!-- -->
            bootstrap.closeStreams();
        } else {<!-- -->
            startCliMonitorThread(System.in);
        }
    }

Among them, INSTANCE.start(); is as follows, which means that the node is started and the surviving thread is running.

private void start() throws NodeValidationException {<!-- -->
        node.start();
        keepAliveThread.start();
    }

1. Construct restController when constructing the node node object

public Node(Environment environment) {<!-- -->
        this(environment, PluginsService.getPluginsServiceCtor(environment), true);
    }
/**
     *Constructs a node
     * Initialization of nodes
     */
    protectedNode(
        final Environment initialEnvironment,
        final Function<Settings, PluginsService> pluginServiceCtor,
        boolean forbidPrivateIndexSettings
    ) {<!-- -->
   //Omit code. . . .
   //restController will be initialized inside
            ActionModule actionModule = new ActionModule(
                settings,
                clusterModule.getIndexNameExpressionResolver(),
                settingsModule.getIndexScopedSettings(),
                settingsModule.getClusterSettings(),
                settingsModule.getSettingsFilter(),
                threadPool,
                pluginsService.filterPlugins(ActionPlugin.class),
                client,
                circuitBreakerService,
                usageService,
                systemIndices,
                tracer,
                clusterService,
                reservedStateHandlers
            );
            modules.add(actionModule);
            //restController is stored in networkModule, and NetworkModule is a module used to handle registration and binding of all network-related classes.
            //There is actionModule.initRestHandlers at the end to initialize the handler
            final RestController restController = actionModule.getRestController();
            final NetworkModule networkModule = new NetworkModule(
                settings,
                pluginsService.filterPlugins(NetworkPlugin.class),
                threadPool,
                bigArrays,
                pageCacheRecycler,
                circuitBreakerService,
                namedWriteableRegistry,
                xContentRegistry,
                networkService,
                restController,
                actionModule::copyRequestHeadersToThreadContext,
                clusterService.getClusterSettings(),
                tracer
            );
            //Omit code. . . .
 //Initialize Rest's Handler
            actionModule.initRestHandlers(() -> clusterService.state().nodesIfRecovered());
}

2. Perform the operation of initializing RestHanders at the end of the node construction object

 public void initRestHandlers(Supplier<DiscoveryNodes> nodesInCluster) {<!-- -->
       //Omit code. . . Here are just a few frequently used ones
        registerHandler.accept(new RestGetIndicesAction());
        registerHandler.accept(new RestIndicesStatsAction());
        registerHandler.accept(new RestCreateIndexAction());
        registerHandler.accept(new RestDeleteIndexAction());
        registerHandler.accept(new RestGetIndexTemplateAction());
        registerHandler.accept(new RestPutIndexTemplateAction());
        registerHandler.accept(new RestDeleteIndexTemplateAction());
        registerHandler.accept(new RestPutMappingAction());
        registerHandler.accept(new RestGetMappingAction());
        registerHandler.accept(new RestGetFieldMappingAction());
        registerHandler.accept(new RestIndexAction());
        registerHandler.accept(new RestSearchAction(restController.getSearchUsageHolder()));
        //Omit code
    }

3. Take the RestGetIndicesAction object registered in the handler as an example

/**
 * The REST handler for get index and head index APIs.
 * REST handlers for the Get Index and Header Index APIs.
 */
@ServerlessScope(Scope.PUBLIC)
public class RestGetIndicesAction extends BaseRestHandler {<!-- -->
//Represents routing matching rules. Through this rule, we know that we need to call this instance. The routing rules of each instance are different.
    @Override
    public List<Route> routes() {<!-- -->
        return List.of(new Route(GET, "/{index}"), new Route(HEAD, "/{index}"));
    }
    @Override
    public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException {<!-- -->
        // starting with 7.0 we don't include types by default in the response to GET requests
        if (request.getRestApiVersion() == RestApiVersion.V_7
             & amp; & amp; request.hasParam(INCLUDE_TYPE_NAME_PARAMETER)
             & amp; & amp; request.method().equals(GET)) {<!-- -->
            deprecationLogger.compatibleCritical("get_indices_with_types", TYPES_DEPRECATION_MESSAGE);
        }

        String[] indices = Strings.splitStringByCommaToArray(request.param("index"));
        final GetIndexRequest getIndexRequest = new GetIndexRequest();
        getIndexRequest.indices(indices);
        getIndexRequest.indicesOptions(IndicesOptions.fromRequest(request, getIndexRequest.indicesOptions()));
        getIndexRequest.local(request.paramAsBoolean("local", getIndexRequest.local()));
        getIndexRequest.masterNodeTimeout(request.paramAsTime("master_timeout", getIndexRequest.masterNodeTimeout()));
        getIndexRequest.humanReadable(request.paramAsBoolean("human", false));
        getIndexRequest.includeDefaults(request.paramAsBoolean("include_defaults", false));
        getIndexRequest.features(GetIndexRequest.Feature.fromRequest(request));
        final var httpChannel = request.getHttpChannel();
        return channel -> new RestCancellableNodeClient(client, httpChannel).admin()
            .indices()
            .getIndex(getIndexRequest, new RestChunkedToXContentListener<>(channel));
    }

   
}

1. Inherited BaseRestHandler, the routes method is used as routing rules, and the parent class calls the prepareRequest implementation of the subclass

public abstract class BaseRestHandler implements RestHandler {<!-- -->
    /**
     * {@inheritDoc}
     */
    @Override
    public abstract List<Route> routes();

    @Override
    public final void handleRequest(RestRequest request, RestChannel channel, NodeClient client) throws Exception {<!-- -->
        //Call the prepareRequest method to prepare the request for execution and process the request parameters.
        final RestChannelConsumer action = prepareRequest(request, client);
        //Filter unused parameters and collect unused parameters into an ordered collection.
        final SortedSet<String> unconsumedParams = request.unconsumedParams()
            .stream()
            .filter(p -> responseParams(request.getRestApiVersion()).contains(p) == false)
            .collect(Collectors.toCollection(TreeSet::new));
        //Verify whether unused parameters are valid. If there are invalid parameters, throw an IllegalArgumentException.
        if (unconsumedParams.isEmpty() == false) {<!-- -->
            final Set<String> candidateParams = new HashSet<>();
            candidateParams.addAll(request.consumedParams());
            candidateParams.addAll(responseParams(request.getRestApiVersion()));
            throw new IllegalArgumentException(unrecognized(request, unconsumedParams, candidateParams, "parameter"));
        }
        //Verify whether the request contains the request body and whether the request body has been consumed. If the conditions are not met, an IllegalArgumentException is thrown.
        if (request.hasContent() & amp; & amp; request.isContentConsumed() == false) {<!-- -->
            throw new IllegalArgumentException("request [" + request.method() + " " + request.path() + "] does not support having a body");
        }
        //Increase usage count
        usageCount.increment();
        //Execute the action and pass the result to the channel.
        action.accept(channel);
    }
     /**
     * Prepare the request for execution.
     * Implementations should consume all request parameters before returning the runnable object for actual execution.
     * Unused parameters will immediately terminate the execution of the request.
     * However, some parameters are used only for handling responses; implementations can override {@link BaseRestHandlerresponseParams()} to indicate such parameters.
     */
    protected abstract RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException;
}

2. BaseRestHandler implements the RestHandler interface

/**
 * Handler for REST requests
 */
@FunctionalInterface
public interface RestHandler {<!-- -->
    /**
     * Handle rest requests
     */
    void handleRequest(RestRequest request, RestChannel channel, NodeClient client) throws Exception;
    /**
     * A list of {@link routes} that this RestHandler is responsible for handling.
     */
    default List<Route> routes() {<!-- -->
        return Collections.emptyList();
    }
 }

The upstream of handerRequest that calls the RestHandler interface is

 @Override
    public void handleRequest(RestRequest request, RestChannel channel, NodeClient client) throws Exception {<!-- -->
    //Requests using the OPTIONS method should be handled elsewhere rather than by calling {@code RestHandlerhandleRequest} HTTP requests using the OPTIONS method bypass authn, so this sanity check prevents unauthenticated requests from being dispatched
        if (request.method() == Method.OPTIONS) {<!-- -->
            handleException(
                request,
                channel,
                new ElasticsearchSecurityException("Cannot dispatch OPTIONS request, as they are not authenticated")
            );
            return;
        }
        if (enabled == false) {<!-- -->
            doHandleRequest(request, channel, client);
            return;
        }
    }

    private void doHandleRequest(RestRequest request, RestChannel channel, NodeClient client) throws Exception {<!-- -->
        threadContext.sanitizeHeaders();
        // operator privileges can short circuit to return a non-successful response
        if (operatorPrivilegesService.checkRest(restHandler, request, channel, threadContext)) {<!-- -->
            try {<!-- -->
                restHandler.handleRequest(request, channel, client);
            } catch (Exception e) {<!-- -->
                logger.debug(() -> format("Request handling failed for REST request [%s]", request.uri()), e);
                throw e;
            }
        }
    }

Other APIs registered in hander are similar to RestGetIndicesAction