cURL & Proxy & HTTPS & Authorization == CURLE_RECV_ERROR

Да, баги в тулзах преследуют меня всю неделю. На сей раз отличился cURL с древним багом связанным с запросом отправляемым по HTTPS через прокси. Похоже что ситуация не слишком распространенная, т.к. надо не только работать через прокси, но еще на прокси должна быть включена авторизация и при отправке запроса пароль должен быть не указан. В принципе, в такой ситуации ожидаешь код 407, просишь пользователя ввести пароль и наступает счастье. Но вот cURL думает иначе. Вместо того чтоб вернуть 407 и страничку с ошибкой от прокси, которые судя по его логу присутствуют, зачем-то возвращается CURLE_RECV_ERROR.

Ignore 1380 bytes of response-body
Received HTTP code 407 from proxy after CONNECT
Closing connection #0
Failure when receiving data from the peer

Какого-либо корректного выхода из данной ситуации я не нашел. Так что, при работе через прокси, если пароль не задан и возникает ошибка CURLE_RECV_ERROR, единственное что остается – повторить запрос, предварительно указав параметры авторизации на проксе.

LLVM, чтоб его.

Баг 2008 года в LLVM портит мне жизнь. Берем тестовый файлик следующего содержания:

class RPC_Service
{
public:
  virtual ~RPC_Service ();
};
class EventLoggingInterface
{
  virtual void
    Ev (const char *file, int line, int sev, const char *fmt,
        ...) const = 0;
};
class RPCCore : RPC_Service, EventLoggingInterface
{
  virtual void
    Ev (const char *file, int line, int sev, const char *fmt, ...) const;
};
void
RPCCore::Ev (const char *file, int line, int sev, const char *format, ...) const
{
}

Выполняем команду:

/Developer/usr/bin/llvm-g++ thunk_error.cpp -c

И в результате получаем:

thunk_error.cpp:19: error: generic thunk code fails for method ‘virtual void RPCCore::Ev(const char*, int, int, const char*, ...) const’ which uses ‘...’

Неужели придется отказаться от LLVM? Интерфейс, в моем случае, поменять врятли удастся. Вобщем писать кроссплатформенный код это еще та засада

Квалификатор restraint

Последнее время я довольно много пишу на Си, да чистое Си, без никаких плюсов, обджектив и прочей радости. На первый взгляд, в Си все очень и очень просто. Но, как оказалось, это только кажется, на том же BrainBench в тесте по Си мне удалось набрать всего 3.25, против 4.34 по плюсам. А еще говорят что BB ни о чем не говорит, говорит и еще как
А вступление это вот к чему. Я решил написать небольшую серию заметок про Си, если говорить точнее, то про C99. Ведь лучший способ что-то запомнить – это написать.
В состав C99 был добавлен новый квалификатор типа – restraint.
Данный квалификатор используется применительно к указателям и говорит компилятору о том, что данные, на которые указывают такие указатели, не являются пересекающимися объектами. Чисто теоретически, подобная информаия позволяет компилятору произвести дополнительные виды оптимизации. Теоретически, т.к. покрутив этот квалификатор и так и сяк я не сумел заставить компилятор хоть как-то изменить генерируемый код.
Основные варианты использования квалификатора restraint:

Continue reading

Работаем с таймерами.

До появления вот этой темы на РСДНе я как-то не предавал особого значения таймерам в Mac OS X. Ну есть они и есть, как будет нужно так разберусь. Но как оказалось, стандартных POSIX вызовов, таких как timer_create/timer_delete у нас нет, а то что есть не всегда подходит.

Хорошенько поковырявшись в документации я пришел к следующему выводу. Всего доступны 3 способа работы с таймерами, дающие приблизительно одинаковый эффект:

  • NSTimer из Foundation;
  • RunLoop из CoreFoundation;
  • Таймеры из Mach. Хотя у меня данный пункт вызвал некоторые трудности.

Так что по порядку, с краткими иллюстрациями.

Continue reading

cURL и корневые сертификаты

В описанном раньше способе работы с HTTPS соединением есть одна проблема – необходимо каким-то способом тащить за собой актуальный список корневых сертификатов. А тащить, обновлять, следить за изменениями… Вобщем задача не из тривиальных.
Не знаю как на Венде, а в Mac OS X можно все это дело переложить на Apple:

sudo security find-certificate -a -p /System/Library/Keychains/SystemRootCertificates.keychain > roots.pem

После чего передать полученный список корневых сертификатов в cURL.

HTTPS & cURL

В пятницу, работая над одной из наших подсистем, я занимался прикручиванием cURL и тихо охреневал с того, что пишет народ народ в интернете по поводу реализации работы с HTTPS при использовании cURL. Лично я всегда считал что HTTPS необходим для установки защищенного соединения. Из чего следует что, во-первых, данные передаются в шифрованном виде, и во-вторых, мы знаем с кем общаемся.
Так вот, как выяснилось на практике, если с шифрованием ни у кого вопросов не возникает, то вот на авторизацию все кладут болт.
Причем основательно, например так:

