- Unreleased changes (release date TBD)
- 0.15.1 (released 2024-12-06)
- 0.15.0 (released 2024-12-04)
- 0.14.0 (released 2024-12-02)
- 0.13.0 (released 2024-11-13)
- 0.12.0 (released 2024-09-26)
- 0.11.0 (released 2024-08-21)
- 0.10.1 (released 2024-05-15)
- 0.10.0 (released 2024-02-06)
- 0.9.0 (released 2023-01-20)
- 0.8.0 (released 2022-09-09)
- 0.7.0 (released 2022-05-06)
- 0.6.0 (released 2021-11-18)
- 0.5.1 (released 2021-03-18)
- 0.5.0 (released 2021-03-03)
- 0.4.0 (released 2021-02-01)
- Prior to 0.3.0
-
#1194 Fixes a bug introduced in 0.15.0 where the internal message for an error was being sent as the external message.
-
The
HttpError
type now contains adropshot::ErrorStatusCode
rather than anhttp::StatusCode
. AnErrorStatusCode
is a newtype aroundhttp::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 replacinghttp::StatusCode::…
withdropshot::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 correspondingTryFrom
implementation). This is analogous to the similarly-named method onhttp::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
implementsTryFrom<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 newHttpError::for_client_error_with_status
constructor, which takes adropshot::ClientErrorStatusCode
type rather than ahttp::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 toErrorStatusCode
. Uses ofHttpError::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 anas_client_error
method that returns aClientErrorStatusCode
if the status code is a client error, or an error. -
HttpError
now contains anOption<Box<http::HeaderMap>>
of headers to add to error responses constructed for the error.Code that constructs
HttpError
literals must now initialize this field.
-
Endpoint handler functions may now return any error type that implements the new
dropshot::HttpResponseError
trait. Previously, they could only returndropshot::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, orexamples/custom-error.rs
.
-
The
request_body_max_bytes
config has been renamed todefault_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 anendpoint
field:-
rqctx.operation_id
is nowrqctx.endpoint.operation_id
. -
rqctx.path_variables
is nowrqctx.endpoint.variables
. -
rqctx.body_content_type
is nowrqctx.endpoint.body_content_type
.
-
-
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
.
-
Dropshot now expects that APIs use Semver values for their version string. Concretely, this only means that the
version
argument toApiDescription::openapi
(which generates an OpenAPI document) must be asemver::Version
. Previously, it wasAsRef<str>
. -
If you’re invoking
ApiEndpoint::new
directly or constructing one as a literal (both of which are uncommon), you must provide a newApiEndpointVersions
value describing which versions this endpoint implements. You can useApiEndpointVersions::All
if you don’t care about versioning.
-
#1122 Adds a new
ServerBuilder
as the primary way of constructing a Dropshot server. This replacesHttpServerStarter::new()
andHttpServerStarter::new_with_tls()
. These older functions still exist for compatibility. They may be removed in an upcoming release, along with theHttpServerStarter
.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 callingstart()
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 withbuild_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.
-
#1028 Updates Dropshot for
hyper
1.0 andhttp
1.0. Since consumers provide Dropshot with values fromhyper
andhttp
, you’ll need to update tohyper
1.0 andhttp
1.0 (or newer compatible versions), too.
-
Update your crate’s dependencies on
hyper
andhttp
to 1.0 (or a newer compatible version) in Cargo.toml. -
Replace any references to
hyper::Body
withdropshot::Body
instead. -
You may need to update your use of
dropshot::Body
; thehttp-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.
-
For
ApiEndpoint::register
, theErr
variant now returns a structuredApiDescriptionRegisterError
rather than a string. -
#1087 The
RequestContext
type now contains theoperation_id
, the name of the endpoint handler. This is the name of the Rust handler function, if one uses thedropshot::endpoint
macro, and the value of theoperationId
field in the generated OpenAPI spec. -
TagConfig
field names have changed, for consistency with tag configuration in API traits. TheDeserialize
implementation will still work with the old field names, but theSerialize
implementation will always produce the new field names.-
endpoint_tag_policy
is now calledpolicy
. -
tag_definitions
is now calledtags
.
-
-
#1060 Optionally include additional header values in request log
-
Trait-based API definitions. See the documentation for details.
-
#1049 Added
HttpResponse::status_code()
-
#676 changed how TLS configuration is provided to Dropshot.
ConfigDropshotTls
is now no longer part ofConfigDropshot
. If you’re using TLS, you need to provide this as a separate argument toHttpServerStarter::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 relatedFrom<hyper::Request<B>>
implementation; instead useRequestInfo::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 calledHandlerTaskMode::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 calledHandlerTaskMode::Detached
, which causes Dropshot to usetokio::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 aServerConfig
you will need to apply the appropriate changes.
-
#660 The
x-dropshot-pagination
extension used to be simply the valuetrue
. Now it is an object with a field,required
, that is an array of parameters that are mandatory on the first invocation.
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
aroundRequestContext
. Previously, endpoint functions and extractors acceptedArc<RequestContext<T>>
. They now accept justRequestContext<T>
. This better reflects the intent that theRequestContext
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:
-
For every endpoint function, change the type of the first argument from
Arc<RequestContext<T>>
toRequestContext<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
. -
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 anUntypedBody
, or twoTypedBody
arguments). Previously, this would have resulted in a runtime error. The main change is that theExtractor
trait has been split into two separate traits:SharedExtractor
andExclusiveExtractor
. Endpoint functions can still accept 0-3 extractors, but only one can be anExclusiveExtractor
and it must be the last one. The function signatures for*Extractor::from_request
have also changed.What you need to do:
-
For any endpoint functions that use a
TypedBody
,UntypedBody
, orWebsocketConnection
extractor, this extractor must be the last argument to the function. Otherwise, you will get a compile error about the extractor not impl’ingSharedExtractor
. -
If you have your own type that impls
Extractor
, you will need to change that to eitherExclusiveExtractor
(if the impl needs amut
reference to the underlyinghyper::Request
, which is usually because it needs to read the request body) orSharedExtractor
. If your extractor only needs to look at the URL or request headers and not the body, it can probably be aSharedExtractor
. If it’s an exclusive extractor, any function that accepts it must accept it as the last argument to the function. -
Again if you have your own type that impls
Extractor
, having now updated it to eitherSharedExtractor
orExclusiveExtractor
, you will also need to change the type signature of thefrom_request
method to accept a&RequestContext<T>
instead ofArc<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 theRequestContext
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 theRequestContext
, but behind anArc<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 theArc
objects — your extractor could deadlock with the handler.) After this change, the raw request is available only through a separateRawRequest
extractor. This is an exclusive extractor, which means you cannot use it withTypedBody
orUntypedBody
. 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 toExclusiveExtractor::from_request()
.What you need to do:
-
If you have a request handler that accesses
rqctx.request
, it’s typically doinglet request = rqctx.request.lock().await
.-
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 withhttp::Request
for accessing the method, URI, headers, and version.) -
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 uselet request = raw_request.into_inner()
.
-
-
If you have an extractor that access
rqctx.request
, then it too is typically doing something likelet request = rqctx.request.lock().await
.-
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 aSharedExtractor
or anExclusiveExtractor
. -
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
(notSharedExtractor
). WithExclusiveExtractor
, thehyper::Request
is available as an argument tofrom_request()
.
-
-
-
#504 Dropshot now allows TLS configuration to be supplied either by path or as bytes. For compatibility, the
AsFile
variant ofConfigTls
contains thecert_file
andkey_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 theDropshotState.using_tls()
method instead. -
#540
ConfigDropshot
now uses acamino::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.
-
#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 featuresmax_level_trace
andrelease_max_level_debug
. Previously, clients were unable to set a release log level oftrace
; 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 ofslog
itself (debug
for debug builds andinfo
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
, andHttpResponseTemporaryRedirect
. -
503 Add an optional
deprecated
field to the[endpoint]
macro.
-
#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 fieldextension_mode: ExtensionMode
, rather thanpaginated: 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 thatextractor_metadata.extension_mode
isExtensionMode::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 ofuuid
. In addition consumers that were using theuuid
feature flag of theschemars
crate (so thatuuid::Uuid
implementsschemars::JsonSchema
) will need to use theuuid1
feature flag instead to force the use ofuuid
version 1.0.0.
-
#363 You can now decode
application/x-www-form-urlencoded
bodies by specifying thecontent_type
property when you invoke theendpoint
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
.
-
#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 specifyingunpublished = true
is now a compile-time error. -
#204 Rust 1.58.0-nightly introduced a new feature
asm_sym
which theusdt
crate requires on macOS. As of this change 1.58.0-nightly or later is required to build with theusdt-probes
feature on macOS. -
#310 changed the name of
HttpResponse::metadata()
toHttpResponse::response_metadata()
.
-
#198 Responses that used
()
(the unit type) as theirBody
type parameter previously (and inaccurately) were represented in OpenAPI as an emptyresponseBody
. They are now more accurately represented as a body whose value isnull
(4 bytes). We encourage those use cases to instead use eitherHttpResponseUpdatedNoContent
orHttpResponseDeleted
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 OpenAPIsummary
and subsequent lines as thedescription
. Previously all lines were used as thedescription
. -
#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 viaHttpResponseHeaders::headers_mut()
. -
#286 OpenAPI output includes descriptions of 4xx and 5xx error responses.
-
#296
ApiDescription
includes atag_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)
-
-
#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 useApiDescription::openapi()
followed by a call toOpenApiDefinition::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.
-
#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:.*}
. Thepath
member should then be of typeVec<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 existingmake_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.
-
Fixes the dependency on the
openapiv3
crate. Because of this problem, builds against Dropshot 0.5.0 will not work.
Warning
|
This release does not build due to downstream dependencies. See 0.5.1. |
-
#86 Dropshot now uses generics to store client context, rather than relying on an internal
Any
object withinRequestContext
. Endpoints signatures are expected to begin with the argumentrqctx: Arc<RequestContext<CallerContext>>
, for someCallerContext
object, and they may callrqtcx.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
, andRequestContext
). For the most part, the specialization is made implicit by passing the context argument to anHttpServerStarter
(formerlyHttpServer
).
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();
...
}
See #81 for details
-
In the old implementation,
HttpServer
represented both a pending and running server. Callers were expected to invokerun()
to begin execution of the old server. -
In the new implementation,
HttpServerStarter
may be used to construct a server, andHttpServer
represents the running server. InvokingHttpServerStarter::start()
creates andHttpServer
object, which represents the new server.
-
In the old implementation,
HttpServer
returned atokio::JoinHandle
, and callers were expected to invokewait_for_shutdown
to await the completion of a server. -
In the new implementation,
HttpServer
implementsFuture
, and may beawait
-ed directly.
// 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;
-
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.
-
ApiDescription::print_openapi()
is now deprecated. It’s been replaced withApiDescription::openapi()
. See #68 below.
-
#68 Improve ergonomics of OpenAPI definition generation. This change deprecates
ApiDescription::print_openapi()
, replacing it with the easier-to-useApiDescription::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.)