nw_connection_receive / nw_connection_send is not working as we expect

Hi All,

We are trying to build demo tool by using Network framework API.

Here is the code in Objective-c:

//
//  main.m
//

#import <Foundation/Foundation.h>
#import <Network/Network.h>

char *hostname = "";
char *port = "";

nw_connection_t serverConnection;

void stopConn()
{
   nw_connection_cancel(serverConnection);
}

void connectionDidEnd(NSError *error)
{
    NSLog(@"connectionDidEnd: %@", [error localizedDescription]);
    stopConn();
}

void connectionDidFail(NSError *error)
{
    NSLog(@"connectionDidFail: %@", [error localizedDescription]);
    stopConn();
}

void connectionReady(nw_connection_t connection)
{
    NSString *data = @"somedata";
    NSData *rawData = [data dataUsingEncoding:NSUTF8StringEncoding];
    nw_connection_send(connection, (dispatch_data_t _Nonnull)rawData, NW_CONNECTION_DEFAULT_MESSAGE_CONTEXT, FALSE, ^(nw_error_t  _Nullable error)
    {
        if (error != NULL)
        {
            NSLog(@"connection did send Failed.");
            connectionDidFail((NSError*)error);
            return;
        }
            NSLog(@"connection did send, data: %@",[[NSString alloc] initWithData:rawData encoding:NSUTF8StringEncoding]);
    });
}

void setupReceive(nw_connection_t connection)
{
    nw_connection_receive(connection, 1, 65536, ^(dispatch_data_t  _Nullable content, nw_content_context_t  _Nullable context, bool is_complete, nw_error_t  _Nullable error) {

        nw_retain(context);
        if (content != NULL)
        {
            NSString *data = [[NSString alloc] initWithData:(NSData*)content encoding:NSUTF8StringEncoding];
            NSLog(@"setupReceive: Receive Data = %@",data);
        }

        if (is_complete) {
            connectionDidEnd((NSError*)error);
        } else if (error != NULL) {
            connectionDidFail((NSError*)error);
        } else {
            setupReceive(connection);
        }
    });
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        nw_endpoint_t endpoint = nw_endpoint_create_host(hostname, port);
        serverConnection = nw_connection_create(endpoint, nw_parameters_create_secure_tcp(NW_PARAMETERS_DEFAULT_CONFIGURATION, NW_PARAMETERS_DEFAULT_CONFIGURATION));
        nw_retain(serverConnection);
        nw_connection_set_queue(serverConnection, dispatch_get_main_queue());  
        nw_connection_set_state_changed_handler(serverConnection, ^(nw_connection_state_t state, nw_error_t  _Nullable error) {
            switch (state) {
                case nw_connection_state_invalid:
                    NSLog(@"Invalid");
                    break;
                case nw_connection_state_waiting:
                    NSLog(@"waiting");
                    break;
                case nw_connection_state_preparing:
                    NSLog(@"Preparing");
                    break;
                case nw_connection_state_ready:
                    NSLog(@"Client connection ready");
                    connectionReady(serverConnection);
                    break;
                case nw_connection_state_failed:
                    NSLog(@"FAILED...");
                    connectionDidFail((NSError*)error);
                    break;
                case nw_connection_state_cancelled:
                    connectionDidEnd((NSError*)error);
                    NSLog(@"connection cancelled");
                    break;
                default:
                    NSLog(@"Unknown State = %d",state);
                    break;
            }
        });
        setupReceive(serverConnection);
        nw_connection_start(serverConnection);
        dispatch_main();
    }
    return 0;
}

Above code is not able to send command to server and always we receive below error

nw_protocol_boringssl_write_frames_block_invoke(892) [C1:1][0x1040682a0] Failed to allocate buffer for external data

Please let us know if we are doing anything wrong here.

Thanks & Regards,

Mohmad Vasim

I don't see anything obviously wrong here. You could try adding the receive loop after the connection is setup and before you do the send, but that should't affect your connection doing TLS. Is this happening on when the data is sent on the connection or when nw_connection_start is called?

Thanks for your response Matteon!

Is this happening on when the data is sent on the connection or when nw_connection_start is called?

Its happening once connection is in ready state and because of BoringSSL issue, unable to proceed for send command.

Thanks & Regards, Mohmad Vasim