curl_easy_setopt( curl_, CURLOPT_SSL_VERIFYPEER, 0 );

Что отключает проверку полученного сертификата. Ну а особо одаренные делают даже так:

curl_easy_setopt( curl_, CURLOPT_SSL_VERIFYHOST, 0 );

Что отключает проверку соответствия хоста, указанного в сертификате, с хостом, с которым мы общаемся.

Что интересно, если проверку канала обычно отключают из за глупости и лени, то проверку хоста может отключить разве что феерический идит, но, как показал гугл, их полно. Подобное решение для меня большая загадка, зачем нужно шифровать данные, если не известно с кем идет общение?

Так вот, как оказалось все дело в том, что если не выключить проверку канала то возникают различные неприятные ошибки связанные с невозможностью проверки сертификата канала. Например вот такие:

error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed

В то же время информации том, что делать в такой ситуации в интернете по большому счету нет. Но все не так уж и страшно, всего-то немного терпения и экспериментов.

Все решается довольно просто. Необходимо скачать набор сертификатов с сайта cURL и передать их в библиотеку при помощи CURLOPT_CAINFO. Хотя в документации к библиотеке и говорится что эта функция не работает под Windows, это не соответствует действительности. Она отлично работает как в Mac OS X так и в Windows. В cURL для установки сертификатов будет вызвана OpenSSL функция SSL_CTX_load_verify_locations, соответственно все сертификаты должны быть в PEM формате.

Собственно это все, после этого все должно отлично работать.

CodeReview. Теория и практика.

CodeReview – классная штука, не правда-ли? Если отвлеченно подумать об открывающихся возможностях и количестве потенциальных ошибок, найденных при проведении CodeReview, то начинает казаться что практика просто великолепна и мега полезна.
Обычно, путь к внедрению этой практики извилист и тернист, но ништяки, которые по легенде ожидают прошедшего, заставляют забыть о сложной дороге.
Как ни странно, первым делом, обычно, создается стандарт кодирования. Думаю, это вызвано тем что любая уважающая себя компания должна иметь собственный стандарт. В стандарте обязательно нужно прописать где ставить скобочки, а где пробелы, какие индентификаторы писать с прописной буквы, а какие со строчной. Стандарт просто необходимо согласовать, проревьювить раз надцать и утвердить. После этого весь код будет однородным и очень красивым, не зря же стандарт составляли лучшие из лучших компании! Continue reading

Objective-C биндинги

Наверное, наиболее привлекательной возможностью в Objective-C я бы назвал биндинги. И если про то, как связать данные с графическое представлением при помощи InterfaceBuilder написано очень и очень много (даже на русском ), то вот о том, как и почему вся эта магия работает можно найти разве что на сайте Apple.
Итак, вся технология биндингов базируется на двух паттернах: Key-Value Coding и Key-Value Observing. Думаю, для начала стоит написать именно про эти паттерны, а потом перейти к самим биндингам.

Итак, Key-Value Coding позволяет обратиться к данным класса по имени, включая вложенную обращения с учетом вложенной иерархии. Для начала небольшой пример иллюстрирующий, о чем же идет речь.

Пусть будет класс MyClass, следующего вида:

@interface MyClass 
@property NSString *stringProperty;
@property NSInteger integerProperty;
@property MyClass *linkedInstance;
@end

Обычно, доступ к данным этого класса реализуется как-то так:

MyClass *myInstance = [[MyClass alloc] init]; 
NSString *string = myInstance.stringProperty;
myInstance.integerProperty = 2;

MyClass *anotherInstance = [[MyClass alloc] init];
anotherInstance.integerProperty = 5;
myInstance. linkedInstance = anotherInstance;
myInstance. linkedInstance. integerProperty = 10;

В то же время, при использовании Key-Value Coding можно написать так:

MyClass *anotherInstance = [[MyClass alloc] init]; 
[anotherInstance setValue:5 forKey:@"integerProperty"];
[myInstance setValue:anotherInstance forKey:@"linkedInstance"];
[myInstance setValue:[NSNumber numberWithInt:10]
forKeyPath:@"linkedInstance.integerProperty"];

Для работы KVO паттерна необходимо чтобы класс соответствовал следующим условиям:
– Должны присутствовать методы –, -is, либо должна существовать переменная класса <key>;
– Для изменяемых значений так же необходимо наличи метода -set:;
– Метод -set: не должен проверять устанавливаемое значение.
– Если проверка устанавливаемого значения все-же необходима, в классе должен присутствовать метод -validate:error:.
На мой взгляд, самый простой способ поддержать эти требования – это создать это использовать свойства.

Для получения значений атрибутов используется метод valueForKey:. В случае если необходимо запросить значение с использованием пути, необходимо воспользоваться методом valueForKeyPath:. В случае, если запрашиваемый ключ отсутствует возбуждается исключение NSUndefinedKeyException.

Задание значения происходит полностью по аналогии с получением значения. Есть методо setValue:forKey:, устанавливающий значение указанного атрибута и setValue:forKeyPath: если необходимо задать путь. При отсутствии заданного ключа, так же будет возбуждено исключение NSUndefinedKeyException.

