-
Notifications
You must be signed in to change notification settings - Fork 123
/
Copy pathUtils.swift
184 lines (168 loc) · 7.76 KB
/
Utils.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
//===----------------------------------------------------------------------===//
//
// This source file is part of the AsyncHTTPClient open source project
//
// Copyright (c) 2018-2020 Apple Inc. and the AsyncHTTPClient project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
import Foundation
#if canImport(Network)
import Network
#endif
import NIO
import NIOHTTP1
import NIOHTTPCompression
import NIOSSL
import NIOTransportServices
import Tracing
internal extension String {
var isIPAddress: Bool {
var ipv4Addr = in_addr()
var ipv6Addr = in6_addr()
return self.withCString { ptr in
inet_pton(AF_INET, ptr, &ipv4Addr) == 1 ||
inet_pton(AF_INET6, ptr, &ipv6Addr) == 1
}
}
}
public final class HTTPClientCopyingDelegate: HTTPClientResponseDelegate {
public typealias Response = Void
let chunkHandler: (ByteBuffer) -> EventLoopFuture<Void>
public init(chunkHandler: @escaping (ByteBuffer) -> EventLoopFuture<Void>) {
self.chunkHandler = chunkHandler
}
public func didReceiveBodyPart(task: HTTPClient.Task<Void>, _ buffer: ByteBuffer) -> EventLoopFuture<Void> {
return self.chunkHandler(buffer)
}
public func didFinishRequest(task: HTTPClient.Task<Void>) throws {
return ()
}
}
extension ClientBootstrap {
fileprivate func makeClientTCPBootstrap(
host: String,
requiresTLS: Bool,
configuration: HTTPClient.Configuration
) throws -> NIOClientTCPBootstrap {
// if there is a proxy don't create TLS provider as it will be added at a later point
if configuration.proxy != nil {
return NIOClientTCPBootstrap(self, tls: NIOInsecureNoTLS())
} else {
let tlsConfiguration = configuration.tlsConfiguration ?? TLSConfiguration.forClient()
let sslContext = try NIOSSLContext(configuration: tlsConfiguration)
let hostname = (!requiresTLS || host.isIPAddress || host.isEmpty) ? nil : host
let tlsProvider = try NIOSSLClientTLSProvider<ClientBootstrap>(context: sslContext, serverHostname: hostname)
return NIOClientTCPBootstrap(self, tls: tlsProvider)
}
}
}
extension NIOClientTCPBootstrap {
/// create a TCP Bootstrap based off what type of `EventLoop` has been passed to the function.
fileprivate static func makeBootstrap(
on eventLoop: EventLoop,
host: String,
requiresTLS: Bool,
configuration: HTTPClient.Configuration
) throws -> NIOClientTCPBootstrap {
var bootstrap: NIOClientTCPBootstrap
#if canImport(Network)
// if eventLoop is compatible with NIOTransportServices create a NIOTSConnectionBootstrap
if #available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *), let tsBootstrap = NIOTSConnectionBootstrap(validatingGroup: eventLoop) {
// if there is a proxy don't create TLS provider as it will be added at a later point
if configuration.proxy != nil {
bootstrap = NIOClientTCPBootstrap(tsBootstrap, tls: NIOInsecureNoTLS())
} else {
// create NIOClientTCPBootstrap with NIOTS TLS provider
let tlsConfiguration = configuration.tlsConfiguration ?? TLSConfiguration.forClient()
let parameters = tlsConfiguration.getNWProtocolTLSOptions()
let tlsProvider = NIOTSClientTLSProvider(tlsOptions: parameters)
bootstrap = NIOClientTCPBootstrap(tsBootstrap, tls: tlsProvider)
}
} else if let clientBootstrap = ClientBootstrap(validatingGroup: eventLoop) {
bootstrap = try clientBootstrap.makeClientTCPBootstrap(host: host, requiresTLS: requiresTLS, configuration: configuration)
} else {
preconditionFailure("Cannot create bootstrap for the supplied EventLoop")
}
#else
if let clientBootstrap = ClientBootstrap(validatingGroup: eventLoop) {
bootstrap = try clientBootstrap.makeClientTCPBootstrap(host: host, requiresTLS: requiresTLS, configuration: configuration)
} else {
preconditionFailure("Cannot create bootstrap for the supplied EventLoop")
}
#endif
if let timeout = configuration.timeout.connect {
bootstrap = bootstrap.connectTimeout(timeout)
}
// don't enable TLS if we have a proxy, this will be enabled later on
if requiresTLS, configuration.proxy == nil {
return bootstrap.enableTLS()
}
return bootstrap
}
static func makeHTTPClientBootstrapBase(
on eventLoop: EventLoop,
host: String,
port: Int,
requiresTLS: Bool,
configuration: HTTPClient.Configuration
) throws -> NIOClientTCPBootstrap {
return try self.makeBootstrap(on: eventLoop, host: host, requiresTLS: requiresTLS, configuration: configuration)
.channelOption(ChannelOptions.socket(SocketOptionLevel(IPPROTO_TCP), TCP_NODELAY), value: 1)
.channelInitializer { channel in
let channelAddedFuture: EventLoopFuture<Void>
switch configuration.proxy {
case .none:
channelAddedFuture = eventLoop.makeSucceededFuture(())
case .some:
channelAddedFuture = channel.pipeline.addProxyHandler(host: host, port: port, authorization: configuration.proxy?.authorization)
}
return channelAddedFuture
}
}
}
extension Connection {
func removeHandler<Handler: RemovableChannelHandler>(_ type: Handler.Type) -> EventLoopFuture<Void> {
return self.channel.pipeline.handler(type: type).flatMap { handler in
self.channel.pipeline.removeHandler(handler)
}.recover { _ in }
}
}
extension SpanStatus {
/// Map status code to canonical code according to OTel spec
///
/// - SeeAlso: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/http.md#status
init(_ responseStatus: HTTPResponseStatus) {
switch responseStatus.code {
case 100...399:
self = SpanStatus(canonicalCode: .ok)
case 400, 402, 405 ... 428, 430 ... 498:
self = SpanStatus(canonicalCode: .invalidArgument, message: responseStatus.reasonPhrase)
case 401:
self = SpanStatus(canonicalCode: .unauthenticated, message: responseStatus.reasonPhrase)
case 403:
self = SpanStatus(canonicalCode: .permissionDenied, message: responseStatus.reasonPhrase)
case 404:
self = SpanStatus(canonicalCode: .notFound, message: responseStatus.reasonPhrase)
case 429:
self = SpanStatus(canonicalCode: .resourceExhausted, message: responseStatus.reasonPhrase)
case 499:
self = SpanStatus(canonicalCode: .cancelled, message: responseStatus.reasonPhrase)
case 500, 505 ... 599:
self = SpanStatus(canonicalCode: .internal, message: responseStatus.reasonPhrase)
case 501:
self = SpanStatus(canonicalCode: .unimplemented, message: responseStatus.reasonPhrase)
case 503:
self = SpanStatus(canonicalCode: .unavailable, message: responseStatus.reasonPhrase)
case 504:
self = SpanStatus(canonicalCode: .deadlineExceeded, message: responseStatus.reasonPhrase)
default:
self = SpanStatus(canonicalCode: .unknown, message: responseStatus.reasonPhrase)
}
}
}