Связь с серверами HTTP

Эта глава объясняет, как создать, отправьте и получите Запросы HTTP и ответы.

Создание запроса CFHTTP

Запрос HTTP является сообщением, состоящим из метода для удаленного сервера для выполнения, объект воздействовать на (URL), заголовки сообщения и тело сообщения. Методы обычно являются одним из следующего: GET, HEAD, PUT, POST, DELETE, TRACE, CONNECT или OPTIONS. Создание Запроса HTTP с CFHTTP требует четырех шагов:

  1. Генерируйте объект сообщения CFHTTP с помощью CFHTTPMessageCreateRequest функция.

  2. Установите организацию сообщения с помощью функции CFHTTPMessageSetBody.

  3. Установите заголовки сообщения с помощью CFHTTPMessageSetHeaderFieldValue функция.

  4. Сериализируйте сообщение путем вызывания функции 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.
}

С десериализованным сообщением можно теперь вызвать любую из следующих функций для извлечения информации из сообщения:

Когда Вы больше не нуждаетесь в сообщении, выпускаете и избавляетесь от него должным образом.

Десериализация входящего ответа 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);

Можно хотеть включить автоматическое перенаправление каждый раз, когда Вы создаете поток чтения.

Отмена незаконченного запроса

Как только запрос был отправлен, не возможно препятствовать тому, чтобы удаленный сервер действовал на него. Однако, если Вы больше не заботитесь о данных ответа, можно закрыть поток.