Coder Social home page Coder Social logo

akamai / boomerang Goto Github PK

View Code? Open in Web Editor NEW

This project forked from bluesmoon/boomerang

1.8K 94.0 290.0 38.12 MB

End user oriented web performance testing and beaconing

Home Page: http://akamai.github.io/boomerang/

License: Other

JavaScript 74.44% HTML 18.62% Smarty 0.80% CSS 6.06% Shell 0.08%

boomerang's Introduction

boomerang always comes back, except when it hits something.

Summary

Join the chat at https://gitter.im/SOASTA/boomerang

boomerang is a JavaScript library that measures the page load time experienced by real users, commonly called RUM (Real User Measurement). It has the ability to send this data back to your server for further analysis. With boomerang, you find out exactly how fast your users think your site is.

Apart from page load time, boomerang measures performance timings, metrics and characteristics of your user's web browsing experience. All you have to do is include it in your web pages and call the BOOMR.init() method. Once the performance data is captured, it will be beaconed to your chosen URL.

boomerang is designed to be a performant and flexible library that can be adapted to your site's needs. It has an extensive plugin architecture, and works with both traditional and modern websites (including Single Page Apps).

boomerang's goal is to not affect the load time of the page (avoiding the Observer Effect). It can be loaded in an asynchronous way that will not delay the page load even if boomerang.js is unavailable.

