Skip to content

Latest commit

 

History

History
497 lines (380 loc) · 33.9 KB

CHANGELOG.adoc

File metadata and controls

497 lines (380 loc) · 33.9 KB

Dropshot Changelog

Unreleased changes (release date TBD)

0.15.1 (released 2024-12-06)

  • #1194 Fixes a bug introduced in 0.15.0 where the internal message for an error was being sent as the external message.

0.15.0 (released 2024-12-04)

Breaking changes

  • The HttpError type now contains a dropshot::ErrorStatusCode rather than an http::StatusCode. An ErrorStatusCode is a newtype around http::StatusCode that may only be constructed from 4xx or 5xx status codes.

    Code which uses http::StatusCode constants for well-known status codes can be updated to the new API by replacing http::StatusCode::…​ with dropshot::ErrorStatusCode. For example:

    dropshot::HttpError {
        status: http::StatusCode::NOT_FOUND,
        // ...
    }

    becomes:

    dropshot::HttpError {
        status: dropshot::ErrorStatusCode::NOT_FOUND,
        // ...
    }

    To represent extension status codes that lack well-known constants, use ErrorStatusCode::from_u16 (or the corresponding TryFrom implementation). This is analogous to the similarly-named method on http::StatusCode, so this:

    http::StatusCode::from_u16(420).expect("420 is a valid status code")

    becomes this:

    dropshot::ErrorStatusCode::from_u16(420).expect("420 is a valid 4xx status code")

    Finally, note that ErrorStatusCode implements TryFrom<http::StatusCode>, so `StatusCode`s from external sources may be converted into `ErrorStatusCode`s as necessary.

  • The HttpError::for_status constructor, which required that the provided status code be a 4xx client error and panicked if it was not, has been removed. It has been replaced with a new HttpError::for_client_error_with_status constructor, which takes a dropshot::ClientErrorStatusCode type rather than a http::StatusCode. Ensuring that only client errors are passed to this constructor at the type level removes the often-surprising panic on non-4xx errors.

    ClientErrorStatusCode provides constants for each well known 4xx status code, similarly to ErrorStatusCode. Uses of HttpError::for_status that use a constant status code, like this:

    HttpError::for_status(None, http::StatusCode::GONE)

    becomes this:

    HttpError::for_client_error_with_status(None, dropshot::ClientErrorStatusCode::GONE)

    Additionally, ErrorStatusCode provides an as_client_error method that returns a ClientErrorStatusCode if the status code is a client error, or an error.

  • HttpError now contains an Option<Box<http::HeaderMap>> of headers to add to error responses constructed for the error.

    Code that constructs HttpError literals must now initialize this field.

Other notable changes

  • Endpoint handler functions may now return any error type that implements the new dropshot::HttpResponseError trait. Previously, they could only return dropshot::HttpError. This change permits endpoints to return user-defined error types, and generate OpenAPI response schemas for those types.

    For details on how to implement HttpResponseError for user-defined types, see the trait documentation, or examples/custom-error.rs.

0.14.0 (released 2024-12-02)

Breaking changes

  • The request_body_max_bytes config has been renamed to default_request_body_max_bytes. This is to make its semantics clear with respect to per-endpoint request limits.

    Defining the old config option will produce an error, guiding you to perform the rename.

  • Within RequestContext, endpoint-specific metadata has been moved to an endpoint field:

    • rqctx.operation_id is now rqctx.endpoint.operation_id.

    • rqctx.path_variables is now rqctx.endpoint.variables.

    • rqctx.body_content_type is now rqctx.endpoint.body_content_type.

Other notable changes

  • Dropshot now supports per-endpoint size limits, via the request_body_max_bytes parameter to #[endpoint]. For example, to set a limit of 1 MiB on an endpoint:

    #[endpoint {
        method = POST,
        path = "/upload-bundle",
        request_body_max_bytes = 1 * 1024 * 1024,
    }]
    async fn upload_bundle(
        rqctx: RequestContext<MyContext>,  // or RequestContext<Self::Context> with API traits
        body: UntypedBody,
    ) -> /* ... */ {
        // ...
    }

    If not specified, the limit defaults to the server configuration’s default_request_body_max_bytes.

