Repository: firecracker-microvm/firecracker
File: src/firecracker/src/api_server/parsed_request.rs (Rust)

Code before:
        info!("The API server received a {description}.");

        // Split request uri by '/' by doing:
        // 1. Trim starting '/' characters
        // 2. Splitting by '/'
        let mut path_tokens = request_uri.trim_start_matches('/').split_terminator('/');
        let path = path_tokens.next().unwrap_or("");

        match (request.method(), path, request.body.as_ref()) {
            (Method::Get, "", None) => parse_get_instance_info(),
            (Method::Get, "balloon", None) => parse_get_balloon(path_tokens.next()),
            (Method::Get, "version", None) => parse_get_version(),
            (Method::Get, "vm", None) if path_tokens.next() == Some("config") => {
                Ok(ParsedRequest::new_sync(VmmAction::GetFullVmConfig))
            }
            (Method::Get, "machine-config", None) => parse_get_machine_config(),
            (Method::Get, "mmds", None) => parse_get_mmds(),
            (Method::Get, _, Some(_)) => method_to_error(Method::Get),
            (Method::Put, "actions", Some(body)) => parse_put_actions(body),
            (Method::Put, "balloon", Some(body)) => parse_put_balloon(body),
            (Method::Put, "boot-source", Some(body)) => parse_put_boot_source(body),
            (Method::Put, "cpu-config", Some(body)) => parse_put_cpu_config(body),
            (Method::Put, "drives", Some(body)) => parse_put_drive(body, path_tokens.next()),
            #[cfg(target_arch = "x86_64")]
            (Method::Put, "hotplug", Some(body)) => parse_put_hotplug(body),
            (Method::Put, "logger", Some(body)) => parse_put_logger(body),
            (Method::Put, "machine-config", Some(body)) => parse_put_machine_config(body),
            (Method::Put, "metrics", Some(body)) => parse_put_metrics(body),
            (Method::Put, "mmds", Some(body)) => parse_put_mmds(body, path_tokens.next()),
            (Method::Put, "network-interfaces", Some(body)) => {
                parse_put_net(body, path_tokens.next())
            }
            (Method::Put, "snapshot", Some(body)) => parse_put_snapshot(body, path_tokens.next()),
            (Method::Put, "vsock", Some(body)) => parse_put_vsock(body),
            (Method::Put, "entropy", Some(body)) => parse_put_entropy(body),
            (Method::Put, _, None) => method_to_error(Method::Put),
            (Method::Patch, "balloon", Some(body)) => parse_patch_balloon(body, path_tokens.next()),
            (Method::Patch, "drives", Some(body)) => parse_patch_drive(body, path_tokens.next()),
            (Method::Patch, "machine-config", Some(body)) => parse_patch_machine_config(body),
            (Method::Patch, "mmds", Some(body)) => parse_patch_mmds(body),
            (Method::Patch, "network-interfaces", Some(body)) => {
                parse_patch_net(body, path_tokens.next())
            }
            (Method::Patch, "vm", Some(body)) => parse_patch_vm_state(body),
            (Method::Patch, _, None) => method_to_error(Method::Patch),
            (method, unknown_uri, _) => Err(RequestError::InvalidPathMethod(
                unknown_uri.to_string(),
                method,
            )),
        }
    }

Review comment (suggestion):
We do not necessarily need to do that in this PR, especially since this is going in a feature branch, but I wanted to hear your thoughts on an alternative API.

I was thinking, instead of adding a `PUT /hotplug` API, to add a `PATCH /machine-config`. Semantically, I think this makes sense from an HTTP point of view. That's the API I would expect as a user. At the moment, we restrict the `/machine-config` end-point to be pre-boot only. This would change, and also we would need to validate that we are only PATCHing things we can hotplug today. I _think_ that this does not require a significant amount of work, but I'm not sure.

WDYT? (also, @firecracker-microvm/compute-capsule) 

Code after:
        );
        info!("The API server received a {description}.");

        // Split request uri by '/' by doing:
        // 1. Trim starting '/' characters
        // 2. Splitting by '/'
        let mut path_tokens = request_uri.trim_start_matches('/').split_terminator('/');
        let path = path_tokens.next().unwrap_or("");

        match (request.method(), path, request.body.as_ref()) {
            (Method::Get, "", None) => parse_get_instance_info(),
            (Method::Get, "balloon", None) => parse_get_balloon(path_tokens.next()),
            (Method::Get, "version", None) => parse_get_version(),
            (Method::Get, "vm", None) if path_tokens.next() == Some("config") => {
                Ok(ParsedRequest::new_sync(VmmAction::GetFullVmConfig))
            }
            (Method::Get, "machine-config", None) => parse_get_machine_config(),
            (Method::Get, "mmds", None) => parse_get_mmds(),
            (Method::Get, _, Some(_)) => method_to_error(Method::Get),
            (Method::Put, "actions", Some(body)) => parse_put_actions(body),
            (Method::Put, "balloon", Some(body)) => parse_put_balloon(body),
            (Method::Put, "boot-source", Some(body)) => parse_put_boot_source(body),
            (Method::Put, "cpu-config", Some(body)) => parse_put_cpu_config(body),
            (Method::Put, "drives", Some(body)) => parse_put_drive(body, path_tokens.next()),
            #[cfg(target_arch = "x86_64")]
            (Method::Put, "hotplug", Some(body)) => parse_put_hotplug(body),
            (Method::Put, "logger", Some(body)) => parse_put_logger(body),
            (Method::Put, "machine-config", Some(body)) => parse_put_machine_config(body),
            (Method::Put, "metrics", Some(body)) => parse_put_metrics(body),
            (Method::Put, "mmds", Some(body)) => parse_put_mmds(body, path_tokens.next()),
            (Method::Put, "network-interfaces", Some(body)) => {
                parse_put_net(body, path_tokens.next())
            }
            (Method::Put, "snapshot", Some(body)) => parse_put_snapshot(body, path_tokens.next()),
            (Method::Put, "vsock", Some(body)) => parse_put_vsock(body),
            (Method::Put, "entropy", Some(body)) => parse_put_entropy(body),
            (Method::Put, _, None) => method_to_error(Method::Put),
            (Method::Patch, "balloon", Some(body)) => parse_patch_balloon(body, path_tokens.next()),
            (Method::Patch, "drives", Some(body)) => parse_patch_drive(body, path_tokens.next()),
            (Method::Patch, "machine-config", Some(body)) => parse_patch_machine_config(body),
            (Method::Patch, "mmds", Some(body)) => parse_patch_mmds(body),
            (Method::Patch, "network-interfaces", Some(body)) => {
                parse_patch_net(body, path_tokens.next())
            }
            (Method::Patch, "vm", Some(body)) => parse_patch_vm_state(body),
            (Method::Patch, _, None) => method_to_error(Method::Patch),
            (method, unknown_uri, _) => Err(RequestError::InvalidPathMethod(
                unknown_uri.to_string(),
                method,
            )),
        }