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     print("TSocketServer: Listening on TCP port \(port)")
101 
102     // tell socket to listen
103     acceptConnectionInBackgroundAndNotify(handle: socketFileHandle)
104   }
105 
acceptConnectionInBackgroundAndNotifynull106   private func acceptConnectionInBackgroundAndNotify(handle: FileHandle) {
107     DispatchQueue(label: "TSocketServer.connectionAccept").async {
108       let acceptedFD = accept(handle.fileDescriptor, nil, nil)
109       DispatchQueue.main.async {
110         self.connectionAccepted(FileHandle(fileDescriptor: acceptedFD))
111       }
112     }
113   }
connectionAcceptednull114   func connectionAccepted(_ clientSocket: FileHandle) {
115     // Now that we have a client connected, handle the request on queue
116     processingQueue.async {
117       self.handleClientConnection(clientSocket)
118     }
119 
120     // continue accepting connections
121     acceptConnectionInBackgroundAndNotify(handle: socketFileHandle)
122   }
123 
createTransportnull124   open func createTransport(fileHandle: FileHandle) -> TTransport {
125     return TFileHandleTransport(fileHandle: fileHandle)
126   }
127 
handleClientConnectionnull128   func handleClientConnection(_ clientSocket: FileHandle) {
129     let transport = createTransport(fileHandle: clientSocket)
130     let inProtocol = InProtocol(on: transport)
131     let outProtocol = OutProtocol(on: transport)
132 
133     do {
134       while true {
135         try processor.process(on: inProtocol, outProtocol: outProtocol)
136       }
137     } catch let error {
138       print("Error processing request: \(error)")
139     }
140     DispatchQueue.main.async {
141       NotificationCenter.default
142         .post(name: Notification.Name(rawValue: TSocketServerClientConnectionFinished),
143               object: nil,
144               userInfo: [TSocketServerProcessorKey: self.processor,
145                          TSocketServerTransportKey: transport])
146     }
147   }
148 }
149 
150 public class TFramedSocketServer<InProtocol: TProtocol, OutProtocol: TProtocol, Processor: TProcessor>: TSocketServer<InProtocol, OutProtocol, Processor> {
createTransportnull151   open override func createTransport(fileHandle: FileHandle) -> TTransport {
152     return TFramedTransport(transport: super.createTransport(fileHandle: fileHandle))
153   }
154 }
155