You mix Unicode and bytes.
>>> msg = u'abc'
To fix this, make sure that the content of self.headers correctly encoded, i.e. all keys, values โโin headers should be knocked out:
self.headers = dict((k.encode('ascii') if isinstance(k, unicode) else k, v.encode('ascii') if isinstance(v, unicode) else v) for k,v in self.headers.items())
Note. The character encoding of the headers has nothing to do with the character encoding of the body, that is, the XML text can be encoded independently (this is just an octet stream in terms of an HTTP message).
The same applies to self.url if it is of unicode type; convert it to a byte string (using the encoding "ascii").
An HTTP message consists of a start line, "headers", an empty line, and possibly the body of the message , so self.headers used for headers. self.url used to start the line (the http method goes here) and probably for the Host http header (if the client is http / 1.1), the XML text goes into the body of the message (as a binary blob).
It is always safe to use ASCII encoding for self.url (IDNA can be used for domain names other than ascii, the result is also ASCII).
Here rfc 7230 talks about character encoding for the headers :
Historically, HTTP allowed the contents of a text field in the Cipher ISO-8859-1 [ISO-8859-1], which only supports other encodings by using the encoding [RFC2047]. In practice, most HTTP field value headers use only a subset of the US-ASCII [USASCII] encoding. New header fields MUST limit their values โโto US-ASCII Octets. The receiver MUST treat other octets in the (obs-text) field as opaque data.
To convert XML to a byte string, see application/xml encoding conditions :
Using UTF-8 without specification, RECOMMENDED for all XML MIME objects.