Reputation: 51
I'm trying to come up with the most appropriate way to make a two way socket connection through a HTTP proxy - lets say it's a telnet style protocol. Unfortunately I also need to support NTLM authentication (with the proxy) as well as Basic and Digest, in addition to any other future authentication mechanisms that I can't forecast.
If it was just basic and digest I'd handle the connection myself, but I really don't want to get stuck in the mire that is NTLM. Looking at the underlying AuthenticationManager API it looks very tied to HttpWebRequest so I can't leverage that functionality if I'm using a socket/tcpclient/whatever or even writing a new WebRequest derivation.
Playing around with HttpWebResponse yields a stream that can't be written, using the RequestStream after the response stream has been retrieved gives a concurrent io exception.
Having run through all the possibilities I can think of, I've come up with some nasty code that gets out the NetworkStream associated with a HttpWebRequest which allows two way communication:
.....
HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
Stream str = resp.GetResponseStream();
System.Type type = str.GetType();
PropertyInfo info = type.GetProperty("Connection", BindingFlags.NonPublic|BindingFlags.Instance| BindingFlags.Public);
object obj = info.GetValue(str, null);
type = obj.GetType();
info = type.GetProperty("NetworkStream", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);
object obj2 = info.GetValue(obj, null);
NetworkStream networkStream = obj2 as NetworkStream;
Which I'm fairly repulsed by (it won't work with Mono for a start), so I'm wondering if there's a better way using public APIs which will allow me to leverage the built in runtime functionality of proxy authentication.
Upvotes: 5
Views: 4478
Reputation: 2957
https://www.replicator.org/journal/201011170043-net-connecting-a-tcpclient-through-an-http-proxy-with-authentication works for me using VS2010 and .net 4 as well as VS2019 and .net 4.7.
Here is the code:
static TcpClient connectViaHTTPProxy(
string targetHost,
int targetPort,
string httpProxyHost,
int httpProxyPort,
string proxyUserName,
string proxyPassword)
{
var uriBuilder = new UriBuilder
{
Scheme = Uri.UriSchemeHttp,
Host = httpProxyHost,
Port = httpProxyPort
};
var proxyUri = uriBuilder.Uri;
var request = WebRequest.Create(
"http://" + targetHost + ":" + targetPort);
var webProxy = new WebProxy(proxyUri);
request.Proxy = webProxy;
request.Method = "CONNECT";
var credentials = new NetworkCredential(
proxyUserName, proxyPassword);
webProxy.Credentials = credentials;
var response = request.GetResponse();
var responseStream = response.GetResponseStream();
Debug.Assert(responseStream != null);
const BindingFlags Flags = BindingFlags.NonPublic | BindingFlags.Instance;
var rsType = responseStream.GetType();
var connectionProperty = rsType.GetProperty("Connection", Flags);
var connection = connectionProperty.GetValue(responseStream, null);
var connectionType = connection.GetType();
var networkStreamProperty = connectionType.GetProperty("NetworkStream", Flags);
var networkStream = networkStreamProperty.GetValue(connection, null);
var nsType = networkStream.GetType();
var socketProperty = nsType.GetProperty("Socket", Flags);
var socket = (Socket)socketProperty.GetValue(networkStream, null);
return new TcpClient { Client = socket };
}
Upvotes: -1
Reputation: 20150
HTTP is two way. Clients can send a dataless request using HTTP GET (although even then data can be put in the URL or headers), or they can send data using HTTP POST, and the server gets to send a response with headers and data.
If when you say "two way" you were thinking something more like a simple TCP socket where client and server read and write at will, then sorry, but that's not what HTTP does. The client sends a request and the server sense a response. That's all. Technically, if you didn't have a client side API getting in the way, enforcing the intended constraints of HTTP, and you could cook up your own non-standard server, you could have multiple client<->server exchanges within a single HTTP request, but at that point it wouldn't really be HTTP anymore, it'd be a TCP connection with an HTTP like handshake, and your proxy might not even allow it.
That said, it sounds like you don't really need to write to the response stream at all, either you're quite confused and you just need to do a POST (see GetRequestStream), or you're just a little confused and you can just send a new request after you've processed the response. You can even reuse the same HttpWebRequest instance once you've called the .Close method on the WebResponse you got. And all this will happen on the same TCP socket (if your server and proxy support it).
Okay, I hope that all made sense. If it didn't answer your question one way or the other, just provide a little more detail about what you're trying to accomplish with regard to "two-way" communication. I understand that you have the constraint of going through an HTTP proxy w/ HTTP authentication requirements which limits things a lot.
Upvotes: 1