Web Services and Unicode

An application that I’m working on uses a SOAP web service to send user registration data to a server, which I implemented using Web Services Core Framework. After the user enters his details, I create a SOAP request to send it to the server:

	NSDictionary* headers = [NSDictionary dictionaryWithObjectsAndKeys: soapaction, @"SOAPAction",  "text/xml; charset=utf-8", @"Content-Type", nil];

	fRef = WSMethodInvocationCreate((CFURLRef) url, (CFStringRef) method, (CFStringRef) kWSSOAP2001Protocol);
	WSMethodInvocationSetProperty(fRef, kWSHTTPExtraHeaders, headers);
	WSMethodInvocationSetProperty(fRef, kWSSOAPBodyEncodingStyle, kWSSOAPStyleDoc);
	WSMethodInvocationSetProperty(fRef, kWSSOAPMethodNamespaceURI, Namespace);
        WSMethodInvocationSetParameters(fRef, (CFDictionaryRef)params, nil);
	fResult = (NSDictionary*)WSMethodInvocationInvoke(fRef);

This works perfectly unless the user enters multi-byte Asian characters. I immediately suspected that I was assuming somewhere that characters are one byte, but it turned out to be crashing deep inside CFNetwork code.

The stack trace always looked like this:

#0  0x94ee7372 in szone_error ()
#1  0x94e119fe in szone_free ()
#2  0x94e112cd in free ()
#3  0x938f98d8 in entityEncodeString ()
#4  0x938f2769 in SOAPProtocolHandler::serialize ()
#5  0x938f3715 in SOAPProtocolHandler::createRequest ()
#6  0x938f381d in SOAPWebServiceMethodInvocation::constructMessage ()
#7  0x938f1041 in HTTPClosureSource::initConnection ()
#8  0x938f16b6 in HTTPClosureSource::perform ()
#9  0x91df263f in CFRunLoopRunSpecific ()
#10 0x91df2cd8 in CFRunLoopRunInMode ()
#11 0x938f6753 in WSMethodInvocationInvoke ()

After lots of googling & experimentation, I came up with a fairly simple fix using WSMethodInvocationAddSerializationOverride to add a custom serialization function.

Right after I create the web service invocation, I added:

 WSMethodInvocationAddSerializationOverride(fRef, CFStringGetTypeID(), _stringToXML, NULL);

My custom serialization function is very trivial:

static CFStringRef _stringToXML(WSMethodInvocationRef invocation, CFTypeRef obj, void *info)
    if ((CFGetTypeID(obj) == CFStringGetTypeID()) && ![(NSString *)obj isEqualToString:@""])
        NSString *result = [[NSString alloc] initWithFormat:@"<%%@>%@</%%@>",  obj];
        return (CFStringRef)result;
    return NULL;

This simply returns an XML snippet such as <Name>Your name here</Name>.

Leave a Reply