File: main.swift

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (130 lines) | stat: -rw-r--r-- 4,441 bytes parent folder | download
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
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftNIO open source project
//
// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import NIO
import NIOFoundationCompat
import NIOHTTP1
import NIOSSL
import Foundation

private final class HTTPResponseHandler: ChannelInboundHandler {

    let promise: EventLoopPromise<Void>

    var closeFuture: EventLoopFuture<Void>? = nil

    init(_ promise: EventLoopPromise<Void>) {
        self.promise = promise
    }

    typealias InboundIn = HTTPClientResponsePart

    func channelRead(context: ChannelHandlerContext, data: NIOAny) {
        let httpResponsePart = unwrapInboundIn(data)
        switch httpResponsePart {
        case .head(let httpResponseHeader):
            print("\(httpResponseHeader.version) \(httpResponseHeader.status.code) \(httpResponseHeader.status.reasonPhrase)")
            for (name, value) in httpResponseHeader.headers {
                print("\(name): \(value)")
            }
        case .body(var byteBuffer):
            if let data = byteBuffer.readData(length: byteBuffer.readableBytes) {
                FileHandle.standardOutput.write(data)
            }
        case .end(_):
            closeFuture = context.channel.close()
            promise.succeed(())
        }
    }

    func channelInactive(context: ChannelHandlerContext) {
        if closeFuture == nil {
            closeFuture = context.channel.close()
            promise.fail(ChannelError.inputClosed)
        }
    }

    func errorCaught(context: ChannelHandlerContext, error: Error) {
        print("Error: ", error)
        closeFuture = context.channel.close()
        promise.succeed(())
    }
}

let arguments = CommandLine.arguments
let arg1 = arguments.dropFirst().first

var url: URL
var cert: [NIOSSLCertificateSource] = []
var key: NIOSSLPrivateKeySource?
var trustRoot: NIOSSLTrustRoots = .default

if let u = arg1 {
    url = URL(string: u)!
} else {
    url = URL(string: "https://::1:4433/get")!
}

// These extra arguments aren't expected to be used, we use them for integration tests only.
if let c = arguments.dropFirst(2).first {
    cert.append(contentsOf: try NIOSSLCertificate.fromPEMFile(c).map { .certificate($0) })
}
if let k = arguments.dropFirst(3).first {
    key = .file(k)
}
if let r = arguments.dropFirst(4).first {
    trustRoot = .file(r)
}

let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1)
let promise: EventLoopPromise<Void> = eventLoopGroup.next().makePromise(of: Void.self)
defer {
    try! promise.futureResult.wait()
    try! eventLoopGroup.syncShutdownGracefully()
}

var tlsConfiguration = TLSConfiguration.makeClientConfiguration()
tlsConfiguration.trustRoots = trustRoot
tlsConfiguration.certificateChain = cert
tlsConfiguration.privateKey = key
tlsConfiguration.renegotiationSupport = .once

let sslContext = try! NIOSSLContext(configuration: tlsConfiguration)

let bootstrap = ClientBootstrap(group: eventLoopGroup)
        .channelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1)
        .channelInitializer { channel in
            let openSslHandler = try! NIOSSLClientHandler(context: sslContext, serverHostname: url.host)
            return channel.pipeline.addHandler(openSslHandler).flatMap {
                channel.pipeline.addHTTPClientHandlers()
            }.flatMap {
                channel.pipeline.addHandler(HTTPResponseHandler(promise))
            }
        }

func sendRequest(_ channel: Channel) -> EventLoopFuture<Void> {
    var request = HTTPRequestHead(version: HTTPVersion(major: 1, minor: 1), method: HTTPMethod.GET, uri: url.absoluteString)
    request.headers = HTTPHeaders([
        ("Host", url.host!),
        ("User-Agent", "swift-nio"),
        ("Accept", "application/json"),
        ("Connection", "close")
    ])
    channel.write(HTTPClientRequestPart.head(request), promise: nil)
    return channel.writeAndFlush(HTTPClientRequestPart.end(nil))
}

bootstrap.connect(host: url.host!, port: url.port ?? 443)
        .flatMap { sendRequest($0) }
        .cascadeFailure(to: promise)