0001    //
0002    //  Proxies.swift
0003    //  Dynamo
0004    //
0005    //  Created by John Holdsworth on 20/06/2015.
0006    //  Copyright (c) 2015 John Holdsworth. All rights reserved.
0007    //
0008    //  $Id: //depot/Dynamo/Sources/Proxies.swift#11 $
0009    //
0010    //  Repo: https://github.com/johnno1962/Dynamo
0011    //
0012    
0013    import Foundation
0014    
0015    #if os(Linux)
0016    import Glibc
0017    import NSLinux
0018    #endif
0019    
0020    // MARK: Proxy Swiftlets
0021    
0022    /**
0023         Swiftlet to allow a DynamoWebServer to act as a http: protocol proxy on the same port.
0024     */
0025    
0026    public class ProxySwiftlet
Proxies.swift:82
public class SSLProxySwiftlet: ProxySwiftlet {
: _NSObject_, DynamoSwiftlet { 0027 0028 var logger
Proxies.swift:32
        self.logger = logger
Proxies.swift:66
                DynamoSelector.relay( host, from: httpClient, to: remoteConnection, logger )
Proxies.swift:92
                    DynamoSelector.relay( httpClient.path, from: httpClient, to: remoteConnection, logger )
: ((String) -> ())? 0029 0030 /** default initialiser with optional "tracer" for all traffic */ 0031 public init( logger: ((String) -> ())? = nil ) { 0032 self.logger = logger 0033 } 0034 0035 /** process as proxy request if request path has "host" */ 0036 public func present( httpClient: DynamoHTTPConnection ) -> DynamoProcessed { 0037 0038 if httpClient.url.host == dummyBase.host { 0039 return .NotProcessed 0040 } 0041 0042 if let host = httpClient.url.host { 0043 if let remoteConnection = DynamoHTTPConnection( url: httpClient.url ) { 0044 0045 var remotePath = httpClient.url.path ?? "/" 0046 if !remotePath.hasSuffix( "/" ) && (httpClient.path.hasSuffix( "/" ) || httpClient.path.rangeOfString( "/?" ) != nil) { 0047 remotePath += "/" 0048 } 0049 if let query = httpClient.url.query { 0050 remotePath += "?"+query 0051 } 0052 0053 remoteConnection.rawPrint( "\(httpClient.method) \(remotePath) \(httpClient.version)\r\n" ) 0054 for (name, value) in httpClient.requestHeaders { 0055 remoteConnection.rawPrint( "\(name): \(value)\r\n" ) 0056 } 0057 remoteConnection.rawPrint( "\r\n" ) 0058 0059 if httpClient.readBuffer.length != 0 { 0060 let readBuffer = httpClient.readBuffer 0061 remoteConnection.write( readBuffer.bytes, count: readBuffer.length ) 0062 readBuffer.replaceBytesInRange( NSMakeRange( 0, readBuffer.length ), withBytes: nil, length: 0 ) 0063 } 0064 remoteConnection.flush() 0065 0066 DynamoSelector.relay( host, from: httpClient, to: remoteConnection, logger ) 0067 } 0068 else { 0069 httpClient.sendResponse( .OK( html: "Unable to resolve host \(host)" ) ) 0070 } 0071 } 0072 return .Processed 0073 } 0074 0075 } 0076 0077 /** 0078 Swiftlet to allow a DynamoWebServer to act as a https: SSL connection protocol proxy on the same port. 0079 This must be come before the DynamoProxySwiftlet in the list of swiftlets for the server for both to work. 0080 */ 0081 0082 public class SSLProxySwiftlet: ProxySwiftlet { 0083 0084 /** connect socket through to destination SSL server for method "CONNECT" */ 0085 public override func present( httpClient: DynamoHTTPConnection ) -> DynamoProcessed { 0086 if httpClient.method == "CONNECT" { 0087 0088 if let urlForDestination = NSURL( string: "https://\(httpClient.path)" ), 0089 remoteConnection = DynamoHTTPConnection( url: urlForDestination ) { 0090 httpClient.rawPrint( "HTTP/1.0 200 Connection established\r\nProxy-agent: Dynamo/1.0\r\n\r\n" ) 0091 httpClient.flush() 0092 DynamoSelector.relay( httpClient.path, from: httpClient, to: remoteConnection, logger ) 0093 } 0094 0095 return .Processed 0096 } 0097 0098 return .NotProcessed 0099 } 0100 0101 } 0102 0103 // MARK: "select()" based fd switching 0104 0105 var dynamoSelector
Proxies.swift:154
        if dynamoSelector == nil {
Proxies.swift:155
            dynamoSelector = DynamoSelector()
Proxies.swift:157
                dynamoSelector!.selectLoop( logger )
Proxies.swift:161
        dynamoSelector!.queue.append( (label,from,to) )
: DynamoSelector? 0106 private let selectBitsPerFlag: Int32 = 32 0107 private let selectShift
Proxies.swift:108
private let selectBitMask: Int32 = (1<<selectShift)-1
Proxies.swift:122
    let set = flags + Int( fd>>selectShift )
Proxies.swift:127
    let set = flags + Int( fd>>selectShift )
Proxies.swift:132
    let set = flags + Int( fd>>selectShift )
: Int32 = 5 0108 private let selectBitMask
Proxies.swift:123
    set.memory &= ~(1<<(fd&selectBitMask))
Proxies.swift:128
    set.memory |= 1<<(fd&selectBitMask)
Proxies.swift:133
    return (set.memory & (1<<(fd&selectBitMask))) != 0
: Int32 = (1<<selectShift)-1 0109 private var dynamoQueueLock
Proxies.swift:152
        dynamoQueueLock.lock()
Proxies.swift:162
        dynamoQueueLock.unlock()
Proxies.swift:176
	    dynamoQueueLock.lock()
Proxies.swift:193
	    dynamoQueueLock.unlock()
= NSLock() 0110 private let dynamoProxyQueue
Proxies.swift:156
            dispatch_async( dynamoProxyQueue, {
= dispatch_queue_create( "DynamoProxyThread", DISPATCH_QUEUE_CONCURRENT ) 0111 0112 /** polling interval for proxy relay */ 0113 public var dynamoPollingUsec
Proxies.swift:225
            timeout.tv_usec = dynamoPollingUsec
: Int32 = 100*1000 0114 private var maxReadAhead
Proxies.swift:201
                if writer.readBuffer.length < maxReadAhead {
= 10*1024*1024 0115 private var maxPacket
Proxies.swift:171
        var buffer = [Int8](count: maxPacket, repeatedValue: 0)
= 2*1024 0116 0117 func FD_ZERO
Proxies.swift:195
            FD_ZERO( readFlags )
Proxies.swift:196
            FD_ZERO( writeFlags )
Proxies.swift:197
            FD_ZERO( errorFlags )
Proxies.swift:238
                    FD_ZERO( readFlags )
Proxies.swift:247
                    FD_ZERO( readFlags )
( flags: UnsafeMutablePointer<Int32> ) { 0118 memset( flags, 0, sizeof(fd_set) ) 0119 } 0120 0121 func FD_CLR( fd: Int32, _ flags: UnsafeMutablePointer<Int32> ) { 0122 let set = flags + Int( fd>>selectShift ) 0123 set.memory &= ~(1<<(fd&selectBitMask)) 0124 } 0125 0126 func FD_SET
Proxies.swift:202
                    FD_SET( fd, readFlags )
Proxies.swift:204
                FD_SET( fd, errorFlags )
Proxies.swift:213
                FD_SET( fd, writeFlags )
Proxies.swift:214
                FD_SET( fd, errorFlags )
Proxies.swift:239
                    FD_SET( fd, readFlags )
Proxies.swift:248
                    FD_SET( fd, readFlags )
( fd: Int32, _ flags: UnsafeMutablePointer<Int32> ) { 0127 let set = flags + Int( fd>>selectShift ) 0128 set.memory |= 1<<(fd&selectBitMask) 0129 } 0130 0131 func FD_ISSET
Proxies.swift:265
                    where FD_ISSET( readFD, readFlags ) || writer.readTotal != 0 && reader.hasBytesAvailable {
Proxies.swift:288
                if FD_ISSET( writeFD, writeFlags ) {
Proxies.swift:309
                if FD_ISSET( errorFD, errorFlags ) {
( fd: Int32, _ flags: UnsafeMutablePointer<Int32> ) -> Bool { 0132 let set = flags + Int( fd>>selectShift ) 0133 return (set.memory & (1<<(fd&selectBitMask))) != 0 0134 } 0135 0136 //#if !os(Linux) 0137 //@asmname("fcntl") 0138 //func fcntl( filedesc: Int32, _ command: Int32, _ arg: Int32 ) -> Int32 0139 //#endif 0140 0141 /** 0142 More efficient than relying on operating system to handle many reads on different threads when proxying 0143 */ 0144 0145 final class DynamoSelector
Proxies.swift:66
                DynamoSelector.relay( host, from: httpClient, to: remoteConnection, logger )
Proxies.swift:92
                    DynamoSelector.relay( httpClient.path, from: httpClient, to: remoteConnection, logger )
Proxies.swift:105
var dynamoSelector: DynamoSelector?
Proxies.swift:155
            dynamoSelector = DynamoSelector()
Servers.swift:213
                        DynamoSelector.relay( "surrogate", from: sslConnection, to: surrogateConnection, dynamoTrace )
{ 0146 0147 var readMap
Proxies.swift:190
                readMap[from.clientSocket] = to
Proxies.swift:191
                readMap[to.clientSocket] = from
Proxies.swift:200
            for (fd,writer) in readMap {
Proxies.swift:235
                dynamoStrerror( "Select error \(readMap) \(writeMap)" )
Proxies.swift:237
                for (fd,_) in readMap {
Proxies.swift:264
                if let writer = readMap[readFD], reader = readMap[writer.clientSocket]
Proxies.swift:264
                if let writer = readMap[readFD], reader = readMap[writer.clientSocket]
Proxies.swift:270
                        logger?( "\(writer.label) \(writer.readTotal)+\(readBuffer.length)+\(bytesRead) bytes (\(readFD)/\(readMap.count)/\(fdcount))" )
Proxies.swift:319
        if let writer = readMap[fd] {
Proxies.swift:320
            readMap.removeValueForKey( writer.clientSocket )
Proxies.swift:322
        readMap.removeValueForKey( fd )
= [Int32:DynamoHTTPConnection]() 0148 var writeMap
Proxies.swift:212
            for (fd,_) in writeMap {
Proxies.swift:235
                dynamoStrerror( "Select error \(readMap) \(writeMap)" )
Proxies.swift:246
                for (fd,writer) in writeMap {
Proxies.swift:250
                        writeMap.removeValueForKey( writer.clientSocket )
Proxies.swift:281
                            writeMap[writer.clientSocket] = writer
Proxies.swift:287
            for (writeFD,writer) in writeMap {
Proxies.swift:293
                            writeMap.removeValueForKey( writer.clientSocket )
Proxies.swift:302
                            writeMap.removeValueForKey( writer.clientSocket )
Proxies.swift:310
                    writeMap.removeValueForKey( errorFD )
= [Int32:DynamoHTTPConnection]() 0149 var queue
Proxies.swift:161
        dynamoSelector!.queue.append( (label,from,to) )
Proxies.swift:177
            while queue.count != 0 {
Proxies.swift:178
                let (label,from,to) = queue.removeAtIndex(0)
= [(String,DynamoHTTPConnection,DynamoHTTPConnection)]() 0150 0151 class func relay
Proxies.swift:66
                DynamoSelector.relay( host, from: httpClient, to: remoteConnection, logger )
Proxies.swift:92
                    DynamoSelector.relay( httpClient.path, from: httpClient, to: remoteConnection, logger )
Servers.swift:213
                        DynamoSelector.relay( "surrogate", from: sslConnection, to: surrogateConnection, dynamoTrace )
( label: String, from: DynamoHTTPConnection, to: DynamoHTTPConnection, _ logger: ((String) -> ())? ) { 0152 dynamoQueueLock.lock() 0153 0154 if dynamoSelector == nil { 0155 dynamoSelector = DynamoSelector() 0156 dispatch_async( dynamoProxyQueue, { 0157 dynamoSelector!.selectLoop( logger ) 0158 } ) 0159 } 0160 0161 dynamoSelector!.queue.append( (label,from,to) ) 0162 dynamoQueueLock.unlock() 0163 } 0164 0165 func selectLoop
Proxies.swift:157
                dynamoSelector!.selectLoop( logger )
( logger: ((String) -> Void)? = nil ) { 0166 0167 let readFlags = UnsafeMutablePointer<Int32>( malloc( sizeof(fd_set) ) ) 0168 let writeFlags = UnsafeMutablePointer<Int32>( malloc( sizeof(fd_set) ) ) 0169 let errorFlags = UnsafeMutablePointer<Int32>( malloc( sizeof(fd_set) ) ) 0170 0171 var buffer = [Int8](count: maxPacket, repeatedValue: 0) 0172 var timeout = timeval() 0173 0174 while true { 0175 0176 dynamoQueueLock.lock() 0177 while queue.count != 0 { 0178 let (label,from,to) = queue.removeAtIndex(0) 0179 to.label = "-> \(label)" 0180 from.label = "<- \(label)" 0181 0182 // #if !os(Linux) 0183 // if label == "surrogate" { 0184 // var flags = fcntl( to.clientSocket, F_GETFL, 0 ) 0185 // flags |= O_NONBLOCK 0186 // fcntl( to.clientSocket, F_SETFL, flags ) 0187 // } 0188 // #endif 0189 0190 readMap[from.clientSocket] = to 0191 readMap[to.clientSocket] = from 0192 } 0193 dynamoQueueLock.unlock() 0194 0195 FD_ZERO( readFlags ) 0196 FD_ZERO( writeFlags ) 0197 FD_ZERO( errorFlags ) 0198 0199 var maxfd: Int32 = -1, fdcount = 0 0200 for (fd,writer) in readMap { 0201 if writer.readBuffer.length < maxReadAhead { 0202 FD_SET( fd, readFlags ) 0203 } 0204 FD_SET( fd, errorFlags ) 0205 if maxfd < fd { 0206 maxfd = fd 0207 } 0208 fdcount += 1 0209 } 0210 0211 var hasWrite = false 0212 for (fd,_) in writeMap { 0213 FD_SET( fd, writeFlags ) 0214 FD_SET( fd, errorFlags ) 0215 if maxfd < fd { 0216 maxfd = fd 0217 } 0218 hasWrite = true 0219 } 0220 0221 timeout.tv_sec = 0 0222 #if os(Linux) 0223 timeout.tv_usec = Int(dynamoPollingUsec) 0224 #else 0225 timeout.tv_usec = dynamoPollingUsec 0226 #endif 0227 0228 if select( maxfd+1, 0229 UnsafeMutablePointer<fd_set>( readFlags ), 0230 hasWrite ? UnsafeMutablePointer<fd_set>( writeFlags ) : nil, 0231 UnsafeMutablePointer<fd_set>( errorFlags ), &timeout ) < 0 { 0232 0233 timeout.tv_sec = 0 0234 timeout.tv_usec = 0 0235 dynamoStrerror( "Select error \(readMap) \(writeMap)" ) 0236 0237 for (fd,_) in readMap { 0238 FD_ZERO( readFlags ) 0239 FD_SET( fd, readFlags ) 0240 if select( fd+1, UnsafeMutablePointer<fd_set>( readFlags ), nil, nil, &timeout ) < 0 { 0241 dynamoLog( "Closing reader: \(fd)" ) 0242 close( fd ) 0243 } 0244 } 0245 0246 for (fd,writer) in writeMap { 0247 FD_ZERO( readFlags ) 0248 FD_SET( fd, readFlags ) 0249 if select( fd+1, UnsafeMutablePointer<fd_set>( readFlags ), nil, nil, &timeout ) < 0 { 0250 writeMap.removeValueForKey( writer.clientSocket ) 0251 dynamoLog( "Closing writer: \(fd)" ) 0252 close( fd ) 0253 } 0254 } 0255 0256 continue 0257 } 0258 0259 if maxfd < 0 { 0260 continue 0261 } 0262 0263 for readFD in 0...maxfd { 0264 if let writer = readMap[readFD], reader = readMap[writer.clientSocket] 0265 where FD_ISSET( readFD, readFlags ) || writer.readTotal != 0 && reader.hasBytesAvailable { 0266 0267 if let bytesRead = reader.receive( &buffer, count: buffer.count ) { 0268 let readBuffer = writer.readBuffer 0269 0270 logger?( "\(writer.label) \(writer.readTotal)+\(readBuffer.length)+\(bytesRead) bytes (\(readFD)/\(readMap.count)/\(fdcount))" ) 0271 0272 if bytesRead <= 0 { 0273 close( readFD ) 0274 } 0275 else { 0276 readBuffer.appendBytes( buffer, length: bytesRead ) 0277 writer.readTotal += bytesRead 0278 } 0279 0280 if readBuffer.length != 0 { 0281 writeMap[writer.clientSocket] = writer 0282 } 0283 } 0284 } 0285 } 0286 0287 for (writeFD,writer) in writeMap { 0288 if FD_ISSET( writeFD, writeFlags ) { 0289 let readBuffer = writer.readBuffer 0290 0291 if let bytesWritten = writer.forward( readBuffer.bytes, count: readBuffer.length ) { 0292 if bytesWritten <= 0 { 0293 writeMap.removeValueForKey( writer.clientSocket ) 0294 dynamoLog( "Short write on relay \(writer.label)" ) 0295 close( writeFD ) 0296 } 0297 else { 0298 readBuffer.replaceBytesInRange( NSMakeRange( 0, bytesWritten ), withBytes: nil, length: 0 ) 0299 } 0300 0301 if readBuffer.length == 0 { 0302 writeMap.removeValueForKey( writer.clientSocket ) 0303 } 0304 } 0305 } 0306 } 0307 0308 for errorFD in 0..<maxfd { 0309 if FD_ISSET( errorFD, errorFlags ) { 0310 writeMap.removeValueForKey( errorFD ) 0311 dynamoLog( "ERROR from select on relay" ) 0312 close( errorFD ) 0313 } 0314 } 0315 } 0316 } 0317 0318 private func close
Proxies.swift:242
                        close( fd )
Proxies.swift:252
                        close( fd )
Proxies.swift:273
                            close( readFD )
Proxies.swift:295
                            close( writeFD )
Proxies.swift:312
                    close( errorFD )
( fd: Int32 ) { 0319 if let writer = readMap[fd] { 0320 readMap.removeValueForKey( writer.clientSocket ) 0321 } 0322 readMap.removeValueForKey( fd ) 0323 } 0324 0325 } 0326