protected override bool OnBeforeSend(Growl.Connector.MessageBuilder mb) { //from <hostname> by <hostname> [with Growl] [id <identifier>]; <ISO 8601 date> foreach (Header header in this.RequestInfo.PreviousReceivedHeaders) { mb.AddHeader(header); } string received = String.Format("from {0} by {1}{2}{3}; {4}", this.RequestInfo.ReceivedFrom, this.RequestInfo.ReceivedBy, (this.RequestInfo.ReceivedWith != null ? String.Format(" with {0}", this.RequestInfo.ReceivedWith) : String.Empty), (this.RequestInfo.RequestID != null ? String.Format(" id {0}", this.RequestInfo.RequestID) : String.Empty), this.RequestInfo.TimeReceived.ToString("u")); Header receivedHeader = new Header("Received", received); mb.AddHeader(receivedHeader); return(base.OnBeforeSend(mb)); }
/// <summary> /// Registers the specified application and notification types and allows for additional request data. /// </summary> /// <param name="application">The <see cref="Application"/> to register.</param> /// <param name="notificationTypes">The <see cref="NotificationType"/>s to register.</param> /// <param name="requestData">The <see cref="RequestData"/> containing the additional information.</param> /// <param name="state">An optional state object that will be passed into the response events associated with this request</param> public virtual void Register(Application application, NotificationType[] notificationTypes, RequestData requestData, object state) { HeaderCollection appHeaders = application.ToHeaders(); List<HeaderCollection> notifications = new List<HeaderCollection>(); foreach (NotificationType notificationType in notificationTypes) { HeaderCollection notificationHeaders = notificationType.ToHeaders(); notifications.Add(notificationHeaders); } MessageBuilder mb = new MessageBuilder(RequestType.REGISTER, this.GetKey()); foreach(Header header in appHeaders) { mb.AddHeader(header); } mb.AddHeader(new Header(Header.NOTIFICATIONS_COUNT, notificationTypes.Length.ToString())); // handle any additional request data if (requestData != null) { HeaderCollection requestDataHeaders = requestData.ToHeaders(); foreach (Header header in requestDataHeaders) { mb.AddHeader(header); } } foreach(HeaderCollection headers in notifications) { MessageSection ms = new MessageSection(); foreach(Header header in headers) { ms.AddHeader(header); } mb.AddMessageSection(ms); } Send(mb, OnResponseReceived, false, state); }
/// <summary> /// Sends a notification to Growl that specifies callback information and allows for additional request data. /// </summary> /// <param name="notification">The <see cref="Notification"/> to send.</param> /// <param name="callbackContext">The <see cref="CallbackContext"/> containing the callback information.</param> /// <param name="requestData">The <see cref="RequestData"/> containing the additional information.</param> /// <param name="state">An optional state object that will be passed into the response events associated with this request</param> public virtual void Notify(Notification notification, CallbackContext callbackContext, RequestData requestData, object state) { bool waitForCallback = false; HeaderCollection notificationHeaders = notification.ToHeaders(); MessageBuilder mb = new MessageBuilder(RequestType.NOTIFY, this.GetKey()); foreach (Header header in notificationHeaders) { mb.AddHeader(header); } if (callbackContext != null) { string url = callbackContext.CallbackUrl; if (!String.IsNullOrEmpty(url)) { mb.AddHeader(new Header(Header.NOTIFICATION_CALLBACK_TARGET, url)); } else { mb.AddHeader(new Header(Header.NOTIFICATION_CALLBACK_CONTEXT, callbackContext.Data)); mb.AddHeader(new Header(Header.NOTIFICATION_CALLBACK_CONTEXT_TYPE, callbackContext.Type.ToString())); waitForCallback = true; } } // handle any additional request data if (requestData != null) { HeaderCollection requestDataHeaders = requestData.ToHeaders(); foreach (Header header in requestDataHeaders) { mb.AddHeader(header); } } Send(mb, OnResponseReceived, waitForCallback, state); }
/// <summary> /// Adds any application-specific headers to the message /// </summary> /// <param name="mb">The <see cref="MessageBuilder"/> used to construct the message</param> /// <param name="requestData">The <see cref="RequestData"/> that contains the application-specific data</param> private void AddRequestData(MessageBuilder mb, RequestData requestData) { if (requestData != null) { HeaderCollection headers = requestData.ToHeaders(); foreach (Header header in headers) { mb.AddHeader(header); } } }
/// <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); } }
/// <summary> /// Writes data to the specified socket. /// </summary> /// <param name="socket">The <see cref="AsyncSocket"/> to write the data to</param> /// <param name="mb">The <see cref="MessageBuilder"/> containing the data to write</param> /// <param name="timeout">The socket write timeout value</param> /// <param name="tag">The tag that will identify the write operation (can be referenced in the socket's DidWrite event)</param> /// <param name="disconnectAfterWriting">Indicates if the server should disconnect the socket after writing the data</param> /// <param name="requestComplete">Indicates if the request is complete once the data is written</param> protected virtual void Write(AsyncSocket socket, MessageBuilder mb, int timeout, long tag, bool disconnectAfterWriting, bool requestComplete) { //Console.WriteLine(alreadyReceived.ToString()); byte[] bytes = mb.GetBytes(); mb = null; FinalWrite(socket, bytes, timeout, tag, disconnectAfterWriting, requestComplete); }
/// <summary> /// Writes an error response back to the original sender. /// </summary> /// <param name="socket">The <see cref="AsyncSocket"/> used to write the response</param> /// <param name="error">The error</param> private void WriteError(AsyncSocket socket, Error error) { if (this.Error != null) { this.Error(error); } HeaderCollection headers = error.ToHeaders(); MessageBuilder mb = new MessageBuilder(ResponseType.ERROR); foreach (Header header in headers) { mb.AddHeader(header); } Write(socket, mb, TIMEOUT_ERROR_RESPONSE, RESPONSE_ERROR_TAG, true, true); }
/// <summary> /// Writes the response back to the original sender. /// </summary> /// <param name="mb">The <see cref="MessageBuilder"/> containing the data to write</param> /// <param name="requestComplete">Indicates if this completes the transaction (all responses and callbacks have been written)</param> public void WriteResponse(MessageBuilder mb, bool requestComplete) { Write(socket, mb, TIMEOUT_ERROR_RESPONSE, RESPONSE_SUCCESS_TAG, false, requestComplete); }
/// <summary> /// Renews the callers subscription to the server to avoid being timed-out. /// </summary> /// <remarks> /// By default, the renewal interval is equal to (Server TTL - 30 seconds). /// </remarks> private void RenewSubscription() { HeaderCollection headers = this.subscriber.ToHeaders(); MessageBuilder mb = new MessageBuilder(RequestType.SUBSCRIBE, this.GetKey()); foreach (Header header in headers) { mb.AddHeader(header); } Send(mb, OnResponseReceived, false, null); }
/// <summary> /// Sends the request and handles any responses /// </summary> /// <param name="mb">The <see cref="MessageBuilder"/> used to contruct the request</param> /// <param name="del">The <see cref="ResponseReceivedEventHandler"/> for handling the response</param> /// <param name="waitForCallback"><c>true</c> to wait for a callback;<c>false</c> otherwise</param> /// <param name="state">An optional state object that will be passed into the response events associated with this request</param> protected void Send(MessageBuilder mb, ResponseReceivedEventHandler del, bool waitForCallback, object state) { // do some of this *before* we spin up a new thread so we can just throw an exception if the error occurs // *before* we even send the request (like when generating the message bytes) bool doSend = this.OnBeforeSend(mb); if (doSend) { byte[] bytes = mb.GetBytes(); mb = null; // start a new thread for the network connection stuff ConnectionState cs = new ConnectionState(bytes, del, waitForCallback, state); ParameterizedThreadStart pts = new ParameterizedThreadStart(SendAsync); Thread t = new Thread(pts); t.Start(cs); } }
/// <summary> /// Fired immediately before the message is constructed and set. /// Allows adding any additional headers to the outgoing request or /// to cancel the request. /// </summary> /// <param name="mb">The <see cref="MessageBuilder"/> used to construct the message</param> /// <returns> /// <c>true</c> to allow the request to be sent; /// <c>false</c> to cancel the request /// </returns> protected virtual bool OnBeforeSend(MessageBuilder mb) { return true; }