0.13.0 (released 2024-11-13)

Breaking Changes

  • Dropshot now expects that APIs use Semver values for their version string. Concretely, this only means that the version argument to ApiDescription::openapi (which generates an OpenAPI document) must be a semver::Version. Previously, it was AsRef<str>.

  • If you’re invoking ApiEndpoint::new directly or constructing one as a literal (both of which are uncommon), you must provide a new ApiEndpointVersions value describing which versions this endpoint implements. You can use ApiEndpointVersions::All if you don’t care about versioning.

Other notable changes

  • #1122 Adds a new ServerBuilder as the primary way of constructing a Dropshot server. This replaces HttpServerStarter::new() and HttpServerStarter::new_with_tls(). These older functions still exist for compatibility. They may be removed in an upcoming release, along with the HttpServerStarter.

    In this release, using the builder interface is not very different from using these older functions. But as we look at adding new construction-time options (e.g., for API versioning), those will only be added to the builder.

    The builder also provides structured errors rather than the GenericError provided by these older functions.

    Most non-TLS callers were using HttpServerStarter::new() and then calling start() right away. In that case, you can replace:

    HttpServerStarter::new(&config, api, private, &log).map_err(...)?.start()

    with:

    ServerBuilder::new(api, private, log).config(config).start().map_err(...)?

    If you were using HttpServerStarter::new_with_tls(), you’d similarly replace:

    HttpServerStarter::new_with_tls(&config, api, private, &log, tls).map_err(...)?.start()

    with:

    ServerBuilder::new(api, private, log).config(config).tls(tls).start().map_err(...)?

    If you were not invoking start() immediately before, you can still construct an intermediate starter object with build_starter(). If you were doing this:

    let starter = HttpServerStarter::new(&config, api, private, &log).map_err(...)?;
    ...
    starter.start()

    Then you can now do:

    let starter = ServerBuilder::new(api, private, log).config(config).build_starter().map_err(...)?;
    ...
    starter.start()

    We’d like to remove the HttpServerStarter altogether, so let us know if you’re still using it for some reason.

  • #1115 Dropshot now includes experimental support for hosting multiple versions of an API at a single server and routing to the correct version based on the incoming request. See documentation for details. If you don’t care about this, you can mostly ignore it, but see "Breaking Changes" below.

    By "experimental" we only mean that the API may change in upcoming releases.

0.12.0 (released 2024-09-26)

Breaking Changes

  • #1028 Updates Dropshot for hyper 1.0 and http 1.0. Since consumers provide Dropshot with values from hyper and http, you’ll need to update to hyper 1.0 and http 1.0 (or newer compatible versions), too.

Upgrading to hyper 1.0

  1. Update your crate’s dependencies on hyper and http to 1.0 (or a newer compatible version) in Cargo.toml.

  2. Replace any references to hyper::Body with dropshot::Body instead.

  3. You may need to update your use of dropshot::Body; the http-body-util can be helpful.

There are no other known breaking changes in these crates that affect Dropshot. If you have any trouble with this upgrade, please let us know by filing an issue.

0.11.0 (released 2024-08-21)

Breaking Changes

  • For ApiEndpoint::register, the Err variant now returns a structured ApiDescriptionRegisterError rather than a string.

  • #1087 The RequestContext type now contains the operation_id, the name of the endpoint handler. This is the name of the Rust handler function, if one uses the dropshot::endpoint macro, and the value of the operationId field in the generated OpenAPI spec.

  • TagConfig field names have changed, for consistency with tag configuration in API traits. The Deserialize implementation will still work with the old field names, but the Serialize implementation will always produce the new field names.

    • endpoint_tag_policy is now called policy.

    • tag_definitions is now called tags.

  • #1060 Optionally include additional header values in request log

Other notable changes

0.10.1 (released 2024-05-15)

Breaking Changes

None

Other notable changes

  • #965 Improved handling of disconnected clients.

  • #994 Preserve schema extensions in the OpenAPI output.

  • #1003 Work around schemars 0.8.19 behavior change.

  • #1005 Update edition to 2021.

  • #988 Add a spurious, trailing newline to OpenAPI output.

0.10.0 (released 2024-02-06)

