The requirement
The organization have a proxy and it requires authentication.
A third party software supports proxy, but does not support proxy authentication.
So we want to write a small proxy program that delegates the requests from the third party software to the organization proxy.
(We use OWIN self hosting for this service. the project is a console application)
The class ProxyHandler
using System;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
...
public class ProxyHandler : DelegatingHandler
{
public static IWebProxy Proxy { get; set; }
public ProxyHandler()
{
}
protected override async Task<HttpResponseMessage>
SendAsync(HttpRequestMessage req, CancellationToken ct)
{
var fwd = new UriBuilder(req.RequestUri);
fwd.Port = 443; //The software uses https only;
//in the test below this is not even called so does not matter
req.RequestUri = fwd.Uri;
var h = new HttpClientHandler();
h.UseCookies = true;
h.AllowAutoRedirect = true;
h.Proxy = Proxy;
var c = new HttpClient(h);
var resp = await c.SendAsync(req, HttpCompletionOption.ResponseHeadersRead);
return resp;
}
}
The class Startup
using Owin;
using System.Net.Http;
using System.Web.Http;
...
class Startup
{
public void Configuration(IAppBuilder b)
{
var cfg = new HttpConfiguration();
cfg.Routes.MapHttpRoute(
"Proxy",
"{*path}",
new {path=RouteParameter.Optional},
null,
HttpClientFactory.CreatePipeline(
new HttpClientHandler(),
new DelegatingHandler[] { }
));
b.UseWebApi(cfg);
}
}
The code that starts the proxy service and test with a download
using Microsoft.Owin.Hosting;
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
...
var p = new WebProxy(proxy_uri, true)
{
Credentials = new NetworkCredential(domain_user, password)
};
ProxyHandler.Proxy = p;
var app = WebApp.Start<Startup>("http://localhost:8181/");
using (app)
{
var wc = new WebClient();
wc.Proxy = new WebProxy("localhost:8181", true);
var downloaded = await wc.DownloadStringTaskAsync
(new Uri("http://example.com/));
Console.WriteLine(downloaded);
}
The result
The call to wc.DownloadStringTaskAsync throws HTTP 400 error. The SendAsync method was not called at all.
The question
How can I make it work?
Related
I created a HttpSelfHostServer service hosting controller for communication in web API
problem is that I want the ability to view images
ex: http://localhost:8080/images/pic.jpg
but the self host doesn't allow me to do this
it use to be IAppBuilder.UseFileServer but it's different with HttpSelfHostServer
here's the server code
using Autofac;
using Autofac.Integration.WebApi;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Formatting;
using System.Text;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.SelfHost;
namespace WebAPIServerV2
{
public class WebAPIServer
{
private HttpSelfHostServer m_Server;
private ILifetimeScope m_lifetimeScope;
public WebAPIServer(ILifetimeScope lifetimeScope)
{
m_lifetimeScope = lifetimeScope;
}
public void Start(string url)
{
if (m_Server != null)
{
Stop();
}
var config = new HttpSelfHostConfiguration(url);
config.DependencyResolver = new AutofacWebApiDependencyResolver(m_lifetimeScope);
config.MaxReceivedMessageSize = int.MaxValue;
config.Routes.MapHttpRoute(
name: "ServerAPI",
routeTemplate: "server/{controller}/{action}"
);
config.Formatters.Clear();
config.Formatters.Add(new JsonMediaTypeFormatter());
config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new StringEnumConverter());
config.Formatters.JsonFormatter.SerializerSettings =
new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
m_Server = new HttpSelfHostServer(config);
m_Server.OpenAsync().Wait();
if (Environment.UserInteractive)
Console.WriteLine($"WebAPI server start at:{url}");
}
public void Stop()
{
m_Server?.Dispose();
m_Server = null;
}
}
}
well I found it!
https://social.msdn.microsoft.com/Forums/en-US/ed7e5248-69e2-4644-aa02-8da7d13c9765/hosting-a-file-with-httpselfhostserverhttpselfhostconfiguration?forum=aspwebapi
using System;
using System.IO;
using System.Net.Http;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
namespace WebAPIServer
{
internal class StaticFileHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request.RequestUri.AbsolutePath.StartsWith("/server/"))
return base.SendAsync(request, cancellationToken);
var path = Environment.CurrentDirectory;
var newUri = new Uri(path);
var newPath = newUri.AbsolutePath + request.RequestUri.AbsolutePath;
newUri = new Uri(newPath);
if (!File.Exists(newUri.LocalPath))
{
return base.SendAsync(request, cancellationToken);
}
else
{
return Task<HttpResponseMessage>.Factory.StartNew(() =>
{
var response = request.CreateResponse();
response.Content = new StreamContent(new FileStream(newUri.LocalPath, FileMode.Open));
return response;
});
}
}
}
}
ofcourse server is the prefix for the API so it was easy
I am upgrading my C# function app from .net 3.1 to 6.0`.
When I run my test cases, I found that, 1 of my test case failed with the below error.
Castle.DynamicProxy.InvalidProxyConstructorArgumentsException : Can not instantiate proxy of class: System.Net.HttpWebRequest. Could not find a parameterless constructor.
Basically, I am trying to mock HttpWebRequest and below is my piece of code for that.
var httpWebRequest = new Mock<HttpWebRequest>();
It is working fine in .Net 3.1. I am using Moq version 4.16.1 in both the projects.
I spent a fair bit of time when .Net 6 was initially released getting my Unit Test suite established. Here's how I do it using the same Moq version 4.16.1:
The Unit Tests get a Moq HttpClientFactory from the BaseClass:
public class UnitTests : BaseUnitTest
{
[Fact]
public async Task Should_Return_GetSomethingAsync()
{
// Arrange
IHttpClientFactory httpClientFactory = base.GetHttpClientFactory(new Uri("ExternalWebsiteUrlToMockTheResponse"), new StringContent("A Mock Response JSON Object"));
YourService yourService = new YourService(httpClientFactory);
// Act
Something something = yourService.GetSomethingAsync().Result;
// Assert
Assert.IsType<Something>(Something);
//..
}
In a BaseUnitTest.cs Class:
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Moq;
using Moq.Protected;
public class BaseUnitTest
{
public IHttpClientFactory GetHttpClientFactory(Uri uri, StringContent content, HttpStatusCode statusCode = HttpStatusCode.OK)
{
Mock<HttpMessageHandler> httpMsgHandler = new Mock<HttpMessageHandler>();
httpMsgHandler.Protected().Setup<Task<HttpResponseMessage>>("SendAsync", new object[2]
{
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>()
}).ReturnsAsync(new HttpResponseMessage
{
StatusCode = statusCode,
Content = content
});
HttpClient client = new HttpClient(httpMsgHandler.Object);
client.BaseAddress = uri;
Mock<IHttpClientFactory> clientFactory = new Mock<IHttpClientFactory>();
clientFactory.Setup((IHttpClientFactory cf) => cf.CreateClient(It.IsAny<string>())).Returns(client);
return clientFactory.Object;
}
Your Service Class or Controller:
public class YourService : IYourService
{
private readonly IHttpClientFactory _clientFactory;
private readonly HttpClient _client;
public YourService(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
_client = _clientFactory.CreateClient("YourAPI");
}
public async Task<Something> GetSomethingAsync()
{
using (var request = new HttpRequestMessage(HttpMethod.Post, _client.BaseAddress))
{
request.Content = new StringContent($#"{{""jsonrpc"":""2.0"",""method"":""Something"",""params"": [""{SomethingHash}""],""id"":1}}");
using (var response = await _client.SendAsync(request))
{
//System.Diagnostics.Debug.WriteLine(response?.Content.ReadAsStringAsync()?.Result);
if (response.IsSuccessStatusCode)
{
using (var responseStream = await response.Content.ReadAsStreamAsync())
{
var options = new JsonSerializerOptions { IncludeFields = true };
var something = await JsonSerializer.DeserializeAsync<Something>(responseStream, options);
// Check if the transactions from the address we're looking for...
if (something != null)
{
if (something.result?.from == address)
{
return something;
}
} } }
else {
string exceptionMsg = $"Message: {response.Content?.ReadAsStringAsync()?.Result}";
throw new YourGeneralException(response.StatusCode, exceptionMsg);
}
}
}
return null;
}
}
In your Program.cs
builder.Services.AddHttpClient("YourAPI", c =>
{
c.BaseAddress = new Uri("ExternalWebsiteUrlToMockTheResponse");
c.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
c.DefaultRequestHeaders.UserAgent.TryParseAdd("Your Agent");
});
You can expand the BaseUnitTest.ccs class to have chained tests as well.
Both HttpWebRequest constructors are obsolete and should not be used. You have to use the static function "Create" to create a new instance of the HttpWebRequest class:
HttpWebRequest myReq =
(HttpWebRequest)WebRequest.Create("http://www.contoso.com/");
To solve your issue, use the HttpClient class instead. This class has a parameterless constructor.
Background: I am using ASP.NET Core 3.1, and integration testing a REST service that requires cookie authentication.
Candidate solution below.
Note:
The reason I use a vanilla Host instead of TestServer is because of the cookie requirement. When using TestServer, it provides an HttpClient for you, but the client does not pass cookies back to the server.
I also attempted to use a custom HttpClient with TestServer. That consistently generated a System.Net.Sockets.SocketException (No connection could be made because the target machine actively refused it.)
using Microsoft.Extensions.Hosting;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Net;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using WebApi; // Contains my Startup.cs
namespace WebApiTest
{
[TestClass]
public class UserTest
{
static IHost HttpHost;
[ClassInitialize]
public static async Task ClassStartup(TestContext context)
{
HttpHost = Host.CreateDefaultBuilder()
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.Build();
await HttpHost.StartAsync();
}
[ClassCleanup]
public static async Task ClassCleanup()
{
await HttpHost.StopAsync();
}
public static HttpContent GetHttpContent(object content)
{
HttpContent httpContent = null;
if (content != null)
{
httpContent = new ByteArrayContent(JsonSerializer.SerializeToUtf8Bytes(content, content.GetType()));
httpContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
}
return httpContent;
}
public static HttpClient GetCookieHttpClient()
{
SocketsHttpHandler handler = new SocketsHttpHandler
{
AllowAutoRedirect = false,
CookieContainer = new CookieContainer(),
UseCookies = true
};
return new HttpClient(handler);
}
[TestMethod]
public async Task GetUserData_ReturnsSuccess()
{
using (HttpClient client = GetCookieHttpClient())
{
var credentials = new
{
Email = "test#test.com",
Password = "password123",
};
HttpResponseMessage response = await client.PostAsync("http://localhost:5000/api/auth/login", GetHttpContent(credentials));
response = await client.GetAsync(String.Format("http://localhost:5000/api/users/{0}", credentials.Email));
Assert.IsTrue(response.StatusCode == HttpStatusCode.OK);
}
}
}
}
HttpClient is a thin-client; it doesn't do anything unless you explicitly tell it to. In other words, it will never send the cookie for you; you must add a Cookie header to the request with the cookie value for each request. The test server "client" is just an HttpClient instance set up to proxy requests to the test server. You should use the test server, as prescribed, along with its client, and then add the Cookie header the requests you make with that.
Solutions based on Chris Pratt's suggestions
After some further digging, Microsoft provides a solution for this (WebApplicationFactory):
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Net;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using WebApi;
namespace WebApiTest
{
[TestClass]
public class Class2
{
static WebApplicationFactory<Startup> Factory;
static WebApplicationFactoryClientOptions ClientOptions;
[ClassInitialize]
public static async Task ClassStartup(TestContext context)
{
Factory = new WebApplicationFactory<Startup>();
ClientOptions = new WebApplicationFactoryClientOptions();
ClientOptions.AllowAutoRedirect = false;
ClientOptions.HandleCookies = true;
ClientOptions.BaseAddress = new Uri("http://localhost:5000");
}
public static HttpContent GetHttpContent(object content)
{
HttpContent httpContent = null;
if (content != null)
{
httpContent = new ByteArrayContent(JsonSerializer.SerializeToUtf8Bytes(content, content.GetType()));
httpContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
}
return httpContent;
}
[TestMethod]
public async Task GetUserData_ReturnsSuccess()
{
using (HttpClient client = Factory.CreateClient(ClientOptions))
{
var credentials = new
{
Email = "test#test.com",
Password = "password123",
};
HttpResponseMessage response = await client.PostAsync("http://localhost:5000/api/auth/login", GetHttpContent(credentials));
response = await client.GetAsync(String.Format("http://localhost:5000/api/users/{0}", credentials.Email));
Assert.IsTrue(response.StatusCode == HttpStatusCode.OK);
}
}
}
}
In case you want to stick with TestServer, here is a manual Cookie-passing implementation:
using Microsoft.AspNetCore.TestHost;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using WebApi;
namespace WebApiTest
{
public class CookieHttpClient : IDisposable
{
private static HttpContent GetHttpContent(object content)
{
HttpContent httpContent = new ByteArrayContent(JsonSerializer.SerializeToUtf8Bytes(content, content.GetType()));
httpContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
return httpContent;
}
private static IEnumerable<string> GetCookieStrings(CookieCollection collection)
{
List<string> output = new List<string>(collection.Count);
foreach (Cookie cookie in collection)
{
output.Add(cookie.Name + "=" + cookie.Value);
}
return output;
}
private HttpClient client;
private CookieContainer container;
public CookieHttpClient(HttpClient client)
{
this.client = client;
this.container = new CookieContainer();
}
public async Task<HttpResponseMessage> SendAsync(HttpMethod method, Uri uri)
{
return await this.SendAsync(method, uri, null);
}
public async Task<HttpResponseMessage> SendAsync(HttpMethod method, Uri uri, object data)
{
HttpRequestMessage request = new HttpRequestMessage(method, uri);
// Add data
if (data != null)
{
request.Content = GetHttpContent(data);
}
// Add cookies
CookieCollection collection = this.container.GetCookies(uri);
if (collection.Count > 0)
{
request.Headers.Add("Cookie", GetCookieStrings(collection));
}
HttpResponseMessage response = await this.client.SendAsync(request);
// Remember cookies before returning
if (response.Headers.Contains("Set-Cookie"))
{
foreach (string s in response.Headers.GetValues("Set-Cookie"))
{
this.container.SetCookies(uri, s);
}
}
return response;
}
public void Dispose()
{
this.client.Dispose();
}
}
[TestClass]
public class Class1
{
static TestServer TestServer;
[ClassInitialize]
public static async Task ClassStartup(TestContext context)
{
IWebHostBuilder builder = new WebHostBuilder()
.UseStartup<Startup>();
TestServer = new TestServer(builder);
}
[TestMethod]
public async Task GetUserData_ReturnsSuccess()
{
using (CookieHttpClient client = new CookieHttpClient(TestServer.CreateClient()))
{
var credentials = new
{
Email = "test#test.com",
Password = "password123",
};
HttpResponseMessage response = await client.SendAsync(HttpMethod.Post, new Uri("http://localhost:5000/api/auth/login"), credentials);
response = await client.SendAsync(HttpMethod.Get, new Uri("http://localhost:5000/api/users/" + credentials.Email));
Assert.IsTrue(response.StatusCode == HttpStatusCode.OK);
}
}
}
}
I am trying to rework my Azure API client to use singleton HttpClient for ServiceClient<T>, since multiple sources suggested to do that (for performance, and it is also suggested approach for HttpClient which is used behind the scenes for Microsoft.Rest.ServiceClient "in general")
I am using Microsoft.Rest.ClientRuntime.2.3.12 version
I see that Microsoft.Rest.ServiceClient has constructor for reusing HttpClient
protected ServiceClient(HttpClient httpClient, bool disposeHttpClient = true);
This is constructor for my API client in order to reuse HttpClient, but when it is called it fails with System.MissingMethodException: Void Microsoft.Rest.ServiceClient`1..ctor(System.Net.Http.HttpClient, Boolean)
public MyAPIclient(ServiceClientCredentials credentials, HttpClient client)
: base(client, disposeHttpClient: false)
{
this._baseUri = new Uri("https://...");
if (credentials == null)
{
throw new ArgumentNullException("credentials");
}
this.Credentials = credentials;
Credentials?.InitializeServiceClient(this);
}
This is how I call API client
using (var apiClient = new MyAPIclient(credentials, HttpClientSingleton.Client))
//I get Method not found 'Void Microsoft.Rest.ServiceClient`1..ctor(System.Net.Http.HttpClient, Boolean)'
{
//some api call
}
This is how I instantiate my http client singleton
public static class HttpClientSingleton
{
public static readonly HttpClient Client = new HttpClient() { Timeout = TimeSpan.FromMinutes(30) };
}
Why do I get this exception (I can see that ServiceClient has ctor(httpClient, bool))?
How to fix this problem?
Like Nkosi said, you should try restoring the nuget packages, clearing your obj/bin and rebuilding. This usually happens when things get out of step. With all the shadow caching etc etc things can end up mismatched. I created a small example based on your code and there weren't any issues with it. If you find that still doesn't work you should include more info on your file, like the class declarations and your using statements, and what version of .net you are using.
The below works for me for 4.5.2 ServiceClient 2.3.12
using Microsoft.Rest;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
using (var apiClient = new MyAPIclient(null, HttpClientSingleton.Client))
//I get Method not found 'Void Microsoft.Rest.ServiceClient`1..ctor(System.Net.Http.HttpClient, Boolean)'
{
var httpClient = apiClient.HttpClient;
}
}
public class MyAPIclient : ServiceClient<MyAPIclient>
{
protected Uri _baseUri { get; set; }
protected ServiceClientCredentials Credentials { get; set; }
public MyAPIclient(ServiceClientCredentials credentials, HttpClient client) : base(client, disposeHttpClient: false)
{
this._baseUri = new Uri("https://stackoverflow.com");
this.Credentials = credentials;
if(credentials != null)
Credentials?.InitializeServiceClient(this);
}
}
public static class HttpClientSingleton
{
public static readonly HttpClient Client = new HttpClient() { Timeout = TimeSpan.FromMinutes(30) };
}
}
}
I have a long-running WCF service hosted in a Windows service. I have a service library whose purpose is to report on the state of the service. How can I get to the instance of the service from inside an instance of the service library?
To illustrate, I created a service that records the time it started, and exposes a method to report how long it's been running. The service library needs to be able to call the service's ElapsedSeconds() method, so the the library needs a reference to the running service.
I thought I could use OperationContext.Current. Here's my service library class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;
namespace TimerService
{
public class TimerServiceLib : ITimerServiceLib
{
TheTimerService m_timerService;
public TimerServiceLib()
{
var currentContext = OperationContext.Current;
var instanceContext = currentContext.InstanceContext;
m_timerService = (TheTimerService)instanceContext.GetServiceInstance();
}
public double SecondsSinceStart()
{
return m_timerService.ElapsedSeconds();
}
}
}
But the call to GetServiceInstance() creates a new instance of TimerServiceLib(), which of course gives me an infinite loop. So, what is the correct way to do this?
Here is my service class, actually being hosted in a console application:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
namespace TimerService
{
public partial class TheTimerService : ServiceBase
{
private DateTime m_startTime;
ServiceHost m_svcHost;
public TheTimerService()
{
InitializeComponent();
Init();
m_startTime = DateTime.Now;
}
public double ElapsedSeconds()
{
return (DateTime.Now - m_startTime).TotalSeconds;
}
protected override void OnStart(string[] args)
{
}
protected override void OnStop()
{
}
public void Init()
{
if (m_svcHost != null)
{
m_svcHost.Close();
}
string httpAddress = "http://localhost:1234/TimerService";
string tcpAddress = "net.tcp://localhost:1235/TimerService";
Uri[] adrbase = { new Uri(httpAddress), new Uri(tcpAddress) };
m_svcHost = new ServiceHost(typeof(TimerServiceLib), adrbase);
ServiceMetadataBehavior mBehave = new ServiceMetadataBehavior();
m_svcHost.Description.Behaviors.Add(mBehave);
var debugBehavior = m_svcHost.Description.Behaviors.Find<ServiceDebugBehavior>();
debugBehavior.IncludeExceptionDetailInFaults = true;
BasicHttpBinding httpBinding = new BasicHttpBinding();
m_svcHost.AddServiceEndpoint(typeof(ITimerServiceLib), httpBinding, httpAddress);
m_svcHost.AddServiceEndpoint(typeof(IMetadataExchange),
MetadataExchangeBindings.CreateMexHttpBinding(), "mex");
NetTcpBinding tcpBinding = new NetTcpBinding();
m_svcHost.AddServiceEndpoint(typeof(ITimerServiceLib), tcpBinding, tcpAddress);
m_svcHost.AddServiceEndpoint(typeof(IMetadataExchange),
MetadataExchangeBindings.CreateMexTcpBinding(), "mex");
m_svcHost.Open();
// SimShopServiceLib.m_shop = new CSimShopManager();
}
}
}
You can do it this way:
First, modify your TimerServiceLib and do the constructor injection of TheTimerService:
public class TimerServiceLib : ITimerServiceLib
{
private readonly TheTimerService m_timerService;
public TimerServiceLib(TheTimerService theTimerService)
{
m_timerService = theTimerService;
}
public double SecondsSinceStart()
{
return m_timerService.ElapsedSeconds();
}
}
Then, in your Init() upon creation of ServiceHost, instantiate first your service and pass the TheTimerService. Since your are creating the ServiceHost inside your windows service, wich is TheTimerService you can pass this.
Uri[] adrbase = { new Uri(httpAddress), new Uri(tcpAddress) };
var timerServiceLib = new TimerServiceLib(this)
m_svcHost = new ServiceHost(timerServiceLib , adrbase);
For more details about passing object in you service see this link.
Disclaimer : The above code is not tested.