I've developed a cross platform mobile app with Xamarin.Forms. I have local services that my app hits. My Xamarin app defines the HTTPClient as shown below. When testing on BrowserStack's App Live product I can not hit my local services from my app using the BrowserStack Local app (I receive a "No Such Host Is Known" response) . I can hit my services using the device's default browser.
HTTPClient setup in Xamarin App:
public static readonly HttpClient client = new HttpClient()
My local services have internal domain names (it's not simply 'localhost:443' but it actually has a domain name like 'customservice.com')
Is it possible for a Xamarin App to use the BrowserStack App Live product while using Local Testing? If so, how?
I found a solution that works for Android and should work for iOS (but doesn't). Maybe with some tinkering it could work for iOS too, but I thought I'd share what I found:
Step 1: Create a Dependency Service
This step is loosely based on this article. The article is incomplete because it does not create a dependency service. So that's fun
In the Shared Project create a IProxyInfoProvider interface as shown:
public interface IProxyInfoProvider
{
WebProxy GetProxySettings();
}
Create the platform implementations:
(Don't forget to add the assembly tag above your namespace to export the Dependency Service!)
iOS
public class ProxyInfoProvider : IProxyInfoProvider
{
public WebProxy GetProxySettings()
{
var systemProxySettings = CFNetwork.GetSystemProxySettings();
var proxyPort = systemProxySettings.HTTPPort;
var proxyHost = systemProxySettings.HTTPProxy;
Console.WriteLine("Proxy Port: " + proxyPort.ToString());
Console.WriteLine("Proxy Host: " + Convert.ToInt64(proxyHost));
return !string.IsNullOrEmpty(proxyHost) && proxyPort != 0
? new WebProxy(proxyHost, proxyPort)
: null;
}
}
Android
public class ProxyInfoProvider : IProxyInfoProvider
{
public WebProxy GetProxySettings()
{
var proxyHost = JavaSystem.GetProperty("http.proxyHost");
var proxyPort = JavaSystem.GetProperty("http.proxyPort");
Console.WriteLine("Proxy Host: " + proxyHost);
Console.WriteLine("Proxy Port: " + proxyPort);
return !string.IsNullOrEmpty(proxyHost) && !string.IsNullOrEmpty(proxyPort)
? new WebProxy($"{proxyHost}:{proxyPort}")
: null;
}
}
Step 2: Update your HttpClientHandler
We want to consume the WebProxy that is now being returned from the dependency service. Update your HttpClient handler so it looks something like this:
var _handler = new HttpClientHandler();
_handler.Proxy = DependencyService.Get<IProxyInfoProvider>().GetProxySettings();
Ensure that your HttpClient is consuming this Handler in it's constructor like: new HttpClient(_handler.Value)
Step 3: Upload your app to BrowserStack
Boom. Then Android works with BrowserStack local testing! Why doesn't it work on iOS you ask? Good question. I'm still trying to figure that part out...
What is also weird is that if you use a proxy to debug your app in your local environment, this solution works! But it stops working when you put your app in BrowserStack.
Since you’re able to access your internal domain via their device browser, it seems that your application is not able to route traffic via BrowserStack’s Local Testing infrastructure.
If that is the case, you can reach out to the BrowserStack Support team for further assistance.
Related
I'm investigating the idea of using Blazor WASM to build a retail application that would run on an office Intranet. The application would be installed on a given machine, to be accessed via browser from any of several machines on the LAN.
The biggest stumbling block I'm running into is the question of how to go about securing the channel.
The app itself would run as a Windows Service, listening on port 443 on one of the workstations, e.g. https://reception/. But how do we tell Blazor to use a self-signed TLS cert for that hostname?
If there's a better way to go about this, I'm all ears. I can't use Let's Encrypt certs, because neither the application nor its hostname will be exposed to the public Internet.
There is a glut of information on working with Blazor to build such an app, but most if not all demos run on localhost. That works fine for dev, but not for production (in a self-hosting scenario, anyway). There doesn't seem to be much discussion at all of this aspect of things.
How can we use a custom certificate for browser requests from the client to a Blazor WASM app?
Any ideas?
I was able to get this working using some slightly modified sample code from the official documentation:
builder.WebHost.ConfigureKestrel(serverOptions =>
{
serverOptions.ListenAnyIP(443, listenOptions =>
{
listenOptions.UseHttps(httpsOptions =>
{
var testCert = CertificateLoader.LoadFromStoreCert(
"test", "My", StoreLocation.CurrentUser,
allowInvalid: true);
var certs = new Dictionary<string, X509Certificate2>(
StringComparer.OrdinalIgnoreCase)
{
["test"] = testCert
};
httpsOptions.ServerCertificateSelector = (connectionContext, name) =>
{
if (name is not null && certs.TryGetValue(name, out var cert))
{
return cert;
}
return testCert;
};
});
});
});
The easiest way to handle SSL is to use IIS that will act as a proxy for your Blazor app.
IIS will give you easy access to well documented SSL settings.
https://learn.microsoft.com/en-us/aspnet/core/blazor/host-and-deploy/webassembly?view=aspnetcore-6.0#standalone-deployment
I have a simple task: A program (executable) is supposed to call a function of another program (also executable) with some parameters. Program A is supposed to be started, call the function and then terminate. Program B is legacy program that has a GUI and runs continuously. Both programs run on the same Windows PC and use the .NET Framework. I have no experience in web development and Program B is not supposed to run as a web service! Named pipes seem like a good option.
I researched what the best method would be and wanted to try WCF. The documentation claims that "A service endpoint can be part of a continuously available service hosted by IIS, or it can be a service hosted in an application". From that I understand that I can run Program B as a service without hosting a web server.
However everything I see in Visual Studio seems to presume I want to run a server. Wenn I want to create a new WCF project in Visual Studio the only options are a library or "A project for creating WCF service application that is hosted in IIS/WAS". Once I've created said project the debugger wants me to choose a browser for hosting the service.
In another StackOverflow topic a popular suggestion was using this website as a guide and simply removing the http references since the guide is for both named pipes and http. Another indication that it should be possible.
So can someone point me in the right direction? What am I missing? How can I use WCF with nothing related to Web Development involved?
You have already been on the way, it is enough to host the web service in Program B, without specifying a web server. this is called a self-hosted WCF. As the link you provided mentioned, the Service host class is used to host the WCF service, which means that we can host the service in the Console/Winform, and so on.
Here is an example of hosting the service in a Winform application.
public partial class Form1 : Form
{
ServiceHost serviceHost = null;
public Form1()
{
InitializeComponent();
Uri uri = new Uri("http://localhost:9009");
BasicHttpBinding binding = new BasicHttpBinding();
serviceHost = new ServiceHost(typeof(MyService), uri);
serviceHost.AddServiceEndpoint(typeof(IService), binding, "");
ServiceMetadataBehavior smb = new ServiceMetadataBehavior()
{
HttpGetEnabled = true
};
serviceHost.Description.Behaviors.Add(smb);
System.ServiceModel.Channels.Binding mexbinding = MetadataExchangeBindings.CreateMexHttpBinding();
serviceHost.AddServiceEndpoint(typeof(IMetadataExchange), mexbinding, "mex");
serviceHost.Open();
}
private void Form1_Load(object sender, EventArgs e)
{
if (serviceHost.State==CommunicationState.Opened)
{
this.label1.Text = "Service is running";
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (serviceHost.State==CommunicationState.Opened&&serviceHost.State!=CommunicationState.Closed)
{
serviceHost.Close();
}
}
}
[ServiceContract]
public interface IService
{
[OperationContract]
string Test();
}
public class MyService:IService
{
public string Test()
{
return DateTime.Now.ToLongTimeString();
}
}
After that, we could consume it by using a client proxy.
https://learn.microsoft.com/en-us/dotnet/framework/wcf/accessing-services-using-a-wcf-client
Feel free to let me know if there is anything I can help with.
I am using VS2019 Preview.
I have created a "server-hosted" Blazor application using the latest Blazor extension (16.0.19227). This is the variant that contains 3 separate projects...
MyApp.Client
MyApp.Server
MyApp.Shared
I can debug this by making MyApp.Server the active project and all works fine but I'm struggling to publish/deploy this to Azure. I have tried the following...
Right-click on MyApp.Server in Solution-Explorer
Choose "Publish"
Go through the wizard to create a new publish profile
Change the deployment mode to "self-contained"
Hit publish
At this point I get an error during deployment...
CSC(0,0): Error CS0006: Metadata file 'D:\work\Applications\Web\MyApp.Client\bin\Release\netstandard2.0\win-x86\MyApp.Client.dll'
could not be found
This appears to be because the "Target Runtime" in the web-deploy profile is set to win-x86. The client application is actually being built as
"D:\work\Applications\Web\MyApp.Client\bin\Release\netstandard2.0\MyApp.Client.dll"
(without the additional win-x86 subfolder) so the deployment process seems to be making an incorrect assumption about the paths used by the build process. There's no way in the publish dialog to specify a blank/don't care target runtime.
Is there a workaround for this or perhaps I am using the wrong approach for deployment?
There is some official documentation but it's not very helpful.
Update It seems that the deployment is using the output path of the Client project and then just appending netstandard2.0{Target Runtime} to it so changing the output path in the Client project is not enough to work around the issue.
Update 2 Removing the RuntimeIdentifier tag in the publish profile by editing the xml simply results in deploy-time error stating that an empty RuntimeIdentifier is incompatible with a self-contained deployment. Unfortunately the self-contained deployment is necessary because Azure does not yet host .net core 3 directly.
because Azure does not yet host .net core 3 directly.
But it does.
In the Azure Portal, go to your WebApp after deployment (or create one beforehand).
Go to Extensions and click Add [+] and select ASP.NET Core 3 (x86 for the free hosting).
Also go to Settings, General and enable WebSockets, they're Off by default.
Temporary:
Note that Preview-6 is not available as an extension, so either use Preview-5 or deploy as self-contained.
Couldnt put a picture in the comment, so I thought i'd show it here. This is my current publish wizard.
Just did it with a brand new project via new project -> Asp.net core web application -> blazor (Asp.net core hosted) built and published fine to azure app service fine.
My answer is:
Configure the publish profile to "Self-contain" deployment mode.
Edit all .csproj files to change <TargetFramework>...</TargetFramework> node name to <TargetFrameworks>...</TargetFrameworks>. (see also: https://stackoverflow.com/a/42855070 )
Fix the web root folder path string at runtime in Startup class like below.
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.ResponseCompression;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Hosting;
using Newtonsoft.Json.Serialization;
using System.IO;
using System.Linq;
namespace BlazorHostedOnAzure.Server
{
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().AddNewtonsoftJson();
services.AddResponseCompression(opts =>
{
opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
new[] { "application/octet-stream" });
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseResponseCompression();
// ---- APPEND PART.1 BEGIN ----
var clientBlazorWebRootPath = default(string);
// ---- APPEND PART.1 END ----
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBlazorDebugging();
}
// ---- APPEND PART.2 BEGIN ----
else
{
if (env.WebRootPath != null)
{
var pathOfIndex = Path.Combine(env.WebRootPath, "index.html");
var pathOfContent = Path.Combine(env.WebRootPath, "_content");
if (!File.Exists(pathOfIndex) && Directory.Exists(pathOfContent))
{
clientBlazorWebRootPath = Directory.GetDirectories(pathOfContent).FirstOrDefault();
if (clientBlazorWebRootPath != null)
{
env.WebRootPath = clientBlazorWebRootPath;
}
}
}
}
// ---- APPEND PART.2 END ----
app.UseClientSideBlazorFiles<Client.Startup>();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
endpoints.MapFallbackToClientSideBlazor<Client.Startup>("index.html");
});
// ---- APPEND PART.3 BEGIN ----
if (clientBlazorWebRootPath != null)
{
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(clientBlazorWebRootPath)
});
}
// ---- APPEND PART.3 BEGIN ----
}
}
}
I published my sample code and README on the GitHub my repository.
https://github.com/sample-by-jsakamoto/BlazorHostedV3Preview6OnAzureWebApp#how-to-configure-client-side-blazor-v300-preview-6-that-is-hosted-on-an-aspnet-core-server-to-deploy-it-to-azure-at-13-jul-2019
I'm building an app with Xamarin Forms. I have no issues with Android, but when I attempt to simulate or deploy the iOS app, my HttpClient can't seem to connect to the server. After the timeout expires, I get a TaskCanceledException.
The HttpClient is actually used in a separate project that is referenced by the iOS app, if that matters. Here's my usage:
string serviceUri = service + methodName;
HttpRequestMessage request = new HttpRequestMessage(methodRequestType, serviceUri)
{
Content = new StringContent(content, Encoding.UTF8, "application/json")
};
HttpResponseMessage response = await _client.SendAsync(request).ConfigureAwait(false);
string returnString = await response.Content.ReadAsStringAsync();
return returnString;
I'm not using any waits or accessing .Result, as I've seen many people try that don't understand async operations.
I've seen older posts (circa 2013) where the wrong HttpClient is used. Is this still an issue in current releases? I've also tried changing the HttpClient implementation in the iOS project settings, to no avail.
Thanks in advance!
The issue was an invalid Deployment Target - It was set to "80" instead of "8.0
In come cases, the build server does not communicate this invalidity back to your machine - hence why it took so long to notice it!
I am writing a bdd test for a component that will startup phantomjs and hit a specific route on my site and do processing on that. Because the component is fundamentally about automating a phantom instance there is no way to easily stub out the http requests.
So I want to stub out a self-hosted endpoint that will stub out the data I'm after. Because this is a unit test I think its really important for it to run in isolation so I do something like this:
async Task can_render_html_for_slide_async() {
var config = new HttpSelfHostConfiguration("http://localhost:54331");
config.Routes.MapHttpRoute("Controller", "{controller}", new {});
using (var server = new HttpSelfHostServer(config)) {
server.OpenAsync().Wait();
var client = new HttpClient();
var resp = await client.GetStringAsync(config.BaseAddress+"/Stub");
Console.WriteLine(resp);
}
}
public class StubController : ApiController
{
public string Get() {
return "Booyah";
}
}
Which gets me
AddressAccessDeniedException : HTTP could not register URL http://+:54331/
I understand that netsh or Admin mode is required for this but I don't understand why. Nodejs for example runs perfectly fine on windows but has no such requirement.
Also using OWIN directly needs no netsh-ing. So....what's going on?
I wrote an article about it on codeproject, it was done to make it possible for multiple application to share the same port.
You can have both, IIS and Apache (or OWIN in your case) listenening port 80. The routing to the right application is done thanks to the path of the url.
IIS and Apache both would use this driver (http.sys). But you need permission to "reserve" a path.
Administrators are always authorized. For other users, use netsh or my GUI tool HttpSysManager to set ACL.
Any method that requires giving permission via netsh uses a Windows kernel driver to provide http access.
If a library opens a socket itself and handles the http communication that way, no netsh use is needed.
So to answer your question, some methods are using the kernel driver and some are handling the protocol themselves.