/// <summary> /// Dispatches the request to each handler registered to receive notification of this request's method /// </summary> /// <param name="dispatcher"></param> /// <param name="request"></param> /// <param name="sender"></param> /// <param name="cancel"></param> private Delegate[] InternalDispatchRequest(object sender, ref HttpRequestCancelEventArgs e) { // look up the list of handlers for the specified hook point, and then look up the method in that list to Hashtable requestHandlers = this[HttpRequestHookPoints.BeforeHttpRuntimeProcessing]; // receive the list of handlers that is handling callback notification for methods in that list HttpRequestCancelEventHandler handlers = requestHandlers[e.Request.Method] as HttpRequestCancelEventHandler; // there is a list of handlers waiting to be notified if (handlers == null) { return new Delegate[] {} } ; // extract the list of handlers to be notified when this method is received Delegate[] delegates = handlers.GetInvocationList(); if (delegates == null) { return new Delegate[] {} } ; // enumerate each handler foreach (Delegate d in delegates) { try { // notify the handler ((HttpRequestCancelEventHandler)d)(sender, e); // someone may have created a response that is ready to be sent for this event so no further processing is required if (e.Response != null) { return(delegates); } // someone may have handled the request manually and there for this event is cancelled and no further processing is required if (e.Cancel) { return(delegates); } } catch (Exception ex) { Debug.WriteLine(ex); } } return(delegates); }
/// <summary> /// Occurs when a request is received /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected virtual void OnRequestReceived(object sender, HttpRequestCancelEventArgs e) { // if (!_isServerSideConnection) // { // Debug.WriteLine(string.Format("User-Agent connection '{0}' auto-logging request '{1}'", _id.ToString(), HttpUtils.StripCRLF(e.Request.FirstLine)), MY_TRACE_CATEGORY); // Debug.WriteIf(_verbose, e.Request.ToString(false)); // } // else // { // Debug.WriteLine(string.Format("Server-Side connection '{0}' auto-logging request '{1}'", _id.ToString(), HttpUtils.StripCRLF(e.Request.FirstLine)), MY_TRACE_CATEGORY); // Debug.WriteIf(_verbose, e.Request.ToString(false)); // } // dispatch the request out to be handled if (_dispatcher != null) { _dispatcher.DispatchRequest(sender, ref e); } else { Debug.WriteLine(string.Format("Received a message, however there is no dispatcher available to handle the message for connection '{0}'.", _id.ToString())); } // did someone cancel the request? if (e.Cancel) { return; // do not send a response, some will have done this manually } /* * IMPORTANT!!! - We are providing a mechanism to override the default HTTP/1.1 specification's behaviour. * The specs state explicitly under all conditions a response must be sent to a request. We have certain scenarious where * we only need to send a request, no further processing is required as to the result of the request by the user-agent, so * the 'Reponse-Needed' header can be included to prevent the 'Server' from responding to the request. * */ if (e.Request.ResponseNeeded) { // if there is a response prepared let's send it now HttpRequest request = e.Request; HttpResponse response = e.Response; // send a response to the request this.SendResponseToRequest(ref request, ref response); } }
/// <summary> /// Occurs when a request is received /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected virtual void OnRequestReceived(object sender, HttpRequestCancelEventArgs e) { // if (!_isServerSideConnection) // { // Debug.WriteLine(string.Format("User-Agent connection '{0}' auto-logging request '{1}'", _id.ToString(), HttpUtils.StripCRLF(e.Request.FirstLine)), MY_TRACE_CATEGORY); // Debug.WriteIf(_verbose, e.Request.ToString(false)); // } // else // { // Debug.WriteLine(string.Format("Server-Side connection '{0}' auto-logging request '{1}'", _id.ToString(), HttpUtils.StripCRLF(e.Request.FirstLine)), MY_TRACE_CATEGORY); // Debug.WriteIf(_verbose, e.Request.ToString(false)); // } // dispatch the request out to be handled if (_dispatcher != null) _dispatcher.DispatchRequest(sender, ref e); else Debug.WriteLine(string.Format("Received a message, however there is no dispatcher available to handle the message for connection '{0}'.", _id.ToString())); // did someone cancel the request? if (e.Cancel) return; // do not send a response, some will have done this manually /* * IMPORTANT!!! - We are providing a mechanism to override the default HTTP/1.1 specification's behaviour. * The specs state explicitly under all conditions a response must be sent to a request. We have certain scenarious where * we only need to send a request, no further processing is required as to the result of the request by the user-agent, so * the 'Reponse-Needed' header can be included to prevent the 'Server' from responding to the request. * */ if (e.Request.ResponseNeeded) { // if there is a response prepared let's send it now HttpRequest request = e.Request; HttpResponse response = e.Response; // send a response to the request this.SendResponseToRequest(ref request, ref response); } }
/// <summary> /// Tries to receive data from the network on a background thread /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected virtual void OnReadFromSocket(object sender, BackgroundThreadStartEventArgs threadStartArgs) { try { // create a new message reader _messageReader = new HttpMessageReader(); while(true) { try { // if (!_isServerSideConnection) // Debug.WriteLine(string.Format("User-Agent connection '{0}' trying to read next incoming message from '{1}'", _id.ToString(), this.RemoteAddress.ToString()), MY_TRACE_CATEGORY); // else // Debug.WriteLine(string.Format("Server-Side connection '{0}' trying to read next incoming message from '{1}'", _id.ToString(), this.RemoteAddress.ToString()), MY_TRACE_CATEGORY); // read a single message HttpMessage message = null; // lock the reader lock(_messageReader) { // read a message message = _messageReader.Read(_socket, _stopEvent); } // what type of message is this ? switch(message.Type) { /* * process the request * */ case HttpMessageTypes.HttpRequest: { // create a request event args HttpRequestCancelEventArgs e = new HttpRequestCancelEventArgs(new HttpRequest(message), false); // process the request by dispatching it and then responding if need be this.OnRequestReceived(this, e); // // next check the request to see if the connection should be closed after the response was sent // if (HttpUtils.Contains(e.Request.Connection, HttpConnections.Close)) // { // // yup, they wanted us to close the connection automatically so lets do that now // this.Close(); // return; // } break; } /* * process the response * */ case HttpMessageTypes.HttpResponse: { this.OnResponseReceived(this, new HttpResponseEventArgs(new HttpResponse(message))); break; } /* * an unknown message type * */ default: { // hopefully this will never happen! Debug.WriteLine(string.Format("A message of unknown type was received from '{0}'...\n{1}", message)); break; } }; } catch(HttpMessageReaderAbortedException) { // the reader was aborted } catch(HttpConnectionClosedByPeerException) { /* * the remote host has closed the connection * */ this.Close(); return; } // see if we should stop receiving if (_stopEvent != null) if (_stopEvent.WaitOne(1, false)) { /* * we have recieved a signal that we should stop receiving * and disconnect the current connection * */ if (_disconnectOnStop) this.Close(); return; } } } catch(ThreadAbortException) { /* * the thread is aborting * */ if (_disconnectOnStop) this.Close(); } catch(ObjectDisposedException) { /* * the connection has closed the socket * */ this.Close(); } catch(SocketException ex) { // if the connection is reset, or a blocking call was cancelled with a call to cancelblockingcall if (ex.ErrorCode == (int)SocketErrors.WSAECONNRESET || ex.ErrorCode == (int)SocketErrors.WSAEINTR) { this.Close(); return; } // notify that this connection has encountered an exception this.OnException(this, new ExceptionEventArgs(ex)); } catch(Exception ex) { // notify that this connection has encountered an exception this.OnException(this, new ExceptionEventArgs(ex)); } // finally // { // Debug.WriteLineIf(!_isServerSideConnection, string.Format("*** exiting receiving thread loop for connection '{0}'", _id.ToString()), MY_TRACE_CATEGORY); // } }
/// <summary> /// Tries to receive data from the network on a background thread /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected virtual void OnReadFromSocket(object sender, BackgroundThreadStartEventArgs threadStartArgs) { try { // create a new message reader _messageReader = new HttpMessageReader(); while (true) { try { // if (!_isServerSideConnection) // Debug.WriteLine(string.Format("User-Agent connection '{0}' trying to read next incoming message from '{1}'", _id.ToString(), this.RemoteAddress.ToString()), MY_TRACE_CATEGORY); // else // Debug.WriteLine(string.Format("Server-Side connection '{0}' trying to read next incoming message from '{1}'", _id.ToString(), this.RemoteAddress.ToString()), MY_TRACE_CATEGORY); // read a single message HttpMessage message = null; // lock the reader lock (_messageReader) { // read a message message = _messageReader.Read(_socket, _stopEvent); } // what type of message is this ? switch (message.Type) { /* * process the request * */ case HttpMessageTypes.HttpRequest: { // create a request event args HttpRequestCancelEventArgs e = new HttpRequestCancelEventArgs(new HttpRequest(message), false); // process the request by dispatching it and then responding if need be this.OnRequestReceived(this, e); // // next check the request to see if the connection should be closed after the response was sent // if (HttpUtils.Contains(e.Request.Connection, HttpConnections.Close)) // { // // yup, they wanted us to close the connection automatically so lets do that now // this.Close(); // return; // } break; } /* * process the response * */ case HttpMessageTypes.HttpResponse: { this.OnResponseReceived(this, new HttpResponseEventArgs(new HttpResponse(message))); break; } /* * an unknown message type * */ default: { // hopefully this will never happen! Debug.WriteLine(string.Format("A message of unknown type was received from '{0}'...\n{1}", message)); break; } } ; } catch (HttpMessageReaderAbortedException) { // the reader was aborted } catch (HttpConnectionClosedByPeerException) { /* * the remote host has closed the connection * */ this.Close(); return; } // see if we should stop receiving if (_stopEvent != null) { if (_stopEvent.WaitOne(1, false)) { /* * we have recieved a signal that we should stop receiving * and disconnect the current connection * */ if (_disconnectOnStop) { this.Close(); } return; } } } } catch (ThreadAbortException) { /* * the thread is aborting * */ if (_disconnectOnStop) { this.Close(); } } catch (ObjectDisposedException) { /* * the connection has closed the socket * */ this.Close(); } catch (SocketException ex) { // if the connection is reset, or a blocking call was cancelled with a call to cancelblockingcall if (ex.ErrorCode == (int)SocketErrors.WSAECONNRESET || ex.ErrorCode == (int)SocketErrors.WSAEINTR) { this.Close(); return; } // notify that this connection has encountered an exception this.OnException(this, new ExceptionEventArgs(ex)); } catch (Exception ex) { // notify that this connection has encountered an exception this.OnException(this, new ExceptionEventArgs(ex)); } // finally // { // Debug.WriteLineIf(!_isServerSideConnection, string.Format("*** exiting receiving thread loop for connection '{0}'", _id.ToString()), MY_TRACE_CATEGORY); // } }
// /// <summary> // /// Unregisters for notification for when the specified method is received in a HttpRequest // /// </summary> // /// <param name="method">The method notification was registered for</param> // /// <param name="onRequest">The callback to be notified when/if the method is received</param> // public void UnregisterForRequestMethodNotification(string method, HttpRequestEventHandler onRequest) // { // // we must have a valid method // HttpUtils.ValidateToken(@"method", method); // // // and we must have a valid callback // if (onRequest == null) // throw new ArgumentNullException("onRequest"); // // // look up the list of handlers for the specified hook point, and then look up the method in that list to // Hashtable requestHandlers = this[HttpRequestHookPoints.AfterHttpRuntimeProcessing]; // // // receive the list of handlers that is handling callback notification for methods in that list // HttpRequestEventHandler handlers = requestHandlers[method] as HttpRequestEventHandler; // // // if the handler list is null // if (handlers != null) // // add the handlers into the list by the method we're registering for // handlers = (HttpRequestEventHandler)Delegate.Remove(handlers, onRequest); // // // if there are still some handlers, and the list is zero length, remove the list entirely // if (handlers != null) // if (handlers.GetInvocationList().Length == 0) // { // requestHandlers.Remove(method); // return; // } // // // save the list of handlers for the next guy // requestHandlers[method] = handlers; // } /// <summary> /// Dispatches the HttpRequest to the registered handlers for processing. If no response is created or sent, then a default response will be prepared. /// </summary> /// <param name="dispatcher"></param> /// <param name="sender"></param> /// <param name="e"></param> public void DispatchRequest(object sender, ref HttpRequestCancelEventArgs e) { // Debug.WriteLine(string.Format("Dispatching request '{0}'.", e.Request.Method)); // internally dispatch the request to the registered handler(s) until someone responds to the request Delegate[] delegates = this.InternalDispatchRequest(sender, ref e); if (!e.Cancel && e.Response == null) { #region Asp Processing // if we have an asp host if (_aspHost != null) { bool alreadyRetried = false; RetryAspProcessing: try { // use the asp host to process the request _aspHost.ProcessRequest((HttpConnection)sender, ref e); } catch (AppDomainUnloadedException) { // Debug.WriteLine(ex); // it might have unloaded on us, so we can try once more if (!alreadyRetried) { // recreate the host this.CreateAspHost(); alreadyRetried = true; // jump back and try again goto RetryAspProcessing; } } } #endregion // after the asp processing, it might have already sent the response so watch out that it didn't cancel // the remaining processing we had to do if (e.Cancel) { return; } // and finally if we still have no response for the request, let's fill in which ever is appropriate if (e.Response == null) { // if there are no handlers that were registered for the request's method if (delegates.Length == 0) { // then the method is not allowed e.Response = new HttpResponse(new MethodNotAllowedStatus()); e.Response.SetBodyFromString(string.Format("405 - The '{0}' method is not allowed.\nThe request for the '{1}' resource cannot be processed.", e.Request.Method, e.Request.RequestUri), HttpUtils.Encoding, MIME.Text.Plain); } else { // resource not found (there was a handler but it didn't respond) so obviously the resource couldn't be found e.Response = new HttpResponse(new NotFoundStatus()); e.Response.SetBodyFromString(string.Format("404 - The resource '{0}' could not be found.", e.Request.RequestUri), HttpUtils.Encoding, MIME.Text.Plain); } } } }
/// <summary> /// Dispatches the request to each handler registered to receive notification of this request's method /// </summary> /// <param name="dispatcher"></param> /// <param name="request"></param> /// <param name="sender"></param> /// <param name="cancel"></param> private Delegate[] InternalDispatchRequest(object sender, ref HttpRequestCancelEventArgs e) { // look up the list of handlers for the specified hook point, and then look up the method in that list to Hashtable requestHandlers = this[HttpRequestHookPoints.BeforeHttpRuntimeProcessing]; // receive the list of handlers that is handling callback notification for methods in that list HttpRequestCancelEventHandler handlers = requestHandlers[e.Request.Method] as HttpRequestCancelEventHandler; // there is a list of handlers waiting to be notified if (handlers == null) return new Delegate[] {}; // extract the list of handlers to be notified when this method is received Delegate[] delegates = handlers.GetInvocationList(); if (delegates == null) return new Delegate[] {}; // enumerate each handler foreach(Delegate d in delegates) { try { // notify the handler ((HttpRequestCancelEventHandler)d)(sender, e); // someone may have created a response that is ready to be sent for this event so no further processing is required if (e.Response != null) return delegates; // someone may have handled the request manually and there for this event is cancelled and no further processing is required if (e.Cancel) return delegates; } catch(Exception ex) { Debug.WriteLine(ex); } } return delegates; }
// /// <summary> // /// Unregisters for notification for when the specified method is received in a HttpRequest // /// </summary> // /// <param name="method">The method notification was registered for</param> // /// <param name="onRequest">The callback to be notified when/if the method is received</param> // public void UnregisterForRequestMethodNotification(string method, HttpRequestEventHandler onRequest) // { // // we must have a valid method // HttpUtils.ValidateToken(@"method", method); // // // and we must have a valid callback // if (onRequest == null) // throw new ArgumentNullException("onRequest"); // // // look up the list of handlers for the specified hook point, and then look up the method in that list to // Hashtable requestHandlers = this[HttpRequestHookPoints.AfterHttpRuntimeProcessing]; // // // receive the list of handlers that is handling callback notification for methods in that list // HttpRequestEventHandler handlers = requestHandlers[method] as HttpRequestEventHandler; // // // if the handler list is null // if (handlers != null) // // add the handlers into the list by the method we're registering for // handlers = (HttpRequestEventHandler)Delegate.Remove(handlers, onRequest); // // // if there are still some handlers, and the list is zero length, remove the list entirely // if (handlers != null) // if (handlers.GetInvocationList().Length == 0) // { // requestHandlers.Remove(method); // return; // } // // // save the list of handlers for the next guy // requestHandlers[method] = handlers; // } /// <summary> /// Dispatches the HttpRequest to the registered handlers for processing. If no response is created or sent, then a default response will be prepared. /// </summary> /// <param name="dispatcher"></param> /// <param name="sender"></param> /// <param name="e"></param> public void DispatchRequest(object sender, ref HttpRequestCancelEventArgs e) { // Debug.WriteLine(string.Format("Dispatching request '{0}'.", e.Request.Method)); // internally dispatch the request to the registered handler(s) until someone responds to the request Delegate[] delegates = this.InternalDispatchRequest(sender, ref e); if (!e.Cancel && e.Response == null) { #region Asp Processing // if we have an asp host if (_aspHost != null) { bool alreadyRetried = false; RetryAspProcessing: try { // use the asp host to process the request _aspHost.ProcessRequest((HttpConnection)sender, ref e); } catch(AppDomainUnloadedException) { // Debug.WriteLine(ex); // it might have unloaded on us, so we can try once more if (!alreadyRetried) { // recreate the host this.CreateAspHost(); alreadyRetried = true; // jump back and try again goto RetryAspProcessing; } } } #endregion // after the asp processing, it might have already sent the response so watch out that it didn't cancel // the remaining processing we had to do if (e.Cancel) return; // and finally if we still have no response for the request, let's fill in which ever is appropriate if (e.Response == null) { // if there are no handlers that were registered for the request's method if (delegates.Length == 0) { // then the method is not allowed e.Response = new HttpResponse(new MethodNotAllowedStatus()); e.Response.SetBodyFromString(string.Format("405 - The '{0}' method is not allowed.\nThe request for the '{1}' resource cannot be processed.", e.Request.Method, e.Request.RequestUri), HttpUtils.Encoding, MIME.Text.Plain); } else { // resource not found (there was a handler but it didn't respond) so obviously the resource couldn't be found e.Response = new HttpResponse(new NotFoundStatus()); e.Response.SetBodyFromString(string.Format("404 - The resource '{0}' could not be found.", e.Request.RequestUri), HttpUtils.Encoding, MIME.Text.Plain); } } } }