中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

C# 使用Proxy代理請求資源的方法步驟

發布時間:2020-09-13 18:47:18 來源:腳本之家 閱讀:395 作者:BUTTERAPPLE 欄目:編程語言

前言

這是上周在開發 C# 中使用 Proxy 代理時開發的一些思考和實踐。主要需求是這樣的,用戶可以配置每次請求是否需要代理,用戶可以配置 HTTP代理,HTTPS代理和代理白名單。

還是太年輕

因為一直用的C# 網絡庫中的HttpWebRequest,所以自然而然先去找找看這個網絡庫有沒有封裝好我所需要的代理呀。果不其然,被我找到了。自從上次發現某些類對老版本不兼容后,每次在微軟官方文檔上找到都會翻到最后,查看一下支持的最低框架。

C# 使用Proxy代理請求資源的方法步驟

我需要的就是這個 Proxy 屬性,也就是說我最終在發送請求前,設置好這個 Proxy 屬性就可以了。先去看看 Proxy

The IWebProxy object to use to proxy the request. The default value is set by calling the Select property.

這樣的意思就是說我只要構造一個WebProxy,然后賦值給 HttpWebRequest.Proxy就可以了。

看到了WebProxy 的構造器,馬上鎖定了

C# 使用Proxy代理請求資源的方法步驟

因為我需要用戶傳的是 string ,所以直接這樣構造就可以了。然后就是測試了,主管大佬寫的 Node.jsProxy代理 o_o 先來測試測試

npm install o_o -g

o_o

這樣就啟動全局安裝并啟動了代理,在控制臺上可以看到監聽的是 8989 端口

C# 使用Proxy代理請求資源的方法步驟

 [Fact]
public void HttpProxy()
{
  var request = new DescribeAccessPointsRequest();
  client.SetHttpProxy("http://localhost:8989");

  var response = client.GetAcsResponse(request);
  Assert.NotNull(response.HttpResponse.Content);

  var expectValue = "HTTP/1.1 o_o";
  string actualValue;
  response.HttpResponse.Headers.TryGetValue("Via", out actualValue);
  Assert.Equal(expectValue, actualValue);
}

如果經過了代理,頭部會出現 "HTTP/1.1 o_o" 字段 ,經過FT測試,是成功的。

本來一切都沒有問題的,除了我自己想的比較簡單外,直到我 Code Review 了一下組里開發JAVA 的人實現這個功能的 Pull Request ,我才發現我還真的是想的太簡單!!!

開始重構

首先發現的一點是,我連Constructor都用錯了,用ILSpy反編譯了一下,發現WebProxy(string,bool,string[])所作的事。

C# 使用Proxy代理請求資源的方法步驟

// System.Net.WebProxy
private static Uri CreateProxyUri(string address)
{
  if (address == null)
  {
    return null;
  }
  if (address.IndexOf("://") == -1)
  {
    address = "http://" + address;
  }
  return new Uri(address);
}

即使傳進去的是string,最后也是構造成 Uri, 為什么會關注的這個呢?因為我發現有些Proxy地址是

http://username:password@localhost:8989 長這樣的,那么我如果直接以這種形式傳入到CreateProxy里面,它會自動給我分解,然后分Credentialproxy 傳入到網絡庫中嗎?接下來就是驗證的過程。

首先需要了解到的一個概念:Basic access authentication

In the context of an HTTP transaction, basic access authentication is a method for an HTTP user agent (e.g. a web browser) to provide a user name and password when making a request. In basic HTTP authentication, a request contains a header field of the form Authorization: Basic <credentials>, where credentials is the base64 encoding of id and password joined by a colon.

It is specified in RFC 7617 from 2015, which obsoletes RFC 2617 from 1999.

由于其不安全性,已在 RFC 中棄用了,轉而代之的是 TLS SSL 那些協議。

問題來了, HttpWebRequest 中支持Basic Authentication嗎?我們可以看到WebProxy中有一個構造方法最后一個參數是 ICredential 的

C# 使用Proxy代理請求資源的方法步驟

是的,就是它,知道前因后果和不足后,我繼續去重構 Http Proxy 的代碼:

