Связь с серверами HTTP
Эта глава объясняет, как создать, отправьте и получите Запросы HTTP и ответы.
Создание запроса CFHTTP
Запрос HTTP является сообщением, состоящим из метода для удаленного сервера для выполнения, объект воздействовать на (URL), заголовки сообщения и тело сообщения. Методы обычно являются одним из следующего: GET
, HEAD
, PUT
, POST
, DELETE
, TRACE
, CONNECT
или OPTIONS
. Создание Запроса HTTP с CFHTTP требует четырех шагов:
Генерируйте объект сообщения CFHTTP с помощью
CFHTTPMessageCreateRequest
функция.Установите организацию сообщения с помощью функции
CFHTTPMessageSetBody
.Установите заголовки сообщения с помощью
CFHTTPMessageSetHeaderFieldValue
функция.Сериализируйте сообщение путем вызывания функции
CFHTTPMessageCopySerializedMessage
.
Пример кода был бы похож на код в Перечислении 3-1.
Перечисление 3-1 , Создающее Запрос HTTP
CFStringRef bodyString = CFSTR(""); // Usually used for POST data |
CFDataRef bodyData = CFStringCreateExternalRepresentation(kCFAllocatorDefault, |
bodyString, kCFStringEncodingUTF8, 0); |
CFStringRef headerFieldName = CFSTR("X-My-Favorite-Field"); |
CFStringRef headerFieldValue = CFSTR("Dreams"); |
CFStringRef url = CFSTR("http://www.apple.com"); |
CFURLRef myURL = CFURLCreateWithString(kCFAllocatorDefault, url, NULL); |
CFStringRef requestMethod = CFSTR("GET"); |
CFHTTPMessageRef myRequest = |
CFHTTPMessageCreateRequest(kCFAllocatorDefault, requestMethod, myURL, |
kCFHTTPVersion1_1); |
CFDataRef bodyDataExt = CFStringCreateExternalRepresentation(kCFAllocatorDefault, bodyData, kCFStringEncodingUTF8, 0); |
CFHTTPMessageSetBody(myRequest, bodyDataExt); |
CFHTTPMessageSetHeaderFieldValue(myRequest, headerFieldName, headerFieldValue); |
CFDataRef mySerializedRequest = CFHTTPMessageCopySerializedMessage(myRequest); |
В этом примере кода, url
сначала преобразовывается в объект CFURL путем вызова CFURLCreateWithString
. Тогда CFHTTPMessageCreateRequest
вызывается с четырьмя параметрами: kCFAllocatorDefault
указывает, что средство выделения системной памяти по умолчанию должно использоваться для создания ссылки сообщения, requestMethod
указывает метод, такой как метод POST, myURL
указывает URL, такой как http://www.apple.com
, и kCFHTTPVersion1_1
указывает, что версия HTTP сообщения должна быть 1.1.
Ссылка объекта сообщения (myRequest
) возвращенный CFHTTPMessageCreateRequest
тогда отправляется в CFHTTPMessageSetBody
вместе с организацией сообщения (bodyData
). Тогда вызовите CFHTTPMessageSetHeaderFieldValue
использование той же ссылки объекта сообщения вместе с именем заголовка (headerField
), и значение, которое будет установлено (value
). Параметр заголовка является объектом CFString такой как Content-Length
, и параметр, передаваемый по значению является объектом CFString такой как 1260
. Наконец, сообщение сериализируется путем вызова CFHTTPMessageCopySerializedMessage
и должен быть отправлен через поток записи в предполагаемого получателя, в этом примере http://www.apple.com
.
Когда сообщение больше не будет необходимо, выпустите объект сообщения и сериализированное сообщение. См. Перечисление 3-2 для примера кода.
Перечисление 3-2 , Выпускающее Запрос HTTP
CFRelease(myRequest); |
CFRelease(myURL); |
CFRelease(url); |
CFRelease(mySerializedRequest); |
myRequest = NULL; |
mySerializedRequest = NULL; |
Создание ответа CFHTTP
Шаги для создания ответа HTTP почти идентичны тем для создания Запроса HTTP. Единственная разница - это вместо вызова CFHTTPMessageCreateRequest
, Вы вызываете функцию CFHTTPMessageCreateResponse
использование тех же параметров.
Десериализация входящего запроса HTTP
Для десериализации входящего Запроса HTTP создайте пустое сообщение с помощью CFHTTPMessageCreateEmpty
функция, передавая TRUE
как isRequest
параметр, чтобы указать, что должно быть создано пустое сообщение запроса. Тогда добавьте входящее сообщение к пустому сообщению с помощью функции CFHTTPMessageAppendBytes
. CFHTTPMessageAppendBytes
десериализовывает сообщение и удаляет любую управляющую информацию, которую оно может содержать.
Продолжайте делать это до функции CFHTTPMessageIsHeaderComplete
возвраты TRUE
. Если Вы не проверяете на CFHTTPMessageIsHeaderComplete
возвратиться TRUE
, сообщение может быть неполным и ненадежным. Выборка использования этих двух функций может быть замечена в Перечислении 3-3.
Перечисление 3-3 , Десериализовывающее сообщение
CFHTTPMessageRef myMessage = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, TRUE); |
if (!CFHTTPMessageAppendBytes(myMessage, &data, numBytes)) { |
//Handle parsing error |
} |
В примере, data
данные, которые должны быть добавлены и numBytes
длина data
. Можно хотеть вызвать CFHTTPMessageIsHeaderComplete
проверять, что заголовок добавленного сообщения завершен.
if (CFHTTPMessageIsHeaderComplete(myMessage)) { |
// Perform processing. |
} |
С десериализованным сообщением можно теперь вызвать любую из следующих функций для извлечения информации из сообщения:
CFHTTPMessageCopyBody
получить копию организации сообщенияCFHTTPMessageCopyHeaderFieldValue
получить копию определенного значения поля заголовкаCFHTTPMessageCopyAllHeaderFields
получить копию всех полей заголовка сообщенияCFHTTPMessageCopyRequestURL
получить копию URL сообщенияCFHTTPMessageCopyRequestMethod
получить копию метода запроса сообщения
Когда Вы больше не нуждаетесь в сообщении, выпускаете и избавляетесь от него должным образом.
Десериализация входящего ответа HTTP
Так же, как создание Запроса HTTP очень подобно созданию ответа HTTP, десериализовывание входящего Запроса HTTP также очень подобно десериализации входящего ответа HTTP. Единственное важное различие - это при вызове CFHTTPMessageCreateEmpty
, необходимо передать FALSE
как isRequest
параметр, чтобы указать, что сообщение, которое будет создаваться, является ответным сообщением.
Используя поток чтения, чтобы сериализировать и отправить запросы HTTP
Можно использовать объект CFReadStream сериализировать и отправить запросы CFHTTP. При использовании объекта CFReadStream отправить запрос CFHTTP открытие потока заставляет сообщение быть сериализированным и отправленным за один шаг. Используя объект CFReadStream отправить запросы CFHTTP упрощает получать ответ на запрос, потому что ответ доступен как свойство потока.
Сериализация и отправка запроса HTTP
Для использования объекта CFReadStream сериализировать и отправить Запрос HTTP сначала создайте запрос CFHTTP и установите тело сообщения и заголовки, как описано в Создании Запроса CFHTTP. Тогда создайте объект CFReadStream путем вызывания функции CFReadStreamCreateForHTTPRequest
и передавая запрос Вы просто создали. Наконец, откройте поток чтения с CFReadStreamOpen
.
Когда CFReadStreamCreateForHTTPRequest
вызывается, это заставляет копию запроса CFHTTP возразить, что это передается. Таким образом, при необходимости, Вы могли бы сразу выпустить объект запроса CFHTTP после вызова CFReadStreamCreateForHTTPRequest
.
Поскольку поток чтения открывает сокетное соединение с сервером, указанным myUrl
параметру, когда запрос CFHTTP создавался, некоторое количество времени, нужно позволить передать, прежде чем поток будет считаться открытым. Открытие потока чтения также заставляет запрос быть сериализированным и отправленным.
Выборка того, как сериализировать и отправить Запрос HTTP, может быть замечена в Перечислении 3-4.
Перечисление 3-4 , Сериализирующее Запрос HTTP с потоком чтения
CFStringRef url = CFSTR("http://www.apple.com"); |
CFURLRef myURL = CFURLCreateWithString(kCFAllocatorDefault, url, NULL); |
CFStringRef requestMethod = CFSTR("GET"); |
CFHTTPMessageRef myRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, |
requestMethod, myUrl, kCFHTTPVersion1_1); |
CFHTTPMessageSetBody(myRequest, bodyData); |
CFHTTPMessageSetHeaderFieldValue(myRequest, headerField, value); |
CFReadStreamRef myReadStream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, myRequest); |
CFReadStreamOpen(myReadStream); |
Проверка ответа
После планирования запроса на цикл выполнения Вы в конечном счете получите заголовок полный обратный вызов. В этой точке можно вызвать CFReadStreamCopyProperty
получить ответ сообщения от потока чтения:
CFHTTPMessageRef myResponse = (CFHTTPMessageRef)CFReadStreamCopyProperty(myReadStream, kCFStreamPropertyHTTPResponseHeader); |
Можно получить полную строку состояния из ответного сообщения путем вызывания функции CFHTTPMessageCopyResponseStatusLine
:
CFStringRef myStatusLine = CFHTTPMessageCopyResponseStatusLine(myResponse); |
Или получите просто код состояния из ответного сообщения путем вызывания функции CFHTTPMessageGetResponseStatusCode
:
UInt32 myErrCode = CFHTTPMessageGetResponseStatusCode(myResponse); |
Обработка ошибок аутентификации
Если код состояния, возвращенный функцией CFHTTPMessageGetResponseStatusCode
401 (удаленный сервер требует, чтобы информация аутентификации) или 407 (прокси-сервер потребовал аутентификации), необходимо добавить информацию аутентификации к запросу и отправить его снова. Считайте Связь с Аутентификацией Серверов HTTP для получения информации о том, как обработать аутентификацию.
Обработка ошибок перенаправления
Когда CFReadStreamCreateForHTTPRequest
создает поток чтения, автоматическое перенаправление для потока отключено по умолчанию. Если унифицированный указатель ресурсов или URL, к которому отправлен запрос, будет перенаправлен к другому URL, то отправление запроса приведет к ошибке, код состояния которой колеблется от 300 до 307. При получении ошибки перенаправления необходимо закрыть поток, создать поток снова, включить автоматическое перенаправление для нее и открыть поток. См. Перечисление 3-5.
Перечисление 3-5 , Перенаправляющее поток HTTP
CFReadStreamClose(myReadStream); |
CFReadStreamRef myReadStream = |
CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, myRequest); |
if (CFReadStreamSetProperty(myReadStream, kCFStreamPropertyHTTPShouldAutoredirect, kCFBooleanTrue) == false) { |
// something went wrong, exit |
} |
CFReadStreamOpen(myReadStream); |
Можно хотеть включить автоматическое перенаправление каждый раз, когда Вы создаете поток чтения.
Отмена незаконченного запроса
Как только запрос был отправлен, не возможно препятствовать тому, чтобы удаленный сервер действовал на него. Однако, если Вы больше не заботитесь о данных ответа, можно закрыть поток.