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

Code before:
    
    /// The header fields for this HTTP request.
    /// The `"Content-Length"` and `"Transfer-Encoding"` headers will be set automatically
    /// when the `body` property is mutated.
    public var headers: HTTPHeaders
    
    internal var isKeepAlive: Bool
    
    // MARK: Metadata
    
    /// Route object we found for this request.
    /// This holds metadata that can be used for (for example) Metrics.
    ///
    ///     req.route?.description // "GET /hello/:name"
    ///
    public var route: Route?

    /// We try to determine true peer address if load balacer or reversed proxy provided info in headers
    ///
    /// Priority of getting value from headers is as following:
    ///
    /// 1. try the "Forwarded" header (e.g. for=192.0.2.60; proto=http; by=203.0.113.43)
    /// 2. try the "X-Forwarded-For" header (e.g. client_IP, proxy1_IP, proxy2_IP)
    /// 3. fallback to the socket's remote address provided by SwiftNIO ( e.g. 192.0.2.60:62934)
    ///
    public var peerAddress: String? {
        if let forwarded = headers.first(name: .forwarded) {
            return forwarded
        }

        if let xForwardedFor = headers.first(name: .xForwardedFor) {
            return xForwardedFor
        }

        return self.remoteAddress?.ipAddress
    }

    // MARK: Content

    private struct _URLQueryContainer: URLQueryContainer {
        let request: Request

        func decode<D>(_ decodable: D.Type, using decoder: URLQueryDecoder) throws -> D
            where D: Decodable
        {
            return try decoder.decode(D.self, from: self.request.url)
        }

        func encode<E>(_ encodable: E, using encoder: URLQueryEncoder) throws
            where E: Encodable
        {

Review comment (suggestion):
I like your docs! But it also made me realise that the different formats for these headers should be mixed carefully. I think it's wiser to expose `peerAddress` as a `SocketAddress`, and try to parse that address from these headers. Possibly providing a fallback for the port, and if there are multiple IP addresses, dealing with that in a later PR or returning a SocketAddress based on the client_IP from your examples.

Code after:
    
    /// The header fields for this HTTP request.
    /// The `"Content-Length"` and `"Transfer-Encoding"` headers will be set automatically
    /// when the `body` property is mutated.
    public var headers: HTTPHeaders
    
    internal var isKeepAlive: Bool
    
    // MARK: Metadata
    
    /// Route object we found for this request.
    /// This holds metadata that can be used for (for example) Metrics.
    ///
    ///     req.route?.description // "GET /hello/:name"
    ///
    public var route: Route?

    /// We try to determine true peer address if load balacer or reversed proxy provided info in headers
    ///
    /// Priority of getting value from headers is as following:
    ///
    /// 1. try the "Forwarded" header (e.g. for=192.0.2.60; proto=http; by=203.0.113.43)
    /// 2. try the "X-Forwarded-For" header (e.g. client_IP, proxy1_IP, proxy2_IP)
    /// 3. fallback to the socket's remote address provided by SwiftNIO ( e.g. 192.0.2.60:62934)
    /// in 1. and 2. will use port 80 as default port, and  3. will have port number provided by NIO if any
    public var peerAddress: SocketAddress? {
        if let clientAddress = headers.forwarded.first?.for {
            return try? SocketAddress.init(ipAddress: clientAddress, port: 80)
        }

        if let xForwardedFor = headers.first(name: .xForwardedFor) {
            return try? SocketAddress.init(ipAddress: xForwardedFor, port: 80)
        }

        return self.remoteAddress
    }

    // MARK: Content

    private struct _URLQueryContainer: URLQueryContainer {
        let request: Request

        func decode<D>(_ decodable: D.Type, using decoder: URLQueryDecoder) throws -> D
            where D: Decodable
        {
            return try decoder.decode(D.self, from: self.request.url)
        }

        func encode<E>(_ encodable: E, using encoder: URLQueryEncoder) throws
            where E: Encodable
        {