国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Swift - 使用 Swift 搭建一個 HTTP 代理

使用 Swift 搭建一個 HTTP 代理

2021-12-14 22:05Swift社區Swift君 Swift

本文將使用Hummingbird[1]作為服務端的基本HTTP框架,以及使用AsyncHTTPClient作為Swift的HTTP客戶端來請求目標服務。

使用 Swift 搭建一個 HTTP 代理

前言

我將通過這篇文章詳述一下如何用Swift搭建一個HTTP代理服務器。本文將使用Hummingbird[1]作為服務端的基本HTTP框架,以及使用AsyncHTTPClient[2]作為Swift的HTTP客戶端來請求目標服務。

什么是代理服務器

代理服務器是一個搭載在客戶端和另一個服務端(后面我們成為目標服務端)的中間服務器,它從客戶端轉發消息到目標服務端,并且從目標服務端獲取響應信息傳回給客戶端。在轉發消息之前,它可以以某種方式處理這些消息,同樣,它也可以處理返回的響應。

讓我們試著構建一個

在本文中,我們將構建一個只將HTTP數據包轉發到目標服務的代理服務器。您可以在這里找到本文的示例代碼。

創建項目

我們使用Hummingbird模板項目[3] 目前最低版本適配 Swift5.5 作為我們服務的初始模板。讀者可以選擇clone這個存儲庫,或者直接點擊Github項目主頁上use this template按鈕來創建我們自己的存儲庫。用這個模板項目創建一個服務端并且啟動它,可以使用一些控制臺選項和文件來配置我們的應用。詳見here[4]

增加 AsyncHTTPClient

我們將把AsyncHTTPClient作為依賴加入Package.swift以便我們后面來使用

  1. dependencies: [
  2. ...
  3. .package(url: "https://github.com/swift-server/async-http-client.git", from: "1.6.0"),
  4. ],

