Repository: vapor/vapor
File: Sources/Vapor/Engine/EngineClient.swift (Swift)

Code before:
                return try self.plaintextRespond(to: req, redirecting: redirecting)
            }
        }
    }
    
    private func response(
        from httpRes: HTTPResponse,
        for req: Request,
        redirecting: Int
    ) throws -> Future<Response> {
        if httpRes.status.code >= 301 && httpRes.status.code <= 303 {
            guard redirecting > 0 else {
                throw VaporError(
                    identifier: "excessive-redirects",
                    reason: "The HTTP Client was redirected more than \(config.maxRedirections) times."
                )
            }
            
            guard let location = httpRes.headers[.location] else {
                throw VaporError(
                    identifier: "invalid-redirect",
                    reason: "The HTTP Client received a status 3xx without a location to redirect to."
                )
            }
            
            req.http.uri = try location.makeURI()
            
            return self.respond(to: req, redirecting: redirecting - 1)
        }
        
        let res = req.makeResponse()
        res.http = httpRes
        return Future(res)
    }

    /// Responds to a Request using TLS client.
    private func tlsRespond(to req: Request, redirecting: Int) throws -> Future<Response> {
        let tcpSocket = try TCPSocket(isNonBlocking: true)
        let tcpClient = try TCPClient(socket: tcpSocket)
        var settings = TLSClientSettings()
        let hostname = try req.http.uri.requireHostname()
        settings.peerDomainName = hostname
        #if os(macOS)
            let tlsClient = try AppleTLSClient(tcp: tcpClient, using: settings)
        #else
            let tlsClient = try OpenSSLClient(tcp: tcpClient, using: settings)
        #endif
        try tlsClient.connect(hostname: hostname, port: req.http.uri.port ?? 443)
        let client = HTTPClient(
            stream: tlsClient.socket.stream(on: self.container),
            on: self.container,

Review comment (bug):
It is incorrect but still-common practice for servers to return "relative" redirects, e.g. `Location: /path/on/same/server`. This case should be detected and the URI defaulted to the previous URI's schema and hostname.

Code after:
    private func respond(to req: Request, maxRedirects redirects: Int) -> Future<Response> {
        return Future.flatMap {
            if req.http.uri.scheme == "https" ? true : false {
                return try self.tlsRespond(to: req, maxRedirects: redirects)
            } else {
                return try self.plaintextRespond(to: req, maxRedirects: redirects)
            }
        }
    }
    
    /// Rediects the input request with the `Location` in the Response
    private func redirect(
        _ response: HTTPResponse,
        for req: Request,
        maxRedirects redirects: Int
    ) throws -> Future<Response> {
        guard redirects > 0 else {
            throw VaporError(
                identifier: "excessiveRedirects",
                reason: "The HTTP Client was redirected more than \(config.maxRedirections) times.",
                source: .capture()
            )
        }
        
        guard let location = response.headers[.location] else {
            throw VaporError(
                identifier: "invalidRedirect",
                reason: "The HTTP Client received a status 3xx without a location to redirect to.",
                source: .capture()
            )
        }
        
        let newURI = try location.makeURI()
        req.http.uri.update(with: newURI)
        
        return self.respond(to: req, maxRedirects: redirects - 1)
    }
    
    /// Processes an HTTP esponse and acts upon redirects accordingly
    private func response(
        from httpRes: HTTPResponse,
        for req: Request,
        maxRedirects redirects: Int
    ) throws -> Future<Response> {
        if httpRes.status.code >= 300 && httpRes.status.code < 400 {
            switch httpRes.status.code {
            case 301, 307, 308:
                return try redirect(httpRes, for: req, maxRedirects: redirects)
            case 302, 303:
                req.http.method = .get
                req.http.body = HTTPBody()