Breaking Changes

  • #676 changed how TLS configuration is provided to Dropshot. ConfigDropshotTls is now no longer part of ConfigDropshot. If you’re using TLS, you need to provide this as a separate argument to HttpServerStarter::new_tls(). See #676 for details.

  • #651 The address of the remote peer is now available to request handlers via the RequestInfo struct. With this change we’ve removed the related From<hyper::Request<B>> implementation; instead use RequestInfo::new<B>(&hyper::Request<B>, std::net::SocketAddr).

  • #701 changes how Dropshot manages the tasks that are used to handle requests. There are two modes, now configurable server-wide using HandlerTaskMode. Prior to this change, the behavior matched what’s now called HandlerTaskMode::CancelOnDisconnect: the Future associated with a request handler could be cancelled if, for example, the client disconnected early. After this change, the default behavior is what’s now called HandlerTaskMode::Detached, which causes Dropshot to use tokio::spawn to run the request handler. That task will never be cancelled. This is useful for consumers whose request handlers may not be cancellation-safe.

  • #849 updates rustls to 0.22 which is a breaking change due to the dependency on rustls::ServerConfig. If your server supplies a ServerConfig you will need to apply the appropriate changes.

Other notable changes

  • #660 The x-dropshot-pagination extension used to be simply the value true. Now it is an object with a field, required, that is an array of parameters that are mandatory on the first invocation.

0.9.0 (released 2023-01-20)

Breaking Changes

There are a number of breaking changes in this release but we expect they will be easy to manage. If you have any trouble updating to this release or want help with it, please do start a discussion or file an issue!

  • #558 Remove Arc around RequestContext. Previously, endpoint functions and extractors accepted Arc<RequestContext<T>>. They now accept just RequestContext<T>. This better reflects the intent that the RequestContext is provided for the duration of your endpoint function.

    We expect this to be an annoying (sorry) but otherwise easy change for consumers to make. If it’s tricky for some reason, please file an issue.

    What you need to do:

    1. For every endpoint function, change the type of the first argument from Arc<RequestContext<T>> to RequestContext<T>. In case it’s useful, the following vim command worked to convert most of the cases we’ve seen: %s/Arc<RequestContext<\([^>]*\)>>/RequestContext<\1>/gc.

    2. For any type you’ve defined that impls Extractor, you will need to adjust the arguments similarly. See the next bullet item to fix these for both this change and #556.

  • #556 Better type-safety around the use of extractors. It is now a compile-time error to define an endpoint that accepts two extractors that use the HTTP request body (e.g., to accept both a TypedBody and an UntypedBody, or two TypedBody arguments). Previously, this would have resulted in a runtime error. The main change is that the Extractor trait has been split into two separate traits: SharedExtractor and ExclusiveExtractor. Endpoint functions can still accept 0-3 extractors, but only one can be an ExclusiveExtractor and it must be the last one. The function signatures for *Extractor::from_request have also changed.

    What you need to do:

    1. For any endpoint functions that use a TypedBody, UntypedBody, or WebsocketConnection extractor, this extractor must be the last argument to the function. Otherwise, you will get a compile error about the extractor not impl’ing SharedExtractor.

    2. If you have your own type that impls Extractor, you will need to change that to either ExclusiveExtractor (if the impl needs a mut reference to the underlying hyper::Request, which is usually because it needs to read the request body) or SharedExtractor. If your extractor only needs to look at the URL or request headers and not the body, it can probably be a SharedExtractor. If it’s an exclusive extractor, any function that accepts it must accept it as the last argument to the function.

    3. Again if you have your own type that impls Extractor, having now updated it to either SharedExtractor or ExclusiveExtractor, you will also need to change the type signature of the from_request method to accept a &RequestContext<T> instead of Arc<RequestContext<T>>. (This should not be a problem unless your extractor was hanging on to a reference via the Arc. We don’t know a reason this would be useful. If you were doing this, please start a discussion or file an issue. In the meantime, you likely can copy whatever information you need out of the RequestContext rather than cloning the Arc.)

  • #557 Simpler, safer access to raw request. Prior to this change, the raw hyper::Request (http::Request) was accessible to endpoint functions via the RequestContext, but behind an Arc<Mutex<…​>>. This was a little strange because your endpoint function was usually the only one with a reference to this object. (You could get into trouble if you defined your own Extractor that cloned one of the Arc objects — your extractor could deadlock with the handler.) After this change, the raw request is available only through a separate RawRequest extractor. This is an exclusive extractor, which means you cannot use it with TypedBody or UntypedBody. As a result, there is no way to wind up with multiple references to the request. There’s no lock and no way to get into this sort of trouble.

    After this change, the hyper::Request is passed as a separate argument to ExclusiveExtractor::from_request().

    What you need to do:

    1. If you have a request handler that accesses rqctx.request, it’s typically doing let request = rqctx.request.lock().await.

      1. If that code is only accessing the HTTP method, URI, headers, or version, then you can skip this step. However, it’s recommended that you replace that with let request = &rqctx.request. (That object has methods compatible with http::Request for accessing the method, URI, headers, and version.)

      2. If that code is accessing other parts of the request (e.g., reading the body or doing a protocol upgrade), then you must instead add a raw_request: RawRequest argument to your endpoint function. Then you can use let request = raw_request.into_inner().

    2. If you have an extractor that access rqctx.request, then it too is typically doing something like let request = rqctx.request.lock().await.

      1. If that code is only accessing the HTTP method, URI, headers, or version, then just like above you can skip this step, but it’s recommended that you replace that with let request = &rqctx.request. This can be done from a SharedExtractor or an ExclusiveExtractor.

      2. If that code is accessing other parts of the request (e.g., reading the body or doing a protocol upgrade), then this extractor must impl ExclusiveExtractor (not SharedExtractor). With ExclusiveExtractor, the hyper::Request is available as an argument to from_request().

  • #504 Dropshot now allows TLS configuration to be supplied either by path or as bytes. For compatibility, the AsFile variant of ConfigTls contains the cert_file and key_file fields, and may be used similarly to the old variant.

  • #502 Dropshot exposes a refresh_tls method to update the TLS certificates being used by a running server.

    What you need to do: If you previously tried to access DropshotState.tls, you can access the DropshotState.using_tls() method instead.

  • #540 ConfigDropshot now uses a camino::Utf8PathBuf for its file path. There is no change to the configuration format itself, just its representation in Rust.

