// Copyright 2023 Oxide Computer Company
//! Dropshot is a general-purpose crate for exposing REST APIs from a Rust
//! program. Planned highlights include:
//!
//! * Suitability for production use on a largely untrusted network.
//! Dropshot-based systems should be high-performing, reliable, debuggable, and
//! secure against basic denial of service attacks (intentional or otherwise).
//!
//! * First-class OpenAPI support, in the form of precise OpenAPI specs generated
//! directly from code. This works because the functions that serve HTTP
//! resources consume arguments and return values of specific types from which
//! a schema can be statically generated.
//!
//! * Ease of integrating into a diverse team. An important use case for
//! Dropshot consumers is to have a team of engineers where individuals might
//! add a few endpoints at a time to a complex server, and it should be
//! relatively easy to do this. Part of this means an emphasis on the
//! principle of least surprise: like Rust itself, we may choose abstractions
//! that require more time to learn up front in order to make it harder to
//! accidentally build systems that will not perform, will crash in corner
//! cases, etc.
//!
//! By "REST API", we primarily mean an API built atop existing HTTP primitives,
//! organized into hierarchical resources, and providing consistent, idempotent
//! mechanisms to create, update, list, and delete those resources. "REST" can
//! mean a range of things depending on who you talk to, and some people are
//! dogmatic about what is or isn't RESTy. We find such dogma not only
//! unhelpful, but poorly defined. (Consider such a simple case as trying to
//! update a resource in a REST API. Popular APIs sometimes use `PUT`, `PATCH`,
//! or `POST` for the verb; and JSON Merge Patch or JSON Patch as the format.
//! (sometimes without even knowing it!). There's hardly a clear standard, yet
//! this is a really basic operation for any REST API.)
//!
//! For a discussion of alternative crates considered, see Oxide RFD 10.
//!
//! We hope Dropshot will be fairly general-purpose, but it's primarily intended
//! to address the needs of the Oxide control plane.
//!
//!
//! ## Usage
//!
//! The bare minimum might look like this:
//!
//! ```no_run
//! use dropshot::ApiDescription;
//! use dropshot::ConfigDropshot;
//! use dropshot::ConfigLogging;
//! use dropshot::ConfigLoggingLevel;
//! use dropshot::HttpServerStarter;
//! use std::sync::Arc;
//!
//! #[tokio::main]
//! async fn main() -> Result<(), String> {
//! // Set up a logger.
//! let log =
//! ConfigLogging::StderrTerminal {
//! level: ConfigLoggingLevel::Info,
//! }
//! .to_logger("minimal-example")
//! .map_err(|e| e.to_string())?;
//!
//! // Describe the API.
//! let api = ApiDescription::new();
//! // Register API functions -- see detailed example or ApiDescription docs.
//!
//! // Start the server.
//! let server =
//! HttpServerStarter::new(
//! &ConfigDropshot {
//! bind_address: "127.0.0.1:0".parse().unwrap(),
//! request_body_max_bytes: 1024,
//! tls: None,
//! },
//! api,
//! Arc::new(()),
//! &log,
//! )
//! .map_err(|error| format!("failed to start server: {}", error))?
//! .start();
//!
//! server.await
//! }
//! ```
//!
//! This server returns a 404 for all resources because no API functions were
//! registered. See `examples/basic.rs` for a simple, documented example that
//! provides a few resources using shared state.
//!
//! For a given `ApiDescription`, you can also print out an OpenAPI spec
//! describing the API. See [`ApiDescription::openapi`].
//!
//!
//! ## API Handler Functions
//!
//! HTTP talks about **resources**. For a REST API, we often talk about
//! **endpoints** or **operations**, which are identified by a combination of the
//! HTTP method and the URI path.
//!
//! Example endpoints for a resource called a "project" might include:
//!
//! * `GET /projects` (list projects)
//! * `POST /projects` (one way to create a project)
//! * `GET /projects/my_project` (fetch one project)
//! * `PUT /projects/my_project` (update (or possibly create) a project)
//! * `DELETE /projects/my_project` (delete a project)
//!
//! With Dropshot, an incoming request for a given API endpoint is handled by a
//! particular Rust function. That function is called an **entrypoint**, an
//! **endpoint handler**, or a **handler function**. When you set up a Dropshot
//! server, you configure the set of available API endpoints and which functions
//! will handle each one by setting up an [`ApiDescription`].
//!
//! Typically, you define an endpoint with a handler function by using the
//! [`endpoint`] macro. Here's an example of a single endpoint that lists
//! a hardcoded project:
//!
//! ```
//! use dropshot::endpoint;
//! use dropshot::ApiDescription;
//! use dropshot::HttpError;
//! use dropshot::HttpResponseOk;
//! use dropshot::RequestContext;
//! use http::Method;
//! use schemars::JsonSchema;
//! use serde::Serialize;
//! use std::sync::Arc;
//!
//! /** Represents a project in our API */
//! #[derive(Serialize, JsonSchema)]
//! struct Project {
//! /** name of the project */
//! name: String,
//! }
//!
//! /** Fetch a project. */
//! #[endpoint {
//! method = GET,
//! path = "/projects/project1",
//! }]
//! async fn myapi_projects_get_project(
//! rqctx: RequestContext<()>,
//! ) -> Result<HttpResponseOk<Project>, HttpError>
//! {
//! let project = Project { name: String::from("project1") };
//! Ok(HttpResponseOk(project))
//! }
//!
//! fn main() {
//! let mut api = ApiDescription::new();
//!
//! /*
//! * Register our endpoint and its handler function. The "endpoint" macro
//! * specifies the HTTP method and URI path that identify the endpoint,
//! * allowing this metadata to live right alongside the handler function.
//! */
//! api.register(myapi_projects_get_project).unwrap();
//!
//! /* ... (use `api` to set up an `HttpServer` ) */
//! }
//! ```
//!
//! There's quite a lot going on here:
//!
//! * The `endpoint` macro specifies the HTTP method and URI path. When we
//! invoke `ApiDescription::register()`, this information is used to register
//! the endpoint that will be handled by our function.
//! * The signature of our function indicates that on success, it returns a
//! `HttpResponseOk<Project>`. This means that the function will
//! return an HTTP 200 status code ("OK") with an object of type `Project`.
//! * The function itself has a Rustdoc comment that will be used to document
//! this _endpoint_ in the OpenAPI schema.
//!
//! From this information, Dropshot can generate an OpenAPI specification for
//! this API that describes the endpoint (which OpenAPI calls an "operation"),
//! its documentation, the possible responses that it can return, and the schema
//! for each type of response (which can also include documentation). This is
//! largely known statically, though generated at runtime.
//!
//!
//! ### `#[endpoint { ... }]` attribute parameters
//!
//! The `endpoint` attribute accepts parameters the affect the operation of
//! the endpoint as well as metadata that appears in the OpenAPI description
//! of it.
//!
//! ```ignore
//! #[endpoint {
//! // Required fields
//! method = { DELETE | GET | PATCH | POST | PUT },
//! path = "/path/name/with/{named}/{variables}",
//!
//! // Optional fields
//! tags = [ "all", "your", "OpenAPI", "tags" ],
//! }]
//! ```
//!
//! This is where you specify the HTTP method and path (including path variables)
//! for the API endpoint. These are used as part of endpoint registration and
//! appear in the OpenAPI spec output.
//!
//! The tags field is used to categorize API endpoints and only impacts the
//! OpenAPI spec output.
//!
//!
//! ### Function parameters
//!
//! In general, a handler function looks like this:
//!
//! ```ignore
//! async fn f(
//! rqctx: RequestContext<Context>,
//! [query_params: Query<Q>,]
//! [path_params: Path<P>,]
//! [body_param: TypedBody<J>,]
//! [body_param: UntypedBody<J>,]
//! [raw_request: RawRequest,]
//! ) -> Result<HttpResponse*, HttpError>
//! ```
//!
//! The `RequestContext` must appear first. The `Context` type is
//! caller-provided context which is provided when the server is created.
//!
//! The types `Query`, `Path`, `TypedBody`, `UntypedBody`, and `RawRequest` are
//! called **Extractors** because they cause information to be pulled out of the
//! request and made available to the handler function.
//!
//! * [`Query`]`<Q>` extracts parameters from a query string, deserializing them
//! into an instance of type `Q`. `Q` must implement `serde::Deserialize` and
//! `schemars::JsonSchema`.
//! * [`Path`]`<P>` extracts parameters from HTTP path, deserializing them into
//! an instance of type `P`. `P` must implement `serde::Deserialize` and
//! `schemars::JsonSchema`.
//! * [`TypedBody`]`<J>` extracts content from the request body by parsing the
//! body as JSON (or form/url-encoded) and deserializing it into an instance
//! of type `J`. `J` must implement `serde::Deserialize` and `schemars::JsonSchema`.
//! * [`UntypedBody`] extracts the raw bytes of the request body.
//! * [`RawRequest`] provides access to the underlying [`hyper::Request`]. The
//! hope is that this would generally not be needed. It can be useful to
//! implement functionality not provided by Dropshot.
//!
//! `Query` and `Path` impl `SharedExtractor`. `TypedBody`, `UntypedBody`, and
//! `RawRequest` impl `ExclusiveExtractor`. Your function may accept 0-3
//! extractors, but only one can be `ExclusiveExtractor`, and it must be the
//! last one. Otherwise, the order of extractor arguments does not matter.
//!
//! If the handler accepts any extractors and the corresponding extraction
//! cannot be completed, the request fails with status code 400 and an error
//! message reflecting the error (usually a validation error).
//!
//! As with any serde-deserializable type, you can make fields optional by having
//! the corresponding property of the type be an `Option`. Here's an example of
//! an endpoint that takes two arguments via query parameters: "limit", a
//! required u32, and "marker", an optional string:
//!
//! ```
//! use http::StatusCode;
//! use dropshot::HttpError;
//! use dropshot::TypedBody;
//! use dropshot::Query;
//! use dropshot::RequestContext;
//! use hyper::Body;
//! use hyper::Response;
//! use schemars::JsonSchema;
//! use serde::Deserialize;
//! use std::sync::Arc;
//!
//! #[derive(Deserialize, JsonSchema)]
//! struct MyQueryArgs {
//! limit: u32,
//! marker: Option<String>
//! }
//!
//! struct MyContext {}
//!
//! async fn myapi_projects_get(
//! rqctx: RequestContext<MyContext>,
//! query: Query<MyQueryArgs>)
//! -> Result<Response<Body>, HttpError>
//! {
//! let query_args = query.into_inner();
//! let context: &MyContext = rqctx.context();
//! let limit: u32 = query_args.limit;
//! let marker: Option<String> = query_args.marker;
//! Ok(Response::builder()
//! .status(StatusCode::OK)
//! .body(format!("limit = {}, marker = {:?}\n", limit, marker).into())?)
//! }
//! ```
//!
//! ### Endpoint function return types
//!
//! Endpoint handler functions are async, so they always return a `Future`. When
//! we say "return type" below, we use that as shorthand for the output of the
//! future.
//!
//! An endpoint function must return a type that implements `HttpResponse`.
//! Typically this should be a type that implements `HttpTypedResponse` (either
//! one of the Dropshot-provided ones or one of your own creation).
//!
//! The more specific a type returned by the handler function, the more can be
//! validated at build-time, and the more specific an OpenAPI schema can be
//! generated from the source code. For example, a POST to an endpoint
//! "/projects" might return `Result<HttpResponseCreated<Project>, HttpError>`.
//! As you might expect, on success, this turns into an HTTP 201 "Created"
//! response whose body is constructed by serializing the `Project`. In this
//! example, OpenAPI tooling can identify at build time that this function
//! produces a 201 "Created" response on success with a body whose schema matches
//! `Project` (which we already said implements `Serialize`), and there would be
//! no way to violate this contract at runtime.
//!
//! These are the implementations of `HttpTypedResponse` with their associated
//! HTTP response code
//! on the HTTP method:
//!
//! | Return Type | HTTP status code |
//! | ----------- | ---------------- |
//! | [`HttpResponseOk`] | 200 |
//! | [`HttpResponseCreated`] | 201 |
//! | [`HttpResponseAccepted`] | 202 |
//! | [`HttpResponseDeleted`] | 204 |
//! | [`HttpResponseUpdatedNoContent`] | 204 |
//!
//! In situations where the response schema is not fixed, the endpoint should
//! return `Response<Body>`, which also implements `HttpResponse`. Note that
//! the OpenAPI spec will not include any status code or type information in
//! this case.
//!
//! ## What about generic handlers that run on all requests?
//!
//! There's no mechanism in Dropshot for this. Instead, it's recommended that
//! users commonize code using regular Rust functions and calling them. See the
//! design notes in the README for more on this.
//!
//!
//! ## Support for paginated resources
//!
//! "Pagination" here refers to the interface pattern where HTTP resources (or
//! API endpoints) that provide a list of the items in a collection return a
//! relatively small maximum number of items per request, often called a "page"
//! of results. Each page includes some metadata that the client can use to make
//! another request for the next page of results. The client can repeat this
//! until they've gotten all the results. Limiting the number of results
//! returned per request helps bound the resource utilization and time required
//! for any request, which in turn facilities horizontal scalability, high
//! availability, and protection against some denial of service attacks
//! (intentional or otherwise). For more background, see the comments in
//! dropshot/src/pagination.rs.
//!
//! Pagination support in Dropshot implements this common pattern:
//!
//! * This server exposes an **API endpoint** that returns the **items**
//! contained within a **collection**.
//! * The client is not allowed to list the entire collection in one request.
//! Instead, they list the collection using a sequence of requests to the one
//! endpoint. We call this sequence of requests a **scan** of the collection,
//! and we sometimes say that the client **pages through** the collection.
//! * The initial request in the scan may specify the **scan parameters**, which
//! typically specify how the results are to be sorted (i.e., by which
//! field(s) and whether the sort is ascending or descending), any filters to
//! apply, etc.
//! * Each request returns a **page** of results at a time, along with a **page
//! token** that's provided with the next request as a query parameter.
//! * The scan parameters cannot change between requests that are part of the
//! same scan.
//! * With all requests: there's a default limit (e.g., 100 items returned at a
//! time). Clients can request a higher limit using a query parameter (e.g.,
//! `limit=1000`). This limit is capped by a hard limit on the server. If the
//! client asks for more than the hard limit, the server can use the hard limit
//! or reject the request.
//!
//! As an example, imagine that we have an API endpoint called `"/animals"`. Each
//! item returned is an `Animal` object that might look like this:
//!
//! ```json
//! {
//! "name": "aardvark",
//! "class": "mammal",
//! "max_weight": "80", /* kilograms, typical */
//! }
//! ```
//!
//! There are at least 1.5 million known species of animal -- too many to return
//! in one API call! Our API supports paginating them by `"name"`, which we'll
//! say is a unique field in our data set.
//!
//! The first request to the API fetches `"/animals"` (with no querystring
//! parameters) and returns:
//!
//! ```json
//! {
//! "page_token": "abc123...",
//! "items": [
//! {
//! "name": "aardvark",
//! "class": "mammal",
//! "max_weight": "80",
//! },
//! ...
//! {
//! "name": "badger",
//! "class": "mammal",
//! "max_weight": "12",
//! }
//! ]
//! }
//! ```
//!
//! The subsequent request to the API fetches `"/animals?page_token=abc123..."`.
//! The page token `"abc123..."` is an opaque token to the client, but typically
//! encodes the scan parameters and the value of the last item seen
//! (`"name=badger"`). The client knows it has completed the scan when it
//! receives a response with no `page_token` in it.
//!
//! Our API endpoint can also support scanning in reverse order. In this case,
//! when the client makes the first request, it should fetch
//! `"/animals?sort=name-descending"`. Now the first result might be `"zebra"`.
//! Again, the page token must include the scan parameters so that in subsequent
//! requests, the API endpoint knows that we're scanning backwards, not forwards,
//! from the value we were given. It's not allowed to change directions or sort
//! order in the middle of a scan. (You can always start a new scan, but you
//! can't pick up from where you were in the previous scan.)
//!
//! It's also possible to support sorting by multiple fields. For example, we
//! could support `sort=class-name`, which we could define to mean that we'll
//! sort the results first by the animal's class, then by name. Thus we'd get
//! all the amphibians in sorted order, then all the mammals, then all the
//! reptiles. The main requirement is that the combination of fields used for
//! pagination must be unique. We cannot paginate by the animal's class alone.
//! (To see why: there are over 6,000 mammals. If the page size is, say, 1000,
//! then the page_token would say `"mammal"`, but there's not enough information
//! there to see where we are within the list of mammals. It doesn't matter
//! whether there are 2 mammals or 6,000 because clients can limit the page size
//! to just one item if they want and that ought to work.)
//!
//!
//! ### Dropshot interfaces for pagination
//!
//! We can think of pagination in two parts: the input (handling the pagination
//! query parameters) and the output (emitting a page of results, including the
//! page token).
//!
//! For input, a paginated API endpoint's handler function should accept a
//! [`Query`]`<`[`PaginationParams`]`<ScanParams, PageSelector>>`, where
//! `ScanParams` is a consumer-defined type specifying the parameters of the scan
//! (typically including the sort fields, sort order, and filter options) and
//! `PageSelector` is a consumer-defined type describing the page token. The
//! PageSelector will be serialized to JSON and base64-encoded to construct the
//! page token. This will be automatically parsed on the way back in.
//!
//! For output, a paginated API endpoint's handler function can return
//! `Result<`[`HttpResponseOk`]<[`ResultsPage`]`<T>, HttpError>` where `T:
//! Serialize` is the item listed by the endpoint. You can also use your own
//! structure that contains a [`ResultsPage`] (possibly using
//! `#[serde(flatten)]`), if that's the behavior you want.
//!
//! There are several complete, documented examples in the "examples" directory.
//!
//!
//! ### Advanced usage notes
//!
//! It's possible to accept additional query parameters besides the pagination
//! parameters by having your API endpoint handler function take two different
//! arguments using `Query`, like this:
//!
//! ```
//! use dropshot::HttpError;
//! use dropshot::HttpResponseOk;
//! use dropshot::PaginationParams;
//! use dropshot::Query;
//! use dropshot::RequestContext;
//! use dropshot::ResultsPage;
//! use dropshot::endpoint;
//! use schemars::JsonSchema;
//! use serde::Deserialize;
//! use std::sync::Arc;
//! # use serde::Serialize;
//! # #[derive(Debug, Deserialize, JsonSchema)]
//! # enum MyScanParams { A };
//! # #[derive(Debug, Deserialize, JsonSchema, Serialize)]
//! # enum MyPageSelector { A(String) };
//! #[derive(Deserialize, JsonSchema)]
//! struct MyExtraQueryParams {
//! do_extra_stuff: bool,
//! }
//!
//! #[endpoint {
//! method = GET,
//! path = "/list_stuff"
//! }]
//! async fn my_list_api(
//! rqctx: RequestContext<()>,
//! pag_params: Query<PaginationParams<MyScanParams, MyPageSelector>>,
//! extra_params: Query<MyExtraQueryParams>,
//! ) -> Result<HttpResponseOk<ResultsPage<String>>, HttpError>
//! {
//! # unimplemented!();
//! /* ... */
//! }
//! ```
//!
//! You might expect that instead of doing this, you could define your own
//! structure that includes a `PaginationParams` using `#[serde(flatten)]`, and
//! this ought to work, but it currently doesn't due to serde_urlencoded#33,
//! which is really serde#1183.
//!
//! ### DTrace probes
//!
//! Dropshot optionally exposes two DTrace probes, `request_start` and
//! `request_finish`. These provide detailed information about each request,
//! such as their ID, the local and remote IPs, and the response information.
//! See the dropshot::dtrace::RequestInfo` and `dropshot::dtrace::ResponseInfo`
//! types for a complete listing of what's available.
//!
//! These probes are implemented via the [`usdt`] crate. They may require a
//! nightly toolchain if built on macOS prior to Rust version 1.66. Otherwise a
//! stable compiler >= v1.59 is required in order to present the necessary
//! features. Given these constraints, USDT functionality is behind the feature
//! flag `"usdt-probes"`, which may become a default feature of this crate in
//! future releases.
//!
//! > *Important:* The probes are internally registered with the DTrace kernel
//! module, making them visible via `dtrace(1M)`. This is done when an `HttpServer`
//! object is created, but it's possible that registration fails. The result of
//! registration is stored in the server after creation, and can be accessed with
//! the [`HttpServer::probe_registration()`] method. This allows callers to decide
//! how to handle failures, but ensures that probes are always enabled if possible.
//!
//! Once in place, the probes can be seen via DTrace. For example, running:
//!
//! ```text
//! $ cargo +nightly run --example basic --features usdt-probes
//! ```
//!
//! And making several requests to it with `curl`, we can see the DTrace
//! probes with an invocation like:
//!
//! ```text
//! ## dtrace -Zq -n 'dropshot*:::request-* { printf("%s\n", copyinstr(arg0)); }'
//! {"ok":{"id":"b793c62e-60e4-45c5-9274-198a04d9abb1","local_addr":"127.0.0.1:61028","remote_addr":"127.0.0.1:34286","method":"GET","path":"/counter","query":null}}
//! {"ok":{"id":"b793c62e-60e4-45c5-9274-198a04d9abb1","local_addr":"127.0.0.1:61028","remote_addr":"127.0.0.1:34286","status_code":200,"message":""}}
//! {"ok":{"id":"9050e30a-1ce3-4d6f-be1c-69a11c618800","local_addr":"127.0.0.1:61028","remote_addr":"127.0.0.1:41101","method":"PUT","path":"/counter","query":null}}
//! {"ok":{"id":"9050e30a-1ce3-4d6f-be1c-69a11c618800","local_addr":"127.0.0.1:61028","remote_addr":"127.0.0.1:41101","status_code":400,"message":"do not like the number 10"}}
//! {"ok":{"id":"a53696af-543d-452f-81b6-5a045dd9921d","local_addr":"127.0.0.1:61028","remote_addr":"127.0.0.1:57376","method":"PUT","path":"/counter","query":null}}
//! {"ok":{"id":"a53696af-543d-452f-81b6-5a045dd9921d","local_addr":"127.0.0.1:61028","remote_addr":"127.0.0.1:57376","status_code":204,"message":""}}
//! ```
// Clippy's style advice is definitely valuable, but not worth the trouble for
// automated enforcement.
// The `usdt` crate may require nightly, enabled if our consumer is enabling
// DTrace probes.
// The macro used to define DTrace probes needs to be defined before anything
// that might use it.
extern crate slog;
pub use ApiDescription;
pub use ApiEndpoint;
pub use ApiEndpointBodyContentType;
pub use ApiEndpointParameter;
pub use ApiEndpointParameterLocation;
pub use ApiEndpointResponse;
pub use EndpointTagPolicy;
pub use ExtensionMode;
pub use OpenApiDefinition;
pub use TagConfig;
pub use TagDetails;
pub use TagExternalDocs;
pub use ConfigDropshot;
pub use ConfigTls;
pub use ProbeRegistration;
pub use HttpError;
pub use HttpErrorResponseBody;
pub use ExclusiveExtractor;
pub use ExtractorMetadata;
pub use Path;
pub use Query;
pub use RawRequest;
pub use SharedExtractor;
pub use TypedBody;
pub use UntypedBody;
pub use http_response_found;
pub use http_response_see_other;
pub use http_response_temporary_redirect;
pub use FreeformBody;
pub use HttpCodedResponse;
pub use HttpResponse;
pub use HttpResponseAccepted;
pub use HttpResponseCreated;
pub use HttpResponseDeleted;
pub use HttpResponseFound;
pub use HttpResponseHeaders;
pub use HttpResponseOk;
pub use HttpResponseSeeOther;
pub use HttpResponseTemporaryRedirect;
pub use HttpResponseUpdatedNoContent;
pub use NoHeaders;
pub use RequestContext;
pub use RequestInfo;
pub use CONTENT_TYPE_JSON;
pub use CONTENT_TYPE_NDJSON;
pub use CONTENT_TYPE_OCTET_STREAM;
pub use CONTENT_TYPE_URL_ENCODED;
pub use HEADER_REQUEST_ID;
pub use ConfigLogging;
pub use ConfigLoggingIfExists;
pub use ConfigLoggingLevel;
pub use EmptyScanParams;
pub use PaginationOrder;
pub use PaginationParams;
pub use ResultsPage;
pub use WhichPage;
pub use ServerContext;
pub use ShutdownWaitFuture;
pub use ;
pub use WebsocketChannelResult;
pub use WebsocketConnection;
pub use WebsocketConnectionRaw;
pub use WebsocketEndpointResult;
pub use WebsocketUpgrade;
// Users of the `endpoint` macro need the following macros:
pub use RequestContextArgument;
pub use Method;
extern crate dropshot_endpoint;
pub use channel;
pub use endpoint;