Okay thank you. I feel like the log for:

nw_protocol_boringssl_write_frames_block_invoke(892) [C1:1][0x1040682a0] Failed to allocate buffer for external data

Is the result of some other error that took place previously. Is there any other logs for C1?

Can you open a bug report here with a sample project that reproduces the issue so that it can be evaluated further? Also, please make sure to include a sysdiagnose after you have reproduced the issue with the exact time and date the issue took place. Please post the Feedback ID here once completed.

Hi,

We have raised bug with sample along with sysdiagnose and system information report and also mentioned exact date and time as requested in bug. Here is the feedback link for more information. [https://feedbackassistant.apple.com/feedback/11357055]

Thanks & Regards,

Mohmad Vasim

Thank you for opening the Feedback and attaching the project! I took a look at the project today and it looks like you have the right idea but I just needed tweak the project a bit.

char *hostname = "www.example.com";
char *port = "443"; // For SSL

...

void connectionReady(nw_connection_t connection)
{
   NSString *request = @"GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n";
   NSData *rawData = [request dataUsingEncoding:NSUTF8StringEncoding];
   dispatch_data_t dispatch_data = dispatch_data_create([rawData bytes], [rawData length], dispatch_get_main_queue(), DISPATCH_DATA_DESTRUCTOR_DEFAULT);
   nw_connection_send(connection, dispatch_data, NW_CONNECTION_DEFAULT_MESSAGE_CONTEXT, FALSE, ^(nw_error_t  _Nullable error)
    {
        if (error != NULL)
        {
            NSLog(@"connection did send Failed.");
            connectionDidFail((NSError*)error);
            return;
        }
        NSLog(@"connection did send, data: %@",[[NSString alloc] initWithData:rawData encoding:NSUTF8StringEncoding]);
    });
}

...

void setupReceive(nw_connection_t connection)
{
    nw_connection_receive(connection, 1, 65536, ^(dispatch_data_t  _Nullable content, nw_content_context_t  _Nullable context, bool is_complete, nw_error_t  _Nullable error) {
        nw_retain(context);
        // Get the data from the read
        if (content != nil) {
            NSData* bufferData = (NSData*)content;
            NSString* newStr = [NSString stringWithUTF8String:[bufferData bytes]];
            NSLog(@"receivedDataOnConnection %lu bytes with string: %@", (unsigned long)[bufferData bytes], newStr);
        }
        // If there is an error, short circuit
        if (error != nil) {
            NSError *err = (NSError *)CFBridgingRelease(nw_error_copy_cf_error((nw_error_t)error));
            NSLog(@"connection received failure, %@ ", err.localizedDescription);
            return;
        }
        
        // Send a new read call if not complete
        if (!is_complete && error == NULL) {
            setupReceive(connection);
            
        } else if (is_complete) {
            stopConn();
        }
    });
    
}

This should give you back an HTML response for example.com. Now, making an HTTP request here like this is not what you are looking for, but I wanted to use this to illustrate the point that this does work with your code as is. Try this and see where it gets you with your existing use case.

I wanted to follow up here and see if the change I made to the sample project you provided worked out or if you are still seeing BoringSSL errors when sending data?

Thanks for your response Meaton!

Apologise for delay in response, will check and will share the details here.

Thanks & Regards,

Mohmad Vasim

I have checked where it was giving BoringSSL error but now with above changes BoringSSL is not getting generated however its stuck in Preparing state itself .

I have checked where it was giving BoringSSL error but now with above changes BoringSSL is not getting generated however its stuck in Preparing state itself .

Thank you for the update. So it sounds like you are no longer getting the errors, but the connection cannot setup, correct? If you use the example.com host / port endpoint that I provided and then the string based HTTP request, does this work? What I am trying to evaluate here is if something is still missing in the code or if you are running into handshake problems with the endpoint that you are trying to negotiate with.

Hi Meaton,

So it sounds like you are no longer getting the errors, but the connection cannot setup, correct?

Yes, correct.

If you use the example.com host / port endpoint that I provided and then the string based HTTP request, does this work?

Yes, tried this too and its intermittent, sometime getting null string and sometime incomplete html string.

Thanks & Regards,

Mohmad Vasim

nw_connection_receive / nw_connection_send is not working as we expect
 
 
Q