пятница, 26 ноября 2010 г.

[prog] Любопытная ошибка при использовании libcurl, CURLOPT_READFUNCTION и std::stringstream

Преамбула. Необходимо было через libcurl делать HTTP POST запросы и получать ответы от HTTP-сервера. Тело POST-запроса подготавливалось в std::stringstream. Для того, чтобы отдать его содержимое библиотеке libcurl я сделал специальную вспомогательную функцию, приблизительно такого вида:

size_t
std_istream_read_function(
  void * to,
  size_t size,
  size_t nmemb,
  void * raw_stream_pointer )
  {
    std::istream * from =
        reinterpret_cast< std::istream * >( raw_stream_pointer );
    if( !from )
      return CURL_READFUNC_ABORT;

    if( !(*from) )
      if( from->eof() )
          return 0;
      else
        return CURL_READFUNC_ABORT;

    const size_t capacity = size * nmemb;
    from->read(
        reinterpret_cast< char * >( to ), capacity );

    return from->gcount();
  }

А затем регистрировал данную функцию в качестве параметра CURLOPT_READFUNCTION (а в качестве CURLOPT_READDATA передавал указатель на stringstream). Соответственно, когда libcurl устанавливал соединение, проходил HTTP-аутентификацию и отсылал на удаленную сторону HTTP-заголовки, он вызывал мою функцию и получал от меня тело POST-а.

Теперь амбула :)

Все это дело красиво работало. Но не всегда. Часть запросов почему-то завершалась неудачно. После чего спецы, которые обслуживали HTTP-сервер, стали жаловаться, что в некоторых случаях от нас приходят только HTTP-заголовки, но нет тела POST-а.

Разбирательство показало, что в этих случаях происходит следующее:

  • для взаимодействия с HTTP-сервером используются Keep-Alive соединения;
  • 1, 2, 3,…, (i-1)-ый обмен с HTTP-сервером проходят без сучка и задоринки;
  • между (i-1)-м и i-м запросами проходит несколько десятков секунд;
  • при выполнении i-го запроса libcurl передает HTTP-заголовки и тело POST, после чего libcurl:
    • пишет, что текущее соединение с сервером оказалось разорвано;
    • предупреждает о том, что попытается восстановить соединение;
    • сообщает, что соединение восстановлено;
    • отсылает HTTP-заголовки;
    • некоторое время ничего не делает;
    • получает ответ от удаленной стороны.

Т.е., действительно, в таких случаях от нас тело POST-а не уходило повторно. По сообщениям libcurl ситуация выглядит приблизительно так:

# Re-using existing connection! (#0) with host YYY
# Connected to YYY (zzz.zzz.zzz.zzz) port 8080 (#0)
< POST /bla-bla-bla HTTP/1.1
< Content-Type: …
< Content-Length:…
<
< <?xml>…</xml>
# Connection died, retryinga fresh connect
# Closing connection #0
# Issue another request to this URL: 'http://YYY:8080/bla-bla-bla'
# About to connect() to YYY port 8080 (#0)
#    Trying 193.41.60.77...
# connected
# Connected to YYY (zzz.zzz.zzz.zzz) port 8080 (#0)
< POST /bla-bla-bla HTTP/1.1
< Content-Type: …
< Content-Length:…
# HTTP 1.0, assume close after body
> HTTP/1.0 200 OK
> Server: Apache-Coyote/1.1
>  Content-Type: text/xml;charset=ISO-8859-1
> Content-Length: 152

Т.е. соединение висело без дела достаточно времени, удаленная сторона его прибила (или оно просто само умерло из-за каких-то сетевых неприятностей). При попытке переиспользования соединения libcurl это диагностировал и попытался восстановить соединение и передать данные заново. Только почему-то не передал.

А не передал по простой причине – моя функция при новом обращении к ней от libcurl-а ничего больше не отдала.

Ларчик, как водится, открывался просто – во время первого обращения к std_istream_read_function был достигнут конец входного потока (у объекта stringstream был установлен eofbit). А посему последующие обращения сразу же обламывались.

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

В процессе разбирательства с libcurl выяснилось, что в нее в версии 7.18.0 была добавлена специальная опция CURLOPT_SEEKFUNCTION, которая специально для таких вещей предназначена – для перехода на конкретную позицию передаваемых на HTTP-сервер данных. Но, как показали эксперименты, для POST-запросов она не используется. Поскольку предназначена для операций upload (т.е. HTTP PUT-запросов) и для случаев с разрывами keep-alive соединений она не задействуется.

