Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

12.0.0. Release Notes #2985

Closed
hueniverse opened this issue Dec 23, 2015 · 6 comments
Closed

12.0.0. Release Notes #2985

hueniverse opened this issue Dec 23, 2015 · 6 comments

Comments

@hueniverse
Copy link
Contributor

@hueniverse hueniverse commented Dec 23, 2015

Summary

hapi v12.0.0 is a small release focused on removing the framework dependency on the qs module. qs is a URL query string parser with special support for representing complex structures in a simple key=value format. Normally, the query a[b]=1 will parse into the object { "a[b]": "1" }, however when passed through the qs module it results in { a: { b: "1" } }. This release removes this feature and reverts it back to simple query string parsing support as well as similar functionality when parsing form-encoded payloads and multipart form submissions (field names). Additional changes include supporting more complex authentication scopes and a minor change to server.inject().

  • Upgrade time: low - no time to a couple of hours for most users
  • Complexity: low - requires a few small code changes for those using the query format
  • Risk: low - low risk of side effects and few changes to keep track of overall
  • Dependencies: low- changes a single API call

Sponsor


The v12.0.0 major release is sponsored by Sideway.

Breaking Changes

  • Removed support for the processing of special form-encoded key=value pairs in query strings, form-encoded payloads, and multipart field names.
  • Removes the connection query.qs configuration option.
  • Removes the route payload.qs configuration option.
  • Removes the parserOptions argument from the request.setUrl() method.
  • Authentication scope strings cannot begin with ! or + as those prefix characters now have a special meaning.
  • request.route (the route public interface) settings.auth changed to move scope and entity inside a new access array.
  • When using server.inject(), any HTTP trailers are no longer included in res.headers but instead are provided under res.trailers to be consistent with node.
  • Removed request.session and request.auth.session placeholders (was set to null before).

