<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

/*!
 *  Copyright (c) 2017 by Contributors
 * \file llvm_common.cc
 */
#ifdef TVM_LLVM_VERSION

#include <tvm/base.h>
#include <atomic>
#include <mutex>
#include <memory>
#include "llvm_common.h"

namespace tvm {
namespace codegen {

struct LLVMEnv {
  std::mutex mu;
  std::atomic<bool> all_initialized{false};

  static LLVMEnv* Global() {
    static LLVMEnv inst;
    return &inst;
  }
};

void InitializeLLVM() {
  LLVMEnv* e = LLVMEnv::Global();
  if (!e->all_initialized.load(std::memory_order::memory_order_acquire)) {
    std::lock_guard<std::mutex> lock(e->mu);
    if (!e->all_initialized.load(std::memory_order::memory_order_acquire)) {
      llvm::InitializeAllTargetInfos();
      llvm::InitializeAllTargets();
      llvm::InitializeAllTargetMCs();
      llvm::InitializeAllAsmParsers();
      llvm::InitializeAllAsmPrinters();
      e->all_initialized.store(true, std::memory_order::memory_order_release);
    }
  }
}

void ParseLLVMTargetOptions(const std::string& target_str,
                            std::string* triple,
                            std::string* mcpu,
                            std::string* mattr,
                            llvm::TargetOptions* options) {
  // setup target triple
  size_t start = 0;
  if (target_str.length() >= 4 &&
      target_str.substr(0, 4) == "llvm") {
    start = 4;
  }
  // simple parser
  triple->resize(0);
  mcpu->resize(0);
  mattr->resize(0);

  bool soft_float_abi = false;
  std::string key, value;
  std::istringstream is(target_str.substr(start, target_str.length() - start));

  while (is >> key) {
    if (key == "--system-lib" || key == "-system-lib") {
      continue;
    }
    size_t pos = key.find('=');
    if (pos != std::string::npos) {
      CHECK_GE(key.length(), pos + 1)
          << "invalid argument " << key;
      value = key.substr(pos + 1, key.length() - 1);
      key = key.substr(0, pos);
    } else {
      CHECK(is >> value)
          << "Unspecified value for option " << key;
    }
    if (key == "-target" ||
        key == "-mtriple") {
      *triple = value;
    } else if (key == "-mcpu") {
      *mcpu = value;
    } else if (key == "-mattr") {
      *mattr = value;
    } else if (key == "-mfloat-abi") {
      if (value == "hard") {
        soft_float_abi = false;
      } else if (value == "soft") {
        soft_float_abi = true;
      } else {
        LOG(FATAL) << "invalid -mfloat-abi option " << value;
      }
    } else if (key == "-device" || key == "-libs" || key == "-model") {
      // pass
    } else {
      LOG(FATAL) << "unknown option " << key;
    }
  }

  if (triple->length() == 0 ||
      *triple == "default") {
    *triple = llvm::sys::getDefaultTargetTriple();
  }
  // set target option
  llvm::TargetOptions& opt = *options;
  opt = llvm::TargetOptions();
  #if TVM_LLVM_VERSION < 50
  opt.LessPreciseFPMADOption = true;
  #endif
  opt.AllowFPOpFusion = llvm::FPOpFusion::Fast;
  opt.UnsafeFPMath = false;
  opt.NoInfsFPMath = false;
  opt.NoNaNsFPMath = true;
  if (soft_float_abi) {
    opt.FloatABIType = llvm::FloatABI::Soft;
  } else {
    opt.FloatABIType = llvm::FloatABI::Hard;
  }
}

std::unique_ptr<llvm::TargetMachine>
GetLLVMTargetMachine(const std::string& target_str,
                     bool allow_null) {
  std::string target_triple, mcpu, mattr;
  llvm::TargetOptions opt;

  ParseLLVMTargetOptions(target_str,
                         &target_triple,
                         &mcpu,
                         &mattr,
                         &opt);

  if (target_triple.length() == 0 ||
      target_triple == "default") {
    target_triple = llvm::sys::getDefaultTargetTriple();
  }
  if (mcpu.length() == 0) {
    mcpu = "generic";
  }

  std::string err;
  const llvm::Target* target =
      llvm::TargetRegistry::lookupTarget(target_triple, err);
  if (target == nullptr) {
    CHECK(allow_null) << err << " target_triple=" << target_triple;
    return nullptr;
  }
  llvm::TargetMachine* tm = target->createTargetMachine(
      target_triple, mcpu, mattr, opt, llvm::Reloc::PIC_);
  return std::unique_ptr<llvm::TargetMachine>(tm);
}

}  // namespace codegen
}  // namespace tvm
#endif  // TVM_LLVM_VERSION
<commit_msg>[CodeGen] Disable -mfloat-abi hard option for LLVM < 6.0 (#4071)<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

/*!
 *  Copyright (c) 2017 by Contributors
 * \file llvm_common.cc
 */
#ifdef TVM_LLVM_VERSION

#include <tvm/base.h>
#include <atomic>
#include <mutex>
#include <memory>
#include "llvm_common.h"

namespace tvm {
namespace codegen {

struct LLVMEnv {
  std::mutex mu;
  std::atomic<bool> all_initialized{false};

  static LLVMEnv* Global() {
    static LLVMEnv inst;
    return &inst;
  }
};

void InitializeLLVM() {
  LLVMEnv* e = LLVMEnv::Global();
  if (!e->all_initialized.load(std::memory_order::memory_order_acquire)) {
    std::lock_guard<std::mutex> lock(e->mu);
    if (!e->all_initialized.load(std::memory_order::memory_order_acquire)) {
      llvm::InitializeAllTargetInfos();
      llvm::InitializeAllTargets();
      llvm::InitializeAllTargetMCs();
      llvm::InitializeAllAsmParsers();
      llvm::InitializeAllAsmPrinters();
      e->all_initialized.store(true, std::memory_order::memory_order_release);
    }
  }
}

void ParseLLVMTargetOptions(const std::string& target_str,
                            std::string* triple,
                            std::string* mcpu,
                            std::string* mattr,
                            llvm::TargetOptions* options) {
  // setup target triple
  size_t start = 0;
  if (target_str.length() >= 4 &&
      target_str.substr(0, 4) == "llvm") {
    start = 4;
  }
  // simple parser
  triple->resize(0);
  mcpu->resize(0);
  mattr->resize(0);

  bool soft_float_abi = false;
  std::string key, value;
  std::istringstream is(target_str.substr(start, target_str.length() - start));

  while (is >> key) {
    if (key == "--system-lib" || key == "-system-lib") {
      continue;
    }
    size_t pos = key.find('=');
    if (pos != std::string::npos) {
      CHECK_GE(key.length(), pos + 1)
          << "invalid argument " << key;
      value = key.substr(pos + 1, key.length() - 1);
      key = key.substr(0, pos);
    } else {
      CHECK(is >> value)
          << "Unspecified value for option " << key;
    }
    if (key == "-target" ||
        key == "-mtriple") {
      *triple = value;
    } else if (key == "-mcpu") {
      *mcpu = value;
    } else if (key == "-mattr") {
      *mattr = value;
    } else if (key == "-mfloat-abi") {
      if (value == "hard") {
#if TVM_LLVM_VERSION < 60
        LOG(FATAL) << "-mfloat-abi hard is only supported for LLVM > 6.0";
#endif
        soft_float_abi = false;
      } else if (value == "soft") {
        soft_float_abi = true;
      } else {
        LOG(FATAL) << "invalid -mfloat-abi option " << value;
      }
    } else if (key == "-device" || key == "-libs" || key == "-model") {
      // pass
    } else {
      LOG(FATAL) << "unknown option " << key;
    }
  }

  if (triple->length() == 0 ||
      *triple == "default") {
    *triple = llvm::sys::getDefaultTargetTriple();
  }
  // set target option
  llvm::TargetOptions& opt = *options;
  opt = llvm::TargetOptions();
  #if TVM_LLVM_VERSION < 50
  opt.LessPreciseFPMADOption = true;
  #endif
  opt.AllowFPOpFusion = llvm::FPOpFusion::Fast;
  opt.UnsafeFPMath = false;
  opt.NoInfsFPMath = false;
  opt.NoNaNsFPMath = true;
  if (soft_float_abi) {
    opt.FloatABIType = llvm::FloatABI::Soft;
  } else {
    opt.FloatABIType = llvm::FloatABI::Hard;
  }
}

std::unique_ptr<llvm::TargetMachine>
GetLLVMTargetMachine(const std::string& target_str,
                     bool allow_null) {
  std::string target_triple, mcpu, mattr;
  llvm::TargetOptions opt;

  ParseLLVMTargetOptions(target_str,
                         &target_triple,
                         &mcpu,
                         &mattr,
                         &opt);

  if (target_triple.length() == 0 ||
      target_triple == "default") {
    target_triple = llvm::sys::getDefaultTargetTriple();
  }
  if (mcpu.length() == 0) {
    mcpu = "generic";
  }

  std::string err;
  const llvm::Target* target =
      llvm::TargetRegistry::lookupTarget(target_triple, err);
  if (target == nullptr) {
    CHECK(allow_null) << err << " target_triple=" << target_triple;
    return nullptr;
  }
  llvm::TargetMachine* tm = target->createTargetMachine(
      target_triple, mcpu, mattr, opt, llvm::Reloc::PIC_);
  return std::unique_ptr<llvm::TargetMachine>(tm);
}

}  // namespace codegen
}  // namespace tvm
#endif  // TVM_LLVM_VERSION
<|endoftext|>


<commit_before>#include <iostream>
#include <codecvt>
#include <realm.hpp>
#include <realm/table.hpp>
#include <realm/commit_log.hpp>
#include <realm/lang_bind_helper.hpp>
#include "ffi.hpp"
#include "transact_log_handler.hpp"
#include "shared_realm.hpp"
#include "schema.hpp"
#include "utf16accessor.hpp"

#ifdef WIN32
#define REALM_EXPORT __declspec( dllexport )
#else
#define REALM_EXPORT
#endif

using namespace realm;

namespace binding {
    realm::RealmDelegate* delegate_instance;
    void process_error(RealmError* realm_error);
}

static void convert_exception_to_error(RealmError* error)
{
    try {
        throw;
    }
    catch (const std::exception& ex) {
        error->type = RealmErrorType::system;
        error->message = ex.what();
    }
    catch (...) {
        error->type = RealmErrorType::unknown;
        error->message = "Unknown exception thrown";
    }
}

template <class T>
struct Default {
    static T default_value() {
        return T{};
    }
};
template <>
struct Default<void> {
    static void default_value() {}
};

template <class F>
auto handle_errors(F&& func) -> decltype(func())
{
    using RetVal = decltype(func());
    try {
        return func();
    }
    catch (...) {
        RealmError* out_error = new RealmError;
        convert_exception_to_error(out_error);
        binding::process_error(out_error);
        return Default<RetVal>::default_value();
    }
}

extern "C" {

using namespace realm;

REALM_EXPORT ObjectSchema* object_schema_create(const char* name)
{
    auto p = new ObjectSchema;
    p->name = name;
    return p;
}

REALM_EXPORT void object_schema_destroy(ObjectSchema* object_schema)
{
    delete object_schema;
}

REALM_EXPORT void object_schema_add_property(ObjectSchema* cls, const char* name, DataType type, const char* object_type,
                                      bool is_primary, bool is_indexed, bool is_nullable)
{
    Property p;
    p.name = name;
    p.type = static_cast<PropertyType>(type);
    p.object_type = object_type ? std::string(object_type) : std::string();
    p.is_primary = is_primary;
    p.is_indexed = is_indexed;
    p.is_nullable = is_nullable;
    cls->properties.push_back(std::move(p));
}

REALM_EXPORT std::vector<ObjectSchema>* schema_initializer_create()
{
    return new std::vector<ObjectSchema>();
}

REALM_EXPORT void schema_initializer_destroy(std::vector<ObjectSchema>* schema_initializer)
{
    delete schema_initializer;
}

REALM_EXPORT void schema_initializer_add_object_schema(std::vector<ObjectSchema>* schema_initializer, ObjectSchema* object_schema)
{
    schema_initializer->push_back(*object_schema);
}

REALM_EXPORT Schema* schema_create(std::vector<ObjectSchema>* object_schemas, size_t len)
{
    return new Schema(*object_schemas);
}

REALM_EXPORT SharedRealm* shared_realm_open(Schema* schema, const char* path, bool read_only, SharedGroup::DurabilityLevel durability,
                        const char* encryption_key)
{
    Realm::Config config;
    config.path = path;
    config.read_only = read_only;
    config.in_memory = durability != SharedGroup::durability_Full;

    config.encryption_key = std::vector<char>(&encryption_key[0], &encryption_key[strlen(encryption_key)]);

    config.schema.reset(schema);
    return new SharedRealm{Realm::get_shared_realm(config)};
}

REALM_EXPORT void shared_realm_destroy(SharedRealm* realm)
{
    delete realm;
}

REALM_EXPORT bool shared_realm_has_table(SharedRealm* realm, const char* name)
{
    Group* g = (*realm)->read_group();
    return g->has_table(name);
}

REALM_EXPORT Table* shared_realm_get_table(SharedRealm* realm, uint16_t* table_name, size_t table_name_len)
{
    Group* g = (*realm)->read_group();

    Utf16StringAccessor str(table_name,table_name_len);

    bool dummy; // get_or_add_table sets this to true if the table was added.
    return LangBindHelper::get_or_add_table(*g, str, &dummy);
}

REALM_EXPORT void shared_realm_begin_transaction(SharedRealm* realm)
{
    handle_errors([&]() {
        (*realm)->begin_transaction();
    });
}

REALM_EXPORT void shared_realm_commit_transaction(SharedRealm* realm)
{
    handle_errors([&]() {
        (*realm)->commit_transaction();
    });
}

REALM_EXPORT void shared_realm_cancel_transaction(SharedRealm* realm)
{
    handle_errors([&]() {
        (*realm)->cancel_transaction();
    });
}

REALM_EXPORT bool shared_realm_is_in_transaction(SharedRealm* realm)
{
    return (*realm)->is_in_transaction();
}

REALM_EXPORT bool shared_realm_refresh(SharedRealm* realm)
{
    return (*realm)->refresh();
}

//REALM_EXPORT void table_retain(const Table* table)
//{
//    LangBindHelper::bind_table_ptr(table);
//}
//
//REALM_EXPORT void table_release(const Table* table)
//{
//    LangBindHelper::unbind_table_ptr(table);
//}
//
//REALM_EXPORT const Table* shared_group_get_table(SharedGroup* sg, const char* name)
//{
//    using sgf = _impl::SharedGroupFriend;
//    return handle_errors([&]() {
//        auto& group = sgf::get_group(*sg);
//        return LangBindHelper::get_table(group, name);
//    });
//}
//
//REALM_EXPORT Table* shared_group_get_table_mut(SharedGroup* sg, const char* name)
//{
//    using sgf = _impl::SharedGroupFriend;
//    return handle_errors([&]() {
//        auto& group = sgf::get_group(*sg);
//        return LangBindHelper::get_table(group, name);
//    });
//}
//
//REALM_EXPORT Table* shared_group_add_table(SharedGroup* sg, const char* name)
//{
//    using sgf = _impl::SharedGroupFriend;
//    return handle_errors([&]() {
//        auto& group = sgf::get_group(*sg);
//        return LangBindHelper::add_table(group, name);
//    });
//}
//
//REALM_EXPORT size_t table_add_column(Table* table, DataType type, const char* name, bool nullable)
//{
//    return handle_errors([&]() {
//        return table->add_column(type, name, nullable);
//    });
//}
//
//REALM_EXPORT size_t table_add_empty_row(Table* table)
//{
//    return handle_errors([&]() {
//        return table->add_empty_row();
//    });
//}
//
//REALM_EXPORT int64_t table_get_int(const Table* table, size_t col_ndx, size_t row_ndx)
//{
//    return table->get_int(col_ndx, row_ndx);
//}
//
//REALM_EXPORT void table_set_int(Table* table, size_t col_ndx, size_t row_ndx, int64_t value)
//{
//    return handle_errors([&]() {
//        table->set_int(col_ndx, row_ndx, value);
//    });
//}
//
//REALM_EXPORT void row_delete(Table::RowExpr* expr) {
//    delete expr;
//}
//
//REALM_EXPORT int64_t row_get_int(const Table::RowExpr* expr, size_t col_ndx) {
//    return expr->get_int(col_ndx);
//}

}
<commit_msg>Add std::move to add_object_schema<commit_after>#include <iostream>
#include <codecvt>
#include <realm.hpp>
#include <realm/table.hpp>
#include <realm/commit_log.hpp>
#include <realm/lang_bind_helper.hpp>
#include "ffi.hpp"
#include "transact_log_handler.hpp"
#include "shared_realm.hpp"
#include "schema.hpp"
#include "utf16accessor.hpp"

#ifdef WIN32
#define REALM_EXPORT __declspec( dllexport )
#else
#define REALM_EXPORT
#endif

using namespace realm;

namespace binding {
    realm::RealmDelegate* delegate_instance;
    void process_error(RealmError* realm_error);
}

static void convert_exception_to_error(RealmError* error)
{
    try {
        throw;
    }
    catch (const std::exception& ex) {
        error->type = RealmErrorType::system;
        error->message = ex.what();
    }
    catch (...) {
        error->type = RealmErrorType::unknown;
        error->message = "Unknown exception thrown";
    }
}

template <class T>
struct Default {
    static T default_value() {
        return T{};
    }
};
template <>
struct Default<void> {
    static void default_value() {}
};

template <class F>
auto handle_errors(F&& func) -> decltype(func())
{
    using RetVal = decltype(func());
    try {
        return func();
    }
    catch (...) {
        RealmError* out_error = new RealmError;
        convert_exception_to_error(out_error);
        binding::process_error(out_error);
        return Default<RetVal>::default_value();
    }
}

extern "C" {

using namespace realm;

REALM_EXPORT ObjectSchema* object_schema_create(const char* name)
{
    auto p = new ObjectSchema;
    p->name = name;
    return p;
}

REALM_EXPORT void object_schema_destroy(ObjectSchema* object_schema)
{
    delete object_schema;
}

REALM_EXPORT void object_schema_add_property(ObjectSchema* cls, const char* name, DataType type, const char* object_type,
                                      bool is_primary, bool is_indexed, bool is_nullable)
{
    Property p;
    p.name = name;
    p.type = static_cast<PropertyType>(type);
    p.object_type = object_type ? std::string(object_type) : std::string();
    p.is_primary = is_primary;
    p.is_indexed = is_indexed;
    p.is_nullable = is_nullable;
    cls->properties.push_back(std::move(p));
}

REALM_EXPORT std::vector<ObjectSchema>* schema_initializer_create()
{
    return new std::vector<ObjectSchema>();
}

REALM_EXPORT void schema_initializer_destroy(std::vector<ObjectSchema>* schema_initializer)
{
    delete schema_initializer;
}

REALM_EXPORT void schema_initializer_add_object_schema(std::vector<ObjectSchema>* schema_initializer, ObjectSchema* object_schema)
{
    schema_initializer->push_back(std::move(*object_schema));
}

REALM_EXPORT Schema* schema_create(std::vector<ObjectSchema>* object_schemas, size_t len)
{
    return new Schema(*object_schemas);
}

REALM_EXPORT SharedRealm* shared_realm_open(Schema* schema, const char* path, bool read_only, SharedGroup::DurabilityLevel durability,
                        const char* encryption_key)
{
    Realm::Config config;
    config.path = path;
    config.read_only = read_only;
    config.in_memory = durability != SharedGroup::durability_Full;

    config.encryption_key = std::vector<char>(&encryption_key[0], &encryption_key[strlen(encryption_key)]);

    config.schema.reset(schema);
    return new SharedRealm{Realm::get_shared_realm(config)};
}

REALM_EXPORT void shared_realm_destroy(SharedRealm* realm)
{
    delete realm;
}

REALM_EXPORT bool shared_realm_has_table(SharedRealm* realm, const char* name)
{
    Group* g = (*realm)->read_group();
    return g->has_table(name);
}

REALM_EXPORT Table* shared_realm_get_table(SharedRealm* realm, uint16_t* table_name, size_t table_name_len)
{
    Group* g = (*realm)->read_group();

    Utf16StringAccessor str(table_name,table_name_len);

    bool dummy; // get_or_add_table sets this to true if the table was added.
    return LangBindHelper::get_or_add_table(*g, str, &dummy);
}

REALM_EXPORT void shared_realm_begin_transaction(SharedRealm* realm)
{
    handle_errors([&]() {
        (*realm)->begin_transaction();
    });
}

REALM_EXPORT void shared_realm_commit_transaction(SharedRealm* realm)
{
    handle_errors([&]() {
        (*realm)->commit_transaction();
    });
}

REALM_EXPORT void shared_realm_cancel_transaction(SharedRealm* realm)
{
    handle_errors([&]() {
        (*realm)->cancel_transaction();
    });
}

REALM_EXPORT bool shared_realm_is_in_transaction(SharedRealm* realm)
{
    return (*realm)->is_in_transaction();
}

REALM_EXPORT bool shared_realm_refresh(SharedRealm* realm)
{
    return (*realm)->refresh();
}

//REALM_EXPORT void table_retain(const Table* table)
//{
//    LangBindHelper::bind_table_ptr(table);
//}
//
//REALM_EXPORT void table_release(const Table* table)
//{
//    LangBindHelper::unbind_table_ptr(table);
//}
//
//REALM_EXPORT const Table* shared_group_get_table(SharedGroup* sg, const char* name)
//{
//    using sgf = _impl::SharedGroupFriend;
//    return handle_errors([&]() {
//        auto& group = sgf::get_group(*sg);
//        return LangBindHelper::get_table(group, name);
//    });
//}
//
//REALM_EXPORT Table* shared_group_get_table_mut(SharedGroup* sg, const char* name)
//{
//    using sgf = _impl::SharedGroupFriend;
//    return handle_errors([&]() {
//        auto& group = sgf::get_group(*sg);
//        return LangBindHelper::get_table(group, name);
//    });
//}
//
//REALM_EXPORT Table* shared_group_add_table(SharedGroup* sg, const char* name)
//{
//    using sgf = _impl::SharedGroupFriend;
//    return handle_errors([&]() {
//        auto& group = sgf::get_group(*sg);
//        return LangBindHelper::add_table(group, name);
//    });
//}
//
//REALM_EXPORT size_t table_add_column(Table* table, DataType type, const char* name, bool nullable)
//{
//    return handle_errors([&]() {
//        return table->add_column(type, name, nullable);
//    });
//}
//
//REALM_EXPORT size_t table_add_empty_row(Table* table)
//{
//    return handle_errors([&]() {
//        return table->add_empty_row();
//    });
//}
//
//REALM_EXPORT int64_t table_get_int(const Table* table, size_t col_ndx, size_t row_ndx)
//{
//    return table->get_int(col_ndx, row_ndx);
//}
//
//REALM_EXPORT void table_set_int(Table* table, size_t col_ndx, size_t row_ndx, int64_t value)
//{
//    return handle_errors([&]() {
//        table->set_int(col_ndx, row_ndx, value);
//    });
//}
//
//REALM_EXPORT void row_delete(Table::RowExpr* expr) {
//    delete expr;
//}
//
//REALM_EXPORT int64_t row_get_int(const Table::RowExpr* expr, size_t col_ndx) {
//    return expr->get_int(col_ndx);
//}

}
<|endoftext|>


<commit_before><commit_msg>Add QUIT command to server.<commit_after><|endoftext|>


<commit_before>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "remoting/client/chromoting_stats.h"
#include "remoting/proto/video.pb.h"

namespace {

// We take the last 10 latency numbers and report the average.
const int kLatencySampleSize = 10;

// UMA histogram names.
const char kRoundTripLatencyHistogram[] = "Chromoting.Video.RoundTripLatency";
const char kVideoCaptureLatencyHistogram[] = "Chromoting.Video.CaptureLatency";
const char kVideoEncodeLatencyHistogram[] = "Chromoting.Video.EncodeLatency";
const char kVideoDecodeLatencyHistogram[] = "Chromoting.Video.DecodeLatency";
const char kVideoPaintLatencyHistogram[] = "Chromoting.Video.PaintLatency";
const char kVideoFrameRateHistogram[] = "Chromoting.Video.FrameRate";
const char kVideoPacketRateHistogram[] = "Chromoting.Video.PacketRate";
const char kVideoBandwidthHistogram[] = "Chromoting.Video.Bandwidth";

// Custom count and custom time histograms are log-scaled by default. This
// results in fine-grained buckets at lower values and wider-ranged buckets
// closer to the maximum.
// The values defined for each histogram below are based on the 99th percentile
// numbers for the corresponding metric over a recent 28-day period.
// Values above the maximum defined for a histogram end up in the max-bucket.
// If the minimum for a UMA histogram is set to be < 1, it is implicitly
// normalized to 1.
// See $/src/base/metrics/histogram.h for more details.

// Video-specific metrics are stored in a custom times histogram.
const int kVideoActionsHistogramsMinMs = 1;
const int kVideoActionsHistogramsMaxMs = 250;
const int kVideoActionsHistogramsBuckets = 50;

// Round-trip latency values are stored in a custom times histogram.
const int kLatencyHistogramMinMs = 1;
const int kLatencyHistogramMaxMs = 20000;
const int kLatencyHistogramBuckets = 50;

// Bandwidth statistics are stored in a custom counts histogram.
const int kBandwidthHistogramMinBps = 0;
const int kBandwidthHistogramMaxBps = 10 * 1000 * 1000;
const int kBandwidthHistogramBuckets = 100;

// Frame rate is stored in a custom enum histogram, because we we want to record
// the frequency of each discrete value, rather than using log-scaled buckets.
// We don't expect video frame rate to be greater than 40fps. Setting a maximum
// of 100fps will leave some room for future improvements, and account for any
// bursts of packets. Enum histograms expect samples to be less than the
// boundary value, so set to 101.
const int kMaxFramesPerSec = 101;
}  // namespace

namespace remoting {

ChromotingStats::ChromotingStats()
    : video_bandwidth_(
          base::TimeDelta::FromSeconds(kStatsUpdateFrequencyInSeconds)),
      video_frame_rate_(
          base::TimeDelta::FromSeconds(kStatsUpdateFrequencyInSeconds)),
      video_packet_rate_(
          base::TimeDelta::FromSeconds(kStatsUpdateFrequencyInSeconds)),
      video_capture_ms_(kLatencySampleSize),
      video_encode_ms_(kLatencySampleSize),
      video_decode_ms_(kLatencySampleSize),
      video_paint_ms_(kLatencySampleSize),
      round_trip_ms_(kLatencySampleSize) {}

ChromotingStats::~ChromotingStats() {
}

void ChromotingStats::SetUpdateUmaCallbacks(
    UpdateUmaCustomHistogramCallback update_uma_custom_counts_callback,
    UpdateUmaCustomHistogramCallback update_uma_custom_times_callback,
    UpdateUmaEnumHistogramCallback update_uma_enum_histogram_callback) {
  uma_custom_counts_updater_ = update_uma_custom_counts_callback;
  uma_custom_times_updater_ = update_uma_custom_times_callback;
  uma_enum_histogram_updater_ = update_uma_enum_histogram_callback;
}

void ChromotingStats::RecordVideoPacketStats(const VideoPacket& packet) {
  // Record this received packet, even if it is empty.
  video_packet_rate_.Record(1);

  // If the packet is empty, there are no other stats to update.
  if (!packet.data().size())
    return;

  video_frame_rate_.Record(1);
  video_bandwidth_.Record(packet.data().size());

  if (packet.has_capture_time_ms()) {
    video_capture_ms_.Record(packet.capture_time_ms());
    if (!uma_custom_times_updater_.is_null())
      uma_custom_times_updater_.Run(
          kVideoCaptureLatencyHistogram, packet.capture_time_ms(),
          kVideoActionsHistogramsMinMs, kVideoActionsHistogramsMaxMs,
          kVideoActionsHistogramsBuckets);
  }

  if (packet.has_encode_time_ms()) {
    video_encode_ms_.Record(packet.encode_time_ms());
    if (!uma_custom_times_updater_.is_null())
      uma_custom_times_updater_.Run(
          kVideoEncodeLatencyHistogram, packet.encode_time_ms(),
          kVideoActionsHistogramsMinMs, kVideoActionsHistogramsMaxMs,
          kVideoActionsHistogramsBuckets);
  }

  if (packet.has_latest_event_timestamp() &&
      packet.latest_event_timestamp() > latest_input_event_timestamp_) {
    // If the timestamp in this video packet has changed from the one we stored
    // for the previous input event, then it is the first video packet following
    // that event. We'll use that timestamp to record round-trip latency.
    latest_input_event_timestamp_ = packet.latest_event_timestamp();

    base::TimeDelta round_trip_latency =
        base::Time::Now() -
        base::Time::FromInternalValue(packet.latest_event_timestamp());

    round_trip_ms_.Record(round_trip_latency.InMilliseconds());

    if (!uma_custom_times_updater_.is_null())
      uma_custom_times_updater_.Run(
          kRoundTripLatencyHistogram, round_trip_latency.InMilliseconds(),
          kLatencyHistogramMinMs, kLatencyHistogramMaxMs,
          kLatencyHistogramBuckets);
  }
}

void ChromotingStats::RecordDecodeTime(double value) {
  video_decode_ms_.Record(value);
  if (!uma_custom_times_updater_.is_null())
    uma_custom_times_updater_.Run(
        kVideoDecodeLatencyHistogram, value, kVideoActionsHistogramsMinMs,
        kVideoActionsHistogramsMaxMs, kVideoActionsHistogramsBuckets);
}

void ChromotingStats::RecordPaintTime(double value) {
  video_paint_ms_.Record(value);
  if (!uma_custom_times_updater_.is_null())
    uma_custom_times_updater_.Run(
        kVideoPaintLatencyHistogram, value, kVideoActionsHistogramsMinMs,
        kVideoActionsHistogramsMaxMs, kVideoActionsHistogramsBuckets);
}

void ChromotingStats::UploadRateStatsToUma() {
  if (!uma_enum_histogram_updater_.is_null()) {
    uma_enum_histogram_updater_.Run(kVideoFrameRateHistogram,
                                    video_frame_rate(), kMaxFramesPerSec);
    uma_enum_histogram_updater_.Run(kVideoPacketRateHistogram,
                                    video_packet_rate(), kMaxFramesPerSec);
    uma_custom_counts_updater_.Run(
        kVideoBandwidthHistogram, video_bandwidth(), kBandwidthHistogramMinBps,
        kBandwidthHistogramMaxBps, kBandwidthHistogramBuckets);
  }
}

}  // namespace remoting
<commit_msg>Record the RTT for empty packets.<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "remoting/client/chromoting_stats.h"
#include "remoting/proto/video.pb.h"

namespace {

// We take the last 10 latency numbers and report the average.
const int kLatencySampleSize = 10;

// UMA histogram names.
const char kRoundTripLatencyHistogram[] = "Chromoting.Video.RoundTripLatency";
const char kVideoCaptureLatencyHistogram[] = "Chromoting.Video.CaptureLatency";
const char kVideoEncodeLatencyHistogram[] = "Chromoting.Video.EncodeLatency";
const char kVideoDecodeLatencyHistogram[] = "Chromoting.Video.DecodeLatency";
const char kVideoPaintLatencyHistogram[] = "Chromoting.Video.PaintLatency";
const char kVideoFrameRateHistogram[] = "Chromoting.Video.FrameRate";
const char kVideoPacketRateHistogram[] = "Chromoting.Video.PacketRate";
const char kVideoBandwidthHistogram[] = "Chromoting.Video.Bandwidth";

// Custom count and custom time histograms are log-scaled by default. This
// results in fine-grained buckets at lower values and wider-ranged buckets
// closer to the maximum.
// The values defined for each histogram below are based on the 99th percentile
// numbers for the corresponding metric over a recent 28-day period.
// Values above the maximum defined for a histogram end up in the max-bucket.
// If the minimum for a UMA histogram is set to be < 1, it is implicitly
// normalized to 1.
// See $/src/base/metrics/histogram.h for more details.

// Video-specific metrics are stored in a custom times histogram.
const int kVideoActionsHistogramsMinMs = 1;
const int kVideoActionsHistogramsMaxMs = 250;
const int kVideoActionsHistogramsBuckets = 50;

// Round-trip latency values are stored in a custom times histogram.
const int kLatencyHistogramMinMs = 1;
const int kLatencyHistogramMaxMs = 20000;
const int kLatencyHistogramBuckets = 50;

// Bandwidth statistics are stored in a custom counts histogram.
const int kBandwidthHistogramMinBps = 0;
const int kBandwidthHistogramMaxBps = 10 * 1000 * 1000;
const int kBandwidthHistogramBuckets = 100;

// Frame rate is stored in a custom enum histogram, because we we want to record
// the frequency of each discrete value, rather than using log-scaled buckets.
// We don't expect video frame rate to be greater than 40fps. Setting a maximum
// of 100fps will leave some room for future improvements, and account for any
// bursts of packets. Enum histograms expect samples to be less than the
// boundary value, so set to 101.
const int kMaxFramesPerSec = 101;
}  // namespace

namespace remoting {

ChromotingStats::ChromotingStats()
    : video_bandwidth_(
          base::TimeDelta::FromSeconds(kStatsUpdateFrequencyInSeconds)),
      video_frame_rate_(
          base::TimeDelta::FromSeconds(kStatsUpdateFrequencyInSeconds)),
      video_packet_rate_(
          base::TimeDelta::FromSeconds(kStatsUpdateFrequencyInSeconds)),
      video_capture_ms_(kLatencySampleSize),
      video_encode_ms_(kLatencySampleSize),
      video_decode_ms_(kLatencySampleSize),
      video_paint_ms_(kLatencySampleSize),
      round_trip_ms_(kLatencySampleSize) {}

ChromotingStats::~ChromotingStats() {
}

void ChromotingStats::SetUpdateUmaCallbacks(
    UpdateUmaCustomHistogramCallback update_uma_custom_counts_callback,
    UpdateUmaCustomHistogramCallback update_uma_custom_times_callback,
    UpdateUmaEnumHistogramCallback update_uma_enum_histogram_callback) {
  uma_custom_counts_updater_ = update_uma_custom_counts_callback;
  uma_custom_times_updater_ = update_uma_custom_times_callback;
  uma_enum_histogram_updater_ = update_uma_enum_histogram_callback;
}

void ChromotingStats::RecordVideoPacketStats(const VideoPacket& packet) {
  // Record this received packet, even if it is empty.
  video_packet_rate_.Record(1);

  // Record the RTT, even for empty packets, otherwise input events that
  // do not cause an on-screen change can give very large, bogus RTTs.
  if (packet.has_latest_event_timestamp() &&
      packet.latest_event_timestamp() > latest_input_event_timestamp_) {
    latest_input_event_timestamp_ = packet.latest_event_timestamp();

    base::TimeDelta round_trip_latency =
        base::Time::Now() -
        base::Time::FromInternalValue(packet.latest_event_timestamp());

    round_trip_ms_.Record(round_trip_latency.InMilliseconds());

    if (!uma_custom_times_updater_.is_null())
      uma_custom_times_updater_.Run(
          kRoundTripLatencyHistogram, round_trip_latency.InMilliseconds(),
          kLatencyHistogramMinMs, kLatencyHistogramMaxMs,
          kLatencyHistogramBuckets);
  }

  // If the packet is empty, there are no other stats to update.
  if (!packet.data().size())
    return;

  video_frame_rate_.Record(1);
  video_bandwidth_.Record(packet.data().size());

  if (packet.has_capture_time_ms()) {
    video_capture_ms_.Record(packet.capture_time_ms());
    if (!uma_custom_times_updater_.is_null())
      uma_custom_times_updater_.Run(
          kVideoCaptureLatencyHistogram, packet.capture_time_ms(),
          kVideoActionsHistogramsMinMs, kVideoActionsHistogramsMaxMs,
          kVideoActionsHistogramsBuckets);
  }

  if (packet.has_encode_time_ms()) {
    video_encode_ms_.Record(packet.encode_time_ms());
    if (!uma_custom_times_updater_.is_null())
      uma_custom_times_updater_.Run(
          kVideoEncodeLatencyHistogram, packet.encode_time_ms(),
          kVideoActionsHistogramsMinMs, kVideoActionsHistogramsMaxMs,
          kVideoActionsHistogramsBuckets);
  }
}

void ChromotingStats::RecordDecodeTime(double value) {
  video_decode_ms_.Record(value);
  if (!uma_custom_times_updater_.is_null())
    uma_custom_times_updater_.Run(
        kVideoDecodeLatencyHistogram, value, kVideoActionsHistogramsMinMs,
        kVideoActionsHistogramsMaxMs, kVideoActionsHistogramsBuckets);
}

void ChromotingStats::RecordPaintTime(double value) {
  video_paint_ms_.Record(value);
  if (!uma_custom_times_updater_.is_null())
    uma_custom_times_updater_.Run(
        kVideoPaintLatencyHistogram, value, kVideoActionsHistogramsMinMs,
        kVideoActionsHistogramsMaxMs, kVideoActionsHistogramsBuckets);
}

void ChromotingStats::UploadRateStatsToUma() {
  if (!uma_enum_histogram_updater_.is_null()) {
    uma_enum_histogram_updater_.Run(kVideoFrameRateHistogram,
                                    video_frame_rate(), kMaxFramesPerSec);
    uma_enum_histogram_updater_.Run(kVideoPacketRateHistogram,
                                    video_packet_rate(), kMaxFramesPerSec);
    uma_custom_counts_updater_.Run(
        kVideoBandwidthHistogram, video_bandwidth(), kBandwidthHistogramMinBps,
        kBandwidthHistogramMaxBps, kBandwidthHistogramBuckets);
  }
}

}  // namespace remoting
<|endoftext|>


<commit_before>// Copyright (c) 2006, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "common/linux/http_upload.h"

#include <assert.h>
#include <dlfcn.h>
#include <curl/curl.h>
#include <curl/easy.h>
#include <curl/types.h>

namespace {

// Callback to get the response data from server.
static size_t WriteCallback(void *ptr, size_t size,
                            size_t nmemb, void *userp) {
  if (!userp)
    return 0;

  std::string *response = reinterpret_cast<std::string *>(userp);
  size_t real_size = size * nmemb;
  response->append(reinterpret_cast<char *>(ptr), real_size);
  return real_size;
}

}  // namespace

namespace google_breakpad {

static const char kUserAgent[] = "Breakpad/1.0 (Linux)";

// static
bool HTTPUpload::SendRequest(const string &url,
                             const map<string, string> &parameters,
                             const string &upload_file,
                             const string &file_part_name,
                             const string &proxy,
                             const string &proxy_user_pwd,
                             const string &ca_certificate_file,
                             string *response_body,
                             string *error_description) {
  if (!CheckParameters(parameters))
    return false;

  void *curl_lib = dlopen("libcurl.so", RTLD_NOW);
  if (!curl_lib) {
    if (error_description != NULL)
      *error_description = dlerror();
    curl_lib = dlopen("libcurl.so.4", RTLD_NOW);
  }
  if (!curl_lib) {
    // Debian gives libcurl a different name when it is built against GnuTLS
    // instead of OpenSSL.
    curl_lib = dlopen("libcurl-gnutls.so.4", RTLD_NOW);
  }
  if (!curl_lib) {
    curl_lib = dlopen("libcurl.so.3", RTLD_NOW);
  }
  if (!curl_lib) {
    return false;
  }

  CURL* (*curl_easy_init)(void);
  *(void**) (&curl_easy_init) = dlsym(curl_lib, "curl_easy_init");
  CURL *curl = (*curl_easy_init)();
  if (error_description != NULL)
    *error_description = "No Error";

  if (!curl) {
    dlclose(curl_lib);
    return false;
  }

  CURLcode err_code = CURLE_OK;
  CURLcode (*curl_easy_setopt)(CURL *, CURLoption, ...);
  *(void**) (&curl_easy_setopt) = dlsym(curl_lib, "curl_easy_setopt");
  (*curl_easy_setopt)(curl, CURLOPT_URL, url.c_str());
  (*curl_easy_setopt)(curl, CURLOPT_USERAGENT, kUserAgent);
  // Set proxy information if necessary.
  if (!proxy.empty())
    (*curl_easy_setopt)(curl, CURLOPT_PROXY, proxy.c_str());
  if (!proxy_user_pwd.empty())
    (*curl_easy_setopt)(curl, CURLOPT_PROXYUSERPWD, proxy_user_pwd.c_str());

  if (!ca_certificate_file.empty())
    (*curl_easy_setopt)(curl, CURLOPT_CAINFO, ca_certificate_file.c_str());

  struct curl_httppost *formpost = NULL;
  struct curl_httppost *lastptr = NULL;
  // Add form data.
  CURLFORMcode (*curl_formadd)(struct curl_httppost **, struct curl_httppost **, ...);
  *(void**) (&curl_formadd) = dlsym(curl_lib, "curl_formadd");
  map<string, string>::const_iterator iter = parameters.begin();
  for (; iter != parameters.end(); ++iter)
    (*curl_formadd)(&formpost, &lastptr,
                 CURLFORM_COPYNAME, iter->first.c_str(),
                 CURLFORM_COPYCONTENTS, iter->second.c_str(),
                 CURLFORM_END);

  // Add form file.
  (*curl_formadd)(&formpost, &lastptr,
               CURLFORM_COPYNAME, file_part_name.c_str(),
               CURLFORM_FILE, upload_file.c_str(),
               CURLFORM_END);

  (*curl_easy_setopt)(curl, CURLOPT_HTTPPOST, formpost);

  // Disable 100-continue header.
  struct curl_slist *headerlist = NULL;
  char buf[] = "Expect:";
  struct curl_slist* (*curl_slist_append)(struct curl_slist *, const char *);
  *(void**) (&curl_slist_append) = dlsym(curl_lib, "curl_slist_append");
  headerlist = (*curl_slist_append)(headerlist, buf);
  (*curl_easy_setopt)(curl, CURLOPT_HTTPHEADER, headerlist);

  if (response_body != NULL) {
    (*curl_easy_setopt)(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
    (*curl_easy_setopt)(curl, CURLOPT_WRITEDATA,
                     reinterpret_cast<void *>(response_body));
  }

  CURLcode (*curl_easy_perform)(CURL *);
  *(void**) (&curl_easy_perform) = dlsym(curl_lib, "curl_easy_perform");
  err_code = (*curl_easy_perform)(curl);
  const char* (*curl_easy_strerror)(CURLcode);
  *(void**) (&curl_easy_strerror) = dlsym(curl_lib, "curl_easy_strerror");
#ifndef NDEBUG
  if (err_code != CURLE_OK)
    fprintf(stderr, "Failed to send http request to %s, error: %s\n",
            url.c_str(),
            (*curl_easy_strerror)(err_code));
#endif
  if (error_description != NULL)
    *error_description = (*curl_easy_strerror)(err_code);

  void (*curl_easy_cleanup)(CURL *);
  *(void**) (&curl_easy_cleanup) = dlsym(curl_lib, "curl_easy_cleanup");
  (*curl_easy_cleanup)(curl);
  if (formpost != NULL) {
    void (*curl_formfree)(struct curl_httppost *);
    *(void**) (&curl_formfree) = dlsym(curl_lib, "curl_formfree");
    (*curl_formfree)(formpost);
  }
  if (headerlist != NULL) {
    void (*curl_slist_free_all)(struct curl_slist *);
    *(void**) (&curl_slist_free_all) = dlsym(curl_lib, "curl_slist_free_all");
    (*curl_slist_free_all)(headerlist);
  }
  dlclose(curl_lib);
  return err_code == CURLE_OK;
}

// static
bool HTTPUpload::CheckParameters(const map<string, string> &parameters) {
  for (map<string, string>::const_iterator pos = parameters.begin();
       pos != parameters.end(); ++pos) {
    const string &str = pos->first;
    if (str.size() == 0)
      return false;  // disallow empty parameter names
    for (unsigned int i = 0; i < str.size(); ++i) {
      int c = str[i];
      if (c < 32 || c == '"' || c > 127) {
        return false;
      }
    }
  }
  return true;
}

}  // namespace google_breakpad
<commit_msg>Propagate failure if http uploads fail with http error codes.<commit_after>// Copyright (c) 2006, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "common/linux/http_upload.h"

#include <assert.h>
#include <dlfcn.h>
#include <curl/curl.h>
#include <curl/easy.h>
#include <curl/types.h>

namespace {

// Callback to get the response data from server.
static size_t WriteCallback(void *ptr, size_t size,
                            size_t nmemb, void *userp) {
  if (!userp)
    return 0;

  std::string *response = reinterpret_cast<std::string *>(userp);
  size_t real_size = size * nmemb;
  response->append(reinterpret_cast<char *>(ptr), real_size);
  return real_size;
}

}  // namespace

namespace google_breakpad {

static const char kUserAgent[] = "Breakpad/1.0 (Linux)";

// static
bool HTTPUpload::SendRequest(const string &url,
                             const map<string, string> &parameters,
                             const string &upload_file,
                             const string &file_part_name,
                             const string &proxy,
                             const string &proxy_user_pwd,
                             const string &ca_certificate_file,
                             string *response_body,
                             string *error_description) {
  if (!CheckParameters(parameters))
    return false;

  void *curl_lib = dlopen("libcurl.so", RTLD_NOW);
  if (!curl_lib) {
    if (error_description != NULL)
      *error_description = dlerror();
    curl_lib = dlopen("libcurl.so.4", RTLD_NOW);
  }
  if (!curl_lib) {
    // Debian gives libcurl a different name when it is built against GnuTLS
    // instead of OpenSSL.
    curl_lib = dlopen("libcurl-gnutls.so.4", RTLD_NOW);
  }
  if (!curl_lib) {
    curl_lib = dlopen("libcurl.so.3", RTLD_NOW);
  }
  if (!curl_lib) {
    return false;
  }

  CURL* (*curl_easy_init)(void);
  *(void**) (&curl_easy_init) = dlsym(curl_lib, "curl_easy_init");
  CURL *curl = (*curl_easy_init)();
  if (error_description != NULL)
    *error_description = "No Error";

  if (!curl) {
    dlclose(curl_lib);
    return false;
  }

  CURLcode err_code = CURLE_OK;
  CURLcode (*curl_easy_setopt)(CURL *, CURLoption, ...);
  *(void**) (&curl_easy_setopt) = dlsym(curl_lib, "curl_easy_setopt");
  (*curl_easy_setopt)(curl, CURLOPT_URL, url.c_str());
  (*curl_easy_setopt)(curl, CURLOPT_USERAGENT, kUserAgent);
  // Set proxy information if necessary.
  if (!proxy.empty())
    (*curl_easy_setopt)(curl, CURLOPT_PROXY, proxy.c_str());
  if (!proxy_user_pwd.empty())
    (*curl_easy_setopt)(curl, CURLOPT_PROXYUSERPWD, proxy_user_pwd.c_str());

  if (!ca_certificate_file.empty())
    (*curl_easy_setopt)(curl, CURLOPT_CAINFO, ca_certificate_file.c_str());

  struct curl_httppost *formpost = NULL;
  struct curl_httppost *lastptr = NULL;
  // Add form data.
  CURLFORMcode (*curl_formadd)(struct curl_httppost **, struct curl_httppost **, ...);
  *(void**) (&curl_formadd) = dlsym(curl_lib, "curl_formadd");
  map<string, string>::const_iterator iter = parameters.begin();
  for (; iter != parameters.end(); ++iter)
    (*curl_formadd)(&formpost, &lastptr,
                 CURLFORM_COPYNAME, iter->first.c_str(),
                 CURLFORM_COPYCONTENTS, iter->second.c_str(),
                 CURLFORM_END);

  // Add form file.
  (*curl_formadd)(&formpost, &lastptr,
               CURLFORM_COPYNAME, file_part_name.c_str(),
               CURLFORM_FILE, upload_file.c_str(),
               CURLFORM_END);

  (*curl_easy_setopt)(curl, CURLOPT_HTTPPOST, formpost);

  // Disable 100-continue header.
  struct curl_slist *headerlist = NULL;
  char buf[] = "Expect:";
  struct curl_slist* (*curl_slist_append)(struct curl_slist *, const char *);
  *(void**) (&curl_slist_append) = dlsym(curl_lib, "curl_slist_append");
  headerlist = (*curl_slist_append)(headerlist, buf);
  (*curl_easy_setopt)(curl, CURLOPT_HTTPHEADER, headerlist);

  if (response_body != NULL) {
    (*curl_easy_setopt)(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
    (*curl_easy_setopt)(curl, CURLOPT_WRITEDATA,
                     reinterpret_cast<void *>(response_body));
  }

  // Fail if 400+ is returned from the web server.
  (*curl_easy_setopt)(curl, CURLOPT_FAILONERROR, 1);

  CURLcode (*curl_easy_perform)(CURL *);
  *(void**) (&curl_easy_perform) = dlsym(curl_lib, "curl_easy_perform");
  err_code = (*curl_easy_perform)(curl);
  const char* (*curl_easy_strerror)(CURLcode);
  *(void**) (&curl_easy_strerror) = dlsym(curl_lib, "curl_easy_strerror");
#ifndef NDEBUG
  if (err_code != CURLE_OK)
    fprintf(stderr, "Failed to send http request to %s, error: %s\n",
            url.c_str(),
            (*curl_easy_strerror)(err_code));
#endif
  if (error_description != NULL)
    *error_description = (*curl_easy_strerror)(err_code);

  void (*curl_easy_cleanup)(CURL *);
  *(void**) (&curl_easy_cleanup) = dlsym(curl_lib, "curl_easy_cleanup");
  (*curl_easy_cleanup)(curl);
  if (formpost != NULL) {
    void (*curl_formfree)(struct curl_httppost *);
    *(void**) (&curl_formfree) = dlsym(curl_lib, "curl_formfree");
    (*curl_formfree)(formpost);
  }
  if (headerlist != NULL) {
    void (*curl_slist_free_all)(struct curl_slist *);
    *(void**) (&curl_slist_free_all) = dlsym(curl_lib, "curl_slist_free_all");
    (*curl_slist_free_all)(headerlist);
  }
  dlclose(curl_lib);
  return err_code == CURLE_OK;
}

// static
bool HTTPUpload::CheckParameters(const map<string, string> &parameters) {
  for (map<string, string>::const_iterator pos = parameters.begin();
       pos != parameters.end(); ++pos) {
    const string &str = pos->first;
    if (str.size() == 0)
      return false;  // disallow empty parameter names
    for (unsigned int i = 0; i < str.size(); ++i) {
      int c = str[i];
      if (c < 32 || c == '"' || c > 127) {
        return false;
      }
    }
  }
  return true;
}

}  // namespace google_breakpad
<|endoftext|>


<commit_before><commit_msg>remove legacy file<commit_after><|endoftext|>


<commit_before><commit_msg>minor speed improvement<commit_after><|endoftext|>


<commit_before>// See http://www.zaphoyd.com/websocketpp/manual/reference/cpp11-support
// for prepocessor directives and choosing between boost and cpp11
// C++11 STL tokens
#define _WEBSOCKETPP_CPP11_FUNCTIONAL_
#define _WEBSOCKETPP_CPP11_MEMORY_
#define _WEBSOCKETPP_CPP11_RANDOM_DEVICE_
#define _WEBSOCKETPP_CPP11_SYSTEM_ERROR_
#define _WEBSOCKETPP_CPP11_THREAD_

#include <cthun-client/connector/connection.hpp>
#include <cthun-client/connector/errors.hpp>
#include <cthun-client/data_container/data_container.hpp>
#include <cthun-client/protocol/message.hpp>

#include <websocketpp/common/connection_hdl.hpp>
#include <websocketpp/client.hpp>
#include <websocketpp/config/asio_client.hpp>

#define LEATHERMAN_LOGGING_NAMESPACE CTHUN_CLIENT_LOGGING_PREFIX".connection"

#include <leatherman/logging/logging.hpp>

#include <chrono>
#include <cstdio>
#include <iostream>

// TODO(ale): disable assert() once we're confident with the code...
// To disable assert()
// #define NDEBUG
#include <cassert>

namespace CthunClient {

//
// Constants
//

static const uint CONNECTION_MIN_INTERVAL { 200000 };  // [us]
static const uint CONNECTION_BACKOFF_LIMIT { 33 };  // [s]
static const uint CONNECTION_BACKOFF_MULTIPLIER { 2 };

//
// Connection
//

Connection::Connection(const std::string& server_url,
                       const ClientMetadata& client_metadata)
        : server_url_ { server_url },
          client_metadata_ { client_metadata },
          connection_state_ { ConnectionStateValues::initialized },
          consecutive_pong_timeouts_ { 0 },
          endpoint_ { new WS_Client_Type() }{
    // Turn off websocketpp logging to avoid runtime errors (see CTH-69)
    endpoint_->clear_access_channels(websocketpp::log::alevel::all);
    endpoint_->clear_error_channels(websocketpp::log::elevel::all);

    // Initialize the transport system. Note that in perpetual mode,
    // the event loop does not terminate when there are no connections
    endpoint_->init_asio();
    endpoint_->start_perpetual();

    try {
        // Bind the event handlers
        using websocketpp::lib::bind;
        endpoint_->set_tls_init_handler(bind(&Connection::onTlsInit, this, ::_1));
        endpoint_->set_open_handler(bind(&Connection::onOpen, this, ::_1));
        endpoint_->set_close_handler(bind(&Connection::onClose, this, ::_1));
        endpoint_->set_fail_handler(bind(&Connection::onFail, this, ::_1));
        endpoint_->set_message_handler(bind(&Connection::onMessage, this, ::_1, ::_2));
        endpoint_->set_ping_handler(bind(&Connection::onPing, this, ::_1, ::_2));
        endpoint_->set_pong_handler(bind(&Connection::onPong, this, ::_1, ::_2));
        endpoint_->set_pong_timeout_handler(bind(&Connection::onPongTimeout, this, ::_1, ::_2));
        endpoint_->set_tcp_pre_init_handler(bind(&Connection::onPreTCPInit, this, ::_1));
        endpoint_->set_tcp_post_init_handler(bind(&Connection::onPostTCPInit, this, ::_1));

        // Start the event loop thread
        endpoint_thread_.reset(new std::thread(&WS_Client_Type::run, endpoint_.get()));
    } catch (...) {
        LOG_DEBUG("Failed to configure the WebSocket endpoint; about to stop "
                  "the event loop");
        cleanUp();
        throw connection_config_error { "failed to initialize" };
    }
}

Connection::~Connection() {
    cleanUp();
}

ConnectionState Connection::getConnectionState() {
    return connection_state_.load();
}

//
// Callback modifiers
//

void Connection::setOnOpenCallback(std::function<void()> c_b) {
    onOpen_callback = c_b;
}

void Connection::setOnMessageCallback(std::function<void(std::string msg)> c_b) {
    onMessage_callback_ = c_b;
}

void Connection::resetCallbacks() {
    onOpen_callback = [](){};  // NOLINT [false positive readability/braces]
    onMessage_callback_ = [](std::string message){};  // NOLINT [false positive readability/braces]
}

//
// Synchronous calls
//

void Connection::connect(int max_connect_attempts) {
    // FSM: states are ConnectionStateValues; as for the transitions,
    //      we assume that the connection_state_:
    // - can be set to 'initialized' only by the Connection constructor;
    // - is set to 'connecting' by connect_();
    // - after a connect_() call, it will become, eventually, open or
    //   closed.

    ConnectionState previous_c_s = connection_state_.load();
    ConnectionState current_c_s;
    int idx { 0 };
    bool try_again { true };
    bool got_max_backoff { false };

    do {
        current_c_s = connection_state_.load();
        idx++;
        if (max_connect_attempts) {
            try_again = (idx < max_connect_attempts);
        }
        got_max_backoff |= (connection_backoff_s_ * CONNECTION_BACKOFF_MULTIPLIER
                            >= CONNECTION_BACKOFF_LIMIT);

        switch (current_c_s) {
        case(ConnectionStateValues::initialized):
            assert(previous_c_s == ConnectionStateValues::initialized);
            connect_();
            usleep(CONNECTION_MIN_INTERVAL);
            break;

        case(ConnectionStateValues::connecting):
            previous_c_s = current_c_s;
            usleep(CONNECTION_MIN_INTERVAL);
            continue;

        case(ConnectionStateValues::open):
            if (previous_c_s != ConnectionStateValues::open) {
                LOG_INFO("Successfully established a WebSocket connection to "
                         "the Cthun server");
                connection_backoff_s_ = CONNECTION_BACKOFF_S;
            }
            return;

        case(ConnectionStateValues::closing):
            previous_c_s = current_c_s;
            usleep(CONNECTION_MIN_INTERVAL);
            continue;

        case(ConnectionStateValues::closed):
            assert(previous_c_s != ConnectionStateValues::open);
            if (previous_c_s == ConnectionStateValues::closed) {
                connect_();
                usleep(CONNECTION_MIN_INTERVAL);
                previous_c_s = ConnectionStateValues::connecting;
            } else {
                LOG_INFO("Failed to establish a WebSocket connection; "
                         "retrying in %1% seconds", connection_backoff_s_);
                sleep(connection_backoff_s_);
                connect_();
                usleep(CONNECTION_MIN_INTERVAL);
                if (try_again && !got_max_backoff) {
                    connection_backoff_s_ *= CONNECTION_BACKOFF_MULTIPLIER;
                }
            }
            break;
        }
    } while (try_again);

    connection_backoff_s_ = CONNECTION_BACKOFF_S;
    throw connection_fatal_error { "failed to establish a WebSocket connection "
                                   "after " + std::to_string(idx) + " attempt"
                                   + (idx > 1 ? "s" : "") };
}

void Connection::send(const std::string& msg) {
    websocketpp::lib::error_code ec;
    endpoint_->send(connection_handle_,
                   msg,
                   websocketpp::frame::opcode::binary,
                   ec);
    if (ec) {
        throw connection_processing_error { "failed to send message: "
                                            + ec.message() };
    }
}

void Connection::send(void* const serialized_msg_ptr, size_t msg_len) {
    websocketpp::lib::error_code ec;
    endpoint_->send(connection_handle_,
                   serialized_msg_ptr,
                   msg_len,
                   websocketpp::frame::opcode::binary,
                   ec);
    if (ec) {
        throw connection_processing_error { "failed to send message: "
                                            + ec.message() };
    }
}

void Connection::ping(const std::string& binary_payload) {
    websocketpp::lib::error_code ec;
    endpoint_->ping(connection_handle_, binary_payload, ec);
    if (ec) {
        throw connection_processing_error { "failed to send WebSocket ping: "
                                            + ec.message() };
    }
}

void Connection::close(CloseCode code, const std::string& reason) {
    LOG_DEBUG("About to close connection");
    websocketpp::lib::error_code ec;
    endpoint_->close(connection_handle_, code, reason, ec);
    if (ec) {
        throw connection_processing_error { "failed to close WebSocket "
                                            "connection: " + ec.message() };
    }
}

//
// Private interface
//

void Connection::cleanUp() {
    endpoint_->stop_perpetual();
    if (connection_state_ == ConnectionStateValues::open) {
        try {
            close();
        } catch (connection_processing_error& e) {
            LOG_ERROR("Failed to close the WebSocket connection: %1%", e.what());
        }
    }
    if (endpoint_thread_ != nullptr && endpoint_thread_->joinable()) {
        endpoint_thread_->join();
    }
}

void Connection::connect_() {
    connection_state_ = ConnectionStateValues::connecting;
    connection_timings_ = ConnectionTimings();
    websocketpp::lib::error_code ec;
    WS_Client_Type::connection_ptr connection_ptr {
        endpoint_->get_connection(server_url_, ec) };
    if (ec) {
        throw connection_processing_error { "failed to establish the WebSocket "
                                            "connection to " + server_url_
                                            + ": " + ec.message() };
    }
    connection_handle_ = connection_ptr->get_handle();
    endpoint_->connect(connection_ptr);
}

//
// Event handlers (private)
//

WS_Context_Ptr Connection::onTlsInit(WS_Connection_Handle hdl) {
    LOG_TRACE("WebSocket TLS initialization event");
    // NB: for TLS certificates, refer to:
    // www.boost.org/doc/libs/1_56_0/doc/html/boost_asio/reference/ssl__context.html
    WS_Context_Ptr ctx {
        new boost::asio::ssl::context(boost::asio::ssl::context::tlsv1) };
    try {
        ctx->set_options(boost::asio::ssl::context::default_workarounds |
                         boost::asio::ssl::context::no_sslv2 |
                         boost::asio::ssl::context::single_dh_use);
        ctx->use_certificate_file(client_metadata_.crt,
                                  boost::asio::ssl::context::file_format::pem);
        ctx->use_private_key_file(client_metadata_.key,
                                  boost::asio::ssl::context::file_format::pem);
        ctx->load_verify_file(client_metadata_.ca);
    } catch (std::exception& e) {
        LOG_ERROR("Failed to configure TLS: %1%", e.what());
    }
    return ctx;
}

void Connection::onClose(WS_Connection_Handle hdl) {
    connection_timings_.close = std::chrono::high_resolution_clock::now();
    LOG_TRACE("WebSocket connection closed");
    connection_state_ = ConnectionStateValues::closed;
}

void Connection::onFail(WS_Connection_Handle hdl) {
    connection_timings_.close = std::chrono::high_resolution_clock::now();
    connection_timings_.connection_failed = true;
    LOG_DEBUG("WebSocket on fail event - %1%", connection_timings_.toString());
    connection_state_ = ConnectionStateValues::closed;
}

bool Connection::onPing(WS_Connection_Handle hdl, std::string binary_payload) {
    LOG_TRACE("WebSocket onPing event - payload: %1%", binary_payload);
    // Returning true so the transport layer will send back a pong
    return true;
}

void Connection::onPong(WS_Connection_Handle hdl, std::string binary_payload) {
    LOG_DEBUG("WebSocket onPong event");
    if (consecutive_pong_timeouts_) {
        consecutive_pong_timeouts_ = 0;
    }
}

void Connection::onPongTimeout(WS_Connection_Handle hdl,
                               std::string binary_payload) {
    LOG_WARNING("WebSocket onPongTimeout event (%1% consecutive)",
                consecutive_pong_timeouts_++);
}

void Connection::onPreTCPInit(WS_Connection_Handle hdl) {
    connection_timings_.tcp_pre_init = std::chrono::high_resolution_clock::now();
    LOG_TRACE("WebSocket pre-TCP initialization event");
}

void Connection::onPostTCPInit(WS_Connection_Handle hdl) {
    connection_timings_.tcp_post_init = std::chrono::high_resolution_clock::now();
    LOG_TRACE("WebSocket post-TCP initialization event");
}

void Connection::onOpen(WS_Connection_Handle hdl) {
    connection_timings_.open = std::chrono::high_resolution_clock::now();
    connection_timings_.connection_started = true;
    LOG_DEBUG("WebSocket on open event - %1%", connection_timings_.toString());
    LOG_INFO("WebSocket connection established");
    connection_state_ = ConnectionStateValues::open;

    if (onOpen_callback) {
        try {
            onOpen_callback();
            return;
        } catch (std::exception&  e) {
            LOG_ERROR("onOpen callback failure: %1%; closing the "
                      "WebSocket connection", e.what());
        } catch (...) {
            LOG_ERROR("onOpen callback failure; closing the WebSocket "
                      "connection");
        }

        close(CloseCodeValues::normal, "onOpen callback failure");
    }
}

void Connection::onMessage(WS_Connection_Handle hdl,
                           WS_Client_Type::message_ptr msg) {
    if (onMessage_callback_) {
        try {
            // NB: on_message_callback_ should not raise; in case of
            // failure; it must be able to notify back the error...
            onMessage_callback_(msg->get_payload());
        } catch (std::exception&  e) {
            LOG_ERROR("onMessage WebSocket callback failure: %1%", e.what());
        } catch (...) {
            LOG_ERROR("onMessage WebSocket callback failure: unexpected error");
        }
    }
}

}  // namespace CthunClient
<commit_msg>(CTH-213) Trivial logging messages fix<commit_after>// See http://www.zaphoyd.com/websocketpp/manual/reference/cpp11-support
// for prepocessor directives and choosing between boost and cpp11
// C++11 STL tokens
#define _WEBSOCKETPP_CPP11_FUNCTIONAL_
#define _WEBSOCKETPP_CPP11_MEMORY_
#define _WEBSOCKETPP_CPP11_RANDOM_DEVICE_
#define _WEBSOCKETPP_CPP11_SYSTEM_ERROR_
#define _WEBSOCKETPP_CPP11_THREAD_

#include <cthun-client/connector/connection.hpp>
#include <cthun-client/connector/errors.hpp>
#include <cthun-client/data_container/data_container.hpp>
#include <cthun-client/protocol/message.hpp>

#include <websocketpp/common/connection_hdl.hpp>
#include <websocketpp/client.hpp>
#include <websocketpp/config/asio_client.hpp>

#define LEATHERMAN_LOGGING_NAMESPACE CTHUN_CLIENT_LOGGING_PREFIX".connection"

#include <leatherman/logging/logging.hpp>

#include <chrono>
#include <cstdio>
#include <iostream>

// TODO(ale): disable assert() once we're confident with the code...
// To disable assert()
// #define NDEBUG
#include <cassert>

namespace CthunClient {

//
// Constants
//

static const uint CONNECTION_MIN_INTERVAL { 200000 };  // [us]
static const uint CONNECTION_BACKOFF_LIMIT { 33 };  // [s]
static const uint CONNECTION_BACKOFF_MULTIPLIER { 2 };

//
// Connection
//

Connection::Connection(const std::string& server_url,
                       const ClientMetadata& client_metadata)
        : server_url_ { server_url },
          client_metadata_ { client_metadata },
          connection_state_ { ConnectionStateValues::initialized },
          consecutive_pong_timeouts_ { 0 },
          endpoint_ { new WS_Client_Type() }{
    // Turn off websocketpp logging to avoid runtime errors (see CTH-69)
    endpoint_->clear_access_channels(websocketpp::log::alevel::all);
    endpoint_->clear_error_channels(websocketpp::log::elevel::all);

    // Initialize the transport system. Note that in perpetual mode,
    // the event loop does not terminate when there are no connections
    endpoint_->init_asio();
    endpoint_->start_perpetual();

    try {
        // Bind the event handlers
        using websocketpp::lib::bind;
        endpoint_->set_tls_init_handler(bind(&Connection::onTlsInit, this, ::_1));
        endpoint_->set_open_handler(bind(&Connection::onOpen, this, ::_1));
        endpoint_->set_close_handler(bind(&Connection::onClose, this, ::_1));
        endpoint_->set_fail_handler(bind(&Connection::onFail, this, ::_1));
        endpoint_->set_message_handler(bind(&Connection::onMessage, this, ::_1, ::_2));
        endpoint_->set_ping_handler(bind(&Connection::onPing, this, ::_1, ::_2));
        endpoint_->set_pong_handler(bind(&Connection::onPong, this, ::_1, ::_2));
        endpoint_->set_pong_timeout_handler(bind(&Connection::onPongTimeout, this, ::_1, ::_2));
        endpoint_->set_tcp_pre_init_handler(bind(&Connection::onPreTCPInit, this, ::_1));
        endpoint_->set_tcp_post_init_handler(bind(&Connection::onPostTCPInit, this, ::_1));

        // Start the event loop thread
        endpoint_thread_.reset(new std::thread(&WS_Client_Type::run, endpoint_.get()));
    } catch (...) {
        LOG_DEBUG("Failed to configure the WebSocket endpoint; about to stop "
                  "the event loop");
        cleanUp();
        throw connection_config_error { "failed to initialize" };
    }
}

Connection::~Connection() {
    cleanUp();
}

ConnectionState Connection::getConnectionState() {
    return connection_state_.load();
}

//
// Callback modifiers
//

void Connection::setOnOpenCallback(std::function<void()> c_b) {
    onOpen_callback = c_b;
}

void Connection::setOnMessageCallback(std::function<void(std::string msg)> c_b) {
    onMessage_callback_ = c_b;
}

void Connection::resetCallbacks() {
    onOpen_callback = [](){};  // NOLINT [false positive readability/braces]
    onMessage_callback_ = [](std::string message){};  // NOLINT [false positive readability/braces]
}

//
// Synchronous calls
//

void Connection::connect(int max_connect_attempts) {
    // FSM: states are ConnectionStateValues; as for the transitions,
    //      we assume that the connection_state_:
    // - can be set to 'initialized' only by the Connection constructor;
    // - is set to 'connecting' by connect_();
    // - after a connect_() call, it will become, eventually, open or
    //   closed.

    ConnectionState previous_c_s = connection_state_.load();
    ConnectionState current_c_s;
    int idx { 0 };
    bool try_again { true };
    bool got_max_backoff { false };

    do {
        current_c_s = connection_state_.load();
        idx++;
        if (max_connect_attempts) {
            try_again = (idx < max_connect_attempts);
        }
        got_max_backoff |= (connection_backoff_s_ * CONNECTION_BACKOFF_MULTIPLIER
                            >= CONNECTION_BACKOFF_LIMIT);

        switch (current_c_s) {
        case(ConnectionStateValues::initialized):
            assert(previous_c_s == ConnectionStateValues::initialized);
            connect_();
            usleep(CONNECTION_MIN_INTERVAL);
            break;

        case(ConnectionStateValues::connecting):
            previous_c_s = current_c_s;
            usleep(CONNECTION_MIN_INTERVAL);
            continue;

        case(ConnectionStateValues::open):
            if (previous_c_s != ConnectionStateValues::open) {
                LOG_INFO("Successfully established a WebSocket connection with "
                         "the Cthun server");
                connection_backoff_s_ = CONNECTION_BACKOFF_S;
            }
            return;

        case(ConnectionStateValues::closing):
            previous_c_s = current_c_s;
            usleep(CONNECTION_MIN_INTERVAL);
            continue;

        case(ConnectionStateValues::closed):
            assert(previous_c_s != ConnectionStateValues::open);
            if (previous_c_s == ConnectionStateValues::closed) {
                connect_();
                usleep(CONNECTION_MIN_INTERVAL);
                previous_c_s = ConnectionStateValues::connecting;
            } else {
                LOG_INFO("Failed to establish a WebSocket connection; "
                         "retrying in %1% seconds", connection_backoff_s_);
                sleep(connection_backoff_s_);
                connect_();
                usleep(CONNECTION_MIN_INTERVAL);
                if (try_again && !got_max_backoff) {
                    connection_backoff_s_ *= CONNECTION_BACKOFF_MULTIPLIER;
                }
            }
            break;
        }
    } while (try_again);

    connection_backoff_s_ = CONNECTION_BACKOFF_S;
    throw connection_fatal_error { "failed to establish a WebSocket connection "
                                   "after " + std::to_string(idx) + " attempt"
                                   + (idx > 1 ? "s" : "") };
}

void Connection::send(const std::string& msg) {
    websocketpp::lib::error_code ec;
    endpoint_->send(connection_handle_,
                   msg,
                   websocketpp::frame::opcode::binary,
                   ec);
    if (ec) {
        throw connection_processing_error { "failed to send message: "
                                            + ec.message() };
    }
}

void Connection::send(void* const serialized_msg_ptr, size_t msg_len) {
    websocketpp::lib::error_code ec;
    endpoint_->send(connection_handle_,
                   serialized_msg_ptr,
                   msg_len,
                   websocketpp::frame::opcode::binary,
                   ec);
    if (ec) {
        throw connection_processing_error { "failed to send message: "
                                            + ec.message() };
    }
}

void Connection::ping(const std::string& binary_payload) {
    websocketpp::lib::error_code ec;
    endpoint_->ping(connection_handle_, binary_payload, ec);
    if (ec) {
        throw connection_processing_error { "failed to send WebSocket ping: "
                                            + ec.message() };
    }
}

void Connection::close(CloseCode code, const std::string& reason) {
    LOG_DEBUG("About to close connection");
    websocketpp::lib::error_code ec;
    endpoint_->close(connection_handle_, code, reason, ec);
    if (ec) {
        throw connection_processing_error { "failed to close WebSocket "
                                            "connection: " + ec.message() };
    }
}

//
// Private interface
//

void Connection::cleanUp() {
    endpoint_->stop_perpetual();
    if (connection_state_ == ConnectionStateValues::open) {
        try {
            close();
        } catch (connection_processing_error& e) {
            LOG_ERROR("Failed to close the WebSocket connection: %1%", e.what());
        }
    }
    if (endpoint_thread_ != nullptr && endpoint_thread_->joinable()) {
        endpoint_thread_->join();
    }
}

void Connection::connect_() {
    connection_state_ = ConnectionStateValues::connecting;
    connection_timings_ = ConnectionTimings();
    websocketpp::lib::error_code ec;
    WS_Client_Type::connection_ptr connection_ptr {
        endpoint_->get_connection(server_url_, ec) };
    if (ec) {
        throw connection_processing_error { "failed to establish the WebSocket "
                                            "connection with " + server_url_
                                            + ": " + ec.message() };
    }
    connection_handle_ = connection_ptr->get_handle();
    endpoint_->connect(connection_ptr);
}

//
// Event handlers (private)
//

WS_Context_Ptr Connection::onTlsInit(WS_Connection_Handle hdl) {
    LOG_TRACE("WebSocket TLS initialization event");
    // NB: for TLS certificates, refer to:
    // www.boost.org/doc/libs/1_56_0/doc/html/boost_asio/reference/ssl__context.html
    WS_Context_Ptr ctx {
        new boost::asio::ssl::context(boost::asio::ssl::context::tlsv1) };
    try {
        ctx->set_options(boost::asio::ssl::context::default_workarounds |
                         boost::asio::ssl::context::no_sslv2 |
                         boost::asio::ssl::context::single_dh_use);
        ctx->use_certificate_file(client_metadata_.crt,
                                  boost::asio::ssl::context::file_format::pem);
        ctx->use_private_key_file(client_metadata_.key,
                                  boost::asio::ssl::context::file_format::pem);
        ctx->load_verify_file(client_metadata_.ca);
    } catch (std::exception& e) {
        LOG_ERROR("Failed to configure TLS: %1%", e.what());
    }
    return ctx;
}

void Connection::onClose(WS_Connection_Handle hdl) {
    connection_timings_.close = std::chrono::high_resolution_clock::now();
    LOG_TRACE("WebSocket connection closed");
    connection_state_ = ConnectionStateValues::closed;
}

void Connection::onFail(WS_Connection_Handle hdl) {
    connection_timings_.close = std::chrono::high_resolution_clock::now();
    connection_timings_.connection_failed = true;
    LOG_DEBUG("WebSocket on fail event - %1%", connection_timings_.toString());
    connection_state_ = ConnectionStateValues::closed;
}

bool Connection::onPing(WS_Connection_Handle hdl, std::string binary_payload) {
    LOG_TRACE("WebSocket onPing event - payload: %1%", binary_payload);
    // Returning true so the transport layer will send back a pong
    return true;
}

void Connection::onPong(WS_Connection_Handle hdl, std::string binary_payload) {
    LOG_DEBUG("WebSocket onPong event");
    if (consecutive_pong_timeouts_) {
        consecutive_pong_timeouts_ = 0;
    }
}

void Connection::onPongTimeout(WS_Connection_Handle hdl,
                               std::string binary_payload) {
    LOG_WARNING("WebSocket onPongTimeout event (%1% consecutive)",
                consecutive_pong_timeouts_++);
}

void Connection::onPreTCPInit(WS_Connection_Handle hdl) {
    connection_timings_.tcp_pre_init = std::chrono::high_resolution_clock::now();
    LOG_TRACE("WebSocket pre-TCP initialization event");
}

void Connection::onPostTCPInit(WS_Connection_Handle hdl) {
    connection_timings_.tcp_post_init = std::chrono::high_resolution_clock::now();
    LOG_TRACE("WebSocket post-TCP initialization event");
}

void Connection::onOpen(WS_Connection_Handle hdl) {
    connection_timings_.open = std::chrono::high_resolution_clock::now();
    connection_timings_.connection_started = true;
    LOG_DEBUG("WebSocket on open event - %1%", connection_timings_.toString());
    LOG_INFO("WebSocket connection established");
    connection_state_ = ConnectionStateValues::open;

    if (onOpen_callback) {
        try {
            onOpen_callback();
            return;
        } catch (std::exception&  e) {
            LOG_ERROR("onOpen callback failure: %1%; closing the "
                      "WebSocket connection", e.what());
        } catch (...) {
            LOG_ERROR("onOpen callback failure; closing the WebSocket "
                      "connection");
        }

        close(CloseCodeValues::normal, "onOpen callback failure");
    }
}

void Connection::onMessage(WS_Connection_Handle hdl,
                           WS_Client_Type::message_ptr msg) {
    if (onMessage_callback_) {
        try {
            // NB: on_message_callback_ should not raise; in case of
            // failure; it must be able to notify back the error...
            onMessage_callback_(msg->get_payload());
        } catch (std::exception&  e) {
            LOG_ERROR("onMessage WebSocket callback failure: %1%", e.what());
        } catch (...) {
            LOG_ERROR("onMessage WebSocket callback failure: unexpected error");
        }
    }
}

}  // namespace CthunClient
<|endoftext|>


<commit_before>// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "flutter/lib/ui/painting/image_shader.h"

#include "lib/tonic/dart_args.h"
#include "lib/tonic/dart_binding_macros.h"
#include "lib/tonic/converter/dart_converter.h"
#include "lib/tonic/dart_library_natives.h"

namespace blink {

static void ImageShader_constructor(Dart_NativeArguments args) {
  DartCallConstructor(&ImageShader::Create, args);
}

IMPLEMENT_WRAPPERTYPEINFO(ui, ImageShader);

#define FOR_EACH_BINDING(V) V(ImageShader, initWithImage)

FOR_EACH_BINDING(DART_NATIVE_CALLBACK)

void ImageShader::RegisterNatives(tonic::DartLibraryNatives* natives) {
  natives->Register(
      {{"ImageShader_constructor", ImageShader_constructor, 1, true},
       FOR_EACH_BINDING(DART_REGISTER_NATIVE)});
}

ftl::RefPtr<ImageShader> ImageShader::Create() {
  return ftl::MakeRefCounted<ImageShader>();
}

void ImageShader::initWithImage(CanvasImage* image,
                                SkShader::TileMode tmx,
                                SkShader::TileMode tmy,
                                const tonic::Float64List& matrix4) {
  FTL_DCHECK(image != NULL);
  SkMatrix sk_matrix = ToSkMatrix(matrix4);
  SkBitmap bitmap;
  image->image()->asLegacyBitmap(&bitmap, SkImage::kRO_LegacyBitmapMode);

  set_shader(SkShader::MakeBitmapShader(bitmap, tmx, tmy, &sk_matrix));
}

ImageShader::ImageShader() : Shader(nullptr) {}

ImageShader::~ImageShader() {}

}  // namespace blink
<commit_msg>Use SkImage::makeShader() in place of bitmap workaround (#3419)<commit_after>// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "flutter/lib/ui/painting/image_shader.h"

#include "lib/tonic/dart_args.h"
#include "lib/tonic/dart_binding_macros.h"
#include "lib/tonic/converter/dart_converter.h"
#include "lib/tonic/dart_library_natives.h"

namespace blink {

static void ImageShader_constructor(Dart_NativeArguments args) {
  DartCallConstructor(&ImageShader::Create, args);
}

IMPLEMENT_WRAPPERTYPEINFO(ui, ImageShader);

#define FOR_EACH_BINDING(V) V(ImageShader, initWithImage)

FOR_EACH_BINDING(DART_NATIVE_CALLBACK)

void ImageShader::RegisterNatives(tonic::DartLibraryNatives* natives) {
  natives->Register(
      {{"ImageShader_constructor", ImageShader_constructor, 1, true},
       FOR_EACH_BINDING(DART_REGISTER_NATIVE)});
}

ftl::RefPtr<ImageShader> ImageShader::Create() {
  return ftl::MakeRefCounted<ImageShader>();
}

void ImageShader::initWithImage(CanvasImage* image,
                                SkShader::TileMode tmx,
                                SkShader::TileMode tmy,
                                const tonic::Float64List& matrix4) {
  FTL_DCHECK(image != NULL);
  SkMatrix sk_matrix = ToSkMatrix(matrix4);
  set_shader(image->image()->makeShader(tmx, tmy, &sk_matrix));
}

ImageShader::ImageShader() : Shader(nullptr) {}

ImageShader::~ImageShader() {}

}  // namespace blink
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: ximpgrp.cxx,v $
 *
 *  $Revision: 1.17 $
 *
 *  last change: $Author: ihi $ $Date: 2006-12-19 17:27:02 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_xmloff.hxx"

#ifndef _XMLOFF_XMLNMSPE_HXX
#include"xmlnmspe.hxx"
#endif

#ifndef _XIMPGROUP_HXX
#include "ximpgrp.hxx"
#endif

#ifndef _XMLOFF_XMLTOKEN_HXX
#include "xmltoken.hxx"
#endif

#ifndef _XIMPSHAPE_HXX
#include "ximpshap.hxx"
#endif

#ifndef _XMLOFF_EVENTIMP_HXX
#include "eventimp.hxx"
#endif
#include "descriptionimp.hxx"

using namespace ::rtl;
using namespace ::com::sun::star;
using namespace ::xmloff::token;

//////////////////////////////////////////////////////////////////////////////

TYPEINIT1( SdXMLGroupShapeContext, SvXMLImportContext );

SdXMLGroupShapeContext::SdXMLGroupShapeContext(
    SvXMLImport& rImport,
    USHORT nPrfx, const OUString& rLocalName,
    const uno::Reference< xml::sax::XAttributeList>& xAttrList,
    uno::Reference< drawing::XShapes >& rShapes,
    sal_Bool bTemporaryShape)
:   SdXMLShapeContext( rImport, nPrfx, rLocalName, xAttrList, rShapes, bTemporaryShape )
{
}

//////////////////////////////////////////////////////////////////////////////

SdXMLGroupShapeContext::~SdXMLGroupShapeContext()
{
}

//////////////////////////////////////////////////////////////////////////////

SvXMLImportContext* SdXMLGroupShapeContext::CreateChildContext( USHORT nPrefix,
    const OUString& rLocalName,
    const uno::Reference< xml::sax::XAttributeList>& xAttrList )
{
    SvXMLImportContext* pContext = 0L;

    // #i68101#
    if( nPrefix == XML_NAMESPACE_SVG &&
        (IsXMLToken( rLocalName, XML_TITLE ) || IsXMLToken( rLocalName, XML_DESC ) ) )
    {
        pContext = new SdXMLDescriptionContext( GetImport(), nPrefix, rLocalName, xAttrList, mxShape );
    }
    else if( nPrefix == XML_NAMESPACE_OFFICE && IsXMLToken( rLocalName, XML_EVENT_LISTENERS ) )
    {
        pContext = new SdXMLEventsContext( GetImport(), nPrefix, rLocalName, xAttrList, mxShape );
    }
    else if( nPrefix == XML_NAMESPACE_DRAW && IsXMLToken( rLocalName, XML_GLUE_POINT ) )
    {
        addGluePoint( xAttrList );
    }
    else
    {
        // call GroupChildContext function at common ShapeImport
        pContext = GetImport().GetShapeImport()->CreateGroupChildContext(
            GetImport(), nPrefix, rLocalName, xAttrList, mxChilds);
    }

    // call parent when no own context was created
    if(!pContext)
        pContext = SvXMLImportContext::CreateChildContext(
        nPrefix, rLocalName, xAttrList);

    return pContext;
}

//////////////////////////////////////////////////////////////////////////////

void SdXMLGroupShapeContext::StartElement(const uno::Reference< xml::sax::XAttributeList>&)
{
    // create new group shape and add it to rShapes, use it
    // as base for the new group import
    AddShape( "com.sun.star.drawing.GroupShape" );

    if(mxShape.is())
    {
        SetStyle( false );

        mxChilds = uno::Reference< drawing::XShapes >::query( mxShape );
        if( mxChilds.is() )
            GetImport().GetShapeImport()->pushGroupForSorting( mxChilds );
    }

    GetImport().GetShapeImport()->finishShape( mxShape, mxAttrList, mxShapes );
}

//////////////////////////////////////////////////////////////////////////////

void SdXMLGroupShapeContext::EndElement()
{
    if( mxChilds.is() )
        GetImport().GetShapeImport()->popGroupAndSort();

    SdXMLShapeContext::EndElement();
}

<commit_msg>INTEGRATION: CWS vgbugs07 (1.17.84); FILE MERGED 2007/06/04 13:23:25 vg 1.17.84.1: #i76605# Remove -I .../inc/module hack introduced by hedaburemove01<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: ximpgrp.cxx,v $
 *
 *  $Revision: 1.18 $
 *
 *  last change: $Author: hr $ $Date: 2007-06-27 15:10:02 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_xmloff.hxx"

#ifndef _XMLOFF_XMLNMSPE_HXX
#include"xmlnmspe.hxx"
#endif

#ifndef _XIMPGROUP_HXX
#include "ximpgrp.hxx"
#endif

#ifndef _XMLOFF_XMLTOKEN_HXX
#include <xmloff/xmltoken.hxx>
#endif

#ifndef _XIMPSHAPE_HXX
#include "ximpshap.hxx"
#endif

#ifndef _XMLOFF_EVENTIMP_HXX
#include "eventimp.hxx"
#endif
#include "descriptionimp.hxx"

using namespace ::rtl;
using namespace ::com::sun::star;
using namespace ::xmloff::token;

//////////////////////////////////////////////////////////////////////////////

TYPEINIT1( SdXMLGroupShapeContext, SvXMLImportContext );

SdXMLGroupShapeContext::SdXMLGroupShapeContext(
    SvXMLImport& rImport,
    USHORT nPrfx, const OUString& rLocalName,
    const uno::Reference< xml::sax::XAttributeList>& xAttrList,
    uno::Reference< drawing::XShapes >& rShapes,
    sal_Bool bTemporaryShape)
:   SdXMLShapeContext( rImport, nPrfx, rLocalName, xAttrList, rShapes, bTemporaryShape )
{
}

//////////////////////////////////////////////////////////////////////////////

SdXMLGroupShapeContext::~SdXMLGroupShapeContext()
{
}

//////////////////////////////////////////////////////////////////////////////

SvXMLImportContext* SdXMLGroupShapeContext::CreateChildContext( USHORT nPrefix,
    const OUString& rLocalName,
    const uno::Reference< xml::sax::XAttributeList>& xAttrList )
{
    SvXMLImportContext* pContext = 0L;

    // #i68101#
    if( nPrefix == XML_NAMESPACE_SVG &&
        (IsXMLToken( rLocalName, XML_TITLE ) || IsXMLToken( rLocalName, XML_DESC ) ) )
    {
        pContext = new SdXMLDescriptionContext( GetImport(), nPrefix, rLocalName, xAttrList, mxShape );
    }
    else if( nPrefix == XML_NAMESPACE_OFFICE && IsXMLToken( rLocalName, XML_EVENT_LISTENERS ) )
    {
        pContext = new SdXMLEventsContext( GetImport(), nPrefix, rLocalName, xAttrList, mxShape );
    }
    else if( nPrefix == XML_NAMESPACE_DRAW && IsXMLToken( rLocalName, XML_GLUE_POINT ) )
    {
        addGluePoint( xAttrList );
    }
    else
    {
        // call GroupChildContext function at common ShapeImport
        pContext = GetImport().GetShapeImport()->CreateGroupChildContext(
            GetImport(), nPrefix, rLocalName, xAttrList, mxChilds);
    }

    // call parent when no own context was created
    if(!pContext)
        pContext = SvXMLImportContext::CreateChildContext(
        nPrefix, rLocalName, xAttrList);

    return pContext;
}

//////////////////////////////////////////////////////////////////////////////

void SdXMLGroupShapeContext::StartElement(const uno::Reference< xml::sax::XAttributeList>&)
{
    // create new group shape and add it to rShapes, use it
    // as base for the new group import
    AddShape( "com.sun.star.drawing.GroupShape" );

    if(mxShape.is())
    {
        SetStyle( false );

        mxChilds = uno::Reference< drawing::XShapes >::query( mxShape );
        if( mxChilds.is() )
            GetImport().GetShapeImport()->pushGroupForSorting( mxChilds );
    }

    GetImport().GetShapeImport()->finishShape( mxShape, mxAttrList, mxShapes );
}

//////////////////////////////////////////////////////////////////////////////

void SdXMLGroupShapeContext::EndElement()
{
    if( mxChilds.is() )
        GetImport().GetShapeImport()->popGroupAndSort();

    SdXMLShapeContext::EndElement();
}

<|endoftext|>