Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

feat(errorHandlingConfig): make the length of URL refrence in error m… #15707

Closed
wants to merge 1 commit into from

Conversation

m-amr
Copy link
Contributor

@m-amr m-amr commented Feb 13, 2017

…essages configurable

Closes #14744

What kind of change does this PR introduce? (Bug fix, feature, docs update, ...)
Feature

What is the current behavior? (You can also link to an open issue here)
Sometimes AngularJS throw error with long messages, very long URL referenced error
Will cause 414 Request-URI Too Large as in Issue #14744

What is the new behavior (if this is a feature change)?
make the Url reference error length configurable.

    errorHandlingConfig({urlMaxLength: 500});

Does this PR introduce a breaking change?
NO

Please check if the PR fulfills these requirements

Other information:
Microsoft Internet Explorer has a maximum uniform resource locator (URL) length of 2,083 characters. Internet Explorer also has a maximum path length of 2,048 characters. This limit applies to both POST request and GET request URLs. https://support.microsoft.com/en-us/help/208427/maximum-url-length-is-2,083-characters-in-internet-explorer which is less than all the other browsers, so i added the default value for url max length to be 2000 characters.

I don't know what should be the minimum length for URL reference error but i implemented it with a constrains to be more than 200.

@m-amr m-amr force-pushed the add_max_length_to_url_error branch from 1ba0811 to d99824f Compare February 13, 2017 23:10
m-amr added a commit to m-amr/angular.js that referenced this pull request Feb 13, 2017
@m-amr m-amr force-pushed the add_max_length_to_url_error branch from d99824f to 6136d59 Compare February 13, 2017 23:16
m-amr added a commit to m-amr/angular.js that referenced this pull request Feb 13, 2017
@m-amr m-amr force-pushed the add_max_length_to_url_error branch from 6136d59 to d861b50 Compare February 13, 2017 23:20
m-amr added a commit to m-amr/angular.js that referenced this pull request Feb 13, 2017
@m-amr m-amr force-pushed the add_max_length_to_url_error branch from d861b50 to 5ea7b0f Compare February 13, 2017 23:20
m-amr added a commit to m-amr/angular.js that referenced this pull request Feb 13, 2017
@m-amr m-amr force-pushed the add_max_length_to_url_error branch from 5ea7b0f to 48e0b69 Compare February 13, 2017 23:27
m-amr added a commit to m-amr/angular.js that referenced this pull request Feb 13, 2017
Copy link
Member

@gkalpak gkalpak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we have tests for errorHandlingConfig()? 😱
Can you add them?

