I'm trying to follow this example to let me make a POST request to a web server and receive its response in pure C using Core Foundation functions. I'll copy and paste it here:
void PostRequest()
{
// Create the POST request payload.
CFStringRef payloadString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("{\"test-data-key\" : \"test-data-value\"}"));
CFDataRef payloadData = CFStringCreateExternalRepresentation(kCFAllocatorDefault, payloadString, kCFStringEncodingUTF8, 0);
CFRelease(payloadString);
//create request
CFURLRef theURL = CFURLCreateWithString(kCFAllocatorDefault, CFSTR("https://httpbin.org/post"), NULL); //https://httpbin.org/post returns post data
CFHTTPMessageRef request = CFHTTPMessageCreateRequest(kCFAllocatorDefault, CFSTR("POST"), theURL, kCFHTTPVersion1_1);
CFHTTPMessageSetBody(request, payloadData);
//add some headers
CFStringRef hostString = CFURLCopyHostName(theURL);
CFHTTPMessageSetHeaderFieldValue(request, CFSTR("HOST"), hostString);
CFRelease(hostString);
CFRelease(theURL);
if (payloadData)
{
CFStringRef lengthString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%ld"), CFDataGetLength(payloadData));
CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Content-Length"), lengthString);
CFRelease(lengthString);
}
CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Content-Type"), CFSTR("charset=utf-8"));
//create read stream for response
CFReadStreamRef requestStream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, request);
CFRelease(request);
//set up on separate runloop (with own thread) to avoid blocking the UI
CFReadStreamScheduleWithRunLoop(requestStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
CFOptionFlags optionFlags = (kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered);
CFStreamClientContext clientContext = {0, (void *)payloadData, RetainSocketStreamHandle, ReleaseSocketStreamHandle, NULL};
CFReadStreamSetClient(requestStream, optionFlags, ReadStreamCallBack, &clientContext);
//start request
CFReadStreamOpen(requestStream);
if (payloadData)
{
CFRelease(payloadData);
}
}
And the callback:
void LogData(CFDataRef responseData)
{
CFIndex dataLength = CFDataGetLength(responseData);
UInt8 *bytes = (UInt8 *)malloc(dataLength);
CFDataGetBytes(responseData, CFRangeMake(0, CFDataGetLength(responseData)), bytes);
CFStringRef responseString = CFStringCreateWithBytes(kCFAllocatorDefault, bytes, dataLength, kCFStringEncodingUTF8, TRUE);
CFShow(responseString);
CFRelease(responseString);
free(bytes);
}
static void ReadStreamCallBack(CFReadStreamRef readStream, CFStreamEventType type, void *clientCallBackInfo)
{
CFDataRef passedInData = (CFDataRef)(clientCallBackInfo);
CFShow(CFSTR("Passed In Data:"));
LogData(passedInData);
//append data as we receive it
CFMutableDataRef responseBytes = CFDataCreateMutable(kCFAllocatorDefault, 0);
CFIndex numberOfBytesRead = 0;
do
{
UInt8 buf[1024];
numberOfBytesRead = CFReadStreamRead(readStream, buf, sizeof(buf));
if (numberOfBytesRead > 0)
{
CFDataAppendBytes(responseBytes, buf, numberOfBytesRead);
}
} while (numberOfBytesRead > 0);
//once all data is appended, package it all together - create a response from the response headers, and add the data received.
//note: just having the data received is not enough, you need to finish the response by retrieving the response headers here...
CFHTTPMessageRef response = (CFHTTPMessageRef)CFReadStreamCopyProperty(readStream, kCFStreamPropertyHTTPResponseHeader);
if (responseBytes)
{
if (response)
{
CFHTTPMessageSetBody(response, responseBytes);
}
CFRelease(responseBytes);
}
//close and cleanup
CFReadStreamClose(readStream);
CFReadStreamUnscheduleFromRunLoop(readStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
CFRelease(readStream);
//just keep the response body and release requests
CFDataRef responseBodyData = CFHTTPMessageCopyBody(response);
if (response)
{
CFRelease(response);
}
//get the response as a string
if (responseBodyData)
{
CFShow(CFSTR("\nResponse Data:"));
LogData(responseBodyData);
CFRelease(responseBodyData);
}
}
I understood how it works, and started implementing it ..... only to get this error:
'CFReadStreamCreateForHTTPRequest' is deprecated: first deprecated in macOS 10.11 - Use NSURLSession API for http requests
There's absolutely zero examples how to use NSURLSession
for C , or how to bypass that idiotic "is deprecated" error.
Any help on how am I supposed to code this in C now?
PS. I don't want to use any third-party libraries. This is a simple task that was available with simple API calls (as I showed above.)
PS2. Sorry I am not an Apple developer, and I'm not used to features being deprecated on the whim.
CodePudding user response:
There are 3 options.
- Ignore the warning.
- Use ObjC runtme.
- Use libcurl
The first one is the easiest and the second one is the hardest solutions for your skills. The third option is easy and the most advanced solution - if you extend you software with new features, CFNetwork
will lack of functionality.