/// <summary> /// Handles the <see cref="MessageHandler.MessageParsed"/> event /// </summary> /// <param name="mh">The <see cref="MessageHandler"/> that parsed the message</param> /// <remarks> /// This method starts a new thread to peform the actual message handling /// </remarks> private void mh_MessageParsed(MessageHandler mh) { ParameterizedThreadStart pts = new ParameterizedThreadStart(HandleParsedMessage); Thread t = new Thread(pts); t.Start(mh); }
/// <summary> /// Handles the <see cref="AsyncSocket.DidAccept"/> event. /// </summary> /// <param name="sender">The listening <see cref="AsyncSocket"/></param> /// <param name="newSocket">The new <see cref="AsyncSocket"/> that was accepted</param> private void listenSocket_DidAccept(AsyncSocket sender, AsyncSocket newSocket) { LogInfo("Accepted client {0}:{1}", newSocket.RemoteAddress, newSocket.RemotePort); // check origin bool isLocal = IPAddress.IsLoopback(newSocket.RemoteAddress); bool isLAN = Growl.CoreLibrary.IPUtilities.IsInSameSubnet(newSocket.LocalAddress, newSocket.RemoteAddress); if (!this.allowNetworkNotifications && !isLocal) { // remote connections not allowed - Should we return a GNTP error response? i think this is better (no reply at all) LogInfo("Blocked network request from '{0}'", newSocket.RemoteAddress); newSocket.Close(); return; } bool passwordRequired = true; if (isLocal && !this.RequireLocalPassword) passwordRequired = false; else if (isLAN && !this.RequireLANPassword) passwordRequired = false; // SUPER IMPORTANT newSocket.AllowMultithreadedCallbacks = true; MessageHandler mh = new MessageHandler(this.serverName, this.passwordManager, passwordRequired, this.logFolder, this.loggingEnabled, this.allowNetworkNotifications, this.allowWebNotifications, this.allowSubscriptions); newSocket.DidClose += new AsyncSocket.SocketDidClose(newSocket_DidClose); mh.MessageParsed += new MessageHandler.MessageHandlerMessageParsedEventHandler(mh_MessageParsed); mh.Error += new MessageHandler.MessageHandlerErrorEventHandler(mh_Error); mh.SocketUsageComplete += new MessageHandler.MessageHandlerSocketUsageCompleteEventHandler(mh_SocketUsageComplete); // lock here since in very rare cases, we can get flooded with so many incoming sockets that // the Add() throws an IndexOutOfRange exception (only ever happened when running GrowlHammer with loads of // simultaneous connections) lock (syncLock) { connectedSockets.Add(new ConnectedSocket(newSocket)); connectedHandlers.Add(newSocket, mh); } mh.InitialRead(newSocket); }
/// <summary> /// Handles the <see cref="AsyncSocket.DidAccept"/> event. /// </summary> /// <param name="sender">The listening <see cref="AsyncSocket"/></param> /// <param name="newSocket">The new <see cref="AsyncSocket"/> that was accepted</param> private void listenSocket_DidAccept(AsyncSocket sender, AsyncSocket newSocket) { LogInfo("Accepted client {0}:{1}", newSocket.RemoteAddress, newSocket.RemotePort); // check origin bool isLocal = IPAddress.IsLoopback(newSocket.RemoteAddress); bool isLAN = Growl.CoreLibrary.IPUtilities.IsInSameSubnet(newSocket.LocalAddress, newSocket.RemoteAddress); if (!this.allowNetworkNotifications && !isLocal) { // remote connections not allowed - Should we return a GNTP error response? i think this is better (no reply at all) LogInfo("Blocked network request from '{0}'", newSocket.RemoteAddress); newSocket.Close(); return; } bool passwordRequired = true; if (isLocal && !this.RequireLocalPassword) { passwordRequired = false; } else if (isLAN && !this.RequireLANPassword) { passwordRequired = false; } // SUPER IMPORTANT newSocket.AllowMultithreadedCallbacks = true; MessageHandler mh = new MessageHandler(this.serverName, this.passwordManager, passwordRequired, this.logFolder, this.loggingEnabled, this.allowNetworkNotifications, this.allowWebNotifications, this.allowSubscriptions); newSocket.DidClose += new AsyncSocket.SocketDidClose(newSocket_DidClose); mh.MessageParsed += new MessageHandler.MessageHandlerMessageParsedEventHandler(mh_MessageParsed); mh.Error += new MessageHandler.MessageHandlerErrorEventHandler(mh_Error); mh.SocketUsageComplete += new MessageHandler.MessageHandlerSocketUsageCompleteEventHandler(mh_SocketUsageComplete); // lock here since in very rare cases, we can get flooded with so many incoming sockets that // the Add() throws an IndexOutOfRange exception (only ever happened when running GrowlHammer with loads of // simultaneous connections) lock (syncLock) { connectedSockets.Add(new ConnectedSocket(newSocket)); connectedHandlers.Add(newSocket, mh); } mh.InitialRead(newSocket); }
/// <summary> /// Handles the <see cref="AsyncSocket.DidClose"/> event /// </summary> /// <param name="sender">The <see cref="AsyncSocket"/> that disconnected</param> void newSocket_DidClose(AsyncSocket sender) { if (sender != null) { lock (syncLock) { if (sender != null) { if (this.connectedHandlers.ContainsKey(sender)) { MessageHandler mh = this.connectedHandlers[sender]; this.connectedHandlers.Remove(sender); if (this.connectedSockets.Contains(sender)) { ConnectedSocket cs = this.connectedSockets[sender]; this.connectedSockets.Remove(sender); if (cs.Socket != null) { cs.Socket.DidClose -= new AsyncSocket.SocketDidClose(newSocket_DidClose); //cs.Socket.DidRead -= new AsyncSocket.SocketDidRead(mh.SocketDidRead); } cs = null; } if (mh != null) { mh.MessageParsed -= new MessageHandler.MessageHandlerMessageParsedEventHandler(mh_MessageParsed); mh.Error -= new MessageHandler.MessageHandlerErrorEventHandler(mh_Error); mh.SocketUsageComplete -= new MessageHandler.MessageHandlerSocketUsageCompleteEventHandler(mh_SocketUsageComplete); } mh = null; } } } sender = null; } }
/// <summary> /// Writes back the GNTP response to the requesting application /// </summary> /// <param name="cbInfo">The <see cref="CallbackInfo"/> associated with the response</param> /// <param name="response">The <see cref="Response"/> to be written back</param> public void WriteResponse(CallbackInfo cbInfo, Response response) { if (!cbInfo.AlreadyResponded) { cbInfo.AlreadyResponded = true; MessageHandler mh = cbInfo.MessageHandler; GNTPRequest request = mh.Request; ResponseType responseType = ResponseType.ERROR; if (response != null) { if (response.IsCallback) { responseType = ResponseType.CALLBACK; } else if (response.IsOK) { responseType = ResponseType.OK; } } else { response = new Response(ErrorCode.INTERNAL_SERVER_ERROR, ErrorDescription.INTERNAL_SERVER_ERROR); } if (cbInfo.AdditionalInfo != null) { foreach (KeyValuePair <string, string> item in cbInfo.AdditionalInfo) { response.CustomTextAttributes.Add(item.Key, item.Value); } } AddServerHeaders(response); MessageBuilder mb = new MessageBuilder(responseType); HeaderCollection responseHeaders = response.ToHeaders(); foreach (Header header in responseHeaders) { mb.AddHeader(header); } // return any application-specific data headers that were received RequestData rd = RequestData.FromHeaders(request.Headers); AddRequestData(mb, rd); mh.WriteResponse(mb, true); } }
/// <summary> /// Handles the parsed message after it is received /// </summary> /// <param name="obj">The <see cref="MessageHandler"/> object that parsed the message</param> private void HandleParsedMessage(object obj) { MessageHandler mh = (MessageHandler)obj; GNTPRequest request = mh.Request; try { Response response = null; switch (request.Directive) { case RequestType.REGISTER: Application application = Application.FromHeaders(request.Headers); List <NotificationType> notificationTypes = new List <NotificationType>(); for (int i = 0; i < request.NotificationsToBeRegistered.Count; i++) { HeaderCollection headers = request.NotificationsToBeRegistered[i]; notificationTypes.Add(NotificationType.FromHeaders(headers)); } response = this.OnRegisterReceived(application, notificationTypes, mh.RequestInfo); break; case RequestType.NOTIFY: Notification notification = Notification.FromHeaders(request.Headers); mh.CallbackInfo.NotificationID = notification.ID; response = this.OnNotifyReceived(notification, mh.CallbackInfo, mh.RequestInfo); break; case RequestType.SUBSCRIBE: Subscriber subscriber = Subscriber.FromHeaders(request.Headers); subscriber.IPAddress = mh.Socket.RemoteAddress.ToString(); subscriber.Key = new SubscriberKey(request.Key, subscriber.ID, request.Key.HashAlgorithm, request.Key.EncryptionAlgorithm); response = this.OnSubscribeReceived(subscriber, mh.RequestInfo); break; } ResponseType responseType = ResponseType.ERROR; if (response != null && response.IsOK) { responseType = ResponseType.OK; response.InResponseTo = request.Directive.ToString(); } // no response if (response == null) { response = new Response(ErrorCode.INTERNAL_SERVER_ERROR, ErrorDescription.INTERNAL_SERVER_ERROR); } AddServerHeaders(response); MessageBuilder mb = new MessageBuilder(responseType); HeaderCollection responseHeaders = response.ToHeaders(); foreach (Header header in responseHeaders) { mb.AddHeader(header); } // return any application-specific data headers that were received RequestData rd = RequestData.FromHeaders(request.Headers); AddRequestData(mb, rd); bool requestComplete = !mh.CallbackInfo.ShouldKeepConnectionOpen(); mh.WriteResponse(mb, requestComplete); } catch (GrowlException gEx) { mh.WriteError(gEx.ErrorCode, gEx.Message, gEx.AdditionalInfo); } catch (Exception ex) { mh.WriteError(ErrorCode.INTERNAL_SERVER_ERROR, ex.Message); } }