然后在目標依賴也添加一下

  1. targets: [
  2. .executableTarget(name: "App",
  3. dependencies: [
  4. ...
  5. .product(name: "AsyncHTTPClient", package: "async-http-client"),
  6. ],

我們將把HTTPClient作為HBApplicatipn的擴展。這樣方便我們管理HTTPClient的生命周期以及在HTTPClient刪除前調用syncShutdown方法。

  1. extension HBApplication {
  2. var httpClient: HTTPClient {
  3. get { self.extensions.get(\.httpClient) }
  4. set { self.extensions.set(\.httpClient, value: newValue) { httpClient in
  5. try httpClient.syncShutdown()
  6. }}
  7. }
  8. }

當HBApplication關閉時候會調用set里面的閉包。這意味著我們當我們引用了HBApplication,即使不使用HTTPClient,我們也有權限去調用它

增加 middleware[中間件]

我們將把我們的代理服務器作為中間件。中間件將獲取一個請求,然后將它發送到目標服務器并且從目標服務器獲取響應信息。下面使我們初始版本的中間件,它需要HTTPClient和目標服務器的URL兩個參數。

  1. struct HBProxyServerMiddleware: HBMiddleware {
  2. let httpClient: HTTPClient
  3. let target: String
  4. func apply(to request: HBRequest, next: HBResponder) -> EventLoopFuture {
  5. return httpClient.execute(
  6. request: request,
  7. eventLoop: .delegateAndChannel(on: request.eventLoop),
  8. logger: request.logger
  9. )
  10. }
  11. }

現在我們有了HTTPClient和HBProxyServerMiddleware中間件,我們將它們加入配置文件HBApplication.configure。然后設置我們代理服務地址為http://httpbin.org

  1. func configure(_ args: AppArguments) throws {
  2. self.httpClient = HTTPClient(eventLoopGroupProvider: .shared(self.eventLoopGroup))
  3. self.middleware.add(HBProxyServerMiddleware(httpClient: self.httpClient, target: "http://httpbin.org"))
  4. }

轉換類型

當我們完成上面的步驟,構建會顯示失敗。因為我們還需要轉換Hummingbird和AsyncHTTPClient之間的請求和響應類型。同時我們需要合并目標服務的URL到請求里。

請求轉換

為了將Hummingbird HBRequest轉化為AsyncHTTPClient HTTPClient.Request,

原因: 我們首先需要整理可能仍在加載的HBRequest的body信息,轉換過程是異步的

解決方案:所以它需要返回一個包含后面轉換結果的EventLoopFuture,讓我們將轉換函數放到HBRequest里面

  1. extension HBRequest {
  2. func ahcRequest(host: String) -> EventLoopFuture {
  3. // consume request body and then construct AHC Request once we have the
  4. // result. The URL for the request is the target server plus the URI from
  5. // the `HBRequest`.
  6. return self.body.consumeBody(on: self.eventLoop).flatMapThrowing { buffer in
  7. return try HTTPClient.Request(
  8. url: host + self.uri.description,
  9. method: self.method,
  10. headers: self.headers,
  11. body: buffer.map { .byteBuffer($0) }
  12. )
  13. }
  14. }
  15. }

響應信息裝換

從HTTPClient.Response到HBResponse的轉換相當簡單

  1. extension HTTPClient.Response {
  2. var hbResponse: HBResponse {
  3. return .init(
  4. status: self.status,
  5. headers: self.headers,
  6. body: self.body.map { HBResponseBody.byteBuffer($0) } ?? .empty
  7. )
  8. }
  9. }

我們現在將這兩個轉換步驟加入HBProxyServerMiddleware的apply函數中。同時加入一些日志打印信息

  1. func apply(to request: HBRequest, next: HBResponder) -> EventLoopFuture {
  2. // log request
  3. request.logger.info("Forwarding \(request.uri.path)")
  4. // convert to HTTPClient.Request, execute, convert to HBResponse
  5. return request.ahcRequest(host: target).flatMap { ahcRequest in
  6. httpClient.execute(
  7. request: ahcRequest,
  8. eventLoop: .delegateAndChannel(on: request.eventLoop),
  9. logger: request.logger
  10. )
  11. }.map { response in
  12. return response.hbResponse
  13. }
  14. }

現在應該可以正常編譯了。中間件將整理HBRequest的請求體,將它轉化為HTTPRequest.Request,然后使用HTTPClient將請求轉發給目標服務器。獲取的響應信息會轉化為HBResponse返回給應用。

運行應用,打開網頁打開localhost:8080。我們應該能看到我們之前設置代理的httpbin.org網頁信息

Streaming[流]

上面的設置不是非常理想。它會等待請求完全加載,然后才將請求轉發給目標服務端。同理響應轉發也是需要等待響應完全加載后才會轉發。這降低了消息發送的效率,同樣會導致請求占用大量內存或者響應信息很大。

我們可以通過流式傳輸請求和響應負載來改進這一點。一旦我們有了它的頭部,就開始將請求發送到目標服務,并在接收到主體部分時對其進行流式處理。類似地,一旦我們有了它的頭,在另一個方向開始發送響應。消除對完整請求或響應的等待將提高代理服務器的性能。

如果客戶端和代理之間的通信以及代理和目標服務之間的通信以不同的速度運行,我們仍然會遇到內存問題。如果我們接收數據的速度比處理數據的速度快,數據就會開始備份。為了避免這種情況發生,我們需要能夠施加背壓以停止讀取額外的數據,直到我們處理了足夠多的內存中的數據。有了這個,我們可以將代理使用的內存量保持在最低限度。

流式請求

流式傳輸請求負載是一個相當簡單的過程。實際上,它簡化了構造 HTTPClient.Request 的過程因為我們不需要等待請求完全加載。我們如何構造 HTTPClient.Request 主體將基于完整的 HBRequest 是否已經在內存中。如果我們返回流請求,則會自動應用背壓,因為 Hummingbird 服務器框架會為我們執行此操作。

  1. func ahcRequest(host: String, eventLoop: EventLoop) throws -> HTTPClient.Request {
  2. let body: HTTPClient.Body?
  3. switch self.body {
  4. case .byteBuffer(let buffer):
  5. body = buffer.map { .byteBuffer($0) }
  6. case .stream(let stream):
  7. body = .stream { writer in
  8. // as we consume buffers from `HBRequest` we write them to
  9. // the `HTTPClient.Request`.
  10. return stream.consumeAll(on: eventLoop) { byteBuffer in
  11. writer.write(.byteBuffer(byteBuffer))
  12. }
  13. }
  14. }
  15. return try HTTPClient.Request(
  16. url: host + self.uri.description,
  17. method: self.method,
  18. headers: self.headers,
  19. body: body
  20. )
  21. }

流式響應

流式響應需要一個遵循 HTTPClientResponseDelegate 的class. 這將在 HTTPClient 響應可用時立即從響應中接收數據。響應正文是 ByteBuffers 格式. 我們可以將這些 ByteBuffers 提供給 HBByteBufferStreamer. 我們回報的 HBResponse 是由這些流構造,而不是靜態的 ByteBuffer。

如果我們將請求流與響應流代碼結合起來,我們的最終的 apply 函數應該是這樣的

  1. func apply(to request: HBRequest, next: HBResponder) -> EventLoopFuture {
  2. do {
  3. request.logger.info("Forwarding \(request.uri.path)")
  4. // create request
  5. let ahcRequest = try request.ahcRequest(host: target, eventLoop: request.eventLoop)
  6. // create response body streamer. maxSize is the maximum size of object it can process
  7. // maxStreamingBufferSize is the maximum size of data the streamer is allowed to have
  8. // in memory at any one time
  9. let streamer = HBByteBufferStreamer(eventLoop: request.eventLoop, maxSize: 2048*1024, maxStreamingBufferSize: 128*1024)
  10. // HTTPClientResponseDelegate for streaming bytebuffers from AsyncHTTPClient
  11. let delegate = StreamingResponseDelegate(on: request.eventLoop, streamer: streamer)
  12. // execute request
  13. _ = httpClient.execute(
  14. request: ahcRequest,
  15. delegate: delegate,
  16. eventLoop: .delegateAndChannel(on: request.eventLoop),
  17. logger: request.logger
  18. )
  19. // when delegate receives head then signal completion
  20. return delegate.responsePromise.futureResult
  21. } catch {
  22. return request.failure(error)
  23. }
  24. }

你會注意到在上面的代碼中我們不等待httpClient.execute. 這是因為如果我們這樣做了,該函數將在繼續之前等待整個響應主體在內存中。我們希望立即處理響應,因此我們向委托添加了一個promise: 一旦我們收到頭部信息,就會通過保存頭部詳情和流到HBResponse來實現。EventLoopFuture這個 promise的是我們從apply函數傳回的。

我沒有在StreamingResponseDelegate這里包含代碼,但您可以在完整的示例代碼中[5]找到它。

示例代碼添加

該示例代碼[6]可能在上面的基礎上做了部分修改。

  1. 默認綁定地址端口是 8081 而不是 8080。大多數 Hummingbird 示例在 8080 上運行,因此要在這些示例旁邊使用代理,它需要綁定到不同的端口。
  2. 我添加了一個位置選項,它允許我們只轉發來自特定基本 URL 的請求
  3. 我為目標和位置添加了命令行選項,因此可以在不重建應用程序的情況下更改這些選項
  4. 我刪除了 host 標題或請求,以便可以用正確的值填寫
  5. 如果提供了 content-length 標頭,則在轉換流請求時,我將其傳遞給 HTTPClient 流送器,以確保 content-length 為目標服務器的請求正確設置標頭。

備擇方案

我們可以使用 HummingbirdCore 代替 Hummingbird 作為代理服務器。這將提供一些額外的性能,因為它會刪除額外的代碼層,但會犧牲靈活性。添加任何額外的路由或中間件需要做更多的工作。我有只使用HummingbirdCore代理服務器的示例代碼在這里[7]。

當然,另一種選擇是使用 Vapor。我想在 Vapor 中的實現看起來與上面描述的非常相似,應該不會太難。不過我會把它留給別人。

參考資料

[1]Hummingbird: https://github.com/hummingbird-project/hummingbird

[2]AsyncHTTPClient: https://github.com/swift-server/async-http-client

[3]Hummingbird模板項目: https://github.com/hummingbird-project/template

[4]here: https://opticalaberration.com/2021/12/hummingbird-template.html

[5]示例代碼中: https://github.com/hummingbird-project/hummingbird-examples/blob/main/proxy-server/Sources/App/Middleware/StreamingResponseDelegate.swift

[6]示例代碼: https://github.com/hummingbird-project/hummingbird-examples/tree/main/proxy-server

[7]在這里: https://github.com/hummingbird-project/hummingbird-examples/tree/main/proxy-server-core

原文鏈接:https://mp.weixin.qq.com/s/PtSVTLlnmUDMDnJB4URnHQ

延伸 · 閱讀

精彩推薦
  • SwiftSwift使用CollectionView實現廣告欄滑動效果

    Swift使用CollectionView實現廣告欄滑動效果

    這篇文章主要為大家詳細介紹了Swift使用CollectionView實現廣告欄滑動效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    Stevin的技術博客12372021-01-13
  • SwiftSwift教程之基礎數據類型詳解

    Swift教程之基礎數據類型詳解

    這篇文章主要介紹了Swift教程之基礎數據類型詳解,本文詳細講解了Swift中的基本數據類型和基本語法,例如常量和變量、注釋、分號、整數、數值類型轉換等...

    Swift教程網5162020-12-18
  • SwiftSwift實現多個TableView側滑與切換效果

    Swift實現多個TableView側滑與切換效果

    這篇文章主要為大家詳細介紹了Swift實現多個TableView側滑與切換效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    乞力馬扎羅的雪雪5822021-01-08
  • SwiftSwift中轉義閉包示例詳解

    Swift中轉義閉包示例詳解

    在Swift 中的閉包類似于結構塊,并可以在任何地方調用,下面這篇文章主要給大家介紹了關于Swift中轉義閉包的相關資料,需要的朋友可以參考下...

    小小小_小朋友11412021-12-26
  • SwiftSwift的74個常用內置函數介紹

    Swift的74個常用內置函數介紹

    這篇文章主要介紹了Swift的74個常用內置函數介紹,這篇文章列舉出了所有的Swift庫函數,內置函數是指無需引入任何模塊即可以直接使用的函數,需要的朋友可...

    Swift教程網5802020-12-19
  • SwiftSwift能代替Objective-C嗎?

    Swift能代替Objective-C嗎?

    這是我在網上上看到的答案,復制粘貼過來和大家分享一下,因為我和很多人一樣很關心Swift的出現對Mac開發的影響和對Objective-C的影響。...

    Swift教程網4412020-12-16
  • Swiftswift where與匹配模式的實例詳解

    swift where與匹配模式的實例詳解

    這篇文章主要介紹了swift where與匹配模式的實例詳解的相關資料,這里附有簡單的示例代碼,講的比較清楚,需要的朋友可以參考下...

    追到夢的魔術師14382021-01-06
  • Swiftmac git xcrun error active developer path 錯誤

    mac git xcrun error active developer path 錯誤

    本文主要是講訴了如何解決在mac下使用git;xcode4.6的環境時,出現了錯誤(mac git xcrun error active developer path)的解決辦法,希望對大家有所幫助...

    Swift教程網2232020-12-16
主站蜘蛛池模板: 三区在线| 亚洲一区中文 | 免费黄色在线 | 噜噜噜视频在线观看 | 亚洲国产高清在线 | 99成人在线 | 激情五月婷婷丁香 | 午夜精品视频在线观看 | 亚洲美女性视频 | 国产精品免费观看 | 中文一区| 国产午夜精品一区二区三区嫩草 | 日本视频网 | 亚洲综合在线视频 | 日韩免费一区 | 欧美三区 | 成人教育av | 欧美一级特黄在线观看 | 欧美在线一区二区三区 | 中文字幕乱码亚洲无线三区 | 久久久精品视频国产 | 国产精品亚洲综合 | www.青青草 | 午夜在线小视频 | 色婷婷一区二区三区 | 精品久久精品 | 转生成为史莱姆这档事第四季在线观看 | 日韩免费在线 | 亚洲一区二区三区视频 | 亚洲91| 色婷婷国产精品免费网站 | 激情综合久久 | 免费av电影网站 | 午夜激情视频在线观看 | 久久久精品视频国产 | 欧美性猛交xxxx黑人猛交 | 久久精品中文 | 欧美一区二区三区精品 | 欧美性福| 国产成年人电影在线观看 | 亚洲 欧美 另类 综合 偷拍 |