- Notifications
You must be signed in to change notification settings - Fork 1.4k
Description
Summary
hapi v15.0.0 is a medium size release focused on refactoring the server events facilities, security enhancements, and improved extensibility. The events improvement introduce a whole new set of features to filter existing events, as well as build application specific events using a new application message bus using the podium module.
- Upgrade time: medium - a couple of hours to a day or two for most users
- Complexity: medium - requires following the list of changes to verifying their impact
- Risk: medium - changes to how events are emitted and changes in some defaults require testing
- Dependencies: low - most existing plugins will work as-is
Sponsor
The v15.0.0 major release is sponsored by Benjamin Flesch and StriveWire. StriveWire is currently hiring full stack developers in Hamburg, Germany.
Breaking Changes
- Replace node's built-in
EventEmitterwith podium. - Change default cookies to use the
HttpOnly,Secure, andSameSite=Strictproperties by default to any cookie set via thestate()method. - Error when
reply()is called with a third argument when not used in theauthenticate()authentication strategy method. - Disable
request.getLog()by default, prevent memory allocation when the request logs are not needed. - Force
server.register()to always return the callback after the next tick. - Override route validation rules when defined at the route level. The last configuration is the only one used for each route, ignoring any connection or server level defaults.
- Error when
reply.continue()is called with an argument when not used in theauthenticate()authentication strategy method unless called in extension methods after the handler is called.
New Features
server.emit(criteria, data, [callback])andserver.event(events)methods for application events.server.on(criteria, listener)andserver.once(criteria, listener)support for new advanced subscription criteria (tags filter, channels, update clone, data spread, and performance optimizations for costly events without listeners).- Support for the new cookie
SameSiteproperty. server.encoder(encoding, encoder)andserver.decoder(encoding, decoder)for custom content encoding support.- Expose route authorization access logic via
route.auth.access(). - Expose authorization scope errors.
- Support
failActionin response validation, similar to request validation. reply().message()to set the HTTP response message.- Control the logging of request-level events.
- Control the input validation of
server.inject()requests for improved performance. - Extensions
'onPostHandler'and'onPreResponse'can override the response usingreply.continue()without ending the request lifecycle.
Bug fixes
- Always set 'Vary' header except when compression is disabled or not applicable to the response type.
- Fix 'Content-Type' parameters separator.
- Better handing for rejection errors in promises.
- Rely on node's header validation instead of a more limited rule in hapi.
- Prevent merging of route validation rules to prevent unexpected results.
- Ensure 206 responses (ranged) are not compressed to avoid corrupted responses.
- Improve performance of
server.start()andserver.stop()when server has multiple connections (replace serial to parallel processing). - Set route validation bind context for custom validation functions.
- Ensure HEAD responses include all the GET headers.
Updated dependencies
- statehood from v4.0.3 to v5.0.0
- boom from v3.2.2 to v4.0.0
- iron from v4.0.2 to v4.0.3
- ammo from v2.0.1 to v2.0.2
- call from v3.0.2 to v3.0.3
- wreck from v8.0.1 to v9.0.0
- subtext from v4.0.5 to v4.2.0
- shot from v3.1.1 to v3.3.1
Migration Checklist
Events
The event emitters used by the request, response, server, and connection interfaces have been replaced to use the new podium module instead of node's built-in EventEmitter. This was done to provide the framework full control over the events interface and support an array of new features.
The following EventEmitter methods and properties are no longer supported:
newListenerandremoveListenereventslistenerCount()defaultMaxListenerseventNames()getMaxListeners()listenerCount()listeners()prependListener()prependOnceListener()setMaxListeners()
The following methods are compatible with new features:
server.on(criteria, listener)server.addListener(criteria, listener)server.once(criteria, listener)
The following method is partially compatible:
server.emit(criteria, data, [callback])- the method only accept a singledataargument for emitting events. In order to emit multiple argument, the event must be registered with thespreadoption and thedataargument passed an array of the arguments applied to the listeners.
The following methods are unchanged:
removeListener(name, listener)removeAllListeners(name)
And adds the following methods:
server.event(events)- the method must be called beforeserver.emit()is called to emit application-specific events.
Checklist:
- If you only use
server.on(),server.once(),server.addListener(), or the other unmodified methods, your code should work as-is without changes. - Verify you do not use any of the methods no longer supported.
- If you "abused" the emitters to emit your own application events, you will now need to properly register those events using the new
server.event()method. You will also have to note the changes toserver.emit()and if you need to pass more than one argument to the event listeners, to use thespreadoptions and pass an array asdata. If you relied on the return value ofserver.emit()it is no longer present.
Default cookie properties
This release changes the default cookie settings to use the HttpOnly, Secure, and SameSite=Strict properties by default to any cookie set via the state() method. Note that cookies set directly with reply().header() are not affected.
While discouraged, to retain the previous behavior (v14 and older), set the server defaults:
const options = { connections: { state: { isHttpOnly: false, isSecure: false, isSameSite: false } } }; const server = new Server(options);Checklist:
- Review all your cookie settings, including those used by other plugins (e.g. yar, hapi-auth-cookie, etc.) and ensure the new defaults are compatible with your needs.
- Where possible, use the new defaults for enhanced security.
- Existing explicit values to the above three options are unaffected.
reply() interface
There are three changes to the reply() interface:
- Calling
reply()with a third argument when not used in theauthenticate()authentication strategy method throws an exception. - Calling
reply.continue()with an argument when not used in theauthenticate()authentication strategy method throws an exception, except - - When calling
reply.continue()with an argument in the'onPostHandler'and'onPreResponse'extension methods, the argument is no longer ignored and is used to override the existing response. However, unlikereply(), it does not terminate the request lifecycle, executing any other extension handlers left.
Checklist:
- Review your code for any place where
reply()is called with more than two arguments.replyis often passed as the callback argument to other methods, including server methods. If those callback signature include additional arguments, the call will throw an exception. Note that cached server methods include a thirdttlargument which will cause it to throw an exception. To avoid errors, replacereplywith(err, result) => reply(err, result)when passingreplydirectly as a callback function. This will ensure only the first two argument are passed. - Ensure
reply.continue()is never used with an argument except for theauthenticate()method. You can use the new response override feature but first must ensure existing code works as expected.
Request logging
The request.getLog() is disabled by default, prevent memory allocation when the request logs are not needed. The new route log configuration option enables the logging.
To retain the previous behavior (v14 and older), set the server defaults:
const options = { connections: { routes: { log: true } } }; const server = new Server(options);Checklist:
- Search your code for
getLogand if found, ensure the route or server is configured to enable it using the routelogoption.
Misc
Checklist:
- Ensure calls to
server.register()do not rely on the callback to be called on the same tick when the pluginregister()method returns on the same tick. - Check if you specify route validation rules at both the server/connection level and the individual route level. If you do, note that the route level will completely override the global configuration per input source (e.g. overriding
headershas no impact onparams). If a route specifies its own validation rules, those rules must explicitly include the global rules as they will completely override them.