New Features

  • Allow passing a pre-processed URL object (from node's URL parse()) to request.setUrl().
  • Support new required and forbidden authentication scopes.
  • Allow specifying multiple scope/entity sets for a single endpoint.
  • New pendingGenerateTimeout cache option for reducing calls to the generate method while another is already pending.
  • Return a Promise when a callback is not provided.
  • Expose CORS origin match status in request.info.cors.isOriginMatch.
  • Expose entire request.auth object in validation context.

Bug fixes

  • Allow setting dynamic scope in server.auth.default()
  • Prevent request.raw.res.end() from being called twice.

Updated dependencies

  • subtext from v3.0.1 to v4.0.0
  • catbox from v7.0.0 to v7.1.0
  • shot from v2.0.1 to v3.0.0
  • mime-db from v1.19.0 to v1.20.0
  • moment from v2.10.6 to v2.11.0
  • isemail from v2.0.0 to v2.1.0
  • joi from v7.0.0 to v7.1.0
  • hoek from v3.0.0 to v3.0.4
  • boom from v3.0.0 to v3.1.1
  • accept from v2.0.0 to v2.1.0
  • statehood from v3.0.0 to v3.1.0

Migration Checklist

qs

You don't have to change anything if you are not using the special qs format in:

  • Incoming requests URL query string (the part between the ? and the #).
  • Form-encoded payloads (typically from web forms using the POST method).
  • Multipart payloads field names (typically from web forms using the POST method with file upload).

There are a few easy ways of identifying if you are expecting this special format:

  • you have field names in web forms which include the [] characters (as well as . if you enable that custom feature of qs).
  • your hapi configuration is setting either of the two qs configuration options or you pass a third argument to request.setUrl().
  • you have route payload or query validation rules which include nested objects (e.g. object with sub objects like { a: { b: 1 } }). If this is set on a payload rule, check if you expect form-encoded submissions to that endpoint along with normal JSON.

Checklist:

  • Figure out if you are using the qs features removed in this release.
  • If you are not, test the release as usual but there are no further steps.
  • If you are using qs, follow these instructions:

Check out the new hapi-qs plugin which incorporates the code below into a simple plugin form. If you prefer to use custom code, apply the code snippets below.

If you want to parse qs formatted query strings, add this to your server code:

const Url = require('url');
const Qs = require('qs');

const onRequest = function (request, reply) {

    const uri = request.raw.req.url;
    const parsed = Url.parse(uri, false);
    parsed.query = Qs.parse(parsed.query);
    request.setUrl(parsed);

    return reply.continue();
};

server.ext('onRequest', onRequest);

If you previously used the connection.query.qs configuration option, pass that setting to the Qs.parse() method above.

If you want to parse qs formatted field names in payloads, add this to your server code:

const Qs = require('qs');

const onPostAuth = function (request, reply) {

    if (typeof request.payload === 'object' &&
        !Buffer.isBuffer(request.payload)) {

        request.payload = Qs.parse(request.payload);
    }

    return reply.continue();
};

server.ext('onPostAuth', onPostAuth);

If you previously used the route.payload.qs configuration option, pass that setting to the Qs.parse() method above.

Note that these code examples are based on simple setups and may require adjustments for your environment.

Authentication scope and entity changes

While the existing route authentication configuration is still supported (for the time being), the scope and entity keys moved from config.auth to config.auth.access. For example:

server.route({
    method: 'GET',
    path: '/',
    handler: handlerFunc,
    config: {
        auth: {
            mode: 'required',
            strategy: 'default',
            entity: 'user',
            scope: ['account']
        }
    }
}

Should now be expressed as:

server.route({
    method: 'GET',
    path: '/',
    handler: handlerFunc,
    config: {
        auth: {
            mode: 'required',
            strategy: 'default',
            access: {
                entity: 'user',
                scope: ['account']
            }
        }
    }
}

The access config can now take an array of objects, each providing a different combination of entity and scopes. This is useful if you want to require a different scope for application accounts from user accounts.

The scope config now supports two special prefix characters: + for required scope strings and ! for forbidden scope strings. For example, the scope ['!a', '+b', 'c', 'd'] means the incoming request credentials' scope must not include 'a', must include 'b', and must include one of 'c' or 'd'.

Checklist:

  • Look for scope configs in your code and make sure you do not use + or ! in your strings.
  • Move entity and scope include access to prepare for future changes (nor required at this point).
  • If you access the the route public interface via request.route, server.handler(), server.lookup(), server.match(), or server.on('route'), and you access the scope or entity settings, look for them under access.scope and access.entity.

Misc

  • If your application uses HTTP trailers and you expect to find those headers in res.headers when using server.inject(), user res.trailers instead. The hapi-auth-hawk module uses trailers but this change only affects the module tests.
  • If you are testing request.session or request.auth.session for equality to null, test for undefined instead as they are no longer initialized to null. This is only likely if you wrote your own session manager (instead of using hapi-auth-cookie or yar.
@SteveALee
Copy link

@SteveALee SteveALee commented Jan 5, 2016

This release note is of exceptional quality! #highfive

@prashaantt
Copy link

@prashaantt prashaantt commented Jan 6, 2016

+1

Leaving behind a link to these release notes on the releases page would be pretty useful.

@hueniverse
Copy link
Contributor Author

@hueniverse hueniverse commented Jan 6, 2016

@manonthemat
Copy link

@manonthemat manonthemat commented Jan 6, 2016

Thanks for the update and these great release notes.

@jeveloper
Copy link

@jeveloper jeveloper commented Jan 6, 2016

good job, will be updating to this one soon

@devinivy
Copy link
Member

@devinivy devinivy commented Jan 27, 2017

The code here for qs-style querystring parsing seems to clobber the router's stripTrailingSlash option. I suggest using,

const Url = require('url');
const Qs = require('qs');

const onRequest = function (request, reply) {

    const uri = request.raw.req.url;
    const parsed = Url.parse(uri, false);
    request.setUrl(Object.assign({}, request.url, {
        query: Qs.parse(parsed.query)
    }));

    return reply.continue();
};

server.ext('onRequest', onRequest);

CC @hueniverse @nlf in case you'd like to update the release notes.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
6 participants