Add implementation for Wi-Fi LAN sockets that use the Network framework

PiperOrigin-RevId: 507922710
This commit is contained in:
Nick Bourdakos
2023-02-07 16:54:11 -08:00
committed by Copybara-Service
parent aa95be4ff4
commit fc3bf91485
3 changed files with 191 additions and 1 deletions
@@ -28,6 +28,7 @@ objc_library(
"WifiLan/GNCMBonjourConnection.m",
"WifiLan/GNCMBonjourService.m",
"WifiLan/GNCMBonjourUtils.m",
"WifiLanV2/GNCWiFiLANSocket.m",
],
hdrs = [
"Ble/GNCMBleCentral.h",
@@ -40,6 +41,7 @@ objc_library(
"WifiLan/GNCMBonjourConnection.h",
"WifiLan/GNCMBonjourService.h",
"WifiLan/GNCMBonjourUtils.h",
"WifiLanV2/GNCWiFiLANSocket.h",
],
deps = [
"//internal/platform/implementation/apple:Shared",
@@ -49,7 +51,7 @@ objc_library(
"//proto/mediums:ble_frames_cc_proto",
"//third_party/apple_frameworks:CoreBluetooth",
"//third_party/apple_frameworks:Foundation",
"//third_party/apple_frameworks:Network",
"//third_party/objective_c/google_toolbox_for_mac:GTM_Logger",
"@com_google_absl//absl/numeric:int128",
],
)
@@ -0,0 +1,64 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import <Foundation/Foundation.h>
#import <Network/Network.h>
@interface GNCWiFiLANSocket : NSObject
/**
* @remark init is not an available initializer.
*/
- (nonnull instancetype)init NS_UNAVAILABLE;
/**
* Creates a socket that allows reading/writing for a given connection.
*
* @param connection A bidirectional data connection between a local and remote endpoint. It is the
* responsibility of the caller to maintain a strong reference to the connection
* object.
*/
- (nonnull instancetype)initWithConnection:(nonnull nw_connection_t)connection
NS_DESIGNATED_INITIALIZER;
/**
* Reads the requested amount of bytes from the connection.
*
* Blocks execution until the bytes have been read or an error occurs.
*
* @param length The number of bytes to read.
* @param[out] error Error that will be populated on failure. A read may return non-nil data along
* with an error. This normally happens if the data read is shorter than the
* requested length.
*/
- (nullable NSData *)readMaxLength:(NSUInteger)length error:(NSError **_Nullable)error;
/**
* Writes the given data to the connection.
*
* Blocks execution until the bytes have been successfully written or an error occurs.
*
* @param data The data to write.
* @param[out] error Error that will be populated on failure.
*/
- (BOOL)write:(NSData *)data error:(NSError **_Nullable)error;
/**
* Gracefully closes the connection to remote endpoint.
*
* If a read or write is in progress, they will be unblocked with an error.
*/
- (void)close;
@end
@@ -0,0 +1,124 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import "internal/platform/implementation/apple/Mediums/WifiLanV2/GNCWiFiLANSocket.h"
#import <Foundation/Foundation.h>
#import <Network/Network.h>
#import "GoogleToolboxForMac/GTMLogger.h"
@interface GNCWiFiLANSocket ()
@property(nonatomic, weak, readonly) nw_connection_t connection;
@end
@implementation GNCWiFiLANSocket {
}
- (instancetype)initWithConnection:(nw_connection_t)connection {
self = [super init];
if (self) {
_connection = connection;
}
return self;
}
- (NSData *)readMaxLength:(NSUInteger)length error:(NSError **)error {
__strong nw_connection_t connection = self.connection;
if (connection == nil) {
return nil;
}
NSCondition *condition = [[NSCondition alloc] init];
[condition lock];
__block NSData *blockResult = nil;
__block NSError *blockError = nil;
nw_connection_receive(
connection, length, length,
^(dispatch_data_t content, nw_content_context_t context, bool is_complete, nw_error_t error) {
[condition lock];
if (error != nil) {
blockError = (__bridge_transfer NSError *)nw_error_copy_cf_error(error);
}
#if __LP64__
// This cast is only safe in a 64-bit runtime.
blockResult = (NSData *)content;
#else
blockResult = nil;
#endif
[condition signal];
[condition unlock];
});
[condition wait];
[condition unlock];
if (error != nil) {
*error = blockError;
}
return blockResult;
}
- (BOOL)write:(NSData *)data error:(NSError **)error {
__strong nw_connection_t connection = self.connection;
if (connection == nil) {
return NO;
}
NSCondition *condition = [[NSCondition alloc] init];
[condition lock];
__block BOOL blockResult = NO;
__block NSError *blockError = nil;
dispatch_data_t dispatchData =
dispatch_data_create(data.bytes, data.length, dispatch_get_main_queue(),
^{
// Since we block until the send is complete, `data` should stay
// alive when needed and released at the end of this method.
});
nw_connection_send(connection, dispatchData, NW_CONNECTION_DEFAULT_MESSAGE_CONTEXT, false,
^(nw_error_t error) {
[condition lock];
blockResult = error == nil;
if (error != nil) {
blockError = (__bridge_transfer NSError *)nw_error_copy_cf_error(error);
}
[condition signal];
[condition unlock];
});
[condition wait];
[condition unlock];
if (error != nil) {
*error = blockError;
}
return blockResult;
}
- (void)close {
__strong nw_connection_t connection = self.connection;
if (connection == nil) {
return;
}
nw_connection_cancel(connection);
}
@end