public void FlightRecorder_PassThru_SysLogProvider() { // Verify that the ISysLogProvider implementation works. Queue <FlightEvent> queue = new Queue <FlightEvent>(); ISysLogProvider orgProvider = SysLog.LogProvider; try { FlightEvent flightEvent; using (var recorder = new FlightRecorder(evt => queue.Enqueue(evt))) { SysLog.LogProvider = recorder; SysLog.LogError("Test Error"); SysLog.LogWarning("Test Warning"); SysLog.LogInformation("Test Information"); SysLog.Flush(); Assert.AreEqual(3, queue.Count); flightEvent = queue.Dequeue(); Assert.AreEqual("SysLog:Error", flightEvent.Operation); Assert.IsTrue(flightEvent.Details.Contains("Test Error")); Assert.IsTrue(flightEvent.IsError); flightEvent = queue.Dequeue(); Assert.AreEqual("SysLog:Warning", flightEvent.Operation); Assert.IsTrue(flightEvent.Details.Contains("Test Warning")); Assert.IsFalse(flightEvent.IsError); flightEvent = queue.Dequeue(); Assert.AreEqual("SysLog:Information", flightEvent.Operation); Assert.IsTrue(flightEvent.Details.Contains("Test Information")); Assert.IsFalse(flightEvent.IsError); // Verify that system events actually serialize exception // and stack trace related information. try { throw new AssertException(); } catch (Exception e) { SysLog.LogException(e); SysLog.Flush(); flightEvent = queue.Dequeue(); Assert.AreEqual("SysLog:Exception", flightEvent.Operation); Assert.IsTrue(flightEvent.Details.Contains("AssertException")); } } } finally { SysLog.LogProvider = orgProvider; } }
/// <summary> /// Called to dispatch a server side session. /// </summary> /// <param name="msg">The message initiating the session.</param> /// <param name="target">The target object instance.</param> /// <param name="method">The target method information.</param> /// <param name="sessionInfo">The session information associated with the handler.</param> /// <remarks> /// The target and method parameter will specify the message handler /// for the message passed. /// </remarks> public void ServerDispatch(Msg msg, object target, MethodInfo method, SessionHandlerInfo sessionInfo) { ISession session; bool start = false; Assertion.Test((msg._Flags & (MsgFlag.OpenSession & MsgFlag.ServerSession)) == (MsgFlag.OpenSession & MsgFlag.ServerSession)); Assertion.Test(msg._SessionID != Guid.Empty); using (TimedLock.Lock(router.SyncRoot)) { // Create a session with this ID if one doesn't already exist. serverSessions.TryGetValue(msg._SessionID, out session); if (session == null) { if (sessionInfo.SessionType == null) { SysLog.LogError("Session creation failed for received [{0}] message: No session type specified in [MsgSession] tag for handler [{1}.{2}({3})}.", msg.GetType().FullName, target.GetType().FullName, method.Name, method.GetParameters()[0].ParameterType.Name); return; } start = true; session = Helper.CreateInstance <ISession>(sessionInfo.SessionType); session.InitServer(router, this, router.SessionTimeout, msg, target, method, sessionInfo); serverSessions.Add(msg._SessionID, session); } } // Dispatch the message outside of the lock (to avoid deadlocks) if (start) { session.StartServer(); } else { session.OnMsg(msg, sessionInfo); } }
/// <summary> /// Constructs the instance and starts the background downloading thread. /// </summary> /// <param name="node">The parent <see cref="GeoTrackerNode" /> instance.</param> /// <remarks> /// <note> /// The constructor will not start a background thread if IP geocoding is disabled. /// </note> /// </remarks> public IPGeocoder(GeoTrackerNode node) { settings = node.Settings; if (!settings.IPGeocodeEnabled) { return; } // Initialize the service including loading the MaxMind database if present. running = true; stopPending = false; pollDataNow = false; maxMind = null; try { if (File.Exists(dataPath)) { maxMind = new LookupService(dataPath, LookupService.GEOIP_MEMORY_CACHE); maxMind.close(); } } catch (Exception e) { // Assume that the database file is corrupted if there's an exception // and delete it so the download thread will download a new copy. SysLog.LogException(e); SysLog.LogError("GeoTracker: The MaxMind database file [{0}] appears to be corrupted. This will be deleted so the downloader can get a fresh copy.", dataPath); Helper.DeleteFile(dataPath); } // Start the background downloader thread. downloadThread = new Thread(new ThreadStart(DownloadThread)); downloadThread.Name = "GeoTracker: GeoData Downloader"; downloadThread.Start(); }
/// <summary> /// The managing <see cref="ISipAgent" /> is responsible for calling this /// method whenever it receives responses correlated to this transaction. /// </summary> /// <param name="transport">The source <see cref="ISipTransport" />.</param> /// <param name="response">The received <see cref="SipResponse" />.</param> public void OnResponse(ISipTransport transport, SipResponse response) { bool callOnComplete = false; bool callOnProceeding = false; bool callOnInviteComplete = false; SipResponse callbackMsg = null; SipStatus status = SipStatus.OK; SipCSeqValue vCSeq; this.transport = transport; try { response.SourceTransaction = this; using (TimedLock.Lock(agent)) { // Ignore messages without a sequence number // // $todo(jeff.lill): Probably should check the method too vCSeq = response.GetHeader <SipCSeqValue>(SipHeader.CSeq); if (vCSeq == null) { return; } // Handle state specific processing switch (base.State) { default: case SipTransactionState.Unknown: SysLog.LogError("Unexpected SIP transaction state."); SetState(SipTransactionState.Terminated); // Setup to call the agent's completion method callOnComplete = true; callbackMsg = null; status = SipStatus.Stack_ProtocolError; return; case SipTransactionState.InviteCalling: if (!request.MatchCSeq(response)) { return; // Ignore responses whose CSeq header doesn't match the request } if (response.IsProvisional) { // Provisional response. SetState(SipTransactionState.InviteProceeding); // Setup to call the agent's proceeding method callOnProceeding = true; callbackMsg = response; status = response.Status; return; } if (response.IsNonSuccessFinal) { // Final response non-2xx response. Generate and // send the ACK request to the server to squelch // any further responses and then enter the // InviteCompleted state to absorb any responses // that do make it through. ackRequest = CreateAckRequest(request, response); transport.Send(remoteEP, ackRequest); SetState(SipTransactionState.InviteCompleted); // Setup to call the agent's invite completed method callOnInviteComplete = true; callbackMsg = response; status = response.Status; return; } // Must be a 2xx response. Setup to call the agent's // completed method and enter the terminated state // without sending an ACK request. // // Note that the agent is required to do this as // described in RFC 3261 on pages 128-129. SetState(SipTransactionState.Terminated); callOnInviteComplete = true; callbackMsg = response; status = response.Status; break; case SipTransactionState.InviteProceeding: if (!request.MatchCSeq(response)) { return; // Ignore responses whose CSeq header doesn't match the request } if (response.IsProvisional) { // Setup to call the agent's proceeding method callOnProceeding = true; callbackMsg = response; status = response.Status; return; } if (response.IsNonSuccessFinal) { // Final response non-2xx response. Generate and // send the ACK request to the server to squelch // any further responses and then enter the // InviteCompleted state to absorb any responses // that do make it through. // $todo(jeff.lill): // // I need to figure out a way to // map to the dialog so that it // can generate the ACK rather than // doing this locally. ackRequest = CreateAckRequest(request, response); transport.Send(remoteEP, ackRequest); SetState(SipTransactionState.InviteCompleted); // Setup to call the agent's invite completed method callOnInviteComplete = true; callbackMsg = response; status = response.Status; return; } // Must be a 2xx response. Setup to call the agent's // completed method and enter the terminated state // without sending an ACK request. // // Note that the agent is required to do this as // described in RFC 3261 on pages 128-129. SetState(SipTransactionState.Terminated); callOnInviteComplete = true; callbackMsg = response; status = response.Status; break; case SipTransactionState.InviteCompleted: // Retransmit the ACK if we get another final response if (response.IsFinal) { transport.Send(remoteEP, ackRequest); } break; case SipTransactionState.Trying: if (!request.MatchCSeq(response)) { return; // Ignore responses whose CSeq header doesn't match the request } if (response.IsProvisional) { // Provisional response. SetState(SipTransactionState.Proceeding); // Setup to call the agent's proceeding method callOnProceeding = true; callbackMsg = response; status = response.Status; return; } else { // Final response SetState(SipTransactionState.Completed); // Setup to call the agent's completion method callOnComplete = true; callbackMsg = response; status = response.Status; return; } case SipTransactionState.Proceeding: if (!request.MatchCSeq(response)) { return; // Ignore responses whose CSeq header doesn't match the request } if (response.IsProvisional) { // Setup to call the agent's proceeding method callOnProceeding = true; callbackMsg = response; status = response.Status; return; } // Final response. SetState(SipTransactionState.Completed); // Setup to call the agent's completion method callOnComplete = true; callbackMsg = response; status = response.Status; return; case SipTransactionState.Completed: break; case SipTransactionState.Terminated: break; } } } finally { // Handle the agent callbacks outside of the lock to avoid // deadlock issues. if (callOnProceeding) { agent.OnProceeding(this, callbackMsg); } if (callOnComplete) { agent.OnComplete(this, status, callbackMsg); } if (callOnInviteComplete) { agent.OnInviteComplete(this, status, response); } } }
/// <summary> /// Handles received packets. /// </summary> /// <param name="ar">The <see cref="IAsyncResult" /> instance.</param> private void OnReceive(IAsyncResult ar) { DnsRequest request = null; int cbRecv; IPEndPoint ep; try { cbRecv = ((EnhancedSocket)ar.AsyncState).EndReceiveFrom(ar, ref remoteEP); } catch { cbRecv = 0; } if (sock == null) { return; // The server has stopped } if (cbRecv != 0) { // Parse the request packet try { request = new DnsRequest(); request.ParsePacket(recvBuf, cbRecv); } catch (Exception e) { SysLog.LogException(e); } } // Save the remote EP and then initiate another async // packet receive. ep = (IPEndPoint)remoteEP; remoteEP = new IPEndPoint(IPAddress.Any, 0); sock.BeginReceiveFrom(recvBuf, 0, recvBuf.Length, SocketFlags.None, ref remoteEP, onReceive, sock); // Process the request and transmit the response (if there is one). if (request != null && RequestEvent != null) { var args = new DnsServerEventArgs(ep, request); RequestEvent(this, args); if (args.Response != null) { byte[] sendBuf; int cbSend; // $todo(jeff.lill): // // Remove this exception code after figuring out why the // response's QName field is sometimes NULL. try { sendBuf = args.Response.FormatPacket(out cbSend); } catch { SysLog.LogError("DNS Formatting Error:\r\n\r\n" + args.Request.GetTraceDetails(ep.Address) + "\r\n" + args.Response.GetTraceDetails(ep.Address)); throw; } lock (syncLock) { if (sock != null) { sock.SendTo(sendBuf, cbSend, SocketFlags.None, args.RemoteEP); } } } } }
private static Dictionary <string, string> voiceNameMap; // Maps friendly voice names to the internal name /// <summary> /// Starts the engine if it is not already running. /// </summary> /// <param name="settings">The engine settings.</param> /// <exception cref="ArgumentNullException">Thrown if <paramref name="settings"/> is <c>null</c>.</exception> public static void Start(SpeechEngineSettings settings) { if (settings == null) { throw new ArgumentNullException("settings"); } lock (syncLock) { if (SpeechEngine.isRunning) { return; } SpeechEngine.settings = settings; SpeechEngine.cache = new PhraseCache(settings); SpeechEngine.isRunning = true; // Create the default audio formats. format_8000KHz = new SpeechAudioFormatInfo(8000, AudioBitsPerSample.Eight, AudioChannel.Mono); format_11025KHz = new SpeechAudioFormatInfo(11025, AudioBitsPerSample.Eight, AudioChannel.Mono); format_16000KHz = new SpeechAudioFormatInfo(16000, AudioBitsPerSample.Eight, AudioChannel.Mono); // Get the fully qualified paths to the error files. noVoicesPath = Path.Combine(CoreApp.InstallPath, "Audio", "NoVoicesError.wav"); synthErrorPath = Path.Combine(CoreApp.InstallPath, "Audio", "SpeechSynthError.wav"); // Enumerate the installed voices and select the default voice. // // Note: The Microsoft Speech Platform voices have really clunky names like: // // "Microsoft Server Speech Text to Speech Voice (en-AU, Hayley)" // // I'm going to simplify these to be just "Microsoft <name>" and maintain // a table that maps back to the original name. voiceNameMap = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase); using (var synth = new SpeechSynthesizer()) { var voices = new Dictionary <string, VoiceInfo>(StringComparer.OrdinalIgnoreCase); foreach (var voice in synth.GetInstalledVoices()) { var voiceName = voice.VoiceInfo.Name; if (!voice.Enabled) { continue; } // $hack(jeff.lill): // // Make sure that the voice can actually be used. I've run into // situations where [Microsoft Anna] was present and enabled but // could not be selected. I believe this may be a 64-bit issue // or perhaps installing Cepstral voices messes with Anna. try { synth.SelectVoice(voice.VoiceInfo.Name); } catch { continue; } if (voiceName.StartsWith("Microsoft Server Speech Text to Speech Voice (")) { int p = voiceName.IndexOf(','); if (p != -1) { voiceName = voiceName.Substring(p + 1); voiceName = "Microsoft " + voiceName.Replace(")", string.Empty).Trim(); voiceNameMap[voiceName] = voice.VoiceInfo.Name; } } voices.Add(voiceName, voice.VoiceInfo); } SpeechEngine.InstalledVoices = voices.ToReadOnly(); SpeechEngine.DefaultVoice = null; SpeechEngine.DefaultVoiceInfo = null; // First see if the desired default voice exists. if (!string.IsNullOrWhiteSpace(settings.DefaultVoice) && String.Compare(settings.DefaultVoice, "auto") != 0) { VoiceInfo voiceInfo; if (voices.TryGetValue(settings.DefaultVoice, out voiceInfo)) { SpeechEngine.DefaultVoice = voiceInfo.Name; } else { SysLog.LogWarning("[SpeechEngine] was not able to locate the requested default voice [{0}]. Another voice will be selected automatically.", settings.DefaultVoice); } } // If not look for an alternative if (SpeechEngine.DefaultVoice == null) { if (voices.ContainsKey("Microsoft Helen")) { SpeechEngine.DefaultVoice = "Microsoft Helen"; } else if (voices.ContainsKey("Microsoft Anna")) { SpeechEngine.DefaultVoice = "Microsoft Anna"; } else { SysLog.LogWarning("[SpeechEngine] was not able to locate the [Microsoft Anna] voice."); var v = voices.Keys.FirstOrDefault(); if (v == null) { SpeechEngine.DefaultVoice = null; SysLog.LogError("[SpeechEngine] was not able to locate any speech synthesis voices. Speech synthesis will be disabled."); } else { SpeechEngine.DefaultVoice = v; } } } if (SpeechEngine.DefaultVoice != null) { SpeechEngine.DefaultVoiceInfo = SpeechEngine.InstalledVoices[GetVoice(SpeechEngine.DefaultVoice)]; } } } }
/// <summary> /// The managing <see cref="ISipAgent" /> is responsible for calling this /// method whenever it needs to send a response for the transaction. /// </summary> /// <param name="response">The <see cref="SipResponse" /> (or <c>null</c> to abort).</param> /// <remarks> /// You may pass <paramref name="response"/> as <c>null</c> to abort the transaction /// without sending a response. This is equivalent to calling <see cref="Abort" />. /// </remarks> public void SendResponse(SipResponse response) { try { using (TimedLock.Lock(agent)) { if (response == null) { // Handle aborting by transitioning to the completed state so // request retransmits will continue to be absorbed by the // transaction. SetState(SipTransactionState.Completed); return; } // Handle state specific processing switch (base.State) { default: case SipTransactionState.Unknown: SysLog.LogError("Unexpected SIP transaction state."); SetState(SipTransactionState.Terminated); return; case SipTransactionState.InviteCalling: break; case SipTransactionState.InviteProceeding: if (response.IsProvisional) { // Provisional provisionalResponse = response; transport.Send(remoteEP, provisionalResponse); return; } if (response.IsSuccess) { // Final response (success) finalResponse = response; transport.Send(remoteEP, finalResponse); SetState(SipTransactionState.Terminated); return; } // Final response (error) finalResponse = response; transport.Send(remoteEP, finalResponse); SetState(SipTransactionState.InviteCompleted); break; case SipTransactionState.InviteCompleted: break; case SipTransactionState.InviteConfirmed: break; case SipTransactionState.Trying: if (response.IsProvisional) { // Provisional provisionalResponse = response; transport.Send(remoteEP, provisionalResponse); SetState(SipTransactionState.Proceeding); return; } // Final response finalResponse = response; transport.Send(remoteEP, finalResponse); SetState(SipTransactionState.Completed); break; case SipTransactionState.Proceeding: if (response.IsProvisional) { // Provisional provisionalResponse = response; transport.Send(remoteEP, provisionalResponse); return; } // Final response finalResponse = response; transport.Send(remoteEP, finalResponse); SetState(SipTransactionState.Completed); return; case SipTransactionState.Completed: break; case SipTransactionState.Terminated: break; } } } finally { } }
/// <summary> /// The managing <see cref="ISipAgent" /> is responsible for calling this /// method whenever it receives requests correlated to this transaction. /// </summary> /// <param name="request">The received <see cref="SipRequest" />.</param> public void OnRequest(SipRequest request) { SipRequest callbackMsg = null; bool callOnRequest = false; bool callOnInviteBegin = false; bool callOnInviteComplete = false; try { request.SourceTransaction = this; using (TimedLock.Lock(agent)) { if (this.request == null) { SipViaValue viaValue; SipContactValue toValue; NetworkBinding sentBy; IPAddress address; // This is the initial transaction request. this.request = request; // Handle the Via "received" and "rport" header parameters (mostly) as described on page // RFC 3261 (page 145) and RFC 3581 (page 4). viaValue = request.GetHeader <SipViaValue>(SipHeader.Via); if (viaValue == null) { // Illegal request SetState(SipTransactionState.Terminated); return; } sentBy = viaValue.SentByBinding; if (sentBy == null || sentBy.IsHost || sentBy.Address != request.RemoteEndpoint.Address) { viaValue.Received = request.RemoteEndpoint.Address.ToString(); } if (viaValue.RPort != null) { viaValue.RPort = request.RemoteEndpoint.Port.ToString(); } // Determine the destination network endpoint based on the // rules described on RFC 3261 (page 146). if (request.SourceTransport.IsStreaming) { // $todo(jeff.lill): // // This implementation is incomplete. To be fully // compliant with the RFC, I'd have to check to // see if the connection is still present in the // transport and if not, use the received and // rport values as described. remoteEP = request.RemoteEndpoint; } else { if (viaValue.MAddr != null) { if (!IPAddress.TryParse(viaValue.MAddr, out address)) { SipException e; // Illegal request SetState(SipTransactionState.Terminated); e = new SipException("Illegal request: Invalid [Via: maddr]."); e.Transport = transport.Name; e.SourceEndpoint = request.RemoteEndpoint; e.BadMessage = request; throw e; } remoteEP = new NetworkBinding(address, viaValue.SentByBinding.Port); } else { remoteEP = request.RemoteEndpoint; } } // INVITE and non-INVITE requests have different state machines. if (request.Method == SipMethod.Invite) { // Start an INVITE transaction SetState(SipTransactionState.InviteProceeding); // If the request has a "To" header without a "tag" parameter then // generate a tag. Note that this code will cause provisional INVITE // responses to include a generated tag which the RFC indicates // SHOULD NOT be done. But, it's much safer to do this once here // for all transaction types, avoiding special cases, and besides, // I've noticed that Asterisk includes a tag in its provisional // INVITE responses. toValue = request.GetHeader <SipContactValue>(SipHeader.To); if (toValue != null) { if (toValue["tag"] == null) { toValue["tag"] = SipHelper.GenerateTagID(); } request.SetHeader(SipHeader.To, toValue); } // Always send an initial provisional trying response. provisionalResponse = request.CreateResponse(SipStatus.Trying, null); SendResponse(provisionalResponse); // Setup to call the agent's OnInviteBegin() method. callOnInviteBegin = true; callbackMsg = request; } else if (request.Method == SipMethod.Ack) { // Allow an ACK request to drop through to the state machine. } else { // Start a non-INVITE transaction SetState(SipTransactionState.Trying); // Setup to call the agent's OnRequest() method. callOnRequest = true; callbackMsg = request; } return; } // Handle state specific processing switch (base.State) { default: case SipTransactionState.Unknown: SysLog.LogError("Unexpected SIP transaction state."); SetState(SipTransactionState.Terminated); return; case SipTransactionState.InviteCalling: break; case SipTransactionState.InviteProceeding: if (provisionalResponse != null) { transport.Send(remoteEP, provisionalResponse); } break; case SipTransactionState.InviteCompleted: if (request.Method == SipMethod.Ack) { SetState(SipTransactionState.InviteConfirmed); // Setup to call OnInviteComplete(ack); callOnInviteComplete = true; callbackMsg = request; return; } Assertion.Test(finalResponse != null); transport.Send(remoteEP, finalResponse); break; case SipTransactionState.InviteConfirmed: break; case SipTransactionState.Trying: break; case SipTransactionState.Proceeding: Assertion.Test(provisionalResponse != null); transport.Send(remoteEP, provisionalResponse); break; case SipTransactionState.Completed: Assertion.Test(finalResponse != null); transport.Send(remoteEP, finalResponse); break; case SipTransactionState.Terminated: break; } } } finally { // Handle the agent callbacks outside of the lock to avoid // deadlock issues. if (callOnRequest) { agent.OnRequest(this, callbackMsg); } if (callOnInviteBegin) { agent.OnInviteBegin(this, request); } if (callOnInviteComplete) { agent.OnInviteComplete(this, this.request, finalResponse, callbackMsg); } } }
/// <summary> /// Implements the background thread. /// </summary> private void DownloadThread() { DateTime lastWarningTime = DateTime.MinValue; PolledTimer pollTimer; bool resetTimer; try { // Initialize the GeoTracker file folder try { Helper.CreateFileTree(dataPath); if (File.Exists(downloadPath)) { SysLog.LogWarning("GeoTracker: Deleting existing temporary [{0}] file on startup.", downloadPath); Helper.DeleteFile(downloadPath); } if (File.Exists(decryptedPath)) { SysLog.LogWarning("GeoTracker: Deleting existing temporary [{0}] file on startup.", decryptedPath); Helper.DeleteFile(decryptedPath); } } catch (Exception e) { SysLog.LogException(e); } // Initalize the poll timer. We'll schedule an immediate download if the data file does // not exist, otherwise we'll delay the polling for a random period of time between // 0 and 15 minutes in the hope that we'll end up staggering the polling times across // the server cluster (so we won't hammer the source website). pollTimer = new PolledTimer(settings.IPGeocodeSourcePollInterval, false); resetTimer = false; if (!File.Exists(dataPath)) { pollTimer.FireNow(); } else { pollTimer.ResetRandomTemporary(TimeSpan.Zero, TimeSpan.FromMinutes(15)); } // The polling loop. while (true) { if (stopPending) { return; } try { if (pollDataNow) { pollTimer.FireNow(); pollDataNow = false; } if (pollTimer.HasFired) { DateTime fileDateUtc = DateTime.UtcNow; bool isUpdate = false; double fileSize = 0; ElapsedTimer downloadTimer; HttpWebRequest request; HttpWebResponse response; HttpStatusCode statusCode; resetTimer = true; // If a database file already exists then extract its last modify // date and use this in an If-Modified-Since request to the source // website to see if there's an updated file. if (File.Exists(dataPath)) { request = (HttpWebRequest)WebRequest.Create(settings.IPGeocodeSourceUri); request.Timeout = (int)TimeSpan.FromSeconds(30).TotalMilliseconds; isUpdate = true; fileDateUtc = File.GetLastWriteTimeUtc(dataPath); request.Method = "HEAD"; request.IfModifiedSince = fileDateUtc; try { using (response = (HttpWebResponse)request.GetResponse()) statusCode = response.StatusCode; } catch (WebException e) { statusCode = ((HttpWebResponse)e.Response).StatusCode; } if (statusCode == HttpStatusCode.NotModified) { // The source website does not have an updated file. I'm going to // do one extra check to see if the file we have is more than 45 // days old and log a warning. Note that we're going to issue this // warning only once a week while the service is running. if (DateTime.UtcNow - fileDateUtc < TimeSpan.FromDays(45) || DateTime.UtcNow - lastWarningTime >= TimeSpan.FromDays(7)) { continue; } lastWarningTime = DateTime.UtcNow; const string warning = @"GeoTracker: The local copy of the MaxMind GeoIP City or GeoLite City database is [{0}] days old and should be updated. You may need to download a new copy of the database from http://maxmind.com, decompress it and upload it to the source website at [{1}]. Note: Make sure that the website is configured with the [.DAT=application/octet-stream] MIME mapping."; SysLog.LogWarning(warning, (int)(DateTime.UtcNow - fileDateUtc).TotalDays, settings.IPGeocodeSourceUri); continue; } } // Download the database to the temporary download file. Helper.DeleteFile(downloadPath); downloadTimer = new ElapsedTimer(true); fileSize = Helper.WebDownload(settings.IPGeocodeSourceUri, downloadPath, settings.IPGeocodeSourceTimeout, out response); downloadTimer.Stop(); // Set the file times to match the Last-Modified header received from the website (it any). string lastModified = response.Headers["Last-Modified"]; if (lastModified != null) { try { fileDateUtc = Helper.ParseInternetDate(lastModified); File.SetCreationTimeUtc(downloadPath, fileDateUtc); File.SetLastWriteTimeUtc(downloadPath, fileDateUtc); } catch (Exception e) { SysLog.LogException(e, "GeoTracker: Website for [{0}] returned invalid Last-Modified header [{1}].", settings.IPGeocodeSourceUri, lastModified); } } // Decrypt the file and set its file dates. var keyChain = new KeyChain(settings.IPGeocodeSourceRsaKey); using (var secureFile = new SecureFile(downloadPath, keyChain)) { secureFile.DecryptTo(decryptedPath); } File.SetCreationTimeUtc(decryptedPath, fileDateUtc); File.SetLastWriteTimeUtc(decryptedPath, fileDateUtc); // Verify the decrypted data file and then swap in new file. const string info = @"GeoTracker: {0} of IP-to-location database from [{1}] completed. Downloaded [{2:#.#}MB] bytes in [{3}]."; SysLog.LogInformation(info, isUpdate ? "Update download" : "Initial download", settings.IPGeocodeSourceUri, fileSize / (1024 * 1024), downloadTimer.ElapsedTime); // Create a new MaxMind lookup intance and then swap it in without interrupting // any queries in progress. try { LookupService newMaxMind; newMaxMind = new LookupService(decryptedPath, LookupService.GEOIP_MEMORY_CACHE); newMaxMind.close(); maxMind = newMaxMind; UpdateCount++; } catch (Exception e) { SysLog.LogException(e); SysLog.LogError("GeoTracker: The MaxMind downloaded database file [{0}] appears to be corrupted. This will be deleted so the downloader can get a fresh copy.", downloadPath); } lock (syncLock) { Helper.DeleteFile(dataPath); File.Copy(decryptedPath, dataPath); File.SetCreationTimeUtc(dataPath, fileDateUtc); File.SetLastWriteTimeUtc(dataPath, fileDateUtc); } // Delete the temporary files. Helper.DeleteFile(decryptedPath); Helper.DeleteFile(downloadPath); } } catch (WebException e) { SysLog.LogException(e); SysLog.LogWarning("GeoTracker: The download of the MaxMind database file has failed. The service will try again in 1 minute."); pollTimer.ResetTemporary(TimeSpan.FromMinutes(1)); resetTimer = false; } catch (ThreadAbortException e) { SysLog.LogException(e); throw; } catch (Exception e) { SysLog.LogException(e); } finally { if (resetTimer) { resetTimer = false; pollTimer.Reset(); } } Thread.Sleep(settings.BkInterval); } } finally { running = false; } }
/// <summary> /// Implements the background tasks. /// </summary> /// <param name="state">Not used.</param> private void OnBkTask(object state) { if (!isRunning) { return; } try { // Update the service run time. perf.Runtime.RawValue = (int)(DateTime.UtcNow - startTime).TotalMinutes; if (pollTimer.HasFired) { // Execute HTTP requests for all of the configured monitor URIs // and then wait for them to return or timeout before setting the health status. // Note that I'm configuring the request so the Host header will be set to // the header in the URI but the request will actually be submitted to the // local host. try { if (services.Count == 0) { isHealthy = true; } else { bool fail = false; for (int i = 0; i < this.services.Count; i++) { var site = services[i]; try { using (var httpConnection = new HttpConnection(HttpOption.None)) { HttpRequest request; HttpResponse response; request = new HttpRequest("GET", site.Service.Uri, null); request["X-Health-Check"] = "true"; httpConnection.Connect(new IPEndPoint(IPAddress.Loopback, site.Service.Uri.Port)); response = httpConnection.Query(request, SysTime.Now + TimeSpan.FromSeconds(5)); if (response.Status != HttpStatus.OK) { if (site.IsHealthy) { if (site.Service.IsCritical) { SysLog.LogError("Critical local service [{0}] transitioned to unhealthy status with code [{1}={2}].", site.Service.Uri, response.Status, (int)response.Status); } else { SysLog.LogWarning("Noncritical local service [{0}] transitioned to unhealthy status with code [{1}={2}].", site.Service.Uri, response.Status, (int)response.Status); } } if (site.Service.IsCritical) { fail = true; } site.IsHealthy = false; site.StatusCode = (int)response.Status; } else { if (!site.IsHealthy) { if (site.Service.IsCritical) { SysLog.LogInformation("Critical local service [{0}] transitioned to healthy status.", site.Service.Uri); } else { SysLog.LogInformation("Noncritical local service [{0}] transitioned to healthy status.", site.Service.Uri); } } site.IsHealthy = true; site.StatusCode = (int)response.Status; } } } catch (Exception e) { if (site.IsHealthy) { if (site.Service.IsCritical) { SysLog.LogError("Critical local service [{0}] transitioned to unhealthy status with exception [{1}]: {2}.", site.Service.Uri, e.GetType().Name, e.Message); } else { SysLog.LogWarning("Noncritical local service [{0}] transitioned to unhealthy status with exception [{1}]: {2}.", site.Service.Uri, e.GetType().Name, e.Message); } } if (site.Service.IsCritical) { fail = true; } site.IsHealthy = false; site.StatusCode = 0; } } isHealthy = !fail; } } finally { pollTimer.Reset(); } } } catch (Exception e) { SysLog.LogException(e); isHealthy = false; } finally { // Update the status performance counter. this.perf.Status.RawValue = isHealthy ? 1 : 0; } }
/// <summary> /// Implements the background thread responsible for persisting buffered <see cref="GeoFix "/>es . /// </summary> private void FlushThread() { bool stopCompleted = false; List <FixRecord> writeFixes; while (true) { writeFixes = null; try { lock (syncLock) { if (!isRunning) { return; } if (stopPending || bufferedFixes.Count >= bufferSize || flushTimer.HasFired) { writeFixes = bufferedFixes; bufferedFixes = new List <FixRecord>(writeFixes.Count); } } if (writeFixes != null && writeFixes.Count > 0) { // Build SQL batch command that to add all of the buffered fixes using // the SQL script template to generate the T-SQL statements for each fix. var processor = new MacroProcessor(); var sqlBatch = new StringBuilder(8192); foreach (var record in writeFixes) { if (record.EntityID == null) { SysLog.LogWarning("SqlGeoFixArchiver: GeoFix has a [EntityID=NULL] field and will be ignored."); } if (!record.Fix.TimeUtc.HasValue) { SysLog.LogWarning("SqlGeoFixArchiver: GeoFix has a [TimeUtc=NULL] field and will be ignored."); } if (double.IsNaN(record.Fix.Latitude)) { SysLog.LogWarning("SqlGeoFixArchiver: GeoFix has a [Latitude=NaN] field and will be ignored."); } if (double.IsNaN(record.Fix.Longitude)) { SysLog.LogWarning("SqlGeoFixArchiver: GeoFix has a [Longitude=NaN] field and will be ignored."); } processor["EntityID"] = ToSqlLiteral(record.EntityID); processor["GroupID"] = ToSqlLiteral(record.GroupID); processor["TimeUtc"] = ToSqlLiteral(record.Fix.TimeUtc); processor["Technology"] = ToSqlLiteral(record.Fix.Technology); processor["Latitude"] = ToSqlLiteral(record.Fix.Latitude); processor["Longitude"] = ToSqlLiteral(record.Fix.Longitude); processor["Altitude"] = ToSqlLiteral(record.Fix.Altitude); processor["Course"] = ToSqlLiteral(record.Fix.Course); processor["Speed"] = ToSqlLiteral(record.Fix.Speed); processor["HorizontalAccuracy"] = ToSqlLiteral(record.Fix.HorizontalAccuracy); processor["VerticalAccurancy"] = ToSqlLiteral(record.Fix.VerticalAccurancy); processor["NetworkStatus"] = ToSqlLiteral(record.Fix.NetworkStatus); sqlBatch.AppendLine(processor.Expand(addScript)); } // Submit the T-SQL batch to the server. var sqlCtx = new SqlContext(conString); sqlCtx.Open(); try { sqlCtx.Execute(sqlBatch.ToString()); node.IncrementFixesReceivedBy(writeFixes.Count); } catch (SqlException e) { const string msgTemplate = @"SQL Error [Line {1}]: {0} {2} "; SysLog.LogException(e); SysLog.LogError(msgTemplate, e.Message, e.LineNumber, sqlBatch); } finally { sqlCtx.Close(); } } } catch (Exception e) { SysLog.LogException(e); } finally { if (writeFixes != null) { writeFixes = null; flushTimer.Reset(); } } if (stopCompleted) { return; } if (stopPending) { // Loop one more time to archive any fixes cached while we were // persisting to the database. stopCompleted = true; continue; } Thread.Sleep(settings.BkInterval); } }