We realize this was a lot of breaking changes. We expect that most of these will affect few people (there don’t seem to be a lot of custom extractor impls out there). The rest are pretty mechanical. We hope the result will be a safer, easier to use API.

Other notable changes

  • #522 Dropshot’s DTrace probes can now be used with a stable compiler on all platforms. This requires Rust >= 1.59 for most platforms, or >= 1.66 for macOS.

  • #452 Dropshot no longer enables the slog cargo features max_level_trace and release_max_level_debug. Previously, clients were unable to set a release log level of trace; now they can. However, clients that did not select their own max log levels will see behavior change from the levels Dropshot was choosing to the default levels of slog itself (debug for debug builds and info for release builds).

  • #451 There are now response types to support 302 ("Found"), 303 ("See Other"), and 307 ("Temporary Redirect") HTTP response codes. See HttpResponseFound, HttpResponseSeeOther, and HttpResponseTemporaryRedirect.

  • 503 Add an optional deprecated field to the [endpoint] macro.

0.8.0 (released 2022-09-09)

Breaking Changes

  • #403 Dropshot now supports WebSockets. See the docs for details.

    As part of this, the ExtractorMetadata type has been changed to represent our nonstandard extensions to OpenAPI in a field extension_mode: ExtensionMode, rather than paginated: bool, which was previously our only nonstandard extension, but is now joined by WebSockets.

    In any existing code that checked extractor_metadata.paginated, you can instead check that extractor_metadata.extension_mode is ExtensionMode::Paginated.

  • #351 The uuid crate has been updated to version 1.0.0 from 0.8.0. Consumers will need to update to a compatible version of uuid. In addition consumers that were using the uuid feature flag of the schemars crate (so that uuid::Uuid implements schemars::JsonSchema) will need to use the uuid1 feature flag instead to force the use of uuid version 1.0.0.

