1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 *   http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 
20 #if os(OSX) || os(iOS) || os(watchOS) || os(tvOS)
21   import Darwin
22 #elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android)
23   import Glibc
24   import Dispatch
25 #endif
26 
27 import Foundation
28 import CoreFoundation
29 
30 public let TSocketServerClientConnectionFinished = "TSocketServerClientConnectionFinished"
31 public let TSocketServerProcessorKey = "TSocketServerProcessor"
32 public let TSocketServerTransportKey = "TSocketServerTransport"
33 
34 open class TSocketServer<InProtocol: TProtocol, OutProtocol: TProtocol, Processor: TProcessor> {
35   var socketFileHandle: FileHandle
36   var processingQueue =  DispatchQueue(label: "TSocketServer.processing",
37                                        qos: .background,
38                                        attributes: .concurrent)
39   let processor: Processor
40 
41   public init(port: Int,
42               inProtocol: InProtocol.Type,
43               outProtocol: OutProtocol.Type,
44               processor: Processor) throws {
45     self.processor = processor
46 
47     // create a socket
48     var fd: Int32 = -1
49     #if os(Linux)
50       let sock = CFSocketCreate(kCFAllocatorDefault, PF_INET, Int32(SOCK_STREAM.rawValue), Int32(IPPROTO_TCP), 0, nil, nil)
51     #else
52       let sock = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_STREAM, IPPROTO_TCP, 0, nil, nil)
53     #endif
54     if sock != nil {
55       CFSocketSetSocketFlags(sock, CFSocketGetSocketFlags(sock) & ~CFOptionFlags(kCFSocketCloseOnInvalidate))
56 
57       fd = CFSocketGetNative(sock)
58       var yes = 1
59       setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, UInt32(MemoryLayout<Int>.size))
60       let inPort = in_port_t(UInt16(truncatingIfNeeded: port).bigEndian)
61       #if os(Linux)
62         var addr = sockaddr_in(sin_family: sa_family_t(AF_INET),
63                                sin_port: inPort,
64                                sin_addr: in_addr(s_addr: in_addr_t(0)),
65                                sin_zero: (0, 0, 0, 0, 0, 0, 0, 0))
66       #else
67         var addr = sockaddr_in(sin_len: UInt8(MemoryLayout<sockaddr_in>.size),
68                                sin_family: sa_family_t(AF_INET),
69                                sin_port: inPort,
70                                sin_addr: in_addr(s_addr: in_addr_t(0)),
71                                sin_zero: (0, 0, 0, 0, 0, 0, 0, 0))
72       #endif
73 
74       let ptr = withUnsafePointer(to: &addr) {
75         return UnsafePointer<UInt8>(OpaquePointer($0))
76       }
77 
78       let address = Data(bytes: ptr, count: MemoryLayout<sockaddr_in>.size)
79 
80       let cfaddr = address.withUnsafeBytes {
81         CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, $0.bindMemory(to: UInt8.self).baseAddress!, address.count, kCFAllocatorNull)
82       }
83       if CFSocketSetAddress(sock, cfaddr) != CFSocketError.success { //kCFSocketSuccess {
84         CFSocketInvalidate(sock)
85         print("TSocketServer: Could not bind to address")
86         throw TTransportError(error: .notOpen, message: "Could not bind to address")
87       }
88 
89     } else {
90       print("TSocketServer: No server socket")
91       throw TTransportError(error: .notOpen, message: "Could not create socket")
92     }
93 
94     // wrap it in a file handle so we can get messages from it
95     socketFileHandle = FileHandle(fileDescriptor: fd, closeOnDealloc: true)
96 
97     // throw away our socket
98     CFSocketInvalidate(sock)
99 
100     // register for notifications of accepted incoming connections
101     _ = NotificationCenter.default.addObserver(forName: .NSFileHandleConnectionAccepted,
102                                                object: nil, queue: nil) {
103                                                 [weak self] notification in
104                                                 guard let strongSelf = self else { return }
105                                                 guard let clientSocket = notification.userInfo?[NSFileHandleNotificationFileHandleItem] as? FileHandle else { return }
106                                                 strongSelf.connectionAccepted(clientSocket)
107     }
108 
109     // tell socket to listen
110     socketFileHandle.acceptConnectionInBackgroundAndNotify()
111 
112     print("TSocketServer: Listening on TCP port \(port)")
113   }
114 
115   deinit {
116     NotificationCenter.default.removeObserver(self)
117   }
118 
connectionAcceptednull119   func connectionAccepted(_ clientSocket: FileHandle) {
120     // Now that we have a client connected, handle the request on queue
121     processingQueue.async {
122       self.handleClientConnection(clientSocket)
123     }
124 
125     // continue accepting connections
126     socketFileHandle.acceptConnectionInBackgroundAndNotify()
127   }
128 
createTransportnull129   open func createTransport(fileHandle: FileHandle) -> TTransport {
130     return TFileHandleTransport(fileHandle: fileHandle)
131   }
132 
handleClientConnectionnull133   func handleClientConnection(_ clientSocket: FileHandle) {
134     let transport = createTransport(fileHandle: clientSocket)
135     let inProtocol = InProtocol(on: transport)
136     let outProtocol = OutProtocol(on: transport)
137 
138     do {
139       while true {
140         try processor.process(on: inProtocol, outProtocol: outProtocol)
141       }
142     } catch let error {
143       print("Error processign request: \(error)")
144     }
145     DispatchQueue.main.async {
146       NotificationCenter.default
147         .post(name: Notification.Name(rawValue: TSocketServerClientConnectionFinished),
148               object: self,
149               userInfo: [TSocketServerProcessorKey: self.processor,
150                          TSocketServerTransportKey: transport])
151     }
152   }
153 }
154 
155 public class TFramedSocketServer<InProtocol: TProtocol, OutProtocol: TProtocol, Processor: TProcessor>: TSocketServer<InProtocol, OutProtocol, Processor> {
createTransportnull156   open override func createTransport(fileHandle: FileHandle) -> TTransport {
157     return TFramedTransport(transport: super.createTransport(fileHandle: fileHandle))
158   }
159 }
160