<html>
   <head>
      <link type="text/css" rel="stylesheet" media="all" href="screen.css" />
      <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js"></script>
      <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
      <script type="text/javascript" src="forge/debug.js"></script>
      <script type="text/javascript" src="forge/util.js"></script>
      <script type="text/javascript" src="forge/log.js"></script>
      <script type="text/javascript" src="forge/socket.js"></script>
      <script type="text/javascript" src="forge/md5.js"></script>
      <script type="text/javascript" src="forge/sha1.js"></script>
      <script type="text/javascript" src="forge/hmac.js"></script>
      <script type="text/javascript" src="forge/aes.js"></script>
      <script type="text/javascript" src="forge/pem.js"></script>
      <script type="text/javascript" src="forge/asn1.js"></script>
      <script type="text/javascript" src="forge/jsbn.js"></script>
      <script type="text/javascript" src="forge/prng.js"></script>
      <script type="text/javascript" src="forge/random.js"></script>
      <script type="text/javascript" src="forge/oids.js"></script>
      <script type="text/javascript" src="forge/rsa.js"></script>
      <script type="text/javascript" src="forge/pbe.js"></script>
      <script type="text/javascript" src="forge/x509.js"></script>
      <script type="text/javascript" src="forge/pki.js"></script>
      <script type="text/javascript" src="forge/tls.js"></script>
      <script type="text/javascript" src="forge/aesCipherSuites.js"></script>
      <script type="text/javascript" src="forge/tlssocket.js"></script>
      <script type="text/javascript" src="forge/http.js"></script>
      <script type="text/javascript" src="ws-webid.js"></script>
      
      <script type="text/javascript">
      //<![CDATA[
      // logging category
      var cat = 'forge.tests.tls';

      swfobject.embedSWF(
         'forge/SocketPool.swf', 'socketPool', '0', '0', '9.0.0',
         false, {}, {allowscriptaccess: 'always'}, {});
      
      // CA certificate for test server
      var certificatePem =
         '-----BEGIN CERTIFICATE-----\r\n' +
         'MIIEaDCCA1CgAwIBAgIJAJuj0AjEWncuMA0GCSqGSIb3DQEBBQUAMH8xCzAJBgNV\r\n' +
         'BAYTAlVTMREwDwYDVQQIEwhWaXJnaW5pYTETMBEGA1UEBxMKQmxhY2tzYnVyZzEd\r\n' +
         'MBsGA1UEChMURGlnaXRhbCBCYXphYXIsIEluYy4xGjAYBgNVBAsTEUZvcmdlIFRl\r\n' +
         'c3QgU2VydmVyMQ0wCwYDVQQDEwR0ZXN0MB4XDTEwMDcxMzE3MjAzN1oXDTMwMDcw\r\n' +
         'ODE3MjAzN1owfzELMAkGA1UEBhMCVVMxETAPBgNVBAgTCFZpcmdpbmlhMRMwEQYD\r\n' +
         'VQQHEwpCbGFja3NidXJnMR0wGwYDVQQKExREaWdpdGFsIEJhemFhciwgSW5jLjEa\r\n' +
         'MBgGA1UECxMRRm9yZ2UgVGVzdCBTZXJ2ZXIxDTALBgNVBAMTBHRlc3QwggEiMA0G\r\n' +
         'CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCm/FobjqK8CVP/Xbnpyhf1tpoyaiFf\r\n' +
         'ShUOmlWqL5rLe0Q0dDR/Zur+sLMUv/1T4wOfFkjjxvZ0Sk5NIjK3Wy2UA41a+M3J\r\n' +
         'RTbCFrg4ujsovFaD4CDmV7Rek0qJB3m5Gp7hgu5vfL/v+WrwxnQObNq+IrTMSA15\r\n' +
         'cO4LzNIPj9K1LN2dB+ucT7xTQFHAfvLLgLlCLiberoabF4rEhgTMTbmMtFVKSt+P\r\n' +
         'xgQIYPnhw1WuAvE9hFesRQFdfARLqIZk92FeHkgtHv9BAunktJemcidbowTCTBaM\r\n' +
         '/njcgi1Tei/LFkph/FCVyGER0pekJNHX626bAQSLo/srsWfmcll9rK6bAgMBAAGj\r\n' +
         'geYwgeMwHQYDVR0OBBYEFCau5k6jxezjULlLuo/liswJlBF8MIGzBgNVHSMEgasw\r\n' +
         'gaiAFCau5k6jxezjULlLuo/liswJlBF8oYGEpIGBMH8xCzAJBgNVBAYTAlVTMREw\r\n' +
         'DwYDVQQIEwhWaXJnaW5pYTETMBEGA1UEBxMKQmxhY2tzYnVyZzEdMBsGA1UEChMU\r\n' +
         'RGlnaXRhbCBCYXphYXIsIEluYy4xGjAYBgNVBAsTEUZvcmdlIFRlc3QgU2VydmVy\r\n' +
         'MQ0wCwYDVQQDEwR0ZXN0ggkAm6PQCMRady4wDAYDVR0TBAUwAwEB/zANBgkqhkiG\r\n' +
         '9w0BAQUFAAOCAQEAnP/2mzFWaoGx6+KAfY8pcgnF48IoyKPx5cAQyzpMo+uRwrln\r\n' +
         'INcDGwNx6p6rkjFbK27TME9ReCk+xQuVGaKOtqErKECXWDtD+0M35noyaOwWIFu2\r\n' +
         '7gPZ0uGJ1n9ZMe/S9yZmmusaIrc66rX4o+fslUlH0g3SrH7yf83M8aOC2pEyCsG0\r\n' +
         'mNNfwSFWfmu+1GMRHXJQ/qT8qBX8ZPhzRY2BAS6vr+eh3gwXR6yXLA8Xm1+e+iDU\r\n' +
         'gGTQoYkixDIL2nhvd4AFFlE977BiE+0sMS1eJKUUbQ36MLAWb5oOZKHrphEvqMKA\r\n' +
         'eGDO3qoDqB5TkZC3x38DXBDvAZ01d9s0fvveag==\r\n' +
         '-----END CERTIFICATE-----';
      
      // local aliases
      var net = window.forge.net;
      var tls = window.forge.tls;
      var http = window.forge.http;
      var util = window.forge.util;

      var client;
      
      function client_init(primed)
      {
         try
         {
            var sp = net.createSocketPool({
               flashId: 'socketPool',
               policyPort: 19945,
               msie: false
            });
            client = http.createClient({
               //url: 'https://localhost:4433',
               url: 'https://' + window.location.host,
               socketPool: sp,
               connections: 10,
               caCerts: [certificatePem],
               // optional cipher suites in order of preference
               cipherSuites: [
                  tls.CipherSuites.TLS_RSA_WITH_AES_128_CBC_SHA,
                  tls.CipherSuites.TLS_RSA_WITH_AES_256_CBC_SHA],
               verify: function(c, verified, depth, certs)
               {
                  forge.log.debug(cat,
                     'TLS certificate ' + depth + ' verified', verified);
                  // Note: change to always true to test verifying without cert
                  //return verified;
                  // FIXME: temporarily accept any cert to allow hitting any bpe
                  if(verified !== true)
                  {
                     forge.log.warning(cat, 
                        'Certificate NOT verified. Ignored for test.');
                  }
                  return true;
               },
               primeTlsSockets: primed
            });
            document.getElementById('feedback').innerHTML =
               'http client created';
         }
         catch(ex)
         {
            forge.log.error(cat, ex);
         }
         
         return false;
      }
      
      function client_cleanup()
      {
         var sp = client.socketPool;
         client.destroy();
         sp.destroy();
         document.getElementById('feedback').innerHTML =
            'http client cleaned up';
         return false;
      }

      function client_send()
      {
         /*
         var request = http.createRequest({
            method: 'POST',
            path: '/',
            body: 'echo=foo',
            headers: [{'Content-Type': 'application/x-www-form-urlencoded'}]
         });
         */
         var request = http.createRequest({
            method: 'GET',
            path: '/'
         });
         
         client.send({
            request: request,
            connected: function(e)
            {
               forge.log.debug(cat, 'connected', e);
            },
            headerReady: function(e)
            {
               forge.log.debug(cat, 'header ready', e);
            },
            bodyReady: function(e)
            {
               forge.log.debug(cat, 'body ready', e);

               // FIXME: current test server doesn't seem to handle keep-alive
               // correctly, so close connection 
               e.socket.close();
            },
            error: function(e)
            {
               forge.log.error(cat, 'error', e);
            }
         });
         document.getElementById('feedback').innerHTML =
            'http request sent';
         return false;
      }

      function client_send_10()
      {
         for(var i = 0; i < 10; ++i)
         {
            client_send();
         }
         return false;
      }

      function client_stress()
      {
         for(var i = 0; i < 10; ++i)
         {
            setTimeout(function()
            {
               for(var i = 0; i < 10; ++i)
               {
                  client_send();
               }
            }, 0);
         }
         return false;
      }

      function client_cookies()
      {
         var cookie =
         {
            name: 'test-cookie',
            value: 'test-value',
            maxAge: -1,
            secure: true,
            path: '/'
         };
         client.setCookie(cookie);
         forge.log.debug(cat, 'cookie', client.getCookie('test-cookie'));
      }

      function client_clear_cookies()
      {
         client.clearCookies();
      }
      
      function websocket_test()
      {
         // create certificate
         var cn = 'client';
         console.log(
            'Generating 512-bit key-pair and certificate for \"' + cn + '\".');
         var keys = forge.pki.rsa.generateKeyPair(512);
         console.log('key-pair created.');

         var cert = forge.pki.createCertificate();
         cert.serialNumber = '01';
         cert.validity.notBefore = new Date();
         cert.validity.notAfter = new Date();
         cert.validity.notAfter.setFullYear(
            cert.validity.notBefore.getFullYear() + 1);
         var attrs = [{
            name: 'commonName',
            value: cn
         }, {
            name: 'countryName',
            value: 'US'
         }, {
            shortName: 'ST',
            value: 'Virginia'
         }, {
            name: 'localityName',
            value: 'Blacksburg'
         }, {
            name: 'organizationName',
            value: 'Test'
         }, {
            shortName: 'OU',
            value: 'Test'
         }];
         cert.setSubject(attrs);
         cert.setIssuer(attrs);
         cert.setExtensions([{
            name: 'basicConstraints',
            cA: true
         }, {
            name: 'keyUsage',
            keyCertSign: true,
            digitalSignature: true,
            nonRepudiation: true,
            keyEncipherment: true,
            dataEncipherment: true
         }, {
            name: 'subjectAltName',
            altNames: [{
               type: 6, // URI
               value: 'http://myuri.com/webid#me'
            }]
         }]);
         // FIXME: add subjectKeyIdentifier extension
         // FIXME: add authorityKeyIdentifier extension
         cert.publicKey = keys.publicKey;
         
         // self-sign certificate
         cert.sign(keys.privateKey);
         
         // save cert and private key in PEM format
         cert = forge.pki.certificateToPem(cert);
         privateKey = forge.pki.privateKeyToPem(keys.privateKey);
         console.log('certificate created for \"' + cn + '\": \n' + cert);

         // create websocket
         var ws = new WebSocket('ws://localhost:8080');
         console.log('created websocket', ws);

         // create TLS client
         var success = false;
         var tls = forge.tls.createConnection(
         {
            server: false,
            caStore: [],
            sessionCache: {},
            // supported cipher suites in order of preference
            cipherSuites: [
               forge.tls.CipherSuites.TLS_RSA_WITH_AES_128_CBC_SHA,
               forge.tls.CipherSuites.TLS_RSA_WITH_AES_256_CBC_SHA],
            virtualHost: 'server',
            verify: function(c, verified, depth, certs)
            {
               console.log(
                  'TLS Client verifying certificate w/CN: \"' +
                  certs[0].subject.getField('CN').value +
                  '\", verified: ' + verified + '...');
               // accept any certificate from the server for this test
               return true;
            },
            connected: function(c)
            {
               console.log('Client connected...');
               
               // send message to server
               setTimeout(function()
               {
                  c.prepare('Hello Server');
               }, 1);
            },
            getCertificate: function(c, hint)
            {
               console.log('Client getting certificate ...');
               return cert;
            },
            getPrivateKey: function(c, cert)
            {
               return privateKey;
            },
            tlsDataReady: function(c)
            {
               // send base64-encoded TLS data to server
               ws.send(forge.util.encode64(c.tlsData.getBytes()));
            },
            dataReady: function(c)
            {
               var response = c.data.getBytes();
               console.log('Client received \"' + response + '\"');
               success = (response === 'Hello Client');
               c.close();
            },
            closed: function(c)
            {
               console.log('Client disconnected.');
               if(success)
               {
                  console.log('PASS');
               }
               else
               {
                  console.log('FAIL');
               }
            },
            error: function(c, error)
            {
               console.log('Client error: ' + error.message);
            }
         });

         ws.onopen = function(evt)
         {
            console.log('websocket connected');

            // do TLS handshake
            tls.handshake();
         };
         ws.onmessage = function(evt)
         {
            // base64-decode data and process it
            tls.process(forge.util.decode64(evt.data));
         };
         ws.onclose = function(evt)
         {
            console.log('websocket closed');
         };
      }
      
      //]]>
      </script>
   </head>
   <body>
      <div class="nav"><a href="index.html">Forge Tests</a> / TLS</div>

      <div class="header">
         <h1>TLS Test</h1>
      </div>

      <div class="content">

      <!-- div used to hold the flash socket pool implemenation -->
      <div id="socketPool">
         <p>Could not load the flash SocketPool.</p>
      </div>

      <fieldset class="section">
         <ul>
           <li>Use the controls below to test the HTTP client over TLS.</li>
           <li>You currently need a JavaScript console to view the output.</li>
           <li>This test connects to a TLS server so you must have one running. The easiest way to run this test is to start the test server with --tls and load this page over HTTPS.</li>
         </ul>
      </fieldset>

      <fieldset class="section">
      <legend>Controls</legend>
      <div id="controls">
         <button id="init" onclick="javascript:return client_init(false);">init</button>
         <button id="init_primed" onclick="javascript:return client_init(true);">init primed</button>
         <button id="cleanup" onclick="javascript:return client_cleanup();">cleanup</button>
         <button id="send" onclick="javascript:return client_send();">send</button>
         <button id="send10" onclick="javascript:return client_send_10();">send 10</button>
         <button id="stress" onclick="javascript:return client_stress();">stress</button>
         <button id="client_cookies" onclick="javascript:return client_cookies();">cookies</button>
         <button id="clear_cookies" onclick="javascript:return client_clear_cookies();">clear cookies</button>
         <button id="websocket" onclick="javascript:return websocket_test();">websocket test</button>
         <button id="websocket-webid" onclick="javascript:return websocket_webid('localhost', 8080);">websocket webid test</button>
      </div>
      </fieldset>

      <fieldset class="section">
      <legend>Feedback</legend>
      <p>Feedback from the flash SocketPool:</p>
      <div id="feedback">
      None
      </div>
      </fieldset>

      </div>
   </body>
</html>