Other notable changes

  • #363 You can now decode application/x-www-form-urlencoded bodies by specifying the content_type property when you invoke the endpoint macro. See docs for details.

  • #370 You can now define handlers for the OPTIONS HTTP method.

  • #420 Handlers can now determine whether the request came in over HTTP or HTTPS using rqctx.server.tls.

0.7.0 (released 2022-05-06)

Breaking Changes

  • #197 Endpoints using wildcard path params (i.e. those using the /foo/{bar:.*} syntax) previously could be included in OpenAPI output albeit in a form that was invalid. Specifying a wildcard path without also specifying unpublished = true is now a compile-time error.

  • #204 Rust 1.58.0-nightly introduced a new feature asm_sym which the usdt crate requires on macOS. As of this change 1.58.0-nightly or later is required to build with the usdt-probes feature on macOS.

  • #310 changed the name of HttpResponse::metadata() to HttpResponse::response_metadata().

Other notable changes

  • #198 Responses that used () (the unit type) as their Body type parameter previously (and inaccurately) were represented in OpenAPI as an empty responseBody. They are now more accurately represented as a body whose value is null (4 bytes). We encourage those use cases to instead use either HttpResponseUpdatedNoContent or HttpResponseDeleted both of which have empty response bodies. If there are other situations where you would like a response type with no body, please file an issue.

  • 252 Endpoints specified with the #[endpoint ..] attribute macro now use the first line of a doc comment as the OpenAPI summary and subsequent lines as the description. Previously all lines were used as the description.

  • #260 Pulls in a newer serde that changes error messages around parsing NonZeroU32.

  • #283 Add support for response headers with the HttpResponseHeaders type. Headers may either be defined by a struct type parameter (in which case they appear in the OpenAPI output) or ad-hoc added via HttpResponseHeaders::headers_mut().

  • #286 OpenAPI output includes descriptions of 4xx and 5xx error responses.

  • #296 ApiDescription includes a tag_config method to specify both predefined tags with descriptions and links as well as a tag policy to ensure that endpoints, for example, only use predefined tags or have at least one tag.

  • #317 Allow use of usdt probes with stable Rust. Dropshot consumers can build with USDT probes enabled on stable compilers >= 1.59 (except on MacOS).

  • #310 Freeform (and streaming) response bodies may be specified with specific HTTP response codes e.g. by having an endpoint return Result<HttpResponseOk<FreeformBody>, HttpError>.

    • #325 The example field (if present) for JsonSchema objects in the API will be present in the OpenAPI output (and note that no validation of the example is performed)

0.6.0 (released 2021-11-18)

Breaking Changes

  • #100 The type used for the "limit" argument for paginated resources has changed. This limit refers to the number of items that an HTTP client can ask for in a single request to a paginated endpoint. The limit is now 4294967295, where it may have previously been larger. This is not expected to affect consumers because this limit is far larger than practical. For details, see #100.

  • #116 Unused, non-pub endpoints from the #[endpoint { …​ }] macro now produce a lint warning. This is technically a breaking change for those who may have had unused endpoints and compiled with #[deny(warning)] or #[deny(dead_code)] thus implicitly relying on the absence of a warning about the endpoint being unused.

  • #118 Path handling has changed. Escape sequences are decoded so that path parameters will no longer include those escape sequences. In addition, paths for endpoints added via ApiDescription::register() may not contain consecutive "/" characters.

  • #161 The ApiDescription::print_openapi() interface (previously deprecated) has been removed. Now use ApiDescription::openapi() followed by a call to OpenApiDefinition::write() for equivalent functionality.

  • #103 When the Dropshot server is dropped before having been shut down, Dropshot now attempts to gracefully shut down rather than panic.

Other notable changes

  • #105 When generating an OpenAPI spec, Dropshot now uses references rather than inline schemas to represent request and response bodies.

  • #110 Wildcard paths are now supported. Consumers may take over routing (e.g. for file serving) by annotating a path component: /static/{path:.*}. The path member should then be of type Vec<String> and it will be filled in with all path components following /static/.

  • #148 Adds local/remote addresses to loggers, including those passed in the context to actual endpoint handlers. This fixes #46, allowing logs for a client to be correlated from connection to completion.

  • #164 Add make_request_with_request to test utils alongside existing make_request_with_body. The caller can specify things like headers by passing in a request.

  • #160 Adds DTrace USDT probes for a request start and finish, with details about the request and response. For more information, see the crate-level documentation.

  • #108 The use of permissive schemas (e.g. serde_json::Value) in API types is allowed.

  • #123 and #133 add several checks on endpoint function signatures.

  • #128 The use of newtype structs in path and query parameters is now supported.