Посему пришлось вносить в свою функцию несколько строк:

if( !(*from) )
  if( from->eof() )
    {
      // Для того, чтобы можно было начать читать поток заново,
      // если это потребуется из-за разрыва keep-alive соединения.
      from->clear();
      from->seekg( 0 );

      return 0;
    }
  else
    return CURL_READFUNC_ABORT;

После чего все заработало.

PS. Логгирования мало не бывает! :)

[life.art] Портреты из мусора Зака Фримана

Американский художник Зак Фриман начиная с 1999 года создает серию картин из мусора – он собирает всякую ерунду, наклеивает ее на деревянную основу:

В результате чего получается вот такое:

Если интересно, то вот галерея картин из мусора.

четверг, 25 ноября 2010 г.

[life.sport.darts] Новая мишень месяц спустя

Прошел месяц после замены мишени :)

Остается надеяться, что месяца полтора она еще проживет :)

вторник, 23 ноября 2010 г.

[prog] Маленький хинт: MS SQL Server, NT Authentification и ODBC

Из C++ных программ работаем с MS SQL Server через ODBC. Обычно настраиваем строку подключения вручную (т.е. с указанием параметров DRIVER, SERVER и пр.), хотя иногда используется и настройка через системный DSN. Но всегда раньше подключались к БД через SQL Server-аутентификацию.

А тут потребовалось для подключения к MS SQL-ю использовать NT Authentification. Попробовали настроить системный DSN (из под другого пользователя-администратора), но при попытке подключиться к БД натыкались на ошибку: [Microsoft][ODBC Driver Manager] Data source name not found and no default driver specified.

Честно скажу, фиг знает почему не удавалось найти DSN. Но в результате гугления нашелся более простой способ. Оказывается, в параметрах подключения нужно указать “Trusted_Connection=yes”. Например, строка подключения к БД может иметь вид:

DRIVER={SQL Native Client};SERVER=somehost;DATABASE=somedb;Trusted_Connection=yes

И все, и не нужно DSN-ы настраивать.

PS. Источник.

[life.sport.darts] Хвастаюсь: первый 180 во время матча

Не смотря на то, что на тренировках я уже довольно регулярно делаю 180, в проходящих у нас в офисе матчах я сделал сегодня 180 в первый раз. В матче это намного сложнее, т.к. в отличии от тренировок, руки трясутся. Сильно трясутся :)

Немного обидно что не смотря на максимум я все-таки проиграл. Причем проиграл и тот лег, в котором максимум выбил. Но радует другое.

У меня уже два проигрыша подряд в течении недели (и это после длинного ряда выигрышей). Причем первый матч я проиграл вообще по всем статьям – у меня совершенно ничего не получалось и я не до конца понимаю, чем же это было вызвано. Но несколько дней усиленных тренировок постепенно выправляют ситуацию и сегодня я играл уже лучше (что было видно и по счету и по моим собственным ощущениям). И, главное, чувствуется, что потихонечку былая форма восстанавливается.

Так что тренироваться продолжу с увеличенной энергией :)

понедельник, 22 ноября 2010 г.

[blog] Небольшое извинение перед читателями

Хочу извинится перед читателями, которые ждут в моем блоге заметок о программировании. К сожалению, в последние дни писать на эти темы некогда – много разнообразной работы, увеличился объем тренировок, да и трансляции Grand Slam of Darts отнимали немало времени. И такая ситуация, боюсь, сохранится еще в течении пары недель. Поэтому вряд ли мне удаться написать что-нибудь интересное на тему программизмов в ближайшее время. Есть несколько тем, на которые хотелось бы высказаться, и я постараюсь это сделать как только представиться возможность.

Пока же буду выкладывать ссылки на поднакопившиеся материалы из категории “Вау!” Надеюсь, что это хоть как-то скрасит ожидание :)

Вот, для затравки и для поднятия настроения в начале рабочей недели, маленький пример занимательной арифметики в картинках:

Остальные примеры можно посмотреть здесь.

воскресенье, 21 ноября 2010 г.

[life.sport.darts] Завершился Grand Slam of Darts 2010

После двух абсолютно невыразительных полуфиналов (Стив Битон сопротивлялся Скотту Уэйтсу лишь до счета 7:7, а потом уступил 9:16; а в матче Джеймса Уэйда против Уэйна Джонса вообще борьбы не было – Уэйд весь матч шел впереди и уверенно выиграл 16:6) турнир Grand Slam of Darts 2010 завершился совершенно невероятным финалом Джеймс Уэйд против Скотта Уэйтса.

