PageRenderTime 33ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/global.php

http://showslow.googlecode.com/
PHP | 860 lines | 662 code | 113 blank | 85 comment | 91 complexity | c1b1526279db7227571205444c7361ec MD5 | raw file
  1. <?php
  2. # change it if you want to allow other profiles including your custom profiles
  3. $YSlow2AllowedProfiles = array('ydefault');
  4. # If not false, then should be an array of prefix matches - if one of them matches, URL will be accepted
  5. $limitURLs = false;
  6. # Track non-http(s) URLs. Disabled by default
  7. $enableNonHTTPURLs = false;
  8. # URL groups to be displayed on URLs measured tab
  9. $URLGroups = array();
  10. # Ignore URLs matching the prefix or a regext. If one of them matches, URLs is going to be ignored
  11. # You might want to remove 10.x, 192.168.x and 172.16-32.x if you're testing web sites on a private network.
  12. $ignoreURLs = array(
  13. 'http://0.0.0.0',
  14. 'http://127.0.0.',
  15. 'http://localhost/',
  16. 'http://localhost:',
  17. 'http://10.',
  18. 'http://192.168.',
  19. 'http://172.16.',
  20. 'http://172.17.',
  21. 'http://172.18.',
  22. 'http://172.19.',
  23. 'http://172.20.',
  24. 'http://172.21.',
  25. 'http://172.22.',
  26. 'http://172.23.',
  27. 'http://172.24.',
  28. 'http://172.25.',
  29. 'http://172.26.',
  30. 'http://172.27.',
  31. 'http://172.28.',
  32. 'http://172.29.',
  33. 'http://172.30.',
  34. 'http://172.31.'
  35. );
  36. # If set to true, drop all query strings. If array, then match prefixes.
  37. $dropQueryStrings = false;
  38. # Custom metrics array
  39. $metrics = array();
  40. # to see if your users are visiting the tool, enable Google Analytics
  41. # (for publicly hosted instances)
  42. $googleAnalyticsProfile = '';
  43. # KissMetrics key
  44. $kissMetricsKey = '';
  45. # show Feedback button
  46. $showFeedbackButton = true;
  47. # AddThis profile, set it to enable sharing functions
  48. $addThisProfile = null;
  49. # how old should data be for deletion (in days)
  50. # anything >0 will delete old data
  51. # don't forget to add a cron job to run deleteolddata.php
  52. $oldDataInterval = 60;
  53. # Enable this if you'd like to clean old yslow beacon details to conserve space
  54. # (beacon details are currently only used for tooltips for latest yslow breakdown)
  55. $cleanOldYSlowBeaconDetails = false;
  56. $homePageMetaTags = '';
  57. # this enables a form to run a test on WebPageTest.org
  58. $webPageTestKey = null; # must be set to something to not null to enable
  59. $webPageTestBase = 'http://www.webpagetest.org/';
  60. $webPageTestPrivateByDefault = false;
  61. $webPageTestFirstRunOnlyByDefault = false;
  62. $webPageTestExtraParams = '';
  63. $keepPrivatePageTests = false;
  64. # a list of URLs to compare by default. Set to NULL to not send any URLs
  65. # $defaultURLsToCompare = array('http://www.google.com/', 'http://www.yahoo.com/', 'http://www.amazon.com/');
  66. $defaultURLsToCompare = NULL;
  67. # Change this to 'pagespeed' to use it for comparison by default
  68. $defaultRankerToCompare = 'yslow';
  69. # Enabling HAR beacon will allow storing HAR data for URLs and display graphically using HAR viewer
  70. $enableHARBeacon = false;
  71. # HAR Viewer base URL
  72. $HARViewerBase = 'http://www.softwareishard.com/har/viewer/';
  73. # Enable user URL monitoring
  74. $enableMyURLs = false;
  75. # Maximum URLs each user can add to the system to be monitored (false means no limit)
  76. $maxURLsPerUser = false;
  77. # Message to show the user when he riches the maximum
  78. $maxURLsMessage = 'The number of URLs tracked is limited because of load constraints.';
  79. # Privileged users who has no limit on URLs
  80. $noMaxURLsForUsers = array();
  81. # how long should monitoring scripts wait between measurements (in hours).
  82. $monitoringPeriod = 24;
  83. # Facebook connect properties, configure them here:
  84. # http://www.facebook.com/developers/createapp.php
  85. $facebookAPIKey = null;
  86. $facebookSecret = null;
  87. # Google Friend connect site ID
  88. # get it from the URL's "id" parameter on Google Friend Connect admin page for the site:
  89. # http://www.google.com/friendconnect/admin/
  90. $googleFriendConnectSiteID = null;
  91. # Smoothing distance (averaging window will be from x-distance to x+distance)
  92. $smoothDistance = 5;
  93. require_once(dirname(__FILE__).'/asset_versions.php');
  94. require_once(dirname(__FILE__).'/svn-assets/asset_functions.php');
  95. # Put description for ShowSlow instance into this variable - it'll be displayed on home page under the header.
  96. $ShowSlowIntro = '<p>Show Slow is an open source tool that helps monitor various website performance metrics over time. It captures the results of <a href="http://developer.yahoo.com/yslow/">YSlow</a> and <a href="http://code.google.com/speed/page-speed/">Page Speed</a> rankings and graphs them, to help you understand how various changes to your site affect its performance.</p>
  97. <p><a href="http://www.showslow.com/">www.ShowSlow.com</a> is a demonstration site that continuously measures the performance of a few reference web pages. It also allows for public metrics reporting.</p>
  98. <p>If you want to make your measurements publicly available on this page, see the instructions in <a href="configure.php">Configuring YSlow / Page Speed</a>. If you want to keep your measurements private, <b><a href="http://code.google.com/p/showslow/source/checkout">download Show Slow</a></b> from the SVN repository and install it on your own server.</p>
  99. <p>You can ask questions and discuss ShowSlow in our group <a href="http://groups.google.com/group/showslow">http://groups.google.com/group/showslow</a> or just leave feedback at <a href="http://showslow.uservoice.com">http://showslow.uservoice.com</a></p>
  100. <table cellpadding="0" cellspacing="0" border="0"><tr>
  101. <td valign="top"><style>
  102. #twitterbutton {
  103. margin-right: 7px;
  104. width: 58px;
  105. height: 23px;
  106. display: block;
  107. background-image: url('.assetURL('follow.png').');
  108. background-position: 0px 0px;
  109. }
  110. #twitterbutton:hover {
  111. background-position: 0px -46px;
  112. }
  113. </style><a id="twitterbutton" href="http://twitter.com/showslow" target="_blank" title="follow @showslow on twitter"/></a></td>
  114. <td valign="top">
  115. <iframe src="http://www.facebook.com/plugins/like.php?href=http%253A%252F%252Fwww.showslow.com%252F&amp;layout=standard&amp;show_faces=false&amp;width=450&amp;action=recommend&amp;font&amp;colorscheme=light&amp;height=35" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:450px; height:23px;" allowTransparency="true"></iframe>
  116. </td>
  117. </tr></table>';
  118. # configuring tabs
  119. $customLists = array();
  120. # additional menu items (url, title are keys for each item)
  121. $additionalMenuItems = array();
  122. # config will override defaults above
  123. require_once(dirname(__FILE__).'/config.php');
  124. # PUT ALL THINGS THAT SHOULDN'T BE CONFIGURABLE BELOW THIS LINE
  125. # metric type constants
  126. define('BYTES', 0);
  127. define('PERCENTS', 1);
  128. define('MS', 2);
  129. define('NUMBER', 3);
  130. # used for legend (in parenthesis)
  131. # if no label needed like for number, just don't insert it here
  132. $metric_types = array(
  133. BYTES => array( 'legend' => 'in bytes', 'units' => ' bytes'),
  134. MS => array( 'legend' => 'im ms', 'units' => ' ms'),
  135. PERCENTS => array( 'legend' => '0-100', 'units' => '%'),
  136. NUMBER => array( 'legend' => '', 'units' => '')
  137. );
  138. # a list of metrics (excluding custom metrics) available to be displayed on the graph
  139. $all_metrics = array(
  140. 'yslow' => array(
  141. 'title' => 'YSlow',
  142. 'url' => 'http://developer.yahoo.com/yslow/',
  143. 'table' => 'yslow2',
  144. 'score_name' => 'grade',
  145. 'score_column' => 'o',
  146. 'metrics' => array(
  147. 'Basic measurements' => array(
  148. array( 'Overall rank', 'o', PERCENTS),
  149. array( 'Page Size', 'w', BYTES),
  150. array( 'Amount of requests with empty cache', 'r', NUMBER),
  151. array( 'Page Load time', 'lt', MS)
  152. ),
  153. 'Best practices' => array(
  154. array( 'Make fewer HTTP requests', 'ynumreq', PERCENTS, 'http://developer.yahoo.com/performance/rules.html#num_http'),
  155. array( 'Use a Content Delivery Network (CDN)', 'ycdn', PERCENTS, 'http://developer.yahoo.com/performance/rules.html#cdn'),
  156. array( 'Add Expires headers', 'yexpires', PERCENTS, 'http://developer.yahoo.com/performance/rules.html#expires'),
  157. array( 'Avoid Empty Image src', 'yemptysrc', PERCENTS, 'http://developer.yahoo.com/performance/rules.html#emptysrc'),
  158. array( 'Compress components with gzip', 'ycompress', PERCENTS, 'http://developer.yahoo.com/performance/rules.html#gzip'),
  159. array( 'Put CSS at top', 'ycsstop', PERCENTS, 'http://developer.yahoo.com/performance/rules.html#css_top'),
  160. array( 'Put JavaScript at bottom', 'yjsbottom', PERCENTS, 'http://developer.yahoo.com/performance/rules.html#js_bottom'),
  161. array( 'Avoid CSS expressions', 'yexpressions', PERCENTS, 'http://developer.yahoo.com/performance/rules.html#css_expressions'),
  162. array( 'Make JavaScript and CSS external', 'yexternal', PERCENTS, 'http://developer.yahoo.com/performance/rules.html#external'),
  163. array( 'Reduce DNS lookups', 'ydns', PERCENTS, 'http://developer.yahoo.com/performance/rules.html#dns_lookups'),
  164. array( 'Minify JavaScript and CSS', 'yminify', PERCENTS, 'http://developer.yahoo.com/performance/rules.html#minify'),
  165. array( 'Avoid URL redirects', 'yredirects', PERCENTS, 'http://developer.yahoo.com/performance/rules.html#redirects'),
  166. array( 'Remove duplicate JavaScript and CSS', 'ydupes', PERCENTS, 'http://developer.yahoo.com/performance/rules.html#js_dupes'),
  167. array( 'Configure entity tags (ETags)', 'yetags', PERCENTS, 'http://developer.yahoo.com/performance/rules.html#etags'),
  168. array( 'Make AJAX cacheable', 'yxhr', PERCENTS, 'http://developer.yahoo.com/performance/rules.html#cacheajax'),
  169. array( 'Use GET for AJAX requests', 'yxhrmethod', PERCENTS, 'http://developer.yahoo.com/performance/rules.html#ajax_get'),
  170. array( 'Reduce the number of DOM elements', 'ymindom', PERCENTS, 'http://developer.yahoo.com/performance/rules.html#min_dom'),
  171. array( 'Avoid HTTP 404 (Not Found) error', 'yno404', PERCENTS, 'http://developer.yahoo.com/performance/rules.html#no404'),
  172. array( 'Reduce cookie size', 'ymincookie', PERCENTS, 'http://developer.yahoo.com/performance/rules.html#cookie_size'),
  173. array( 'Use cookie-free domains', 'ycookiefree', PERCENTS, 'http://developer.yahoo.com/performance/rules.html#cookie_free'),
  174. array( 'Avoid AlphaImageLoader filter', 'ynofilter', PERCENTS, 'http://developer.yahoo.com/performance/rules.html#no_filters'),
  175. array( 'Do not scale images in HTML', 'yimgnoscale', PERCENTS, 'http://developer.yahoo.com/performance/rules.html#no_scale'),
  176. array( 'Make favicon small and cacheable', 'yfavicon', PERCENTS, 'http://developer.yahoo.com/performance/rules.html#favicon')
  177. )
  178. )
  179. ),
  180. 'pagespeed' => array(
  181. 'title' => 'Page Speed',
  182. 'url' => 'http://code.google.com/speed/page-speed/',
  183. 'table' => 'pagespeed',
  184. 'score_name' => 'score',
  185. 'score_column' => 'o',
  186. 'metrics' => array(
  187. 'Basic measurements' => array(
  188. array( 'Page size', 'w', BYTES),
  189. array( 'Page load time', 'l', MS),
  190. array( 'Transfer size of all resources', 't', BYTES),
  191. array( 'Total Requests', 'r', NUMBER),
  192. array( 'Overall grade', 'o', PERCENTS)
  193. ),
  194. 'Optimize caching' => array(
  195. array( 'Leverage browser caching', 'pBrowserCache', PERCENTS, 'http://code.google.com/speed/page-speed/docs/caching.html#LeverageBrowserCaching'),
  196. array( 'Leverage proxy caching', 'pCacheValid', PERCENTS, 'http://code.google.com/speed/page-speed/docs/caching.html#LeverageProxyCaching'),
  197. ),
  198. 'Minimize round-trip times' => array(
  199. array( 'Minimize DNS lookups', 'pMinDns', PERCENTS, 'http://code.google.com/speed/page-speed/docs/rtt.html#MinimizeDNSLookups'),
  200. array( 'Minimize redirects', 'pMinRedirect', PERCENTS, 'http://code.google.com/speed/page-speed/docs/rtt.html#AvoidRedirects'),
  201. array( 'Avoid bad requests', 'pBadReqs', PERCENTS, 'http://code.google.com/speed/page-speed/docs/rtt.html#AvoidBadRequests'),
  202. array( 'Combine external JavaScript', 'pCombineJS', PERCENTS, 'http://code.google.com/speed/page-speed/docs/rtt.html#CombineExternalJS'),
  203. array( 'Combine external CSS', 'pCombineCSS', PERCENTS, 'http://code.google.com/speed/page-speed/docs/rtt.html#CombineExternalCSS'),
  204. array( 'Combine images using CSS sprites', 'pSprite', PERCENTS, 'http://code.google.com/speed/page-speed/docs/rtt.html#SpriteImages'),
  205. array( 'Optimize the order of styles and scripts', 'pCssJsOrder', PERCENTS, 'http://code.google.com/speed/page-speed/docs/rtt.html#PutStylesBeforeScripts'),
  206. array( 'Avoid document.write', 'pDocWrite', PERCENTS, 'http://code.google.com/speed/page-speed/docs/rtt.html#AvoidDocumentWrite'),
  207. array( 'Avoid CSS @import', 'pCssImport', PERCENTS, 'http://code.google.com/speed/page-speed/docs/rtt.html#AvoidCssImport'),
  208. array( 'Prefer asynchronous resources', 'pPreferAsync', PERCENTS, 'http://code.google.com/speed/page-speed/docs/rtt.html#PreferAsyncResources'),
  209. array( 'Parallelize downloads across hostnames', 'pParallelDl', PERCENTS, 'http://code.google.com/speed/page-speed/docs/rtt.html#ParallelizeDownloads')
  210. ),
  211. 'Minimize request overhead' => array(
  212. array( 'Minimize request size', 'pMinReqSize', PERCENTS, 'http://code.google.com/speed/page-speed/docs/request.html#MinimizeRequestSize'),
  213. array( 'Serve static content from a cookieless domain', 'pNoCookie', PERCENTS, 'http://code.google.com/speed/page-speed/docs/request.html#ServeFromCookielessDomain')
  214. ),
  215. 'Minimize payload size' => array(
  216. array( 'Enable compression', 'pGzip', PERCENTS, 'http://code.google.com/speed/page-speed/docs/payload.html#GzipCompression'),
  217. array( 'Remove unused CSS', 'pUnusedCSS', PERCENTS, 'http://code.google.com/speed/page-speed/docs/payload.html#RemoveUnusedCSS'),
  218. array( 'Minify JavaScript', 'pMinifyJS', PERCENTS, 'http://code.google.com/speed/page-speed/docs/payload.html#MinifyJS'),
  219. array( 'Minify CSS', 'pMinifyCSS', PERCENTS, 'http://code.google.com/speed/page-speed/docs/payload.html#MinifyCSS'),
  220. array( 'Minify HTML', 'pMinifyHTML', PERCENTS, 'http://code.google.com/speed/page-speed/docs/payload.html#MinifyHTML'),
  221. array( 'Defer loading of JavaScript', 'pDeferJS', PERCENTS, 'http://code.google.com/speed/page-speed/docs/payload.html#DeferLoadingJS'),
  222. array( 'Optimize images', 'pOptImgs', PERCENTS, 'http://code.google.com/speed/page-speed/docs/payload.html#CompressImages'),
  223. array( 'Serve scaled images', 'pScaleImgs', PERCENTS, 'http://code.google.com/speed/page-speed/docs/payload.html#ScaleImages'),
  224. array( 'Serve resources from a consistent URL', 'pDupeRsrc', PERCENTS, 'http://code.google.com/speed/page-speed/docs/payload.html#duplicate_resources')
  225. ),
  226. 'Optimize browser rendering' => array(
  227. array( 'Use efficient CSS selectors', 'pCssSelect', PERCENTS, 'http://code.google.com/speed/page-speed/docs/rendering.html#UseEfficientCSSSelectors'),
  228. array( 'Put CSS in the document head', 'pCssInHead', PERCENTS, 'http://code.google.com/speed/page-speed/docs/rendering.html#PutCSSInHead'),
  229. array( 'Specify image dimensions', 'pImgDims', PERCENTS, 'http://code.google.com/speed/page-speed/docs/rendering.html#SpecifyImageDimensions'),
  230. array( 'Specify a character set early', 'pCharsetEarly', PERCENTS, 'http://code.google.com/speed/page-speed/docs/rendering.html#SpecifyCharsetEarly'),
  231. )
  232. )
  233. ),
  234. 'dynatrace' => array(
  235. 'title' => 'dynaTrace',
  236. 'url' => 'http://ajax.dynatrace.com/',
  237. 'table' => 'dynatrace',
  238. 'score_name' => 'rank',
  239. 'score_column' => 'rank',
  240. 'metrics' => array(
  241. 'Event times' => array(
  242. array( 'Time to first impression', 'timetoimpression', MS),
  243. array( 'Time to onLoad', 'timetoonload', MS),
  244. array( 'Time to full page load', 'timetofullload', MS)
  245. ),
  246. 'Total time breakdown' => array(
  247. array( 'Total time on network', 'timeonnetwork', MS),
  248. array( 'Total time in JavaScript', 'timeinjs', MS),
  249. array( 'Total time in rendering', 'timeinrendering', MS)
  250. ),
  251. 'Requests and size' => array(
  252. array( 'Number of requests', 'reqnumber', NUMBER),
  253. array( 'Number of XHR requests', 'xhrnumber', NUMBER),
  254. array( 'Total page size', 'pagesize', BYTES),
  255. array( 'Total cachable size', 'cachablesize', BYTES),
  256. array( 'Total non-cachable size', 'noncachablesize', BYTES)
  257. ),
  258. 'Best practices' => array(
  259. array( 'Overall rank', 'rank', PERCENTS),
  260. array( 'Caching rank', 'cache', PERCENTS, 'https://community.dynatrace.com/community/display/PUB/Best+Practices+on+Browser+Caching'),
  261. array( 'Network rank', 'net', PERCENTS, 'https://community.dynatrace.com/community/display/PUB/Best+Practices+on+Network+Requests+and+Roundtrips'),
  262. array( 'Server rank', 'server', PERCENTS, 'https://community.dynatrace.com/community/display/PUB/Best+Practices+on+Server-Side+Performance+Optimization'),
  263. array( 'JavaScript rank', 'js', PERCENTS, 'https://community.dynatrace.com/community/display/PUB/Best+Practices+on+JavaScript+and+AJAX+Performance')
  264. )
  265. )
  266. )
  267. );
  268. function prettyScore($num) {
  269. $letter = 'F';
  270. if ( 90 <= $num )
  271. $letter = 'A';
  272. else if ( 80 <= $num )
  273. $letter = 'B';
  274. else if ( 70 <= $num )
  275. $letter = 'C';
  276. else if ( 60 <= $num )
  277. $letter = 'D';
  278. else if ( 50 <= $num )
  279. $letter = 'E';
  280. return $letter;
  281. }
  282. function scoreColorStep($num, $total = 13) {
  283. for($i=1; $i<=$total; $i++)
  284. {
  285. if ($num <= $i*100/$total)
  286. {
  287. return $i;
  288. }
  289. }
  290. }
  291. $colorSteps = array(
  292. 'EE0000',
  293. 'EE2800',
  294. 'EE4F00',
  295. 'EE7700',
  296. 'EE9F00',
  297. 'EEC600',
  298. 'EEEE00',
  299. 'C6EE00',
  300. '9FEE00',
  301. '77EE00',
  302. '4FEE00',
  303. '28EE00',
  304. '00EE00'
  305. );
  306. $colorStepShades = array(
  307. 'CF0000',
  308. 'CF2200',
  309. 'CF4400',
  310. 'CF6600',
  311. 'CF8800',
  312. 'CFAA00',
  313. 'CDCF00',
  314. 'ABCF00',
  315. '89CF00',
  316. '67CF00',
  317. '45CF00',
  318. '23CF00',
  319. '01CF00'
  320. );
  321. function scoreColor($num, $darker = false) {
  322. global $colorSteps, $colorStepShades;
  323. $colors = $darker ? $colorStepShades : $colorSteps;
  324. return '#'.$colors[scoreColorStep($num, count($colors))-1];
  325. }
  326. # returns true if URL should be ignored
  327. function isURLIgnored($url) {
  328. global $ignoreURLs;
  329. if ($ignoreURLs !== false && is_array($ignoreURLs)) {
  330. $matched = false;
  331. foreach ($ignoreURLs as $ignoreString) {
  332. // checking if string is a regex or just a prefix
  333. if (preg_match('/^[^a-zA-Z\\\s]/', $ignoreString))
  334. {
  335. if (preg_match($ignoreString, $url)) {
  336. $matched = true;
  337. }
  338. } else if (substr($url, 0, strlen($ignoreString)) == $ignoreString) {
  339. $matched = true;
  340. break;
  341. }
  342. }
  343. return $matched;
  344. }
  345. return false;
  346. }
  347. # returns true if URL is in the limitedURLs array or all URLs are allowed
  348. function isURLAllowed($url) {
  349. global $limitURLs;
  350. if ($limitURLs !== false && is_array($limitURLs)) {
  351. $matched = false;
  352. foreach ($limitURLs as $limitString) {
  353. // checking if string is a regex or just a prefix
  354. if (preg_match('/^[^a-zA-Z\\\s]/', $limitString))
  355. {
  356. if (preg_match($limitString, $url)) {
  357. $matched = true;
  358. }
  359. } else if (substr($url, 0, strlen($limitString)) == $limitString) {
  360. $matched = true;
  361. break;
  362. }
  363. }
  364. return $matched;
  365. }
  366. return true;
  367. }
  368. # returns true if this URLS is not an HTTP URL and should be ignored
  369. function shouldBeIgnoredAsNonHTTP($url) {
  370. global $enableNonHTTPURLs;
  371. return (
  372. !$enableNonHTTPURLs &&
  373. (substr(strtolower($url), 0, 7) != 'http://' && substr(strtolower($url), 0, 8) != 'https://')
  374. );
  375. }
  376. # returns URL if it's valid and passes all checks or null if not.
  377. # if second parameter is true (default), then 404 error page is shown
  378. #
  379. # used in getUrlId and event beacon (which tests prefix, not URL)
  380. #
  381. # TODO rewrite to use exceptions instead of $outputerror contraption
  382. function validateURL($url, $outputerror = true) {
  383. $url = filter_var(urldecode(trim($url)), FILTER_VALIDATE_URL);
  384. if ($url === FALSE) {
  385. if (!$outputerror) {
  386. return null;
  387. }
  388. header('HTTP/1.0 400 Bad Request');
  389. ?><html>
  390. <head>
  391. <title>Bad Request: ShowSlow beacon</title>
  392. </head>
  393. <body>
  394. <h1>Bad Request: ShowSlow beacon</h1>
  395. <p>Invalid URL submitted.</p>
  396. </body></html>
  397. <?php
  398. exit;
  399. }
  400. if (shouldBeIgnoredAsNonHTTP($url)) {
  401. if (!$outputerror) {
  402. return null;
  403. }
  404. header('HTTP/1.0 400 Bad Request');
  405. ?><html>
  406. <head>
  407. <title>Bad Request: ShowSlow beacon</title>
  408. </head>
  409. <body>
  410. <h1>Bad Request: ShowSlow beacon</h1>
  411. <p>This instance of Show Slow only tracks HTTP(S) URLs.</p>
  412. </body></html>
  413. <?php
  414. exit;
  415. }
  416. if (isURLIgnored($url)) {
  417. if (!$outputerror) {
  418. return null;
  419. }
  420. header('HTTP/1.0 400 Bad Request');
  421. ?><html>
  422. <head>
  423. <title>Bad Request: ShowSlow beacon</title>
  424. </head>
  425. <body>
  426. <h1>Bad Request: ShowSlow beacon</h1>
  427. <p>This URL matched ignore list for this instance of Show Slow.</p>
  428. </body></html>
  429. <?php
  430. exit;
  431. }
  432. if (!isURLAllowed($url)) {
  433. if (!$outputerror) {
  434. return null;
  435. }
  436. header('HTTP/1.0 400 Bad Request');
  437. ?><html>
  438. <head>
  439. <title>Bad Request: ShowSlow beacon</title>
  440. </head>
  441. <body>
  442. <h1>Bad Request: ShowSlow beacon</h1>
  443. <p>URL doesn't match any URLs allowed allowed to this instance.</p>
  444. </body></html>
  445. <?php
  446. exit;
  447. }
  448. return $url;
  449. }
  450. $webPageTestLocations = array();
  451. $webPageTestLocationsById = array();
  452. function getPageTestLocations() {
  453. global $webPageTestLocations, $webPageTestLocationsById, $webPageTestBase, $webPageTestKey;
  454. if (count($webPageTestLocations) > 0 || is_null($webPageTestKey)) {
  455. return;
  456. }
  457. // Getting a list of locations from WebPageTest
  458. $ch = curl_init();
  459. curl_setopt($ch, CURLOPT_URL, $webPageTestBase.'getLocations.php?f=xml&k='.urlencode($webPageTestKey));
  460. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  461. $output = curl_exec($ch);
  462. if (empty($output)) {
  463. $err = curl_error($ch);
  464. curl_close($ch);
  465. failWithMessage("API call ($locationsURL) failed: ".$err);
  466. }
  467. $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  468. if ($code != 200) {
  469. curl_close($ch);
  470. failWithMessage("PageTest didn't accept the request: $code");
  471. }
  472. curl_close($ch);
  473. $xml = new SimpleXMLElement($output);
  474. if (empty($xml)) {
  475. failWithMessage("Failed to parse XML response");
  476. }
  477. if ($xml->statusCode != 200) {
  478. failWithMessage("PageTest getLocations returned failure status code: ".$xml->statusCode." (".$xml->statusText.")");
  479. }
  480. foreach ($xml->data->location as $location) {
  481. $id = $location->id;
  482. $loc = array(
  483. 'id' => $id,
  484. 'default' => $location->default == 1 ? true : false,
  485. 'title' => $location->Label.' ('.$location->Browser.')',
  486. 'tests' => $location->PendingTests->Total
  487. );
  488. $webPageTestLocations[] = $loc;
  489. $webPageTestLocationsById["$id"] = $loc;
  490. }
  491. }
  492. function getUrlId($url, $outputerror = true)
  493. {
  494. global $dropQueryStrings;
  495. $url = validateURL($url, $outputerror);
  496. if (is_null($url)) {
  497. return null;
  498. }
  499. if ($dropQueryStrings) {
  500. $drop = false;
  501. if (is_array($dropQueryStrings)) {
  502. foreach ($dropQueryStrings as $prefix) {
  503. if (substr($url, 0, strlen($prefix)) == $prefix) {
  504. $drop = true;
  505. break;
  506. }
  507. }
  508. } else {
  509. $drop = true;
  510. }
  511. if ($drop) {
  512. $querypos = strpos($url, '?');
  513. if ($querypos !== false) {
  514. $url = substr($url, 0, $querypos);
  515. }
  516. }
  517. }
  518. # get URL id
  519. $query = sprintf("SELECT id FROM urls WHERE url = '%s'", mysql_real_escape_string($url));
  520. $result = mysql_query($query);
  521. if (!$result) {
  522. beaconError(mysql_error());
  523. }
  524. if (mysql_num_rows($result) == 1) {
  525. $row = mysql_fetch_assoc($result);
  526. return $row['id'];
  527. } else if (mysql_num_rows($result) == 0) {
  528. // Emulating unique index on a blob with unlimited length by locking the table on write
  529. // locking only when we're about to insert so we don't block the whole thing on every read
  530. // locking the table to make sure we pass it only by one concurrent process
  531. $result = mysql_query('LOCK TABLES urls WRITE');
  532. if (!$result) {
  533. beaconError(mysql_error());
  534. }
  535. // selecting the URL again to make sure there was no concurrent insert for this URL
  536. $query = sprintf("SELECT id FROM urls WHERE url = '%s'", mysql_real_escape_string($url));
  537. $result = mysql_query($query);
  538. if (!$result) {
  539. $mysql_err = mysql_error();
  540. mysql_query('UNLOCK TABLES'); // unlocking the table if in trouble
  541. beaconError($mysql_err);
  542. }
  543. // repeating the check
  544. if (mysql_num_rows($result) == 1) {
  545. $row = mysql_fetch_assoc($result);
  546. $url_id = $row['id'];
  547. } else if (mysql_num_rows($result) == 0) {
  548. $query = sprintf("INSERT INTO urls (url) VALUES ('%s')", mysql_real_escape_string($url));
  549. $result = mysql_query($query);
  550. if (!$result) {
  551. $mysql_err = mysql_error();
  552. mysql_query('UNLOCK TABLES'); // unlocking the table if in trouble
  553. beaconError($mysql_err);
  554. }
  555. $url_id = mysql_insert_id();
  556. } else if (mysql_num_rows($result) > 1) {
  557. mysql_query('UNLOCK TABLES'); // unlocking the table if in trouble
  558. beaconError('more then one entry found for the URL (when lock is aquired)');
  559. }
  560. $result = mysql_query('UNLOCK TABLES'); // now concurrent thread can try reading again
  561. if (!$result) {
  562. beaconError(mysql_error());
  563. }
  564. return $url_id;
  565. } else {
  566. beaconError('more then one entry found for the URL');
  567. }
  568. }
  569. // httpd_build_url replacement from http://www.mediafire.com/?zjry3tynkg5
  570. // added base function feature that allows to pass an array as first parameter
  571. if (!function_exists('http_build_url'))
  572. {
  573. define('HTTP_URL_REPLACE', 1); // Replace every part of the first URL when there's one of the second URL
  574. define('HTTP_URL_JOIN_PATH', 2); // Join relative paths
  575. define('HTTP_URL_JOIN_QUERY', 4); // Join query strings
  576. define('HTTP_URL_STRIP_USER', 8); // Strip any user authentication information
  577. define('HTTP_URL_STRIP_PASS', 16); // Strip any password authentication information
  578. define('HTTP_URL_STRIP_AUTH', 32); // Strip any authentication information
  579. define('HTTP_URL_STRIP_PORT', 64); // Strip explicit port numbers
  580. define('HTTP_URL_STRIP_PATH', 128); // Strip complete path
  581. define('HTTP_URL_STRIP_QUERY', 256); // Strip query string
  582. define('HTTP_URL_STRIP_FRAGMENT', 512); // Strip any fragments (#identifier)
  583. define('HTTP_URL_STRIP_ALL', 1024); // Strip anything but scheme and host
  584. // Build an URL
  585. // The parts of the second URL will be merged into the first according to the flags argument.
  586. //
  587. // @param mixed (Part(s) of) an URL in form of a string or associative array like parse_url() returns
  588. // @param mixed Same as the first argument
  589. // @param int A bitmask of binary or'ed HTTP_URL constants (Optional)HTTP_URL_REPLACE is the default
  590. // @param array If set, it will be filled with the parts of the composed url like parse_url() would return
  591. function http_build_url($url, $parts=array(), $flags=HTTP_URL_REPLACE, &$new_url=false)
  592. {
  593. $keys = array('user','pass','port','path','query','fragment');
  594. // HTTP_URL_STRIP_ALL becomes all the HTTP_URL_STRIP_Xs
  595. if ($flags & HTTP_URL_STRIP_ALL)
  596. {
  597. $flags |= HTTP_URL_STRIP_USER;
  598. $flags |= HTTP_URL_STRIP_PASS;
  599. $flags |= HTTP_URL_STRIP_PORT;
  600. $flags |= HTTP_URL_STRIP_PATH;
  601. $flags |= HTTP_URL_STRIP_QUERY;
  602. $flags |= HTTP_URL_STRIP_FRAGMENT;
  603. }
  604. // HTTP_URL_STRIP_AUTH becomes HTTP_URL_STRIP_USER and HTTP_URL_STRIP_PASS
  605. else if ($flags & HTTP_URL_STRIP_AUTH)
  606. {
  607. $flags |= HTTP_URL_STRIP_USER;
  608. $flags |= HTTP_URL_STRIP_PASS;
  609. }
  610. // Parse the original URL
  611. if (is_array($url)) {
  612. $parse_url = $url;
  613. } else {
  614. $parse_url = parse_url($url);
  615. }
  616. // Scheme and Host are always replaced
  617. if (isset($parts['scheme']))
  618. $parse_url['scheme'] = $parts['scheme'];
  619. if (isset($parts['host']))
  620. $parse_url['host'] = $parts['host'];
  621. // (If applicable) Replace the original URL with it's new parts
  622. if ($flags & HTTP_URL_REPLACE)
  623. {
  624. foreach ($keys as $key)
  625. {
  626. if (isset($parts[$key]))
  627. $parse_url[$key] = $parts[$key];
  628. }
  629. }
  630. else
  631. {
  632. // Join the original URL path with the new path
  633. if (isset($parts['path']) && ($flags & HTTP_URL_JOIN_PATH))
  634. {
  635. if (isset($parse_url['path']))
  636. $parse_url['path'] = rtrim(str_replace(basename($parse_url['path']), '', $parse_url['path']), '/') . '/' . ltrim($parts['path'], '/');
  637. else
  638. $parse_url['path'] = $parts['path'];
  639. }
  640. // Join the original query string with the new query string
  641. if (isset($parts['query']) && ($flags & HTTP_URL_JOIN_QUERY))
  642. {
  643. if (isset($parse_url['query']))
  644. $parse_url['query'] .= '&' . $parts['query'];
  645. else
  646. $parse_url['query'] = $parts['query'];
  647. }
  648. }
  649. // Strips all the applicable sections of the URL
  650. // Note: Scheme and Host are never stripped
  651. foreach ($keys as $key)
  652. {
  653. if ($flags & (int)constant('HTTP_URL_STRIP_' . strtoupper($key)))
  654. unset($parse_url[$key]);
  655. }
  656. $new_url = $parse_url;
  657. return
  658. ((isset($parse_url['scheme'])) ? $parse_url['scheme'] . '://' : '')
  659. .((isset($parse_url['user'])) ? $parse_url['user'] . ((isset($parse_url['pass'])) ? ':' . $parse_url['pass'] : '') .'@' : '')
  660. .((isset($parse_url['host'])) ? $parse_url['host'] : '')
  661. .((isset($parse_url['port'])) ? ':' . $parse_url['port'] : '')
  662. .((isset($parse_url['path'])) ? $parse_url['path'] : '')
  663. .((isset($parse_url['query'])) ? '?' . $parse_url['query'] : '')
  664. .((isset($parse_url['fragment'])) ? '#' . $parse_url['fragment'] : '')
  665. ;
  666. }
  667. }
  668. function resolveRedirects($url) {
  669. if (function_exists('curl_init')) {
  670. $ch = curl_init($url);
  671. curl_setopt_array($ch, array(
  672. CURLOPT_NOBODY => TRUE,
  673. CURLOPT_FOLLOWLOCATION => TRUE,
  674. CURLOPT_MAXREDIRS => 10
  675. ));
  676. if (curl_exec($ch)) {
  677. $new_url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
  678. # TODO also test for success code
  679. # TODO maybe, fix www. when it's missing.
  680. if ($new_url) {
  681. $url = $new_url;
  682. }
  683. }
  684. }
  685. // now, let's fix trailing slash in case of domain-only request
  686. $urlparts = parse_url($url);
  687. if (!array_key_exists('path', $urlparts) || $urlparts['path'] == '') {
  688. $urlparts['path'] = '/';
  689. }
  690. $new_url = http_build_url($urlparts);
  691. if ($new_url) {
  692. $url = $new_url;
  693. }
  694. return $url;
  695. }
  696. function failWithMessage($message)
  697. {
  698. error_log("[Page Error] ".$message);
  699. header('HTTP/1.0 500 ShowSlow Error');
  700. ?>
  701. <head>
  702. <title>500 ShowSlow Error</title>
  703. </head>
  704. <body>
  705. <h1>500 ShowSlow Error</h1>
  706. <p>Something went wrong. If it persists, please report it to <a href="http://code.google.com/p/showslow/issues/list">issue tracker</a>.</a>
  707. <p><?php echo $message?></p>
  708. </body></html>
  709. <?php
  710. exit;
  711. }
  712. function beaconError($message)
  713. {
  714. error_log($message);
  715. header('HTTP/1.0 500 Beacon Error');
  716. ?>
  717. <head>
  718. <title>500 Beacon Error</title>
  719. </head>
  720. <body>
  721. <h1>500 BeaconError</h1>
  722. <p><?php echo $message?></p>
  723. </body></html>
  724. <?php
  725. exit;
  726. }
  727. /*
  728. * Cuts the string to be $maxlength and replaces the last $margin characters with ellipsis
  729. */
  730. function ellipsis($string, $maxlength, $margin = 2) {
  731. if (strlen($string) > ($maxlength)) {
  732. return substr($string, 0, $maxlength - $margin)."...";
  733. }
  734. return $string;
  735. }
  736. mysql_connect($host, $user, $pass);
  737. mysql_select_db($db);