blob: 09b603cf4ad857bcf653c6c439d2140e745f0e03 [file] [log] [blame]
David Reissea2cba82009-03-30 21:35:00 +00001/*
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
Andrew McGeachie6db89f22009-07-21 14:45:12 +000020#import <Foundation/Foundation.h>
Mark Slee77575e62007-09-24 19:24:53 +000021#import "TSocketServer.h"
22#import "TNSFileHandleTransport.h"
23#import "TProtocol.h"
Jens Geyer56e5b9b2015-10-09 22:01:55 +020024#import "TTransportError.h"
25
Andrew McGeachie0c895712009-07-21 21:14:19 +000026#import <sys/socket.h>
27#include <netinet/in.h>
Chris Vassellidf3223c2016-06-21 16:45:39 -070028#include <sys/un.h>
Andrew McGeachie0c895712009-07-21 21:14:19 +000029
30
Jens Geyer56e5b9b2015-10-09 22:01:55 +020031NSString *const TSocketServerClientConnectionFinished = @"TSocketServerClientConnectionFinished";
32NSString *const TSocketServerProcessorKey = @"TSocketServerProcessor";
33NSString *const TSockerServerTransportKey = @"TSockerServerTransport";
34
35
36@interface TSocketServer ()
37
38@property(strong, nonatomic) id<TProtocolFactory> inputProtocolFactory;
39@property(strong, nonatomic) id<TProtocolFactory> outputProtocolFactory;
40@property(strong, nonatomic) id<TProcessorFactory> processorFactory;
41@property(strong, nonatomic) NSFileHandle *socketFileHandle;
42@property(strong, nonatomic) dispatch_queue_t processingQueue;
Chris Vassellidf3223c2016-06-21 16:45:39 -070043@property(strong, nonatomic) NSString *domainSocketPath;
Jens Geyer56e5b9b2015-10-09 22:01:55 +020044
45@end
Mark Slee77575e62007-09-24 19:24:53 +000046
47
48@implementation TSocketServer
49
Chris Vassellidf3223c2016-06-21 16:45:39 -070050-(instancetype) initWithSocket:(CFSocketRef)socket
Jens Geyer56e5b9b2015-10-09 22:01:55 +020051 protocolFactory:(id <TProtocolFactory>)protocolFactory
52 processorFactory:(id <TProcessorFactory>)processorFactory;
Mark Slee77575e62007-09-24 19:24:53 +000053{
54 self = [super init];
55
Jens Geyer56e5b9b2015-10-09 22:01:55 +020056 _inputProtocolFactory = protocolFactory;
57 _outputProtocolFactory = protocolFactory;
58 _processorFactory = processorFactory;
59
60 dispatch_queue_attr_t processingQueueAttr =
61 dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT, QOS_CLASS_BACKGROUND, 0);
62
63 _processingQueue = dispatch_queue_create("TSocketServer.processing", processingQueueAttr);
David Reiss0c90f6f2008-02-06 22:18:40 +000064
Andrew McGeachie0c895712009-07-21 21:14:19 +000065 // create a socket.
Chris Vassellidf3223c2016-06-21 16:45:39 -070066 int fd = CFSocketGetNative(socket);
Jens Geyer56e5b9b2015-10-09 22:01:55 +020067
Andrew McGeachie0c895712009-07-21 21:14:19 +000068 // wrap it in a file handle so we can get messages from it
Jens Geyer56e5b9b2015-10-09 22:01:55 +020069 _socketFileHandle = [[NSFileHandle alloc] initWithFileDescriptor:fd
70 closeOnDealloc:YES];
71
Andrew McGeachieddfe0c92010-02-10 01:03:01 +000072 // throw away our socket
73 CFSocketInvalidate(socket);
74 CFRelease(socket);
Jens Geyer56e5b9b2015-10-09 22:01:55 +020075
76 // register for notifications of accepted incoming connections
77 [[NSNotificationCenter defaultCenter] addObserver:self
78 selector:@selector(connectionAccepted:)
79 name:NSFileHandleConnectionAcceptedNotification
80 object:_socketFileHandle];
81
Andrew McGeachie0c895712009-07-21 21:14:19 +000082 // tell socket to listen
Jens Geyer56e5b9b2015-10-09 22:01:55 +020083 [_socketFileHandle acceptConnectionInBackgroundAndNotify];
84
Chris Vassellidf3223c2016-06-21 16:45:39 -070085 return self;
86}
Jens Geyer56e5b9b2015-10-09 22:01:55 +020087
Chris Vassellidf3223c2016-06-21 16:45:39 -070088- (id) initWithPort: (int) port
89 protocolFactory: (id <TProtocolFactory>) protocolFactory
90 processorFactory: (id <TProcessorFactory>) processorFactory
91{
92 CFSocketRef socket = [[self class] createSocketWithPort:port];
93 if (socket == NULL) {
94 return nil;
95 }
96
97 if (self = [self initWithSocket:socket protocolFactory:protocolFactory processorFactory:processorFactory]) {
98 NSLog(@"TSocketServer: Listening on TCP port %d", port);
99 }
Mark Slee77575e62007-09-24 19:24:53 +0000100 return self;
101}
102
103
Chris Vassellidf3223c2016-06-21 16:45:39 -0700104+(CFSocketRef) createSocketWithPort:(int)port
105{
106 CFSocketRef socket = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL);
107 if (socket) {
108 CFSocketSetSocketFlags(socket, CFSocketGetSocketFlags(socket) & ~kCFSocketCloseOnInvalidate);
109 int fd = CFSocketGetNative(socket);
110 int yes = 1;
111 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));
112
113 struct sockaddr_in addr;
114 memset(&addr, 0, sizeof(addr));
115 addr.sin_len = sizeof(addr);
116 addr.sin_family = AF_INET;
117 addr.sin_port = htons(port);
118 addr.sin_addr.s_addr = htonl(INADDR_ANY);
119 NSData *address = [NSData dataWithBytes:&addr length:sizeof(addr)];
120 if (CFSocketSetAddress(socket, (__bridge CFDataRef)address) != kCFSocketSuccess) {
121 CFSocketInvalidate(socket);
122 CFRelease(socket);
123 NSLog(@"TSocketServer: Could not bind to address");
124 return NULL;
125 }
126
127 return socket;
128 }
129 else {
130 NSLog(@"TSocketServer: No server socket");
131 return NULL;
132 }
133}
134
135- (id) initWithPath: (NSString *) path
136 protocolFactory: (id <TProtocolFactory>) protocolFactory
137 processorFactory: (id <TProcessorFactory>) processorFactory
138{
139 _domainSocketPath = path;
140 CFSocketRef socket = [[self class] createSocketWithPath:path];
141 if (socket == NULL) {
142 return nil;
143 }
144
145 if (self = [self initWithSocket:socket protocolFactory:protocolFactory processorFactory:processorFactory]) {
146 NSLog(@"TSocketServer: Listening on path %@", path);
147 }
148 return self;
149}
150
151+ (CFSocketRef) createSocketWithPath: (NSString *) path
152{
153 CFSocketRef socket = CFSocketCreate(kCFAllocatorDefault, PF_LOCAL, SOCK_STREAM, IPPROTO_IP, 0, NULL, NULL);
154 if (socket) {
155 CFSocketSetSocketFlags(socket, CFSocketGetSocketFlags(socket) & ~kCFSocketCloseOnInvalidate);
156 int fd = CFSocketGetNative(socket);
157 int yes = 1;
158 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));
159
160 size_t nullTerminatedPathLength = path.length + 1;
161 struct sockaddr_un addr;
162 if (nullTerminatedPathLength> sizeof(addr.sun_path)) {
163 NSLog(@"TSocketServer: Unable to create socket at path %@. Path is too long.", path);
164 return NULL;
165 }
166
167 addr.sun_family = AF_LOCAL;
168 memcpy(addr.sun_path, path.UTF8String, nullTerminatedPathLength);
169 addr.sun_len = SUN_LEN(&addr);
170
171 NSData *address = [NSData dataWithBytes:&addr length:sizeof(addr)];
172 if (CFSocketSetAddress(socket, (__bridge CFDataRef)address) != kCFSocketSuccess) {
173 CFSocketInvalidate(socket);
174 CFRelease(socket);
175 NSLog(@"TSocketServer: Could not bind to address");
176 return NULL;
177 }
178
179 return socket;
180 } else {
181 NSLog(@"TSocketServer: No server socket");
182 return NULL;
183 }
184}
185
Jens Geyer56e5b9b2015-10-09 22:01:55 +0200186-(void) dealloc
187{
Jake Farrell6f3a5262012-07-27 15:48:37 +0000188 [[NSNotificationCenter defaultCenter] removeObserver:self];
Chris Vassellidf3223c2016-06-21 16:45:39 -0700189
190 if (_domainSocketPath != nil) {
191 unlink(_domainSocketPath.UTF8String);
192 }
Mark Slee77575e62007-09-24 19:24:53 +0000193}
194
195
Jens Geyer56e5b9b2015-10-09 22:01:55 +0200196-(void) connectionAccepted:(NSNotification *)notification
Mark Slee77575e62007-09-24 19:24:53 +0000197{
Jens Geyer56e5b9b2015-10-09 22:01:55 +0200198 NSFileHandle *socket = [notification.userInfo objectForKey:NSFileHandleNotificationFileHandleItem];
David Reiss0c90f6f2008-02-06 22:18:40 +0000199
Jens Geyer56e5b9b2015-10-09 22:01:55 +0200200 // Now that we have a client connected, handle request on queue
201 dispatch_async(_processingQueue, ^{
David Reiss0c90f6f2008-02-06 22:18:40 +0000202
Jens Geyer56e5b9b2015-10-09 22:01:55 +0200203 [self handleClientConnection:socket];
204
205 });
206
207 // Continue accepting connections
208 [_socketFileHandle acceptConnectionInBackgroundAndNotify];
Mark Slee77575e62007-09-24 19:24:53 +0000209}
210
211
Jens Geyer56e5b9b2015-10-09 22:01:55 +0200212-(void) handleClientConnection:(NSFileHandle *)clientSocket
Mark Slee77575e62007-09-24 19:24:53 +0000213{
Jens Geyer56e5b9b2015-10-09 22:01:55 +0200214 @autoreleasepool {
215
216 TNSFileHandleTransport *transport = [[TNSFileHandleTransport alloc] initWithFileHandle:clientSocket];
217 id<TProcessor> processor = [_processorFactory processorForTransport:transport];
218
219 id <TProtocol> inProtocol = [_inputProtocolFactory newProtocolOnTransport:transport];
220 id <TProtocol> outProtocol = [_outputProtocolFactory newProtocolOnTransport:transport];
221
222 NSError *error;
223 if (![processor processOnInputProtocol:inProtocol outputProtocol:outProtocol error:&error]) {
224 // Handle error
225 NSLog(@"Error processing request: %@", error);
Jake Farrell9689d892011-12-06 01:07:17 +0000226 }
David Reiss0c90f6f2008-02-06 22:18:40 +0000227
Jens Geyer56e5b9b2015-10-09 22:01:55 +0200228 dispatch_async(dispatch_get_main_queue(), ^{
David Reiss0c90f6f2008-02-06 22:18:40 +0000229
Jens Geyer56e5b9b2015-10-09 22:01:55 +0200230 [NSNotificationCenter.defaultCenter postNotificationName:TSocketServerClientConnectionFinished
231 object:self
232 userInfo:@{TSocketServerProcessorKey: processor,
233 TSockerServerTransportKey: transport}];
234 });
235
236 }
Mark Slee77575e62007-09-24 19:24:53 +0000237}
238
Mark Slee77575e62007-09-24 19:24:53 +0000239@end