src/Angular.js Outdated
@@ -152,12 +154,19 @@ var minErrConfig = {
* * `objectMaxDepth` **{Number}** - The max depth for stringifying objects. Setting to a
* non-positive or non-numeric value, removes the max depth limit.
* Default: 5
*
* * `urlMaxLength` **{Number}** - The max length of the URL reference in the error messages, it can be any number more than or equal 200. Setting to a
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This like is too long. Also, why a 200 limit?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there is no good reason for the 200 limit. Let's remove it.
And then we can reuse isValidObjectMaxDepth() for both validations (under a different, more generic name 😉).

@@ -143,6 +144,7 @@ var minErrConfig = {
* current configuration if used as a getter. The following options are supported:
*
* - **objectMaxDepth**: The maximum depth to which objects are traversed when stringified for error messages.
* - **urlMaxLength**: The maximum length of the URL reference in the error messages.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a note explaining the consequences this will have to longer URLs (e.g. will they break? will they lack information? will the stack traces be truncated?)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Restricting this will definitely mess with the error parsing in the docs app. I was wondering if we should additionally to objectMaxDepth have arrayMaxLength? Because when you have an array with 1000 objects, then objectMaxDepth isn't going to help your shortening the error output

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Limiting the array length makes sense (although not directly related to this PR).

Copy link
Contributor Author

@m-amr m-amr Feb 18, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is that each parameter value is encoded using encodeURIComponent
so when slice this encoded value it become invalid for some cases.

for (i = 0, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
      url += paramPrefix + 'p' + i + '=' + encodeURIComponent(templateArgs[i]);

      if (url.length > minErrConfig.urlMaxLength) {
        url = url.substr(0, minErrConfig.urlMaxLength - 3) + '...';
        break;
      }
    }

for example:
This link produced when i set urlMaxLength to 400
http://errors.angularjs.org/1.6.3-local+sha.48e0b69/$injector/modulerr?p0=n…cies%20as%20the%20second%20argument.%0Ahttp%3A%2F%2Ferrors.angularjs.org%2...

it look like it turn into
https://docs.angularjs.org/error/$injector/modulerr?p0=undefined

I will try to find a solution that slice the url without harming the encoded data.

src/minErr.js Outdated
url = url.slice(0, minErrConfig.urlMaxLength - URL_MAX_LENGTH_END.length);
url += URL_MAX_LENGTH_END;
break;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems more complex than necessary :confused. I wonder if we could simplify the implementation.

return message.slice(urlStartAt);
}

function createDummyString(size) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need a helper for this. IIRC, we use new Array(size).join('a') elsewhere in the tests.

var myError = testError('26', 'a when default=2000 is {0}', a);
var url = extractUrlFromErrorMessage(myError.message);
expect(url.length).toBe(2000);
expect(errorHandlingConfig().urlMaxLength).toBe(2000);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for these assertions (here and below).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These assertions are important to test that the default value is 2000 and doesn't change.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You essentially testing two different things: errorHandlingConfig and minErr.
There should be different tests about errorHandlingConfig, that verify that it works. Then here, you don't need to verify that. Just that minErr works as expected

@m-amr
Copy link
Contributor Author

m-amr commented Feb 15, 2017

There are tests for 'errorHandlingConfig'

it('should slice error reference URL in the message if it exceeds url max length', function() {
    var a = createDummyString(2000);

    var myError = testError('26', 'a when default=2000 is {0}', a);
    var url = extractUrlFromErrorMessage(myError.message);
    expect(url.length).toBe(2000);
    expect(errorHandlingConfig().urlMaxLength).toBe(2000);

    errorHandlingConfig({urlMaxLength: 500});
    myError = testError('26', 'a when urlMaxLength=500 is {0}', a);
    url = extractUrlFromErrorMessage(myError.message);
    expect(url.length).toBe(500);
    expect(errorHandlingConfig().urlMaxLength).toBe(500);

    errorHandlingConfig({urlMaxLength: undefined});
    myError = testError('26', 'a when urlMaxLength=undefined is {0}', a);
    url = extractUrlFromErrorMessage(myError.message);
    expect(url.length).toBe(500);
    expect(errorHandlingConfig().urlMaxLength).toBe(500);
  });

  they('should ignore url max length when urlMaxLength = $prop',
      [NaN, null, true, false, -1, 0], function(maxLength) {
        errorHandlingConfig({urlMaxLength: maxLength});
        var a = createDummyString(2100);
        var myError = testError('26', 'a is {0}', a);
        var url = extractUrlFromErrorMessage(myError.message);
        expect(url.length).toBeGreaterThan(2100);
        expect(errorHandlingConfig().urlMaxLength).toBeNaN();
      }
  );

Do you mean to create a separate test for errorHandlingConfig? and the same will be for objectMaxDepth.

describe('errorHandlingConfig', function(){
  it('should the default urlMaxLength be 2000', function(){
      expect(errorHandlingConfig().urlMaxLength).toBe(2000);
   };)

  it('should set urlMaxLength', function(){
      errorHandlingConfig({urlMaxLength:3000});
      expect(errorHandlingConfig().urlMaxLength).toBe(3000);
   });

   they('should set urlMaxLength to be NAN when it is setting to $prop', 
        [false, true, -1,  null, NAN], function(maxLength){
            errorHandlingConfig({urlMaxLength: maxLength});
            expect(errorHandlingConfig().urlMaxLength).toBeNAN();;
   });
});

@gkalpak
Copy link
Member

gkalpak commented Feb 15, 2017

Yes. We should have tests for errorHandlingConfig() (independent of minErr) that verify things like:

  • Default values.
  • It works as a setter.
  • It works as a getter.
  • It sanitizes the inputs.

@m-amr m-amr force-pushed the add_max_length_to_url_error branch from 48e0b69 to 0e1cfce Compare February 16, 2017 03:44
m-amr added a commit to m-amr/angular.js that referenced this pull request Feb 16, 2017
@m-amr m-amr force-pushed the add_max_length_to_url_error branch from 0e1cfce to 4d5d1df Compare February 16, 2017 03:54
m-amr added a commit to m-amr/angular.js that referenced this pull request Feb 16, 2017
@m-amr m-amr force-pushed the add_max_length_to_url_error branch from 4d5d1df to 93fe2cf Compare February 16, 2017 03:55
m-amr added a commit to m-amr/angular.js that referenced this pull request Feb 16, 2017
@m-amr m-amr force-pushed the add_max_length_to_url_error branch from 93fe2cf to 9bf1bdd Compare February 16, 2017 03:56
m-amr added a commit to m-amr/angular.js that referenced this pull request Feb 16, 2017
@m-amr m-amr force-pushed the add_max_length_to_url_error branch from 9bf1bdd to 82e1893 Compare February 16, 2017 04:20

if (url.length > minErrConfig.urlMaxLength) {
url = url.substr(0, minErrConfig.urlMaxLength - 3) + '...';
break;
Copy link
Contributor Author

@m-amr m-amr Feb 16, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gkalpak
I feel it is confusing too, if we set urlMaxlength to 1000, then it will substr(0, 997) since it will add '...'
So the max length become 997 not 1000.
Should I remove '...' ? but still need something to tell that this is not the full url.
Should I substr(0, 1000) then add '...' and handle this in the unit test by asserting it to equal 1003

Do you have any suggestion ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ... (or whatever we choose to indicate the URL was truncated) is not actually part of the URL. So, it shouldn't count against its length.

I suggest, you don't take it into account when calculating the URL length. And instead of appending ... (which might not be clear), I suggest we add soething like (truncated URL) right after.

@Narretz
Copy link
Contributor

Narretz commented Oct 16, 2017

If we cannot truncate the url reliably, can we truncate the stack instead? This means we won't know exactly how long the url is, but this isn't really needed anyway. We just want a url that has a managable size.

@Narretz
Copy link
Contributor

Narretz commented Oct 23, 2017

So, it turns out the error stack is not an explicit parameter to the minErr. It simply gets added whenever throwing a minErr triggers another minErr. A common cause is when you include a module that doesn't exist: this triggers "nomod" and then "modulerr", i.e. modulerr includes the message (good) and stack (bad) of nomod, e.g. this error url

So we cannot say "truncate only the stack", but we can say "truncate all error template arguments", which will sometimes include the stack of another error.
However, even if you say argLength = 0 you will still get a stack appended in the console, which is normal browser behavior, but since it is errors in errors it can still be unwieldy, depending on the error.

We could also allow urlArgsMaxLength=0 and then remove the part with the parameters completely, as proposed in #16283

Here's a PoC:
http://plnkr.co/edit/6zfFojrkH4xha9HmXodj?p=preview

In Chrome you get a "nice" stack which is the moderr, and then for some reason 4 times the same modulerr.

In Firefox, it's just a giant blob that is not even clickable (but that is the default behavior).

So by restricting the argument length, it becomes a bit better (and the urls actually work), but the stacks can still be confusing.

@m-amr
Copy link
Contributor Author

m-amr commented Oct 23, 2017

I think that PR #16283 has a better solution than this PR
I will close my pull request when that PR is merged.

Narretz pushed a commit to Narretz/angular.js that referenced this pull request Jun 6, 2018
Specific errors, such as nested module loading, can create very long
error urls because they include the error stack. These urls create visual
clutter in the browser console, are often not clickable, and may be rejected
by the docs page because they are simply too long.

We've already made improvements to the error display in angular#16283, which excludes
the error url from error parameters, which results in cleaner error messages.

Further, modern browsers restrict console message length intelligently.

This option can still be useful for older browsers like Internet Explorer, or
in general to reduce visual clutter in the console.

Closes angular#14744
Closes angular#15707
Closes angular#16283
Closes angular#16299
Narretz added a commit that referenced this pull request Jun 6, 2018
Specific errors, such as those during nested module loading, can create very long
error urls because the error message includes the error stack. These urls create visual
clutter in the browser console, are often not clickable, and may be rejected
by the docs page because they are simply too long.

We've already made improvements to the error display in #16283, which excludes
the error url from error parameters, which results in cleaner error messages.

Further, modern browsers restrict console message length intelligently.

This option can still be useful for older browsers like Internet Explorer, or
in general to reduce visual clutter in the console.

Closes #14744
Closes #15707
Closes #16283
Closes #16299 
Closes #16591
Narretz added a commit that referenced this pull request Jun 6, 2018
Specific errors, such as those during nested module loading, can create very long
error urls because the error message includes the error stack. These urls create visual
clutter in the browser console, are often not clickable, and may be rejected
by the docs page because they are simply too long.

We've already made improvements to the error display in #16283, which excludes
the error url from error parameters, which results in cleaner error messages.

Further, modern browsers restrict console message length intelligently.

This option can still be useful for older browsers like Internet Explorer, or
in general to reduce visual clutter in the console.

Closes #14744
Closes #15707
Closes #16283
Closes #16299 
Closes #16591
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Get rid of huge error messages
5 participants