| 
									
										
										
										
											2015-12-17 03:03:07 +00:00
										 |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var crypto = require('crypto'); | 
					
						
							|  |  |  | var tls = require('tls'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-17 05:08:14 +00:00
										 |  |  | module.exports.create = function (opts) { | 
					
						
							| 
									
										
										
										
											2015-12-17 03:03:07 +00:00
										 |  |  |   var ipc = {}; // in-process cache
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-12 20:04:40 -05:00
										 |  |  |   // function (/*err, hostname, certInfo*/) {}
 | 
					
						
							|  |  |  |   function handleRenewFailure(err, hostname, certInfo) { | 
					
						
							|  |  |  |     console.error("ERROR: Failed to renew domain '", hostname, "':"); | 
					
						
							|  |  |  |     if (err) { | 
					
						
							|  |  |  |       console.error(err.stack || err); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (certInfo) { | 
					
						
							|  |  |  |       console.error(certInfo); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-17 05:08:14 +00:00
										 |  |  |   if (!opts) { throw new Error("requires opts to be an object"); } | 
					
						
							| 
									
										
										
										
											2016-02-12 20:04:40 -05:00
										 |  |  |   if (opts.debug) { | 
					
						
							| 
									
										
										
										
											2016-02-16 23:04:56 +01:00
										 |  |  |     console.debug("[LEX] creating sniCallback", JSON.stringify(opts, function(k,v){ | 
					
						
							|  |  |  |       if(v instanceof Array) | 
					
						
							|  |  |  |          return JSON.stringify(v); | 
					
						
							|  |  |  |       return v; | 
					
						
							|  |  |  |     },'  ')); | 
					
						
							| 
									
										
										
										
											2016-02-12 20:04:40 -05:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-04-16 09:20:24 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-17 05:08:14 +00:00
										 |  |  |   if (!opts.letsencrypt) { throw new Error("requires opts.letsencrypt to be a letsencrypt instance"); } | 
					
						
							| 
									
										
										
										
											2015-12-17 03:03:07 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-17 05:08:14 +00:00
										 |  |  |   if (!opts.lifetime) { opts.lifetime = 90 * 24 * 60 * 60 * 1000; } | 
					
						
							|  |  |  |   if (!opts.failedWait) { opts.failedWait = 5 * 60 * 1000; } | 
					
						
							|  |  |  |   if (!opts.renewWithin) { opts.renewWithin = 3 * 24 * 60 * 60 * 1000; } | 
					
						
							|  |  |  |   if (!opts.memorizeFor) { opts.memorizeFor = 1 * 24 * 60 * 60 * 1000; } | 
					
						
							| 
									
										
										
										
											2015-12-17 03:03:07 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-17 05:08:14 +00:00
										 |  |  |   if (!opts.approveRegistration) { opts.approveRegistration = function (hostname, cb) { cb(null, null); }; } | 
					
						
							| 
									
										
										
										
											2016-02-10 15:25:03 -05:00
										 |  |  |   //opts.approveRegistration = function (hostname, cb) { cb(null, null); };
 | 
					
						
							| 
									
										
										
										
											2016-02-12 20:04:40 -05:00
										 |  |  |   if (!opts.handleRenewFailure) { opts.handleRenewFailure = handleRenewFailure; } | 
					
						
							| 
									
										
										
										
											2015-12-17 03:03:07 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |   function assignBestByDates(now, certInfo) { | 
					
						
							|  |  |  |     certInfo = certInfo || { loadedAt: now, expiresAt: 0, issuedAt: 0, lifetime: 0 }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-17 08:44:55 +00:00
										 |  |  |     var rnds = crypto.randomBytes(3); | 
					
						
							| 
									
										
										
										
											2015-12-17 03:03:07 +00:00
										 |  |  |     var rnd1 = ((rnds[0] + 1) / 257); | 
					
						
							|  |  |  |     var rnd2 = ((rnds[1] + 1) / 257); | 
					
						
							|  |  |  |     var rnd3 = ((rnds[2] + 1) / 257); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Stagger randomly by plus 0% to 25% to prevent all caches expiring at once
 | 
					
						
							| 
									
										
										
										
											2015-12-17 05:08:14 +00:00
										 |  |  |     var memorizeFor = Math.floor(opts.memorizeFor + ((opts.memorizeFor / 4) * rnd1)); | 
					
						
							| 
									
										
										
										
											2015-12-17 03:03:07 +00:00
										 |  |  |     // Stagger randomly to renew between n and 2n days before renewal is due
 | 
					
						
							|  |  |  |     // this *greatly* reduces the risk of multiple cluster processes renewing the same domain at once
 | 
					
						
							| 
									
										
										
										
											2015-12-17 05:08:14 +00:00
										 |  |  |     var bestIfUsedBy = certInfo.expiresAt - (opts.renewWithin + Math.floor(opts.renewWithin * rnd2)); | 
					
						
							| 
									
										
										
										
											2015-12-17 03:03:07 +00:00
										 |  |  |     // Stagger randomly by plus 0 to 5 min to reduce risk of multiple cluster processes
 | 
					
						
							|  |  |  |     // renewing at once on boot when the certs have expired
 | 
					
						
							|  |  |  |     var renewTimeout = Math.floor((5 * 60 * 1000) * rnd3); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     certInfo.loadedAt = now; | 
					
						
							|  |  |  |     certInfo.memorizeFor = memorizeFor; | 
					
						
							|  |  |  |     certInfo.bestIfUsedBy = bestIfUsedBy; | 
					
						
							|  |  |  |     certInfo.renewTimeout = renewTimeout; | 
					
						
							| 
									
										
										
										
											2015-12-17 08:44:55 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return certInfo; | 
					
						
							| 
									
										
										
										
											2015-12-17 03:03:07 +00:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function renewInBackground(now, hostname, certInfo) { | 
					
						
							| 
									
										
										
										
											2015-12-17 05:08:14 +00:00
										 |  |  |     if ((now - certInfo.loadedAt) < opts.failedWait) { | 
					
						
							| 
									
										
										
										
											2015-12-17 03:03:07 +00:00
										 |  |  |       // wait a few minutes
 | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (now > certInfo.bestIfUsedBy && !certInfo.timeout) { | 
					
						
							|  |  |  |       // EXPIRING
 | 
					
						
							|  |  |  |       if (now > certInfo.expiresAt) { | 
					
						
							|  |  |  |         // EXPIRED
 | 
					
						
							|  |  |  |         certInfo.renewTimeout = Math.floor(certInfo.renewTimeout / 2); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-17 05:08:14 +00:00
										 |  |  |       if (opts.debug) { | 
					
						
							| 
									
										
										
										
											2015-12-17 08:44:55 +00:00
										 |  |  |         console.debug("[LEX] skipping stagger '" + certInfo.renewTimeout + "' and renewing '" + hostname + "' now"); | 
					
						
							| 
									
										
										
										
											2015-12-17 05:08:14 +00:00
										 |  |  |         certInfo.renewTimeout = 500; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-17 03:03:07 +00:00
										 |  |  |       certInfo.timeout = setTimeout(function () { | 
					
						
							| 
									
										
										
										
											2015-12-17 05:08:14 +00:00
										 |  |  |         var args = { domains: [ hostname ], duplicate: false }; | 
					
						
							|  |  |  |         opts.letsencrypt.renew(args, function (err, certInfo) { | 
					
						
							| 
									
										
										
										
											2015-12-17 03:03:07 +00:00
										 |  |  |           if (err || !certInfo) { | 
					
						
							| 
									
										
										
										
											2015-12-17 05:08:14 +00:00
										 |  |  |             opts.handleRenewFailure(err, hostname, certInfo); | 
					
						
							| 
									
										
										
										
											2015-12-17 03:03:07 +00:00
										 |  |  |           } | 
					
						
							|  |  |  |           ipc[hostname] = assignBestByDates(now, certInfo); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       }, certInfo.renewTimeout); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-17 08:44:55 +00:00
										 |  |  |   function cacheResult(err, hostname, certInfo, sniCb) { | 
					
						
							|  |  |  |     if (certInfo && certInfo.fullchain && certInfo.privkey) { | 
					
						
							| 
									
										
										
										
											2015-12-17 05:08:14 +00:00
										 |  |  |       if (opts.debug) { | 
					
						
							| 
									
										
										
										
											2015-12-17 08:44:55 +00:00
										 |  |  |         console.debug('cert is looking good'); | 
					
						
							| 
									
										
										
										
											2015-12-17 03:03:07 +00:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2015-12-17 08:44:55 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |       try { | 
					
						
							|  |  |  |         certInfo.tlsContext = tls.createSecureContext({ | 
					
						
							|  |  |  |           key: certInfo.privkey || certInfo.key         // privkey.pem
 | 
					
						
							|  |  |  |         , cert: certInfo.fullchain || certInfo.cert     // fullchain.pem (cert.pem + '\n' + chain.pem)
 | 
					
						
							| 
									
										
										
										
											2016-04-14 10:55:08 +02:00
										 |  |  |         , ca: (opts.httpsOptions.ca ? opts.httpsOptions.ca + '\n' + certInfo.ca : certInfo.ca) | 
					
						
							| 
									
										
										
										
											2016-04-16 09:20:24 -06:00
										 |  |  |         , crl: opts.httpsOptions.crl | 
					
						
							| 
									
										
										
										
											2016-04-16 09:38:30 -06:00
										 |  |  |         , requestCert: opts.httpsOptions.requestCert | 
					
						
							|  |  |  |         , rejectUnauthorized: opts.httpsOptions.rejectUnauthorized | 
					
						
							| 
									
										
										
										
											2016-02-10 15:25:03 -05:00
										 |  |  |         }); | 
					
						
							| 
									
										
										
										
											2015-12-17 08:44:55 +00:00
										 |  |  |       } catch(e) { | 
					
						
							|  |  |  |         console.warn("[Sanity Check Fail]: a weird object was passed back through le.fetch to lex.fetch"); | 
					
						
							|  |  |  |         console.warn("(either missing or malformed certInfo.key and / or certInfo.fullchain)"); | 
					
						
							|  |  |  |         err = e; | 
					
						
							| 
									
										
										
										
											2015-12-17 03:03:07 +00:00
										 |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-17 08:44:55 +00:00
										 |  |  |       sniCb(err, certInfo.tlsContext); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       if (opts.debug) { | 
					
						
							|  |  |  |         console.debug('cert is NOT looking good'); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       sniCb(err || new Error("couldn't get certInfo: unknown"), null); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2015-12-17 05:08:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-17 08:44:55 +00:00
										 |  |  |     var now = Date.now(); | 
					
						
							|  |  |  |     certInfo = ipc[hostname] = assignBestByDates(now, certInfo); | 
					
						
							|  |  |  |     renewInBackground(now, hostname, certInfo); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function registerCert(hostname, sniCb) { | 
					
						
							|  |  |  |     if (opts.debug) { | 
					
						
							|  |  |  |       console.debug("[LEX] '" + hostname + "' is not registered, requesting approval"); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-04-16 09:20:24 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-09 12:02:57 -06:00
										 |  |  |     if (!hostname) { | 
					
						
							|  |  |  |       sniCb(new Error('[registerCert] no hostname')); | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2015-12-17 08:44:55 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     opts.approveRegistration(hostname, function (err, args) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (opts.debug) { | 
					
						
							|  |  |  |         console.debug("[LEX] '" + hostname + "' registration approved, attempting register"); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (err) { | 
					
						
							|  |  |  |         cacheResult(err, hostname, null, sniCb); | 
					
						
							| 
									
										
										
										
											2015-12-17 03:03:07 +00:00
										 |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-17 08:44:55 +00:00
										 |  |  |       if (!(args && args.agreeTos && args.email && args.domains)) { | 
					
						
							|  |  |  |         cacheResult(new Error("not approved or approval is missing arguments - such as agreeTos, email, domains"), hostname, null, sniCb); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2015-12-17 05:08:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-17 08:44:55 +00:00
										 |  |  |       opts.letsencrypt.register(args, function (err, certInfo) { | 
					
						
							|  |  |  |         if (opts.debug) { | 
					
						
							|  |  |  |           console.debug("[LEX] '" + hostname + "' register completed", err && err.stack || null, certInfo); | 
					
						
							| 
									
										
										
										
											2016-02-10 15:25:03 -05:00
										 |  |  |           if ((!err || !err.stack) && !certInfo) { | 
					
						
							|  |  |  |             console.error((new Error("[LEX] SANITY FAIL: no error and yet no certs either")).stack); | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											2015-12-17 08:44:55 +00:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2015-12-17 05:08:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-17 08:44:55 +00:00
										 |  |  |         cacheResult(err, hostname, certInfo, sniCb); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2015-12-17 05:08:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-17 08:44:55 +00:00
										 |  |  |   function fetch(hostname, sniCb) { | 
					
						
							| 
									
										
										
										
											2016-04-09 12:02:57 -06:00
										 |  |  |     if (!hostname) { | 
					
						
							|  |  |  |       sniCb(new Error('[sniCallback] [fetch] no hostname')); | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-04-16 09:20:24 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-17 08:44:55 +00:00
										 |  |  |     opts.letsencrypt.fetch({ domains: [hostname] }, function (err, certInfo) { | 
					
						
							|  |  |  |       if (opts.debug) { | 
					
						
							|  |  |  |         console.debug("[LEX] fetch from disk result '" + hostname + "':"); | 
					
						
							|  |  |  |         console.debug(certInfo && Object.keys(certInfo)); | 
					
						
							| 
									
										
										
										
											2015-12-17 05:08:14 +00:00
										 |  |  |         if (err) { | 
					
						
							| 
									
										
										
										
											2015-12-17 08:44:55 +00:00
										 |  |  |           console.error(err.stack || err); | 
					
						
							| 
									
										
										
										
											2015-12-17 05:08:14 +00:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2015-12-17 08:44:55 +00:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2015-12-17 05:08:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-17 08:44:55 +00:00
										 |  |  |       if (err) { | 
					
						
							|  |  |  |         sniCb(err, null); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2015-12-17 05:08:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-17 08:44:55 +00:00
										 |  |  |       if (certInfo) { | 
					
						
							|  |  |  |         cacheResult(err, hostname, certInfo, sniCb); | 
					
						
							|  |  |  |         return; | 
					
						
							| 
									
										
										
										
											2015-12-17 05:08:14 +00:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2015-12-17 08:44:55 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |       registerCert(hostname, sniCb); | 
					
						
							| 
									
										
										
										
											2015-12-17 03:03:07 +00:00
										 |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return function sniCallback(hostname, cb) { | 
					
						
							|  |  |  |     var now = Date.now(); | 
					
						
							|  |  |  |     var certInfo = ipc[hostname]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-09 12:02:57 -06:00
										 |  |  |     if (!hostname) { | 
					
						
							|  |  |  |       cb(new Error('[sniCallback] no hostname')); | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2015-12-17 08:44:55 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     //
 | 
					
						
							|  |  |  |     // No cert is available in cache.
 | 
					
						
							|  |  |  |     // try to fetch it from disk quickly
 | 
					
						
							|  |  |  |     // and return to the browser
 | 
					
						
							|  |  |  |     //
 | 
					
						
							| 
									
										
										
										
											2015-12-17 03:03:07 +00:00
										 |  |  |     if (!certInfo) { | 
					
						
							| 
									
										
										
										
											2015-12-17 05:08:14 +00:00
										 |  |  |       if (opts.debug) { | 
					
						
							| 
									
										
										
										
											2015-12-17 08:44:55 +00:00
										 |  |  |         console.debug("[LEX] no certs loaded for '" + hostname + "'"); | 
					
						
							| 
									
										
										
										
											2015-12-17 05:08:14 +00:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2015-12-17 03:03:07 +00:00
										 |  |  |       fetch(hostname, cb); | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-17 08:44:55 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     //
 | 
					
						
							|  |  |  |     // A cert is available
 | 
					
						
							|  |  |  |     // See if it's old enough that
 | 
					
						
							|  |  |  |     // we should refresh it from disk
 | 
					
						
							|  |  |  |     // (in the background)
 | 
					
						
							| 
									
										
										
										
											2016-02-10 15:25:03 -05:00
										 |  |  |     //
 | 
					
						
							| 
									
										
										
										
											2015-12-17 08:44:55 +00:00
										 |  |  |     // TODO once ECDSA is available, wait for cert renewal if its due (maybe?)
 | 
					
						
							| 
									
										
										
										
											2015-12-17 05:08:14 +00:00
										 |  |  |     if (certInfo.tlsContext) { | 
					
						
							|  |  |  |       cb(null, certInfo.tlsContext); | 
					
						
							| 
									
										
										
										
											2015-12-17 03:03:07 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |       if ((now - certInfo.loadedAt) < (certInfo.memorizeFor)) { | 
					
						
							|  |  |  |         // these aren't stale, so don't fall through
 | 
					
						
							| 
									
										
										
										
											2015-12-17 05:08:14 +00:00
										 |  |  |         if (opts.debug) { | 
					
						
							| 
									
										
										
										
											2015-12-17 08:44:55 +00:00
										 |  |  |           console.debug("[LEX] certs for '" + hostname + "' are fresh from disk"); | 
					
						
							| 
									
										
										
										
											2015-12-17 05:08:14 +00:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2015-12-17 03:03:07 +00:00
										 |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2015-12-17 05:08:14 +00:00
										 |  |  |     else if ((now - certInfo.loadedAt) < opts.failedWait) { | 
					
						
							|  |  |  |       if (opts.debug) { | 
					
						
							| 
									
										
										
										
											2015-12-17 08:44:55 +00:00
										 |  |  |         console.debug("[LEX] certs for '" + hostname + "' recently failed and are still in cool down"); | 
					
						
							| 
									
										
										
										
											2015-12-17 05:08:14 +00:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2015-12-17 03:03:07 +00:00
										 |  |  |       // this was just fetched and failed, wait a few minutes
 | 
					
						
							|  |  |  |       cb(null, null); | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-17 05:08:14 +00:00
										 |  |  |     if (opts.debug) { | 
					
						
							| 
									
										
										
										
											2015-12-17 08:44:55 +00:00
										 |  |  |       console.debug("[LEX] certs for '" + hostname + "' are stale on disk and should be will be fetched again"); | 
					
						
							|  |  |  |       console.debug({ | 
					
						
							|  |  |  |         age: now - certInfo.loadedAt | 
					
						
							|  |  |  |       , loadedAt: certInfo.loadedAt | 
					
						
							|  |  |  |       , issuedAt: certInfo.issuedAt | 
					
						
							|  |  |  |       , expiresAt: certInfo.expiresAt | 
					
						
							|  |  |  |       , privkey: !!certInfo.privkey | 
					
						
							|  |  |  |       , chain: !!certInfo.chain | 
					
						
							|  |  |  |       , fullchain: !!certInfo.fullchain | 
					
						
							|  |  |  |       , cert: !!certInfo.cert | 
					
						
							|  |  |  |       , memorizeFor: certInfo.memorizeFor | 
					
						
							|  |  |  |       , failedWait: opts.failedWait | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2015-12-17 05:08:14 +00:00
										 |  |  |     } | 
					
						
							|  |  |  |     fetch(hostname, cb); | 
					
						
							| 
									
										
										
										
											2015-12-17 03:03:07 +00:00
										 |  |  |   }; | 
					
						
							|  |  |  | }; |