Features

  • Supports:
    • IE 6+, Edge, all major versions of Firefox, Chrome, Opera, and Safari
    • Desktop and mobile devices
  • Captures (all optional):
    • Page characteristics such as the URL and Referrer
    • Overall page load times (via NavigationTiming if available)
    • DNS, TCP, Request and Response timings (via NavigationTiming)
    • Browser characteristics such as screen size, orientation, memory usage, visibility state
    • DOM characteristics such as the number of nodes, HTML length, number of images, scripts, etc
    • ResourceTiming data (to reconstruct the page's Waterfall)
    • Bandwidth
    • Mobile connection data
    • DNS latency
    • JavaScript Errors
    • XMLHttpRequest instrumentation
    • Third-Party analytics providers IDs
    • Single Page App interactions

Usage

boomerang can be included on your page in one of two ways: synchronously or asynchronously.

The asynchronous method is recommended.

The simple synchronous way

<script src="boomerang.js"></script>
<script src="plugins/rt.js"></script>
<!-- any other plugins you want to include -->
<script>
  BOOMR.init({
    beacon_url: "http://yoursite.com/beacon/"
  });
</script>

Note: You must include at least one plugin (it doesn't have to be RT) or else the beacon will never fire.

Each plugin has its own configuration as well -- these configuration options should be included in the BOOMR.init() call:

BOOMR.init({
  beacon_url: "http://yoursite.com/beacon/",
  ResourceTiming: {
    enabled: true,
    clearOnBeacon: true
  }
});

The faster, more involved, asynchronous way

Loading boomerang asynchronously ensures that even if boomerang.js is unavailable (or loads slowly), your host page will not be affected.

1. Add a plugin to init your code

Create a plugin (or use the sample zzz-last-plugin.js) with a call to BOOMR.init:

BOOMR.init({
  config: parameters,
  ...
});
BOOMR.t_end = new Date().getTime();

You could also include any other code you need. For example, you could include a timer to measure when boomerang has finished loading (as above).

2. Build boomerang

The build process bundles boomerang.js and all of the plugins listed in plugins.json (in that order).

To build boomerang with all of your desired plugins, you would run:

grunt clean build

This creates a deployable boomerang in the build directory, e.g. build/boomerang-<version>.min.js.

Install this file on your web server or origin server where your CDN can pick it up. Set a far future max-age header for it. This file will never change.

3. Asynchronously include the script on your page

There are two methods of asynchronously including boomerang on your page: by adding it to your main document, or via the IFRAME/Preload method.

The former method could block your onload event (affecting the measured performance of your page), so the later method is recommended.

3.1. Adding it to the main document

Include the following code at the top of your HTML document:

<script>
(function(d, s) {
  var js = d.createElement(s),
      sc = d.getElementsByTagName(s)[0];

  js.src="http://your-cdn.host.com/path/to/boomerang-<version>.js";
  sc.parentNode.insertBefore(js, sc);
}(document, "script"));
</script>

Best practices will suggest including all scripts at the bottom of your page. However, that only applies to scripts that block downloading of other resources.

Including a script this way will not block other resources, however it will block onload.

Including the script at the top of your page gives it a good chance of loading before the rest of your page does, thereby reducing the probability of it blocking the onload event.

If you don't want to block onload either, use the following IFRAME/Preload method:

3.2. Adding it via an IFRAME/Preload

The method described in 3.1 will still block onload on most browsers.

To avoid blocking onload, we can load boomerang in an asynchronous IFRAME or via LINK preload (for browsers that support it). The general process is documented on in this blog post.

For boomerang, the asynchronous loader snippet you'll use is:

<script>
(function() {
  // Boomerang Loader Snippet version 15
  if (window.BOOMR && (window.BOOMR.version || window.BOOMR.snippetExecuted)) {
    return;
  }

  window.BOOMR = window.BOOMR || {};
  window.BOOMR.snippetStart = new Date().getTime();
  window.BOOMR.snippetExecuted = true;
  window.BOOMR.snippetVersion = 15;

  // NOTE: Set Boomerang URL here
  window.BOOMR.url = "";

  // document.currentScript is supported in all browsers other than IE
  var where = document.currentScript || document.getElementsByTagName("script")[0],
      // Parent element of the script we inject
      parentNode = where.parentNode,
      // Whether or not Preload method has worked
      promoted = false,
      // How long to wait for Preload to work before falling back to iframe method
      LOADER_TIMEOUT = 3000;

  // Tells the browser to execute the Preloaded script by adding it to the DOM
  function promote() {
    if (promoted) {
      return;
    }

    var script = document.createElement("script");

    script.id = "boomr-scr-as";
    script.src = window.BOOMR.url;

    // Not really needed since dynamic scripts are async by default and the script is already in cache at this point,
    // but some naive parsers will see a missing async attribute and think we're not async
    script.async = true;

    parentNode.appendChild(script);

    promoted = true;
  }

  // Non-blocking iframe loader (fallback for non-Preload scenarios) for all recent browsers.
  // For IE 6/7/8, falls back to dynamic script node.
  function iframeLoader(wasFallback) {
    promoted = true;

    var dom,
        doc = document,
        bootstrap, iframe, iframeStyle,
        win = window;

    window.BOOMR.snippetMethod = wasFallback ? "if" : "i";

    // Adds Boomerang within the iframe
    bootstrap = function(parent, scriptId) {
      var script = doc.createElement("script");

      script.id = scriptId || "boomr-if-as";
      script.src = window.BOOMR.url;

      BOOMR_lstart = new Date().getTime();

      parent = parent || doc.body;
      parent.appendChild(script);
    };

    // For IE 6/7/8, we'll just load the script in the current frame:
    // * IE 6/7 don't support 'about:blank' for an iframe src (it triggers warnings on secure sites)
    // * IE 8 required a doc write call for it to work, which is bad practice
    // This means loading on IE 6/7/8 may cause SPoF.
    if (!window.addEventListener && window.attachEvent && navigator.userAgent.match(/MSIE [678]\./)) {
      window.BOOMR.snippetMethod = "s";

      bootstrap(parentNode, "boomr-async");

      return;
    }

    // The rest of this function is for browsers that don't support Preload hints but will work with CSP & iframes
    iframe = document.createElement("IFRAME");

    // An empty frame
    iframe.src = "about:blank";

    // We set title and role appropriately to play nicely with screen readers and other assistive technologies
    iframe.title = "";
    iframe.role = "presentation";

    // Ensure we're not loaded lazily
    iframe.loading = "eager";

    // Hide the iframe
    iframeStyle = (iframe.frameElement || iframe).style;
    iframeStyle.width = 0;
    iframeStyle.height = 0;
    iframeStyle.border = 0;
    iframeStyle.display = "none";

    // Append to the end of the current block
    parentNode.appendChild(iframe);

    // Try to get the iframe's document object
    try {
      win = iframe.contentWindow;
      doc = win.document.open();
    }
    catch (e) {
      // document.domain has been changed and we're on an old version of IE, so we got an access denied.
      // Note: the only browsers that have this problem also do not have CSP support.

      // Get document.domain of the parent window
      dom = document.domain;

      // Set the src of the iframe to a JavaScript URL that will immediately set its document.domain
      // to match the parent.
      // This lets us access the iframe document long enough to inject our script.
      // Our script may need to do more domain massaging later.
      iframe.src = "javascript:var d=document.open();d.domain='" + dom + "';void 0;";
      win = iframe.contentWindow;

      doc = win.document.open();
    }

    // document.domain hasn't changed, regular method should be OK
    win._boomrl = function() {
      bootstrap();
    };

    if (win.addEventListener) {
      win.addEventListener("load", win._boomrl, false);
    }
    else if (win.attachEvent) {
      win.attachEvent("onload", win._boomrl);
    }

    // Finish the document
    doc.close();
  }

  // See if Preload is supported or not
  var link = document.createElement("link");

  if (link.relList &&
      typeof link.relList.supports === "function" &&
      link.relList.supports("preload") &&
      ("as" in link)) {
    window.BOOMR.snippetMethod = "p";

    // Set attributes to trigger a Preload
    link.href = window.BOOMR.url;
    link.rel  = "preload";
    link.as   = "script";

    // Add our script tag if successful, fallback to iframe if not
    link.addEventListener("load", promote);
    link.addEventListener("error", function() {
      iframeLoader(true);
    });

    // Have a fallback in case Preload does nothing or is slow
    setTimeout(function() {
      if (!promoted) {
        iframeLoader(true);
      }
    }, LOADER_TIMEOUT);

    // Note the timestamp we started trying to Preload
    BOOMR_lstart = new Date().getTime();

    // Append our link tag
    parentNode.appendChild(link);
  }
  else {
    // No Preload support, use iframe loader
    iframeLoader(false);
  }

  // Save when the onload event happened, in case this is a non-NavigationTiming browser
  function boomerangSaveLoadTime(e) {
    window.BOOMR_onload = (e && e.timeStamp) || new Date().getTime();
  }

  if (window.addEventListener) {
    window.addEventListener("load", boomerangSaveLoadTime, false);
  }
  else if (window.attachEvent) {
    window.attachEvent("onload", boomerangSaveLoadTime);
  }
})();
</script>

Minified:

<script>(function(){if(window.BOOMR&&(window.BOOMR.version||window.BOOMR.snippetExecuted)){return}window.BOOMR=window.BOOMR||{};window.BOOMR.snippetStart=(new Date).getTime();window.BOOMR.snippetExecuted=true;window.BOOMR.snippetVersion=15;window.BOOMR.url="";var e=document.currentScript||document.getElementsByTagName("script")[0],a=e.parentNode,s=false,t=3e3;function n(){if(s){return}var e=document.createElement("script");e.id="boomr-scr-as";e.src=window.BOOMR.url;e.async=true;a.appendChild(e);s=true}function i(e){s=true;var t,i=document,n,o,d,r=window;window.BOOMR.snippetMethod=e?"if":"i";n=function(e,t){var n=i.createElement("script");n.id=t||"boomr-if-as";n.src=window.BOOMR.url;BOOMR_lstart=(new Date).getTime();e=e||i.body;e.appendChild(n)};if(!window.addEventListener&&window.attachEvent&&navigator.userAgent.match(/MSIE [678]\./)){window.BOOMR.snippetMethod="s";n(a,"boomr-async");return}o=document.createElement("IFRAME");o.src="about:blank";o.title="";o.role="presentation";o.loading="eager";d=(o.frameElement||o).style;d.width=0;d.height=0;d.border=0;d.display="none";a.appendChild(o);try{r=o.contentWindow;i=r.document.open()}catch(e){t=document.domain;o.src="javascript:var d=document.open();d.domain='"+t+"';void 0;";r=o.contentWindow;i=r.document.open()}r._boomrl=function(){n()};if(r.addEventListener){r.addEventListener("load",r._boomrl,false)}else if(r.attachEvent){r.attachEvent("onload",r._boomrl)}i.close()}var o=document.createElement("link");if(o.relList&&typeof o.relList.supports==="function"&&o.relList.supports("preload")&&"as"in o){window.BOOMR.snippetMethod="p";o.href=window.BOOMR.url;o.rel="preload";o.as="script";o.addEventListener("load",n);o.addEventListener("error",function(){i(true)});setTimeout(function(){if(!s){i(true)}},t);BOOMR_lstart=(new Date).getTime();a.appendChild(o)}else{i(false)}function d(e){window.BOOMR_onload=e&&e.timeStamp||(new Date).getTime()}if(window.addEventListener){window.addEventListener("load",d,false)}else if(window.attachEvent){window.attachEvent("onload",d)}})();</script>

Change the boomerangUrl to the location of Boomerang on your server.

The id of the script node created by this code MUST be boomr-if-as (for IFRAME mode) or boomr-scr-as (for Preload mode) as boomerang looks for those ids to determine if it's running within an IFRAME and to determine the URL of the script.

boomerang will still export the BOOMR object to the parent window if running inside an IFRAME, so the rest of your code should remain unchanged.

3.3. Identifying when boomerang has loaded

If you load boomerang asynchronously, there's some uncertainty in when boomerang has completed loading. To get around this, you can subscribe to the onBoomerangLoaded Custom Event on the document object:

// Modern browsers
if (document.addEventListener) {
  document.addEventListener("onBoomerangLoaded", function(e) {
    // e.detail.BOOMR is a reference to the BOOMR global object
  });
}
// IE 6, 7, 8 we use onPropertyChange and look for propertyName === "onBoomerangLoaded"
else if (document.attachEvent) {
  document.attachEvent("onpropertychange", function(e) {
    if (!e) e=event;
    if (e.propertyName === "onBoomerangLoaded") {
      // e.detail.BOOMR is a reference to the BOOMR global object
    }
  });
}

Note that this only works on browsers that support the CustomEvent interface, which is Chrome (including Android), Firefox 6+ (including Android), Opera (including Android, but not Opera Mini), Safari (including iOS), IE 6+ (but see the code above for the special way to listen for the event on IE6, 7 & 8).

boomerang also fires the onBeforeBoomerangBeacon and onBoomerangBeacon events just before and during beaconing.

Installation

There are several ways of including Boomerang in your project:

  1. Boomerang can be downloaded from the official Boomerang Github repository.

  2. NPM: npm install boomerangjs

  3. Bower: bower install boomerang

Once fetched, see Building Boomerang for more details on how to include the plugins you require.

Documentation

Documentation is in the docs/ directory. Boomerang documentation is written in Markdown and is built via JSDoc.

You can build the current documentation by running Grunt:

grunt jsdoc

HTML files will be built under build/docs.

Open-source Boomerang Documentation is currently published at akamai.github.io/boomerang/.

The team at Akamai works on mPulse Boomerang, which contains a few mPulse-specific plugins and may have additional changes being tested before being backported to the open-source Boomerang. mPulse Boomerang usage documentation is available at docs.soasta.com/boomerang/ and mPulse Boomerang API documentation is at developer.akamai.com/tools/boomerang/docs/.

Additional documentation:

Source code

The boomerang source code is primarily on GitHub at github.com/akamai/boomerang.

Feel free to fork it and contribute to it.

You can also get a check out the releases or download a tarball or zip of the code.

Support

We use GitHub Issues for discussions, feature requests and bug reports.

Get in touch at github.com/akamai/boomerang/issues.

boomerang is supported by the developers at Akamai, and the awesome community of open-source developers that use and hack it. That's you. Thank you!

Contributions

Boomerang is brought to you by:

To help out, please read our contributing page.

Copyright

  • Copyright (c) 2011, Yahoo! Inc. All rights reserved.
  • Copyright (c) 2011-2012, Log-Normal Inc. All rights reserved.
  • Copyright (c) 2012-2017 SOASTA, Inc. All rights reserved.
  • Copyright (c) 2017-2023, Akamai Technologies, Inc. All rights reserved.
  • Copyrights licensed under the BSD License. See the accompanying LICENSE.txt file for terms.

boomerang's People

Contributors

abarre avatar andreas-marschke avatar ashenoy2014 avatar bbrewer avatar bluesmoon avatar bripkens avatar ceckoslab avatar cvazac avatar cybermaxs avatar dweitemeyer avatar earlonrails avatar edgarklein avatar erikfried avatar gitter-badger avatar hanoii avatar jamischarles avatar jonpliske avatar liufei avatar munjalpatel avatar nicjansma avatar okuryu avatar paulcgt avatar philbooth avatar ptimilsi avatar querymetrics avatar sukratkashyap avatar tghamm avatar tollmanz avatar vigneshshanmugam avatar xzyfer avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

boomerang's Issues

t_resp not accurate with lazy loading

I have the code for loading boomerang asynchronously in the footer. I think the same thing will happen even if it was in the header.

It looks like the time used for detecting when the head is loaded doesn't know when the boomerang script is loaded asynchronously and gets confused, returning a negative value for t_resp. Is there a known solution for this? What would be a good way to save the time the head loaded? It seems like we should have a <script> tag that can be inserted in the head to add a value to a global variable somewhere and boomerang should look for such values on init.

Huge jump in t_done median when moving to the latest boomerang version

I've been running with the boomerang script from this version (035b7ea), and I recently updated to the latest (1614c7b). I'm using the RT plugin. Since doing that, the t_done measurements have jumped up, the median has gone from 2400ms to around 5000ms.

You can see in the graph below, I turned it on for a few hours, then rolled back to the previous version. Also you can see that the t_page and t_resp numbers don't move.
t_done_jump

Is this something I should have expected to happen? Has their been a significant change in the way the t_done is calculated? The only other change that I made when I upgraded was to stop using the navtiming plugin, I was using that before but not in version I deployed with the latest boomerang code.

Frustrated beginner question

Hi, i have a very basic problem i can't seem to get over with: i simply can get the library to work, even simply following the how to's.

I set up a simple page with just the <script> tag including the library, a very simple initialization and a console.log() instruction to test if the subscribed before_beacon works.

The plug seems to be correctly initialized (i can find the BOOMR object with firebug) but nothing happens when i load the page.

the code is:

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Boomerang testing</title>
    <script src="js/boomerang.js"></script>
</head>
<body>
<h1>Boomerang test</h1>
<div id="results"></div>
    <script type="text/javascript">
    BOOMR.init({
        user_ip: "<?php echo $_SERVER['REMOTE_ADDR'] ?>",
        beacon_url: "http://xxx.xxx.xxx.xxx/boomerang/images/beacon.gif",
        autorun: true
    });
    BOOMR.subscribe('before_beacon', function(o) {
        console.log("i'm doing stuff");
    });
    </script>
</body>
</html>

if i directly call the BOOMR.sendBeacon() method the subscribed event fires and the beacon get sent. otherwise nothing happens.

i tried putting the code both in the <head> tag and inside the <body> bit it makes no difference.

i'm testing the page from a XAMMP Apache webserver both with FF 30 and Chrome 35.

Any help would be really appreciated.
Thanks.
Marcello

Tips on what to do with the beaconed data

We've been using boomerang for almost a year now. Gathering tons and tons and tons of juicy data. However, we're yet to produce one single, useful graph from the data. I've tried various approaches, but haven't gotten to anything useful.

As this seems to be the primary support / discussion forum, I'm asking here. Any blog posts out there on what people are doing with boomerang data? Any advice from others using boomerang?

As far as my searching goes, it's either so obvious nobody bothers to write about it, or it's so hard nobody wants to share it, I can't find any info at all. I've even read slides from talks @bluesmoon has given, but couldn't get any concrete suggestions there either. I'd also be grateful for more insight into what the "one number to rule them all" slide means in one of the talks! :-)

Weird values for t_done/t_page and nt_domcomp

I'm seeing really weird values reported back to the beacon. About 90% looks just about right. But then 10% have t_done and t_page values ranging from a couple of minutes up to 10 hours. It doesn't seem to be limited to any specific browser or version. In most, but not all, such cases the nt plugin also reports high values for nt_domcomp.

We have a bunch of other custom timers placed in head and stopped throughout the page, including one just below body and their values look sensible. For instance, for one page view made with Mobile Safari 8 we got:
t_resp: null
t_page: 69160352
t_done: 69160352
domComplete_minus_domContentLoadedEventEnd: 9263870 (calculated value base on navigation timings reported)
t_syncPage: 685 (custom timer started in head and completed just before )

Any thoughts about what could be going on?

I should add that the site is not a single page app but does load quit a lot of things on the client, including:

  • ads
  • content fetch asynchronously, typically after onLoad
  • images fetch asynchronously, typically after onLoad
  • Often, but not always, Facebook likes count etc

When I compare with another RUM tool, Pingdom, our average numbers using Boomerang is waaaaay higher and median numbers are about 20%. However, if I exclude the 95% percentile and calculate median on the rest I get pretty much the same numbers.

load timing for AJAX call

Hi,
I would like to fetch load timing for an AJAX call on my page.
I am following steps @ http://lognormal.github.io/boomerang/doc/howtos/howto-2.html
But while calling BOOMR.plugins.RT.startTimer("t_done");
a. Do I need to send 2nd parameter as current time ?
b. Do I need to clear any timings for getting data for every AJAX call on page
c. The scenario that have will trigger AJAX call after complete page is loaded.
So can I just use BOOMR.plugins.RT.startTimer('t_page', timeInmilliseconds) again to get timing data for AJAX call. Or I will have to use "t_done" for AJAX calls?
Please suggest..

-lucky

Capture resource timing from inside iframes

@philbooth this one is for you.

At the moment, ResourceTiming does not pull in anything from iframes since there's a different window.performance object in each iframe. I think it would be cool if we could capture that within the restrictions of cross-domain checks.

IMO, we'd attempt to access the iframe's documentElement inside a try/catch block, and if successful, we'll pull the entire restiming object from there, and also the window.performance.timing object (for navigation timing).

LoadEventEnd=0 with IE9

@bluesmoon

Hi Phil.

We have been running into the issue where IE9 (7 and 8 too I believe) always reports a LoadEventEnd of 0. This breaks the Page Load Time calculations.

loadeventend_bug

To work around this issue we have had to edit the following section, adding a timeout to allow for the event to happen before we call boomr.

        // The developer can override onload by setting autorun to false
        if(!impl.onloadfired && (config.autorun === undefined || config.autorun !== false)) {
            if(d.readyState && d.readyState === "complete") {
                this.setImmediate(BOOMR.page_ready, null, null, BOOMR);
            }
            else {
                if(w.onpagehide || w.onpagehide === null) {
                    boomr.utils.addListener(w, "pageshow", BOOMR.page_ready);
                }
                else {
/*** CODE CHANGED HERE ***/
                    boomr.utils.addListener(w, "load", function() {
                        setTimeout(function() {
                            BOOMR.page_ready();
                        }, 0);
                    });
/*** CODE CHANGED HERE ***/
                }
            }
        }

I was wondering if this issue was known or whether you think we are doing something wrong. We are calling the tag on the head before anything loads..

Thanks,

R

BW plugin never fires sendBeacon

I've just started using boomerang, so apologies if I've simply missed something. It looks like a commit in December 2014 removed the calls to sendBeacon in the BW plugin. Because the plugin also returns a hard-coded true value to is_complete, I can't see how to either make it send a beacon when complete, or wait like the rest of the plugins do.
Am I missing something? Thanks!

w.performance on mozilla with iframe

Hello,

I am using the iframe method on mozilla and it does not work. On the iframe window.performance is null.

I verified using a breakpoint.

Using firefox 21.0. Any idea?

[17:15:20,648] TypeError: w.performance is null 

Ajax api call with relative url

Boomerang is overriding url logic of jquery ajax call.
We use relative url to load static json file from the server with jquery ajax.
Let's say we have a json file with path:
http://www.example.com/json/my.json
var r_url = 'json/my.json'
$.ajax({
url: r_url,
type: "GET",
async: false,
timeout: 10000, // 10 seconds
success: function() {
},
error: function() {
});

Suppose current page is http://www.example.com/some/page
The actual url of the api becomes
http://www.example.com/some/page/json/my.json

We don't have this problem without boomerang library.
After investigating, we found that boomerang is making the ajax api call not jquery.
I think boomerang is overriding the relative url logic of jquery and cause the problem.

Thanks.

Few requests to the beacon have a t_done

By looking at the data received in the beacon, I saw that few number of request contains the t_done. Maybe 1/3 or 1/4 of the requests. Are you aware of that ?

I saw that this parameter is optional in the documentation but I'm wondering if it's normal to have this ratio of requests without t_done.

For all the requests without the t_done, is it a good approximation to replace t_done by rt.end - rt.bstart?

BW Testing - Image Compression in Proxy Servers

What happens if the Image which is used for the BW testing is compressed by the Proxy server?.

It has major chance of affecting the overall bandwidth calculation. Can we fix this in any way?.

Like injecting cache-headers with no-transform or something similar?.

Error plugin?

I guess this may be out of scope for boomerang; it's not performance-related although it is RUM.

But would there be any interest in a plugin that reports a simple count of the unhandled JavaScript errors caught by window.onerror?

We measure this outside of boomerang at the moment, sending an aggregate count to our back-end on load and then sending any further increments as they occur. A graph of the mean count is a useful indicator because there are so many ways for a JS payload to break and affect the page, of which we'd otherwise be unaware.

Of course, it's no problem for us to continue doing this separately, but if you thought there might be a place for it here, I'm happy to put something together.

Boomerang measurement of dynamic data

I am attempting to use Boomerangโ€™s round trip page load timer, and it seems to work great for page-to-page transitions. But for dynamic content, I am not getting good results.

I have a small part of the application that uses AngularJS, and inside the controller I have coded the following:
BOOMR.plugins.RT.startTimer("t_done");
// workโ€ฆ service calls via Ajax
BOOMR.plugins.RT.done();

And no beacon gets sent. In fact, I can enter this code in the Javascript console, and nothing occurs either. Any suggestions?

Network infrastructure compresses images resulting in incorrect bandwidth calculation

Hi there,

I'm hosting Boomerang on a webserver that's fronted by a load balancer appliance. It appears as though the load balancer compresses images that the BW plugin downloads.

Even though the cteonnt-length response header indicates that the content size is 4509613 bytes, Firebug reports that the amount of data downloaded to retrieve image-5.png is 1.5MB.

Cache-Control private
Content-Encoding gzip
Content-Type text/plain
Date Wed, 28 Jan 2015 10:11:52 GMT
Last-Modified Thu, 11 Dec 2014 08:30:58 GMT
Transfer-Encoding chunked
cteonnt-length 4509613
p3p CP="NON CUR OTPi OUR NOR UNI"

While I could have our admins turn off compression, I'm concerned that another device in the chain may at some point in time be configured to compress images.

I've tried encrypting the image files with AES256 (because encrypted data isn't compressible, or, not much at any rate) but when I use Boomerang, I don't get any bandwidth measurement metrics. I've found that the browser console reports that variable t=null:

boomerang.bw: [debug] r=start=1422440260422
end=1422440260433
t=null
state=false
run=5,start=1422440260433
end=1422440260443
t=null
state=false
run=5,start=1422440260443
end=1422440260458
t=null

Do you perhaps have any thoughts or advice on this?

Multiple Ajax Calls

Hello, I'm trying to use boomerang to also track timing for the ajax calls after page has loaded. I have put the start_timer(t_done) call in jquery's beforeSend function and done() when the request is complete.

Is there a way for BOOMR to keep track of all the separate timers? I noticed it won't send beacons for the ajax calls because the original onload timer has fired?

Boomerang.d.ts file

I want to use boomerang in a typescript project. Do you have any plans in the future to create a .d.ts for it?

RFC: Debug logging vs. Release no logging

Just a small question to you guys:
Would it be okay to extend the Gruntfile to set

BOOMR.util.log = function() {};

for the non-debug version while the debug version still has the logging intact as written in the source-code?

This would minimize logging on the console for the release versions of boomerang and still have debugging capabillities as desired when using the debug version.

Just my 2ยข.

Thanks!

Can we use boomerang to track the metrics at the method level in my code.

I have gone through the documentation couple of times, and the test app.
The documentation was brief. I am trying to understand how can i use the boomerang to capture the metrics at method level. say my JavaScript is making a ajax call. In that case how do i measure the timings in that scenario.

Can some one tell me if this is possible to do that, or does this work only at page level
and most importantly does with work with angular js?

Fire conversion event async

Phillip and I talked today at Velocity about my use case for boomerang. I need to have a conversion event fired async that is not part of a separate page load. This is similar to Google Analytics event tracking. It is not associated with a new page within the session. Phillip said he thought he knew how to do this and was planning on working on the async code soon. I thought I would register an issue as a place for communication around the idea.

Thanks!

Custom beacon callback

I need the ability to pass in a custom beacon function into the configuration so that I can choose what format and what method is to be used to send the data to the backend. For example, I would like to send everything as a json using a POST, but this is not currently possible. I don't mind making the necessary modification myself. Let me know if there's anything I should know before I start.

IE support?

What's the official status of the IE support? I seem to be getting awkward numbers from IE 7 and 9.

In IE 9 I get nt_load_end == 0.
Also, the time from data.nt_res_st to data.nt_res_end is strangely short.

IE 9 data:
{
"rt.start": "navigation",
"rt.tstart": 1348594531495,
"rt.bstart": 1348594542667,
"rt.end": 1348594542781,
"t_resp": 3743,
"t_page": 7543,
"t_done": 11286,
"r": "_",
"r2": "",
"t_other": "boomerang|0,boomr_fb|11172,t_domloaded|11271",
"nt_red_cnt": 0,
"nt_nav_type": 1,
"nt_nav_st": 1348594531495,
"nt_red_st": 0,
"nt_red_end": 0,
"nt_fet_st": 1348594531607,
"nt_dns_st": 1348594531607,
"nt_dns_end": 1348594531607,
"nt_con_st": 1348594531607,
"nt_con_end": 1348594531607,
"nt_req_st": 1348594531610,
"nt_res_st": 1348594535238,
"nt_res_end": 1348594535254,
"nt_domloading": 1348594535254,
"nt_domint": 1348594535254,
"nt_domcontloaded_st": 1348594542806,
"nt_domcontloaded_end": 1348594542809,
"nt_domcomp": 1348594542810,
"nt_load_st": 1348594542820,
"nt_load_end": 0,
"nt_unload_st": 1348594531495,
"nt_unload_end": 1348594531540,
"v": "0.9",
"u": "
_
"
}

In IE 7 this is all I get:
{
"rt.start": "none",
"rt.bstart": 1348595056160,
"rt.end": 1348595058647,
"r": "_",
"r2": "",
"t_other": "boomerang|0",
"v": "0.9",
"u": "
_
"
}

Support Closure Compiler in Advanced Mode.

If I run the current code (boomerang.js) against the closure compiler running in advanced mode I receive a number of compile warnings. Can the code be updated to support the closure compiler?

JSC_INEXISTENT_PROPERTY: Property page_ready never defined on BOOMR at line 248 character >32
impl.addListener(w, "pageshow", BOOMR.page_ready);
^
JSC_INEXISTENT_PROPERTY: Property page_ready never defined on BOOMR at line 251 character >28
impl.addListener(w, "load", BOOMR.page_ready);
^
JSC_INEXISTENT_PROPERTY: Property is_complete never defined on ? at line 381 character 4
if(!this.plugins[k].is_complete()) {
^
JSC_DELETE_VARIABLE: variables, functions, and arguments cannot be deleted in ES5 strict mode at >line 430 character 0
delete BOOMR_start;

Capture ID headers in Boomerang?

Hi Has anyone experience of / a plugin to capture specific header information in Boomerang?
We have a client who wishes to associate information on the specific app or database server with their end user performance?

Recommended infrastructure

Hi I'd welcome some advice. We are new to Boomerang but are considering using it to monitor a client site with circa 82m Page Views per month traffic.
Do we need to supply our own infrastructure for data storage etc, or is there some out there for general use? If the latter, how is it paid for, and if the former grateful if somebody could recommend a spec for an environment to support this.
In other words, can we just deploy the tag and capture the results, or do we need to setup an infrastructure to do this?
By all means point me to any docs on the subject.
Many thanks

Beacon not fired automatically with basic configuration

I was trying to get a new instance of boomerang set up, and to keep things as simple as possible, I loaded boomerang.js and had a basic call to BOOMR.init(). The only property in my config object was beacon_url, and it was set to a simple backend script that logs all incoming params to a file. Easy enough.

The only problem was that the docs implied a beacon would be fired containing the params v and u, which would be perfect for testing basic functionality. Unfortunately, no beacon was fired until I started loading plugins, and then it hit me---sendBeacon() was being called by the plugins, but it wasn't being called by default.

For a new boomerang user such as myself, this caused a lot of frustration, all of which could have been avoided by mentioning the need to explicitly call sendBeacon() in the docs. Of course, I eventually figured it out, but not without banging my head against the wall for an hour.

Everything looks great now that it's working, though!

Thoughts on a unit test suite?

Discussion forked from bluesmoon#9.

@andreas-marschke said:

I was thinking about unit-testing and best practices of implementing Unit-Tests in boomerang itself. I only hesitated yesterday to open up yet another issue just for an RFC on Unit-Testing or whats preferred by the users of Boomerang.

What I can think of given that grunt has been integrated into boomerang on bluesmoon/boomerang I'd guess something like phantomjs or selenium and qunit or jasmine would be good.

I worry however that not all parts of core boomerang.js can be tested this way as of now as they are not directly accessible. Splitting them up and building the closure for BOOMR upon calling grunt would be a good way forward.

An added benefit would be a better separation of concerns inside boomerang to make it easier for newcomers to look at and understand the code instead of having to grok through a large monolithic file.

Does anyone of the people subscribed or watching this repository have pointers or suggestions for a good javascript Unit-Testing Framework?

What we'd also need is the abillity to unit-test all the plugins since they do most of the actual work in terms of gathering the data. Therefore having an extensible suite of Unit-Tests that can be extended towards plugins would be excellent.

@bluesmoon If I understood your history correctly you should've worked at yahoo around the time NCZ was there. Anything regarding his work on maintainable Javascript in production that you can remember?

By the way the book that came out of his experience at yahoo is a tremendous resource.

DISCLAIMER: I'm not affiliated with O'Reilly, NCZ or any of the book publishing involved parties, I just thought it was a really good book.

Is it possible to load the script with async=true

@bluesmoon, after reading your article on the webperf calendar, I was still confused on the async=true mentioned in the comments.

My question is : if we load the boomerang script with async=true or by iframe or even synchronously, is there a possibility of getting corrupted timings collected by boomerang or is the collection not impacted by the way the script is loaded?

Plugin for the Resource Timing API?

At my company we're about to start tracking metrics from the Resource Timing API [1] and it's seems like a shame for us to deliver separate code to the client for sending the data.

Would there be any interest in a plugin for this API in boomerang? I'd be happy to take a stab at it and send you a PR, if you think it's a good idea and it's not already in progress or whatever.

[1] http://www.w3.org/TR/resource-timing/

First latency value not discarded after sorting

Via email from fitch wang:

I have a question about the calc_latency() function, you comment in the code like this:

// First we get the arithmetic mean, standard deviation and standard error
// We ignore the first since it paid the price of DNS lookup, TCP connect
// and slow start
for(i=1; i<n; i++) {
   sum += lat_filtered[i];
   sumsq += lat_filtered[i] * lat_filtered[i];
}

Is it means that you drop the first value use i=1?
If so, I find that the lat_filtered have been sorted and do IQR filtering before like this

lat_filtered = this.iqr(this.latencies.sort(this.ncmp));

so the lat_filtered is a sorted array, if you use the sorted array to calculate the sum and amean, maybe it is not the expected result, beacuse the first value of the array may be is not the value that paid the price of DNS lookup and so on, so can you tell me what I have understand is true?

Tracking of user perceived response times

I'm working on a performance of a web-based enterprise application and typical problem is to measure the time it takes to process button clicks.

The initial thought was "ok, I need to measure UI performance, then I use boomerang".
As I applied boomerang, it turned out to be something unrelated to performance validation.
None of the user clicks/actions result in just a page reload.

The issues (see below) can be solved to a certain degree with plugins, however it looks like blocking all the default boomerang beaconing behavior and rolling a new one.
I've been using boomerang.js for 2 months for measuring the response times, however I had to roll my own set of plugins.

Can you please clarify if you feel boomerang will/need/should support tracking of user perceived response times?

I understand that hosted applications and open-to-internet applications might have different requirements (e.g. in hosted enterprise app you have access.logs, thus you do not need resource timing beacons from client). However, it looks like current boomerang approach makes little sense for enterprise apps.

Here is the list of issues I ran into as I tried boomerang:

  1. Boomerang is page-level based. For instance, it "does not work" for single-page applications when the page is loaded just once, and further actions are performed without page reloads.
    I do not want per-ajax tracking (RT.startTimer/done). I want per end-user action tracking.
  2. Boomerang does not glue multiple page transitions under a single "user activity". For instance, a single button click might perform some ajax calls, and then perform redirect to another page. Boomerang captures this redirect as a "new page".
  3. Boomerang does not support frames/iframes. E.g. a click in main window might result in children iframe being created. I expect to get overall timing for the whole activity.
  4. Boomerang does not track/capture page rendering activities. Big pages often take much time to process at javascript/css/html paints. If your SLAs are "4 seconds for full page render", you need take rendering into consideration and measure it.
  5. Boomerang does not figure out the action user performs (e.g. the name of the button that was clicked, etc)

Correcting latency/bandwidth occasionally gives NaN

Hello,

I'm using boomerang just to measure bandwidth and latency; unfortunately, these are the two measurements that sometimes don't work (maybe 1 in 20 times).

I'm including boomerang.js, bw.js, and rt.js, and using a pretty basic init (and then subscribing to before_beacon):

BOOMR.init({
    beacon_url: "/syscheck/images/image-l.gif",
    user_ip: "<?= $_SERVER['REMOTE_ADDR'] ?>",
    BW: {
        base_url: "/syscheck/images/",
        cookie_exp: 60
    }
});

And here is the debug log output when it doesn't work: https://gist.github.com/awitschi/b4b3955d577bb8b6ec26

This isn't a lot of information to go on, but I don't really know much more. If there's any advice you can give me on why it doesn't always work, I'd appreciate it.

Thanks.

Beginners level question to ask?

I am new boomerang, looked at the relevant documents, found in very few examples online, my local build javaweb project to test the access path is local localhost, I want to know how to use a local project this javaweb boomerang, of course I do not have a dedicated server, only the local tomcat, ask specifically how to write the server and page? Can attach code to the best, really do not know how to start, thank you very much!

Enabling beacon and bandwidth tests using HTTPS

Mirroring YahooArchive#46 by @qcu

You said, in follow-up to Issue YahooArchive#18: "yeah, my guess is that no one really runs it over HTTPS. There was a similar bug someone emailed me about but regarding their own plugin."

@bluesmoon, I do actually need to run boomerang beacon over HTTPS, especially including bandwidth/latency tests. Also, the image GETs really need to run over HTTPS as well for my test scenario.

Before I look into this further, is this something that just needs some attention and testing, or are there significant issues blocking this (i.e., "SSL stuff will mess up b/w calculations").

How to store the result in boomerang?

I am using boomerang.js to get the page loading time. The result displays on same page only. I want store that data in my salesforce. How to do this? How can i store the data using beacon url? How can i retrieve the stored data?

Here is my code
<apex:page controller="jstrackerexample">

<title>Boomerang Exp</title> <script src="/resource/1427268234000/boomeranexp"></script> <script type="text/javascript"> BOOMR.init({ beacon_url: "https://company-name.my.salesforce.com/resource/1427275857000/beaconresult", site_domain: "https://company-name.cs7.visual.force.com", user_ip: "203.129.222.211", }); BOOMR.subscribe('before_beacon', function (o) { var html = "", t_name, t_other, others = [];
        if (!o.t_other) o.t_other = "";

        for (var k in o) {
            if (!k.match(/^(t_done|t_other|bw|lat|bw_err|lat_err|u|r2?)$/)) {
                if (k.match(/^t_/)) {
                    o.t_other += "," + k + "|" + o[k];
                }
                else {
                    others.push(k + " = " + o[k]);
                }
            }
        }

        if (o.t_done) { html += "This page took " + o.t_done + " ms to load<br>"; }
        if (o.t_other) {
            t_other = o.t_other.replace(/^,/, '').replace(/\|/g, ' = ').split(',');
            html += "Other timers measured: <br>";
            for (var i = 0; i < t_other.length; i++) {
                html += "&nbsp;&nbsp;&nbsp;" + t_other[i] + " ms<br>";
            }
        }
        if (o.bw) { html += "Your bandwidth to this server is " + parseInt(o.bw * 8 / 1024) + "kbps (&#x00b1;" + parseInt(o.bw_err * 100 / o.bw) + "%)<br>"; }
        if (o.lat) { html += "Your latency to this server is " + parseInt(o.lat) + "&#x00b1;" + o.lat_err + "ms<br>"; }

        var r = document.getElementById('results');
        r.innerHTML = html;

        if (others.length) {
            r.innerHTML += "Other parameters:<br>";

            for (var i = 0; i < others.length; i++) {
                var t = document.createTextNode(others[i]);
                r.innerHTML += "&nbsp;&nbsp;&nbsp;";
                r.appendChild(t);
                r.innerHTML += "<br>";

            }
        }
     });
</script>
<script>
    function setVar(param){
        jQuery('[id$=myHiddenField]').val(param);
        passStringToController();
    }
</script>
<body>
    <h1>Boomerang Example:</h1>
    <p id="results">
    </p>
</body>
/apex:page

result page is

This page took 2100 ms to Load
Other timers measured:
boomerang = 2 ms
boomr_fb = 1502 ms
t_resp = 1382 ms
t_page = 718 ms
Other parameters:
rt.start = navigation
rt.bstart = 1427350686076
rt.end = 1427350686674

Prevent execute html content before boomerang.js finished

situation is:

Javascript boomerang.js used as test of client net speed, problem is that html content (images,text, etc..) is executed by browser and is not waiting for boomerang result. But boomerang result is needed because there are some changes in html content.

How it is possible to do not execute html content before boomernag.js has result?

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <link rel='stylesheet' type='text/css' media='all' href='css/style.css' />
    <link rel='stylesheet' type='text/css' media='all' href='css/colorbox.css' />
    <script type="text/javascript" src="js/speedtest/boomerang.min.js"></script>
    <script type="text/javascript" src="js/speedtest/bw.js"></script>
    <script>
     BOOMR.init({ beacon_url: "http://www.zzz.xx", user_ip: '10.0.0.1', site_domain: "http://www.zzz.xx", BW: { nruns: 3, base_url: 'http://www.zzz.xx/test_images/', cookie: 'SPEED-test'}});
      </script>
       <script type="text/javascript" src="js/preload.js"></script>

preload.js is just check cookies and if exist do not make test again, if does not, run speedtest by BOOMR.subscribe

    var version_set=getCookie("speedtest_version_set");

     if (version_set == "low"){

                              $(document).ready(website_init_low);      

       } else if (version_set == "high"){

                              $(document).ready(website_init_high);

            } else {


         BOOMR.subscribe('before_beacon', function(o) {  

          if(o.bw) {  
                      netspeed = Math.abs(parseInt(o.bw*8/1024)); 
           var netspeed_limit = 5000; 

      if (netspeed < netspeed_limit) { 

                 document.cookie="speedtest_version_set = low";
                 $(document).ready(website_init_low);

        } else {
                document.cookie="speedtest_version_set = high";  
                $(document).ready(website_init_high);


        }

     }
          });

       }

2 Beacons sent yet one configured

Hi team!

I've recently set up another test-page with boomerang and came across a strange issue where
boomerang would send a beacon when the side was loaded prior to anything else happening(ie. BW-Plugin run) and once afterwards basically giving me 2 beacons even though I had thought to have configured only one. The HTML is very basic:

<html><head>
  <meta charset="utf-8">
  <title></title>
  <meta name="description" content="">
  <meta name="viewport" content="width=device-width">
  <script type="text/javascript" src="/js/boomerang-0.9.0.js"></script>
  <script type="text/javascript">
    var u = ["//localhost:4001/", "54579344cbf7586a01f06d37/demo-webpage" + "/contact/start" ];
    BOOMR.init({
        beacon_url: u[0] + "beacon/" + u[1],
        BW: {
            base_url: u[0] ,
            cookie: "bandwidth",
            nruns: 1
        },
        RT: {}
    });
    BOOMR.addVar("plugins",Object.keys(BOOMR.plugins).join(","));

  </script>
  <link rel="stylesheet" href="/vendor/bootstrap/dist/css/bootstrap.min.css">
  <link rel="stylesheet" href="/css/style.css">
  <script type="text/javascript">

  </script>

  <script type="text/javascript">

  </script>

</head>
<body>
<!-- Basic HTML -->
</body>
</html>

I have only added the script on top and init() configuration with some basic plugins (RT, BW ). But it would still send 2 beacons. A quick filter on url on my beacon-server:

"/beacon/54579344cbf7586a01f06d37/demo-webpage/shop/start"
"/image-l.gif?t=14157583130340.24289115308783948"
"/image-l.gif?t=14157583130590.3801573468372226"
"/image-l.gif?t=14157583131170.4063714947551489"
"/image-l.gif?t=14157583131730.46400602627545595"
"/image-l.gif?t=14157583132290.8678311358671635"
"/image-l.gif?t=14157583132840.9571576223243028"
"/image-l.gif?t=14157583133410.30458096507936716"
"/image-l.gif?t=14157583134090.8698015201371163"
"/image-l.gif?t=14157583134650.8809814790729433"
"/image-l.gif?t=14157583135200.416472946992144"
"/image-0.png?t=14157583135770.18505705893039703"
"/image-1.png?t=14157583136220.4129941747523844"
"/image-2.png?t=14157583136660.4191694213077426"
"/image-3.png?t=14157583136750.40637911669909954"
"/image-4.png?t=14157583136850.75371362012811"
"/image-5.png?t=14157583137000.6175008073914796"
"/image-6.png?t=14157583137630.6538159237243235"
"/beacon/54579344cbf7586a01f06d37/demo-webpage/contact/start"

The server it sends to is written by me (boomerang-express). And as far as I can see nothing should happen there even more strange the 2 requests have distinct request-ids meaning it must be something in the browser (tested with recent Chrome and Firefox). The setup is such that I have 2 express apps (boomerang-express + demo-web) both running on localhost (respectively localhost:4001 for b-e and localhost:4000 demo-web). They do not share any commonality between the 2 except that they are running on the same host machine.

I've used a recent build from Git to do this test. I can not see that theres an extra eventListeners, however there has been a fired event from setImmediate when navigating away from the page. But why that fires I can not really tell.

Thanks in advance!

Move the "core" plugins into their own files

I don't want to have the bandwidth plugin and I don't think I should have to carve it out of the boomerang.js myself to remove it. Please allow all plugins to be optional by moving them into their own files. It would also be nice if plugins were in a "plugins" folder so it's clear which .js files in the root are plugins.

Any known issues with current master against latest Firefox?

I've built the current master with the rt and NavigationTiming plugins. Using Firefox 32.0.3, no beacon request is received by my server, whereas with Chrome 37.0.2062.124 the beacon request is received correctly.

Using the debug build, the console log looks the same in both browsers, no errors.

I also set a breakpoint in sendBeacon() in Firefox, where the image is created and that works fine. But the network tab shows no request being made (although I'm less familiar with Firefox dev tools, so this could be user error) and the server definitely receives nothing.

Have you seen or heard of any similar issues, or do you have any ideas about what might be wrong?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.