views:

1392

answers:

2

Ultimately, I want to block downloads in .NET WebBrowser control, effectively restricting it to displaying HTML, images, scripts and the like, but never, ever display a "Save as" dialog to the user.

The solution proposed in an answer to that question was to hook up a custom proxy server that checks the responses and filters out anything that would lead to a download prompt. How can I implement such a proxy in my .NET application? I don't want to install third-party software or libraries.

I found the HttpListener class, but I've got two problems with it:

  1. The listener requires a predefined set of prefixes that trigger it. I would, however, prefer not to hard-code host names or port numbers in my application, and keep it generic.
  2. I would have to implement the code that does the actual request myself - isn't there something that does this for me where I can simply tap into the line, examining the contents of the response and changing them as neccessary (like a request filter in J2EE does on the server side)?

Update

Okay, I guess I need to make this clearer: My .NET (rich client) application is being used in multiple projects which also have web-based applications. The .NET application includes a reusable, generic form with a WebBrowser control. Other developers use that form to integrate access to their web applications into the .NET application.

I want to block downloads, so I want to have my WebBrowser form intercept all traffic and ensure that it does not lead to a "Save as" dialog. Thus...

  • I do not know what the host names will be
  • The WebBrowser points to the real URLs, the user clicks regular links, triggers JavaScript... all as served by the web application
  • If the WebBrowser pointed to localhost (as proposed), I'd have to parse responses and rewrite all links so they point at localhost again, preserving the original URL. I don't want to go through that hassle.
+2  A: 

HttpListener should be fine but it's just wrapper around http.sys and this library is available only on Windows XP and higher.

HttpPrefixes

You only need one prefix http://127.0.0.1:8080/ because it's just for your local webcontrol. Alternatively, wildcards are supported like http://*:8080/ but there is no reason to use it in your case.

Jens Bannmann wrote:

The applications that are accessed are not on localhost, they can be anywhere. That's why I don't want to hard-code anything.

what do you mean by applications? you mean websites? this is something completely else, your special proxy server will listen for HttpListenerRequests on http://127.0.0.1:8080/, and therefore your webcontrol has to use proxy server http://127.0.0.1:8080/. It's still all within local machine at this point.

Converting between HttpListenerRequest/Response and HttpWebRequest/Response

Convert each incoming HttpListenerRequest into HttpWebRequest, ask for response and you'll get HttpWebResponse object, analyze it whether it's allowed response for your WebBrowser control and if it is, write it into HttpListnererResponse object otherwise write in something else (error status).

This is probably the easiest way to build your own proxy server on .NET

Jens Bannmann wrote:

Right, this conversion was the thing I wanted to avoid having to do. Or can I do that in only a few code lines? From looking at the API, it looks more complicated.

It's actually quite easy because http protocol is trivial. It has basically three parts.

  • Request line (it contains URL, http method and http version)
  • Headers (this is actually what makes API look so huge and important but in reality all those properties and methods are just thin layer over raw http headers. All you need to do is to copy all headers directly in generic way from HttpListenerRequest into HttpWebRequest. Both classes have generic Headers property for raw access)
  • Message body (just copy its content if there's any)

Whole conversion would look something like this:

HttpListenerRequest listenerRequest;

WebRequest webRequest = WebRequest.Create(listenerRequest.Url);
webRequest.Method = listenerRequest.HttpMethod;
webRequest.Headers.Add(listenerRequest.Headers);
byte[] body = new byte[listenerRequest.InputStream.Length];
listenerRequest.InputStream.Read(body, 0, body.Length);
webRequest.GetRequestStream().Write(body, 0, body.Length);

WebResponse webResponse = webRequest.GetResponse();

If you need more assistence about http protocol, refer to this wikipedia article.

You can also check this open source project for another way of doing it. It doesn't depend on HttpListener class but it's complete proxy server solution and it should be easy to modify for your needs.

lubos hasko
1. The applications that are accessed are not on `localhost`, they can be anywhere. That's why I don't want to hard-code anything.2. Right, this conversion was the thing I wanted to avoid having to do. Or can I do that in only a few code lines? From looking at the API, it looks more complicated.
Jens Bannmann
"what do you mean by applications? you mean websites?" Yeah, websites. When the form is displayed, the WebBrowser is pointed to an URL, say http://foo/app/. Then the user works with the application and clicks links, e.g. http://foo/app/x.do. How does localhost come into play here?
Jens Bannmann
your web-browser never contacts these websites directly, it always delegates request to your special proxy server running on localhost. I think you will have to start banging out some code to fully understand how it all fits together.
lubos hasko
Hi, using the conversion code posted, I always get an Exception:"The 'Host' header cannot be modified directly. Parameter name: name"
Matías
Not all headers can be set via Headers.Add(), they have their own properties.
Vladimir Dyuzhev
A: 

Perhaps an open source proxy?

http://www.mentalis.org/soft/projects/proxy/

Greg Ogle