0.5.1 (released 2021-03-18)

  • Fixes the dependency on the openapiv3 crate. Because of this problem, builds against Dropshot 0.5.0 will not work.

0.5.0 (released 2021-03-03)

Warning
This release does not build due to downstream dependencies. See 0.5.1.

Breaking Changes

Generic Context

  • #86 Dropshot now uses generics to store client context, rather than relying on an internal Any object within RequestContext. Endpoints signatures are expected to begin with the argument rqctx: Arc<RequestContext<CallerContext>>, for some CallerContext object, and they may call rqtcx.context() to access the inner type.

  • To provide this generic context, many Dropshot types are now generic, acting on a specialized context object (this includes ApiDescription, ApiEndpoint, OpenApiDefinition, HttpServer, HttpServerStarter, and RequestContext). For the most part, the specialization is made implicit by passing the context argument to an HttpServerStarter (formerly HttpServer).

struct ExampleContext { ... }

// Old Version:
#[endpoint { method = GET, path = "/endpoint" }]
pub async fn example_endpoint(
    rqctx: Arc<RequestContext>,
) -> Result<HttpResponseOk<...>, HttpError> {
    let ctx: Arc<dyn Any + Send + Sync + 'static> = Arc::clone(&rqctx.server.private);
    let example_context = ctx.downcast::<ExampleContext>().expect("Wrong type");
    ...
}

// New Version
#[endpoint { method = GET, path = "/endpoint" }]
pub async fn example_endpoint(
    rqctx: Arc<RequestContext<ExampleContext>>,
) -> Result<HttpResponseOk<...>, HttpError> {
    let example_context = rqctx.context();
    ...
}

HttpServer

HttpServer Split in Two
  • In the old implementation, HttpServer represented both a pending and running server. Callers were expected to invoke run() to begin execution of the old server.

  • In the new implementation, HttpServerStarter may be used to construct a server, and HttpServer represents the running server. Invoking HttpServerStarter::start() creates and HttpServer object, which represents the new server.

HttpServer implements Future
  • In the old implementation, HttpServer returned a tokio::JoinHandle, and callers were expected to invoke wait_for_shutdown to await the completion of a server.

  • In the new implementation, HttpServer implements Future, and may be await-ed directly.

Example
// Old Version:
let mut server = HttpServer::new( /* Arguments are the same between versions */ )
  .map_err(|error| format!("failed to start server: {}", error))?;

let server_task = server.run();
server.wait_for_shutdown(server_task).await;

// New Version
let server = HttpServerStarter::new( /* Arguments are the same between versions */ )
  .map_err(|error| format!("failed to start server: {}", error))?
  .start();

server.await;

Notable changes

  • #44 The new extractor UntypedBody allows API endpoints to accept either raw bytes or a UTF-8 string.

  • #90 HttpError now impls std::error::Error.

0.4.0 (released 2021-02-01)

Breaking changes

  • Dropshot now uses tokio 1.0 and hyper 0.14. tokio 1.0 is incompatible at runtime with previous versions (0.2 and earlier). Consumers must update to tokio 1.0 when updating to Dropshot {{version}}. tokio does not expect to introduce new breaking changes in the foreseeable future, so we do not expect to have to do this again.

Deprecated

  • ApiDescription::print_openapi() is now deprecated. It’s been replaced with ApiDescription::openapi(). See #68 below.

Other notable changes

  • #68 Improve ergonomics of OpenAPI definition generation. This change deprecates ApiDescription::print_openapi(), replacing it with the easier-to-use ApiDescription::openapi(), which provides a builder interface.

  • #64 The maximum request size is now configurable. It defaults to the previously hardcoded value of 1024 bytes. (The default is aggressive just to ensure test coverage.)

  • #61 The schemars dependency is updated to 0.8. Consumers must be using the same version of schemars. (See #67.)

Prior to 0.3.0

Changes not documented.