/// <summary> /// Populates the remote body from context. /// </summary> /// <param name="incomingHttpContext">The incoming HTTP context.</param> /// <param name="remoteWebRequest">The remote web request.</param> /// <returns> /// Returns a <see cref="ProxyHandler.WebResponseResult"/> containing the HTTP response and status. /// </returns> private async Task <WebResponseResult> PopulateRemoteBodyFromContext( IStumpsHttpContext incomingHttpContext, HttpWebRequest remoteWebRequest) { var result = new WebResponseResult { Success = true }; try { if (incomingHttpContext.Request.BodyLength > 0) { remoteWebRequest.ContentLength = incomingHttpContext.Request.BodyLength; var requestStream = await remoteWebRequest.GetRequestStreamAsync(); await requestStream.WriteAsync(incomingHttpContext.Request.GetBody(), 0, incomingHttpContext.Request.BodyLength); } } catch (WebException wex) { if (wex.Response != null) { result.Response = (HttpWebResponse)wex.Response; } else { result.Success = false; } } return(result); }
/// <summary> /// Processes an incoming HTTP request. /// </summary> /// <param name="context">The <see cref="T:Stumps.IStumpsHttpContext" /> representing both the incoming request and the response.</param> /// <returns> /// A member of the <see cref="T:Stumps.Http.ProcessHandlerResult" /> enumeration. /// </returns> /// <exception cref="System.ArgumentNullException"><paramref name="context"/> is <c>null</c>.</exception> public async Task<ProcessHandlerResult> ProcessRequest(IStumpsHttpContext context) { if (context == null) { throw new ArgumentNullException("context"); } var result = ProcessHandlerResult.Continue; foreach (var handler in _handlers) { result = await handler.ProcessRequest(context); if (result != ProcessHandlerResult.Continue) { break; } } if (this.ContextProcessed != null) { this.ContextProcessed(this, new StumpsContextEventArgs(context)); } return result; }
public async Task<ProcessHandlerResult> ProcessRequest(IStumpsHttpContext context) { foreach (var value in _headers) { context.Response.Headers[value.Item1] = value.Item2; } context.Response.StatusCode = this.StatusCode; context.Response.StatusDescription = this.StatusDescription; if (_body != null && _body.Length > 0) { context.Response.ClearBody(); context.Response.AppendToBody(_body); } if (this.ContextProcessed != null) { this.ContextProcessed(this, new StumpsContextEventArgs(context)); } return ProcessHandlerResult.Terminate; }
/// <summary> /// Processes an incoming HTTP request. /// </summary> /// <param name="context">The <see cref="T:Stumps.IStumpsHttpContext" /> representing both the incoming request and the response.</param> /// <returns> /// A member of the <see cref="T:Stumps.Http.ProcessHandlerResult" /> enumeration. /// </returns> /// <exception cref="System.ArgumentNullException"><paramref name="context"/> is <c>null</c>.</exception> public async Task<ProcessHandlerResult> ProcessRequest(IStumpsHttpContext context) { if (context == null) { throw new ArgumentNullException("context"); } context.Response.Headers.Clear(); context.Response.ClearBody(); context.Response.StatusCode = _statusCode; context.Response.StatusDescription = _statusCodeDescription; var stumpsResponse = context.Response as StumpsHttpResponse; if (stumpsResponse != null) { stumpsResponse.Origin = _origin; } if (this.ContextProcessed != null) { this.ContextProcessed(this, new StumpsContextEventArgs(context)); } return ProcessHandlerResult.Terminate; }
public async Task <ProcessHandlerResult> ProcessRequest(IStumpsHttpContext context) { Interlocked.Increment(ref _processRequestCalls); this.ContextProcessed?.Invoke(this, new StumpsContextEventArgs(context)); return(await Task.FromResult(_cannedResponse)); }
/// <summary> /// Writes the context body from the remote response. /// </summary> /// <param name="incomingHttpContext">The incoming HTTP context.</param> /// <param name="remoteWebResponse">The remote web response.</param> private async Task WriteContextBodyFromRemoteResponse(IStumpsHttpContext incomingHttpContext, HttpWebResponse remoteWebResponse) { if (remoteWebResponse.ContentLength != 0) { var responseStream = remoteWebResponse.GetResponseStream(); incomingHttpContext.Response.ClearBody(); incomingHttpContext.Response.AppendToBody(await StreamUtility.ConvertStreamToByteArray(responseStream)); } }
/// <summary> /// Writes the context headers from the remote response. /// </summary> /// <param name="incomingHttpContext">The incoming HTTP context.</param> /// <param name="remoteWebResponse">The remote web response.</param> private void WriteContextHeadersFromResponse( IStumpsHttpContext incomingHttpContext, HttpWebResponse remoteWebResponse) { incomingHttpContext.Response.Headers.Clear(); foreach (var headerName in remoteWebResponse.Headers.AllKeys) { incomingHttpContext.Response.Headers[headerName] = remoteWebResponse.Headers[headerName]; } }
/// <summary> /// Initializes a new instance of the <see cref="RecordedContext" /> class. /// </summary> /// <param name="context">The <see cref="IStumpsHttpContext"/> used to initialize the instance.</param> /// <param name="decoderHandling">The <see cref="ContentDecoderHandling" /> requirements for the HTTP body.</param> /// <exception cref="ArgumentNullException"><paramref name="context"/> is <c>null</c>.</exception> public RecordedContext(IStumpsHttpContext context, ContentDecoderHandling decoderHandling) { context = context ?? throw new ArgumentNullException(nameof(context)); this.Request = new RecordedRequest(context.Request, decoderHandling); this.Response = new RecordedResponse(context.Response, decoderHandling); this.ReceivedDate = context.ReceivedDate; this.UniqueIdentifier = context.UniqueIdentifier; }
/// <summary> /// Builds the remote URL from context. /// </summary> /// <param name="incomingHttpContext">The incoming HTTP context.</param> /// <returns> /// A string representing the URL for the remote server. /// </returns> private string BuildRemoteUrlFromContext(IStumpsHttpContext incomingHttpContext) { var urlPath = incomingHttpContext.Request.RawUrl; var urlHost = _externalHostUri.AbsoluteUri; urlPath = urlPath.StartsWith("/", StringComparison.Ordinal) ? urlPath.Remove(0, 1) : urlPath; var url = urlHost + urlPath; return(url); }
/// <summary> /// Processes an incoming HTTP request. /// </summary> /// <param name="context">The <see cref="IStumpsHttpContext" /> representing both the incoming request and the response.</param> /// <returns> /// A member of the <see cref="ProcessHandlerResult" /> enumeration. /// </returns> /// <exception cref="ArgumentNullException"><paramref name="context"/> is <c>null</c>.</exception> public async Task <ProcessHandlerResult> ProcessRequest(IStumpsHttpContext context) { context = context ?? throw new ArgumentNullException(nameof(context)); var remoteUrl = BuildRemoteUrlFromContext(context); // Create a new HTTP web request var remoteWebRequest = (HttpWebRequest)WebRequest.Create(remoteUrl); remoteWebRequest.AllowAutoRedirect = false; // Populate the headers for the new HTTP request from the incoming HTTP request PopulateRemoteHeadersFromContext(context, remoteWebRequest); // Populate the HTTP body for the request var webResponseResult = await PopulateRemoteBodyFromContext(context, remoteWebRequest); var continueProcess = webResponseResult.Success; // Execute the remote web request if (continueProcess) { await ExecuteRemoteWebRequest(remoteWebRequest, webResponseResult); } if (!continueProcess) { // Return a response to the client that the service is unavailable at this time. context.Response.StatusCode = HttpStatusCodes.HttpServiceUnavailable; context.Response.StatusDescription = HttpStatusCodes.GetStatusDescription(HttpStatusCodes.HttpServiceUnavailable); } else if (webResponseResult.Response != null) { // Write the headers and the body of the response from the remote HTTP request // to the incoming HTTP context. WriteContextHeadersFromResponse(context, webResponseResult.Response); await WriteContextBodyFromRemoteResponse(context, webResponseResult.Response); context.Response.StatusCode = (int)webResponseResult.Response.StatusCode; context.Response.StatusDescription = webResponseResult.Response.StatusDescription; var disposable = webResponseResult.Response as IDisposable; disposable.Dispose(); } if (context.Response is StumpsHttpResponse stumpsResponse) { stumpsResponse.Origin = HttpResponseOrigin.RemoteServer; } this.ContextProcessed?.Invoke(this, new StumpsContextEventArgs(context)); return(await Task.FromResult <ProcessHandlerResult>(ProcessHandlerResult.Continue)); }
public async Task<ProcessHandlerResult> ProcessRequest(IStumpsHttpContext context) { Interlocked.Increment(ref _processRequestCalls); var contextEvent = this.ContextProcessed; if (contextEvent != null) { contextEvent(this, new StumpsContextEventArgs(context)); } return _cannedResponse; }
/// <summary> /// Initializes a new instance of the <see cref="T:Stumps.Server.RecordedContext" /> class. /// </summary> /// <param name="context">The <see cref="T:Stumps.IStumpsHttpContext"/> used to initialize the instance.</param> /// <param name="decoderHandling">The <see cref="T:Stumps.Server.ContentDecoderHandling" /> requirements for the HTTP body.</param> /// <exception cref="System.ArgumentNullException"><paramref name="context"/> is <c>null</c>.</exception> public RecordedContext(IStumpsHttpContext context, ContentDecoderHandling decoderHandling) { if (context == null) { throw new ArgumentNullException("context"); } this.Request = new RecordedRequest(context.Request, decoderHandling); this.Response = new RecordedResponse(context.Response, decoderHandling); this.ReceivedDate = context.ReceivedDate; this.UniqueIdentifier = context.UniqueIdentifier; }
/// <summary> /// Processes an incoming HTTP request. /// </summary> /// <param name="context">The <see cref="IStumpsHttpContext" /> representing both the incoming request and the response.</param> /// <returns> /// A member of the <see cref="ProcessHandlerResult" /> enumeration. /// </returns> /// <exception cref="ArgumentNullException"><paramref name="context"/> is <c>null</c>.</exception> public async Task <ProcessHandlerResult> ProcessRequest(IStumpsHttpContext context) { context = context ?? throw new ArgumentNullException(nameof(context)); context.Response.Headers.Clear(); context.Response.ClearBody(); context.Response.StatusCode = _statusCode; context.Response.StatusDescription = _statusCodeDescription; if (context.Response is StumpsHttpResponse stumpsResponse) { stumpsResponse.Origin = _origin; } this.ContextProcessed?.Invoke(this, new StumpsContextEventArgs(context)); return(await Task.FromResult <ProcessHandlerResult>(ProcessHandlerResult.Terminate)); }
/// <summary> /// Initializes a new instance of the <see cref="StumpsContextEventArgs" /> class. /// </summary> /// <param name="context">The <see cref="IStumpsHttpContext" /> associated with the event.</param> internal StumpsContextEventArgs(IStumpsHttpContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (context.Response is StumpsHttpResponse stumpsResponse) { this.ResponseOrigin = stumpsResponse.Origin; this.StumpId = stumpsResponse.StumpId; } else { this.ResponseOrigin = HttpResponseOrigin.Unprocessed; this.StumpId = null; } this.Context = context; }
/// <summary> /// Initializes a new instance of the <see cref="T:Stumps.StumpsContextEventArgs" /> class. /// </summary> /// <param name="context">The <see cref="T:Stumps.IStumpsHttpContext" /> associated with the event.</param> internal StumpsContextEventArgs(IStumpsHttpContext context) { if (context == null) { throw new ArgumentNullException("context"); } var stumpsResponse = context.Response as StumpsHttpResponse; if (stumpsResponse != null) { this.ResponseOrigin = stumpsResponse.Origin; this.StumpId = stumpsResponse.StumpId; } else { this.ResponseOrigin = HttpResponseOrigin.Unprocessed; this.StumpId = null; } this.Context = context; }
private void PopulateRemoteHeadersFromContext(IStumpsHttpContext incomingHttpContext, HttpWebRequest remoteWebRequest) { var headers = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase); foreach (var key in incomingHttpContext.Request.Headers.HeaderNames) { headers.Add(key, incomingHttpContext.Request.Headers[key]); } remoteWebRequest.Method = incomingHttpContext.Request.HttpMethod; remoteWebRequest.Accept = GetHeaderValue(headers, "accept", null); remoteWebRequest.ContentType = GetHeaderValue(headers, "content-type", string.Empty); remoteWebRequest.Referer = GetHeaderValue(headers, "referer", null); remoteWebRequest.TransferEncoding = GetHeaderValue(headers, "transfer-encoding", null); remoteWebRequest.UserAgent = GetHeaderValue(headers, "user-agent", null); ReservedHeaders.ForEach(header => headers.Remove(header)); if (headers.Count <= 0) { return; } foreach (var key in headers.Keys) { try { remoteWebRequest.Headers.Add(key, headers[key]); } catch (ArgumentException) { // The header could fail to add because it is being referenced // as a property - this is OK. // TODO: Log error } } }
/// <summary> /// Populates the response of the HTTP context from the Stump. /// </summary> /// <param name="incommingHttpContext">The incomming HTTP context.</param> /// <param name="stump">The <see cref="T:Stumps.Stump"/> used to populate the response.</param> private void PopulateResponse(IStumpsHttpContext incommingHttpContext, Stump stump) { // Write the status code information incommingHttpContext.Response.StatusCode = stump.Response.StatusCode; incommingHttpContext.Response.StatusDescription = stump.Response.StatusDescription; // Write the headers incommingHttpContext.Response.Headers.Clear(); stump.Response.Headers.CopyTo(incommingHttpContext.Response.Headers); // Write the body incommingHttpContext.Response.ClearBody(); if (stump.Response.BodyLength > 0) { var buffer = stump.Response.GetBody(); if (stump.Response.Headers["Content-Encoding"] != null) { var encoder = new ContentEncoder(stump.Response.Headers["Content-Encoding"]); buffer = encoder.Encode(buffer); } incommingHttpContext.Response.AppendToBody(buffer); } }
/// <summary> /// Adds the specified <see cref="T:Stumps.IStumpsHttpContext"/> to the collection. /// </summary> /// <param name="context">The <see cref="T:Stumps.IStumpsHttpContext"/> to add to the collection.</param> /// <exception cref="System.ArgumentNullException"><paramref name="context"/> is <c>null</c>.</exception> internal void Add(IStumpsHttpContext context) { if (context == null) { throw new ArgumentNullException("context"); } var recordedContext = new RecordedContext(context, ContentDecoderHandling.DecodeRequired); lock (_syncRoot) { _recordings.Add(recordedContext); } }
/// <summary> /// Processes an incoming HTTP request. /// </summary> /// <param name="context">The <see cref="T:Stumps.IStumpsHttpContext" /> representing both the incoming request and the response.</param> /// <returns> /// A member of the <see cref="T:Stumps.Http.ProcessHandlerResult" /> enumeration. /// </returns> /// <exception cref="System.ArgumentNullException"><paramref name="context"/> is <c>null</c>.</exception> public async Task<ProcessHandlerResult> ProcessRequest(IStumpsHttpContext context) { if (context == null) { throw new ArgumentNullException("context"); } var remoteUrl = BuildRemoteUrlFromContext(context); // Create a new HTTP web request var remoteWebRequest = (HttpWebRequest)WebRequest.Create(remoteUrl); remoteWebRequest.AllowAutoRedirect = false; // Populate the headers for the new HTTP request from the incoming HTTP request PopulateRemoteHeadersFromContext(context, remoteWebRequest); // Setup a repsponse used by the class HttpWebResponse response = null; // Populate the HTTP body for the request var continueProcess = PopulateRemoteBodyFromContext(context, remoteWebRequest, ref response); // Execute the remote web request if (continueProcess) { continueProcess = ExecuteRemoteWebRequest(remoteWebRequest, ref response); } if (!continueProcess) { // Return a response to the client that the service is unavailable at this time. context.Response.StatusCode = HttpStatusCodes.HttpServiceUnavailable; context.Response.StatusDescription = HttpStatusCodes.GetStatusDescription(HttpStatusCodes.HttpServiceUnavailable); } else if (response != null) { // Write the headers and the body of the response from the remote HTTP request // to the incoming HTTP context. WriteContextHeadersFromResponse(context, response); WriteContextBodyFromRemoteResponse(context, response); context.Response.StatusCode = (int)response.StatusCode; context.Response.StatusDescription = response.StatusDescription; var disposable = response as IDisposable; disposable.Dispose(); } var stumpsResponse = context.Response as StumpsHttpResponse; if (stumpsResponse != null) { stumpsResponse.Origin = HttpResponseOrigin.RemoteServer; } if (this.ContextProcessed != null) { this.ContextProcessed(this, new StumpsContextEventArgs(context)); } return ProcessHandlerResult.Continue; }
/// <summary> /// Writes the context headers from the remote response. /// </summary> /// <param name="incommingHttpContext">The incomming HTTP context.</param> /// <param name="remoteWebResponse">The remote web response.</param> private void WriteContextHeadersFromResponse( IStumpsHttpContext incommingHttpContext, HttpWebResponse remoteWebResponse) { incommingHttpContext.Response.Headers.Clear(); foreach (var headerName in remoteWebResponse.Headers.AllKeys) { incommingHttpContext.Response.Headers[headerName] = remoteWebResponse.Headers[headerName]; } }
/// <summary> /// Writes the context body from the remote response. /// </summary> /// <param name="incommingHttpContext">The incomming HTTP context.</param> /// <param name="remoteWebResponse">The remote web response.</param> private void WriteContextBodyFromRemoteResponse(IStumpsHttpContext incommingHttpContext, HttpWebResponse remoteWebResponse) { if (remoteWebResponse.ContentLength != 0) { var responseStream = remoteWebResponse.GetResponseStream(); incommingHttpContext.Response.ClearBody(); incommingHttpContext.Response.AppendToBody(StreamUtility.ConvertStreamToByteArray(responseStream)); } }
private void PopulateRemoteHeadersFromContext(IStumpsHttpContext incommingHttpContext, HttpWebRequest remoteWebRequest) { var headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); foreach (var key in incommingHttpContext.Request.Headers.HeaderNames) { headers.Add(key, incommingHttpContext.Request.Headers[key]); } remoteWebRequest.Method = incommingHttpContext.Request.HttpMethod; remoteWebRequest.Accept = GetHeaderValue(headers, "accept", null); remoteWebRequest.ContentType = GetHeaderValue( headers, "content-type", string.Empty); remoteWebRequest.Referer = GetHeaderValue(headers, "referer", null); remoteWebRequest.TransferEncoding = GetHeaderValue(headers, "transfer-encoding", null); remoteWebRequest.UserAgent = GetHeaderValue(headers, "user-agent", null); headers.Remove("accept"); headers.Remove("connection"); headers.Remove("content-length"); headers.Remove("content-type"); headers.Remove("expect"); headers.Remove("date"); headers.Remove("host"); headers.Remove("if-modified-since"); headers.Remove("range"); headers.Remove("referer"); headers.Remove("transfer-encoding"); headers.Remove("user-agent"); if (headers.Count <= 0) { return; } foreach (var key in headers.Keys) { try { remoteWebRequest.Headers.Add(key, headers[key]); } catch (Exception) { // The header could fail to add because it is being referenced // as a property - this is OK. // TODO: Log error } } }
/// <summary> /// Populates the remote body from context. /// </summary> /// <param name="incommingHttpContext">The incomming HTTP context.</param> /// <param name="remoteWebRequest">The remote web request.</param> /// <param name="remoteWebResponse">The remote web response.</param> /// <returns> /// <c>true</c> if the remote body was populated successfully; otherwise, <c>false</c>. /// </returns> private bool PopulateRemoteBodyFromContext( IStumpsHttpContext incommingHttpContext, HttpWebRequest remoteWebRequest, ref HttpWebResponse remoteWebResponse) { var success = true; try { if (incommingHttpContext.Request.BodyLength > 0) { remoteWebRequest.ContentLength = incommingHttpContext.Request.BodyLength; var requestStream = remoteWebRequest.GetRequestStream(); requestStream.Write(incommingHttpContext.Request.GetBody(), 0, incommingHttpContext.Request.BodyLength); } } catch (WebException wex) { if (wex.Response != null) { remoteWebResponse = (HttpWebResponse)wex.Response; } else { success = false; } } return success; }
/// <summary> /// Finds the Stump that matches an incomming HTTP request. /// </summary> /// <param name="context">The incoming HTTP request context.</param> /// <returns> /// A <see cref="T:Stumps.Stump"/> that matches the incomming HTTP request. /// </returns> /// <remarks> /// A <c>null</c> value is returned if a matching Stump is not found. /// </remarks> public Stump FindStumpForContext(IStumpsHttpContext context) { Stump foundStump = null; _lock.EnterReadLock(); foreach (var stump in _stumpList) { if (stump.IsMatch(context)) { foundStump = stump; break; } } _lock.ExitReadLock(); return foundStump; }
/// <summary> /// Processes an incoming HTTP request. /// </summary> /// <param name="context">The <see cref="T:Stumps.IStumpsHttpContext" /> representing both the incoming request and the response.</param> /// <returns> /// A member of the <see cref="T:Stumps.Http.ProcessHandlerResult" /> enumeration. /// </returns> /// <exception cref="System.ArgumentNullException"><paramref name="context"/> is <c>null</c>.</exception> public async Task<ProcessHandlerResult> ProcessRequest(IStumpsHttpContext context) { if (context == null) { throw new ArgumentNullException("context"); } // Early exit, if all Stumps are disabled if (!_handlerEnabled) { return ProcessHandlerResult.Continue; } var result = ProcessHandlerResult.Continue; var stump = _stumpsManager.FindStumpForContext(context); if (stump != null) { if (stump.ResponseDelay > StumpsHandler.MinimumResponseDelay) { var delay = stump.ResponseDelay; delay = delay < StumpsHandler.MaximumResponseDelay ? delay : StumpsHandler.MaximumResponseDelay; //TimerWait.Wait(delay); await Task.Delay(delay); } } if (stump != null && stump.TerminateConnection) { result = ProcessHandlerResult.DropConnection; } else if (stump != null && !stump.TerminateConnection) { PopulateResponse(context, stump); var stumpsResponse = context.Response as StumpsHttpResponse; if (stumpsResponse != null) { stumpsResponse.Origin = HttpResponseOrigin.Stump; stumpsResponse.StumpId = stump.StumpId; } if (this.ContextProcessed != null) { this.ContextProcessed(this, new StumpsContextEventArgs(context)); } result = ProcessHandlerResult.Terminate; } return result; }
/// <summary> /// Builds the remote URL from context. /// </summary> /// <param name="incommingHttpContext">The incomming HTTP context.</param> /// <returns> /// A string representing the URL for the remote server. /// </returns> private string BuildRemoteUrlFromContext(IStumpsHttpContext incommingHttpContext) { var urlPath = incommingHttpContext.Request.RawUrl; var urlHost = _externalHostUri.AbsoluteUri; urlPath = urlPath.StartsWith("/", StringComparison.Ordinal) ? urlPath.Remove(0, 1) : urlPath; var url = urlHost + urlPath; return url; }
/// <summary> /// Determines whether the context of the HTTP request is match for the current instance. /// </summary> /// <param name="context">The HTTP request.</param> /// <returns> /// <c>true</c> if the instance matches the HTTP request; otherwise, <c>false</c>. /// </returns> public bool IsMatch(IStumpsHttpContext context) { if (context == null || _response == null || _ruleList.Count == 0) { return false; } var match = true; foreach (var rule in _ruleList) { match &= rule.IsMatch(context.Request); if (!match) { break; } } return match; }