I have a self hosted service that needs to listen for upload notifications coming from a BITS server (they are a simple HTTP POST request with custom headers). If I was not self hosting my service and was using IIS I would just make a ASPX page and I could handle the incoming requests, but I am using self hosted WCF and I can not switch to IIS.
I looked in to using WebInvokeAttribute, however that appears to only be for sending JSON or XML as a reply and I need to follow the protocol spec. Also I did not see a way of pulling out the custom headers.
The next thing I looked in to was HttpListener and it appears to do what I need, however I did not see if there is a way to configure it via my app.config file like normal WCF endpoints.
Do I just add the address to my applicationSettings section or is there a better way to achieve what I am trying to do?
I ended up just using the Properties class and storing the url there.
//This is run on it's own thread
HttpListener listener = new HttpListener();
listener.Prefixes.Add(Properties.Settings.Default.BitsReplierAddress);
listener.Start();
while (_running)
{
// Note: The GetContext method blocks while waiting for a request.
// Could be done with BeginGetContext but I was having trouble
// cleanly shutting down
HttpListenerContext context = listener.GetContext();
HttpListenerRequest request = context.Request;
var requestUrl = request.Headers["BITS-Original-Request-URL"];
var requestDatafileName = request.Headers["BITS-Request-DataFile-Name"];
//(Snip...) Deal with the file that was uploaded
}
listener.Stop();
Related
I've written a REST API service in Delphi, which runs as its own stand-alone service. I also have IIS hosting some sites on port 80. I would like to use port 80 for my REST API as well, but since I'm already using it in IIS, I'm forced to use another port.
What I'd like to do to overcome this is create a very simple site in IIS (Preferably with ASP.NET/C#) which simply redirects all incoming HTTP requests to this other service running on a different port. This would allow me to take advantage of binding multiple sites under the same port in IIS. I don't want to perform a literal "redirect", but just replicate the request to the desired server, and respond back - as if user was connecting to original REST server. Only, without having to use a non-standard port. The URL on the client-side shouldn't change (as what you normally see with an HTTP Redirect).
Essentially, if user makes such a request:
http://api.mydomain.com/SomeReq?some=query
It will turn around and make a corresponding request to the real server, and carry over the entire URI - just a different port number:
http://api.mydomain.com:8664/SomeReq?some=query
How can I accomplish this?
Using ASP.NET Web API it is pretty simple to write such a proxy server. All you need is a delegating handler:
public class ProxyHandler : DelegatingHandler
{
private static HttpClient client = new HttpClient();
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
// strip the /proxy portion of the path when making the request
// to the backend node because our server will be made to listen
// to :80/proxy/* (see below when registering the /proxy route)
var forwardUri = new UriBuilder(request.RequestUri.AbsoluteUri.Replace("/proxy", string.Empty));
// replace the port from 80 to the backend target port
forwardUri.Port = 8664;
request.RequestUri = forwardUri.Uri;
if (request.Method == HttpMethod.Get)
{
request.Content = null;
}
// replace the Host header when making the request to the
// backend node
request.Headers.Host = "localhost:8664";
var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
return response;
}
}
and finally all that's left is to register this handler:
config.Routes.MapHttpRoute(
name: "Proxy",
routeTemplate: "proxy/{*path}",
handler: HttpClientFactory.CreatePipeline(
innerHandler: new HttpClientHandler(),
handlers: new DelegatingHandler[]
{
new ProxyHandler()
}
),
defaults: new { path = RouteParameter.Optional },
constraints: null
);
In this example the proxy will listen on :80/proxy/* and forward it to :8664/*.
So if you send the following request to your Web API:
GET http://localhost:80/proxy/SomeReq?some=query HTTP/1.1
Host: localhost:80
Connection: close
it will be translated to:
GET http://localhost:8664/SomeReq?some=query HTTP/1.1
Host: localhost:8664
Connection: close
This will also work for POST and other verbs made to :80/proxy/*.
Obviously if you want to turn your entire webserver into a proxy and listen to :80/* then you could get rid of the /proxy prefix that I used in my example.
This being said, this is only a proof-of-concept proxy server. In a real production system I would off-load this task to a full blown frontend load balancer such as nginx or HAProxy which are designed exactly for this purpose. Then both your IIS and Delphi application could listen on arbitrary ports and your nginx configured to listen to port 80 and forward the traffic to the backend node(s) based on some patterns. Using a load balancer also has other benefits as if you are having multiple backend nodes, it will distribute the load between them and also give you the possibility to make updates to your applications without any downtime (because you have full control over which node is in the load balancer pool).
This ServiceStack client code works:
var client = new JsonServiceClient("http://localhost:32949/test");
var request = new MyRequest { ClassificationId = new ClassificationId (21300) };
var response = client.Post(request);
However, when observing the traffic in Fiddler, I see nothing. I would like to observe the traffic to get a better idea on how to build the required JSON request for other clients I have to write.
To make the above code work, I had to reference the assembly that has the service, and I am suspecting that ServiceStack is making some clever calls to avoid sending a HTTP request. Is this the case ?
Why am I not seeing any traffic in Fiddler, and how do I force it ?
HTTP traffic to localhost endpoints via the browser is shown correctly.
Edit your hosts file, located at
C:\Windows\System32\drivers\etc\hosts
and add the following entry
127.0.0.1 mymachine.com
then point your client to mymachine.com instead of localhost
I will answer my own question here - commenter #wal pointed out the problem to me:
This has nothing to do with ServiceStack, and requests actually go over the http protocol. The problem was looping back to localhost did not send the traffic through fiddler. It is actually explained on the Fiddler2 FAQ page.
The other trick is to replace your "localhost" uri with your machine name, and that should work out of the box with Fiddler.
http://machinename:port/test
There is a web service that can only be consumed via http and a client that can only consume https web services. Therefore I need some intermediary that forwards on requests received in https and returns responses in http.
Supposing that the intermediary is completely dumb save for the fact that it knows where the web service endpoint is (i.e. it doesn't know what the signature of the service is, it just knows that it can communicate with it via an http web request, and it listens on some https uri, forwarding on anything it receives), what is the most simple way of achieving this?
I've been playing around with this all day and am not sure how to achieve the "dumb" bit, i.e. not knowing the signature for passing back the verbatim response.
A dumb intermediary is essentially a proxy. Your best bet might to be just use standard asp.net pages (instead of shoehorning into service functionality like ASMX or WCF which are just going to fight you) so you can receive the request exactly as-is and process it in a simple way using standard request/response. You can make use of HttpWebRequest class to forward the request on to the other endpoint.
Client requests https://myserver.com/forwarder.aspx?forwardUrl=http://3rdparty.com/api/login
myserver.com (your proxy) reads querystring forwardUrl and any POST or GET request included.
myserver.com requests to http://3rdparty.com/api/login and passes along GET or POST data sent from the client.
myserver.com takes response and sends back as response to other endpoint (essentially just Response.Write contents out to the response)
You would need to write forwarder.aspx to process the requests. Code for forwarder.aspx would be something like this (untested):
protected void Page_Load(object sender, EventArgs e)
{
var forwardUrl = Request.QueryString["forwardUrl"];
var post = new StreamReader(Request.InputStream).ReadToEnd();
var req = (HttpWebRequest) HttpWebRequest.Create(forwardUrl);
new StreamWriter(req.GetRequestStream()).Write(post);
var resp = (HttpWebResponse)req.GetResponse();
var result = new StreamReader(resp.GetResponseStream).ReadToEnd();
Response.Write(result); // send result back to caller
}
As part of learning node.js, I just created a very basic chat server with node.js and socket.io. The server basically adds everyone who visits the chat.html wep page into a real time chat and everything seems to be working!
Now, I'd like to have a C# desktop application take part in the chat (without using a web browser control :)).
What's the best way to go about this?
I created a socket server in nodejs, and connected to it using TcpClient.
using (var client = new TcpClient())
{
client.Connect(serverIp, port));
using (var w = new StreamWriter(client.GetStream()))
w.Write("Here comes the message");
}
Try using the HttpWebRequest class. It is pretty easy to use and doesn't have any dependencies on things like System.Web or any specific web browser. I use it simulating browser requests and analyzing responses in testing applications. It is flexible enough to allow you to set your own per request headers (in case you are working with a restful service, or some other service with expectations of specific headers). Additionally, it will follow redirects for you by default, but this behavior easy to turn off.
Creating a new request is simple:
HttpWebRequest my_request = (HttpWebRequest)WebRequest.Create("http://some.url/and/resource");
To submit the request:
HttpWebResponse my_response = my_request.GetResponse();
Now you can make sure you got the right status code, look at response headers, and you have access to the response body through a stream object. In order to do things like add post data (like HTML form data) to the request, you just write a UTF8 encoded string to the request object's stream.
This library should be pretty easy to include into any WinForms or WPF application. The docs on MSDN are pretty good.
One gotcha though, if the response isn't in the 200-402 range, HttpWebRequest throws an exception that you have to catch. Fortunately you can still access the response object, but it is kind of annoying that you have to handle it as an exception (especially since the exception is on the server side and not in your client code).
I need to access simultaniously multiple instances of a web services with the following Url. The web services is hosted in IIS and has SSL enabled.
https://services.mysite.com/data/data.asmx
Usually, when we do this process manually, we go one by one and update the Windows host file (c:\Windows\System32\drivers\etc\hosts) like this :
192.1.1.100 services.mysite.com
I would like to automate the process and do it with some multithreading. So I cannot change the Host file. Is there a way to simulate a host file when we do a HTTP request in C#?
Thanks!
If you know the IP address of the server's SSL endpoint (which isn't necessarily the same as the server's default IP address), then you could just aim you web-service at that? Obviously the SSL check will fail, but you can disable that through code...
ServicePointManager.ServerCertificateValidationCallback += delegate
{
return true; // you might want to check some of the certificate detials...
};
I think you get the same effect by setting the proxy server of that specific request to the IP address of the actual Web server you want to send the request to.
You can change the URL that your request is hitting at runtime, something like this:
svc.Url = "http://firstServer.com";
So if you create a program that loops through each of your desired servers, just update the URL property directly (that example is taken from WSE 3 based web services).