originProxyUri = new Uri(proxy);
if (!String.IsNullOrEmpty(originProxyUri.UserInfo))
{
  authorization = Convert.ToBase64String(System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes(originProxyUri.UserInfo));
  finalProxyUri = new Uri(originProxyUri.Scheme + "://" + originProxyUri.Authority);
  var userInfoArray = originProxyUri.UserInfo.Split(':');
  credential = new NetworkCredential(userInfoArray[0], userInfoArray[1]);

  httpRequest.WebProxy = new WebProxy(finalProxyUri, false, noProxy, credential);
}

先拆分出 UserInfo CredentialUri信息,然后分別重新構造相應的類型傳入到 WebProxy 中。上面也有一個坑,我之前還想用正則把usernamepassword 分別提取出去了,沒想到 Uri 已經封裝好了,直接取里面的userinfo 信息。哈哈,省力了。

StackOverFlow上也有挺多關于如何傳入 CredentialProxy中,基本上用的也是這個方法,按理說這樣就完事了,直到我做了測試,我發現微軟這個Credential根本沒有起作用,如果是正確的話,會在 HEADER 中添加

Authorization: Basic <credentials> ,和上面那段測試代碼一樣,

[Fact]
public void HttpProxyWithCredential()
{
  DescribeAccessPointsRequest request = new DescribeAccessPointsRequest();
  client.SetHttpProxy("http://username:password@localhost:8989");
  var response = client.GetAcsResponse(request);

  var expectValue = "HTTP/1.1 o_o";
  string actualValue;
  response.HttpResponse.Headers.TryGetValue("Via", out actualValue);

  Assert.Equal(expectValue, actualValue);
  Assert.NotNull(response.HttpResponse.Content);
}

我去測試了發現,這個頭部里面根本沒有加這個 Authorization 屬性啊,尷尬了,是官方文檔坑還是我使用不正確呢,基于此,想到了之前 主管 開發的那個 Proxy 代理 o_o ,我又去找了一個驗證 basic-authnode.js 代理服務器 basic-auth

npm install basic-auth
var http = require('http')
var auth = require('basic-auth')
var compare = require('tsscmp')

// Create server
var server = http.createServer(function (req, res) {
 var credentials = auth(req)

 // Check credentials
 // The "check" function will typically be against your user store
 if (!credentials || !check(credentials.name, credentials.pass)) {
  res.statusCode = 401
  res.setHeader('WWW-Authenticate', 'Basic realm="example"')
  res.end('Access denied')
 } else {
  res.end('Access granted')
 }
})

// Basic function to validate credentials for example
function check (name, pass) {
 var valid = true

 // Simple method to prevent short-circut and use timing-safe compare
 valid = compare(name, 'john') && valid
 valid = compare(pass, 'secret') && valid

 return valid
}

// Listen
server.listen(3000)

將上面那段 Js代碼打包成一個 js文件,然后執行

node tets.js

該代理服務器監聽 3000端口,我使用剛才那段代碼,果不其然,返回的是 401 ,這不是坑嗎,官方文檔上這樣說可以,然而都不行。

最后只能強制加上這個 Authorization 代碼

originProxyUri = new Uri(proxy);
if (!String.IsNullOrEmpty(originProxyUri.UserInfo))
{
  authorization = Convert.ToBase64String(System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes(originProxyUri.UserInfo));
  finalProxyUri = new Uri(originProxyUri.Scheme + "://" + originProxyUri.Authority);
  var userInfoArray = originProxyUri.UserInfo.Split(':');
  credential = new NetworkCredential(userInfoArray[0], userInfoArray[1]);

  httpRequest.WebProxy = new WebProxy(finalProxyUri, false, noProxy, credential);
  httpRequest.Headers.Add("Authorization", "Basic " + authorization);          
}

最后在測試經過 3000 端口的代理服務器,確認是沒問題的,把問題想得簡單的結果就是發了一個新版本后,還沒有下載,然而已經發了新版本說,用戶您好,我們又有新版本了。尷尬。需要以此為鑒啊。

后記

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

江津市| 岗巴县| 渝北区| 中超| 墨玉县| 和平区| 仁布县| 陈巴尔虎旗| 荔波县| 桂东县| 罗田县| 和平区| 郯城县| 琼中| 荣成市| 娱乐| 商都县| 汨罗市| 中西区| 普陀区| 临泉县| 信阳市| 梁平县| 习水县| 吴忠市| 嘉义县| 永寿县| 贵州省| 常德市| 天长市| 尤溪县| 土默特左旗| 乐亭县| 哈密市| 彝良县| 铅山县| 苏尼特左旗| 曲阳县| 南江县| 沙河市| 松阳县|