Джеймс Уэйд начал очень мощно – 8:0! Но Скотт Уэйтс сначала потихонечку (8:2), затем увереннее (9:6), затем еще увереннее (11:9) перехватывал инициативу. Чтобы при счете 11:11 наступил, на мой взгляд, переломный момент матча.

Уэйтс вышел на не очень большой checkout (по-моему, у него было меньше сотни), но не смог закрыться и оставил себе 9 очков. Тогда как у Уэйда было 166. Уэйд смог выйти на 40. Затем пришел черед бросать Уэйтсу. И Скотт умудрился первым дротиком вместо 1 попасть в 18! Огромный шанс для Уэйда. Однако, случилось почти невероятное – Уэйд не смог закрыть 40 тремя дротиками (первым промахнулся, второй пошел в 20, а третий мимо d10). Ну а тут уж Уэйтс своей возможности не упустил – первым дротиком в 1, вторым в d4. Счет стал 11:12, после чего вполне закономерно дошел и до 12:16 – Скотт Уэйтс выиграл!

Лично мне интересно было следить именно за Уэйтсом по двум причинам. Во-первых, это игрок BDO, который, тем не менее, смог уделать сильных представителей PDC. А во-вторых, Уэйтс играет дротками фирмы Red Dragon Darts (у которой я и сам когда-то закупался). И, кроме того, я впервые увидел, как кто-то играет дротиками такой необычной (на мой взгляд) формы:

Да как играет! :)

[life.photo] Тибет на фотографиях Юрия Бирюкова

У нас в городе живет фотограф, путешественник и преподаватель Юрий Бирюков. Я сам с ним, однако, не знаком, но несколько моих коллег знают его лично и даже посещали его занятия. Как раз с их подачи я и почитываю иногда блог Юрия. А сегодня представляю в рубрике “Знакомство с фотомастером” некоторые из его фотографий, сделанные в путешествиях по Тибету.

Под катом размещено несколько фотографий из блога Брия Бирюкова и ссылки на заметки из которых я их вытащил. Имхо, лучше пройтись по этим ссылкам, почитать рассказы о путешествии и посмотреть другие снимки – атмосфера будет совсем-совсем другая.

[life.sport.darts] Стив Битон выбил Фила Тейлора из Grand Slam of Darts!

Только что закончился последний четвертьфинал Grand Slam of Darts. В сумасшедшем матче Стив Битон выиграл у Фила Тейлора – 16:14!!!

Сначала Фил Тейлор выигрывал 4:1, потом Битон сравнял до 5:5, а потом и вышел вперед 7:5, затем Фил Тейлор вырвался далеко вперед (10:7, 14:11) и казалось, что Битону ничего не светит. Но счет стал сначала 14:14 и, наконец, 15:14.

А checkout в последнем леге был вообще невообразимым. Когда Битон вышел списывать 105 у Тейлора было порядка 260. Но Битон не смог сделать checkout – у него осталось 40 (40 – это всего лишь одно попадание в d20). Тейлор списал свой счет до 93, но пришла очередь бросать Битону. Первый дротик мимо, а второй – в 5! Пришлось третьим дротиком Стиву выходить на 32.

У Тейлора появился реальный шанс сравнять счет, но третьим дротиком он не попал в Bull и у него осталось 25.

Бросать вышел Битон. Нужно было попадать в d16, а в этот сектор Стив весь матч попадал стабильно. Но под тяжестью момента он промазал первым дротиком. Затем вторым. Попал третьим!

В отличии от Тейлора у Битона в этом матче с попаданиями в удвоения было все нормально. Что и сказалось в драматической развязке последнего лега.

Аналогичная история сегодня приключилась в матче Джеймса Уэйда и Терри Дженкинса. Когда при счете в матче 15:15 и при счете в леге, кажется, 20:20 Уэйд вышел бросать и вместо своего любимого d10 попал просто в 10, а потом вторым дротиком промазал мимо d5, но закрылся все-таки третьим броском.

В общем, три четвертьфинала из четырех оказались просто фантастическими триллерами. Только Скот Уэйтс более-менее уверенно уделал Ко Стомпа. И завтра он сразится с Битоном в полуфинале. Предполагаю, там будет на что посмотреть. Оба игрока на турнире демонстрируют очень мощную игру.