А вот функционал операций с коллекциями меня приятно удивил. В Objective-C есть операторы коллекций (Collection Operators), благодаря которым можно манипулировать с коллекциями по подобию SQL-запросов. Само собой, возможности очень ограниченные, тем не менее полезные.
Операторы коллекций записываются следующим образом:
путьВКоллекции.@операторКоллекции.путьКСвойству

Операторы коллекций делятся на 3 группы: простые операторы, операторы объектов и операторы массивов.
К простым операторам относятся @avg, @count, @max, @min, @sum. Их использование выгляди следующим образом. Допустим есть массив объектов Transaction с полями payee, amount и date. Тогда для получения среднего значения по полю amount можно написать такой запрос:

NSNumber *transactionAverage=[transactions valueForKeyPath”@avg.amount”];

А вот с операторы объектов радуют еще больше, например для получения выборки уникальных значений поля payee можно воспользоваться вот таким запросом:

NSArray *payees=[transactions valueForKeyPath"@distinctUnionOfObjects.payee"];

Кроме того, есть еще один оператор объектов – @unionOfObjects.

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

Разве что отсутствие возможности добавить собственный оператор для работы с коллекциями меня огорчило. Может я и не прав, но никакой информации о том, как добавить еще один оператор я не нашл.

Вторая составляющая механизма биндингов это Key-Value Observing (KVO). KVO позволяет получать уведомления об изменении заданных свойств других объектов. Проще говоря, это callback механизм, работающий по заданному стандарту.

Для регистрации observer используется сообщение addObserver:forKeyPath:options:context:. Например для того чтобы получать сообщения об изменениях свойства openingBalance объекта account необходимо послать следующее сообщение:

[account addObserver:self
forKeyPath:@"openingBalance" options:(NSKeyValueObservingOptionNew) context:NULL];

В результате чего, при изменении значения openingBalance объекта account будет вызван метод observeValueForKeyPath:ofObject:change:context: для self. В качестве context можно передать любой C-указатель.

Нотификации об изменении свойства приходит в метод observeValueForKeyPath:ofObject:change:context:. Нужно не забывать о том, что полученное сообщение должно быть проброшено дальше:

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
{
// to-do smth.
[super observeValueForKeyPath:keyPath
ofObject:object change:change
context:context];
}

Если получать нотификации больше не требуется, необходимо вспользоваться методом removeObserver:forKeyPath:.

Несмотря на то, что в большинстве случаев вполне достаточно автоматического извещения об изменениях свойств, уведомления можно сделать и в ручную. Лично мне эта возможность пригодилась только однажды – для оптимизации скорости работы GUI. Дело в том, что при отображении массива динамически изменяемых данных, интерфейс начинает перерисовываться при изменении каждого из элементов массива. И, как следствие, вместо одного обновления всех элементов разом, происходило N обновлений, что требовало дополнительных ресурсов.
К счастью, при помощи метода willChangeValueForKey: можно сообщить о том, что началось изменение значений для определенного ключа и закончить его вызовом метода didChangeValueForKey:.

Честно сказать, после того как становится понятно с KVO и KVC говорить про биндинги уже особо и нечего. Для создания биндинга используется сообщение bind:toObject:withKeyPath:options:, которое говорит получателю поддерживать заданный атрибут в синхронном состоянии. Например сообщение [joystick bind:@”angle” toObject:GraphicController withKeyPath:@”selection.shadowAngle” options:options]; можно интерпретировать следующим образом:
– bind: @”angle” если значение ассоциируемое с angle изменится,
– toObject: GraphicController то необходимо сообщиться заданному объекту GraphicController
– withKeyPath: @”selection.shadowAngle” что его значение selection.shadowAngle было изменено
– options: options используя трансформер options

Так же стоит заметить, что в iOS метод bind:toObject:withKeyPath:options: вообще не доступен. И при необходимости реализуется самостоятельно.

Для упрощения работы с биндигами, существует ряд стандартных контроллеров, облегчающих работу с массивами и древовидными структурами данных. Это NSArrayController, NSTreeController и NSDictionaryController. Кроме того, существуют контроллеры для управления видами NSViewController, объектами NSObjectController и пользовательскими настройками UserDefaultsController.

Как подружить NSColor и NSUserDefaultsController

Почему-то, скорее всего из-за не внимательного изучения документации Apple, я был уверен что без каких-то особых приседаний не возможно сохранить NSColor в NSUserDefaultsController. Ну да, у NSUserDefaultsController есть поддержка только ограниченного количества количества типов, и на просторах интернета можно найти кучу решений по сохранению неподдерживаемых типов, таких как NSColor. Например сохранить в виде NSInteger или, еще более интересный вариант – в виде строки. В то же время, есть такой замечательный трансформер (как эту штуку правильно назвать по-русски?), NSUnarchiveFromData, который позволяет преобразовать любой тип, поддерживающий протокол NSCoding, в тип NSData, который в свою очередь может быть сохранен по средствам NSUserDefaultsController.
Например, для того чтобы привязать объект NSColorWell к NSUserDefaultsController

Даже код писать не надо: