<commit_before>/*
 * Copyright 2016 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include "serializer.hh"
namespace ser {

template<typename T>
void set_size(seastar::simple_output_stream& os, const T& obj) {
    serialize(os, get_sizeof(obj));
}

template<typename T>
void set_size(seastar::measuring_output_stream& os, const T& obj) {
    serialize(os, uint32_t(0));
}

template<typename Output>
void safe_serialize_as_uint32(Output& out, uint64_t data) {
    if (data > std::numeric_limits<uint32_t>::max()) {
        throw std::runtime_error("Size is too big for serialization");
    }
    serialize(out, uint32_t(data));
}

template<typename T>
constexpr bool can_serialize_fast() {
    return !std::is_same<T, bool>::value && std::is_integral<T>::value && (sizeof(T) == 1 || __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__);
}

template<bool Fast, typename T>
struct serialize_array_helper;

template<typename T>
struct serialize_array_helper<true, T> {
    template<typename Container, typename Output>
    static void doit(Output& out, const Container& v) {
        out.write(reinterpret_cast<const char*>(v.data()), v.size() * sizeof(T));
    }
};

template<typename T>
struct serialize_array_helper<false, T> {
    template<typename Container, typename Output>
    static void doit(Output& out, const Container& v) {
        for (auto&& e : v) {
            serialize(out, e);
        }
    }
};

template<typename T, typename Container, typename Output>
static inline void serialize_array(Output& out, const Container& v) {
    serialize_array_helper<can_serialize_fast<T>(), T>::doit(out, v);
}

template<typename Container>
struct container_traits;

template<typename T>
struct container_traits<std::vector<T>> {
    struct back_emplacer {
        std::vector<T>& c;
        back_emplacer(std::vector<T>& c_) : c(c_) {}
        void operator()(T&& v) {
            c.emplace_back(std::move(v));
        }
    };
    void resize(std::vector<T>& c, size_t size) {
        c.resize(size);
    }
};

template<typename T, size_t N>
struct container_traits<std::array<T, N>> {
    struct back_emplacer {
        std::array<T, N>& c;
        size_t idx = 0;
        back_emplacer(std::array<T, N>& c_) : c(c_) {}
        void operator()(T&& v) {
            c[idx++] = std::move(v);
        }
    };
    void resize(std::array<T, N>& c, size_t size) {}
};

template<bool Fast, typename T>
struct deserialize_array_helper;

template<typename T>
struct deserialize_array_helper<true, T> {
    template<typename Input, typename Container>
    static void doit(Input& in, Container& v, size_t sz) {
        container_traits<Container> t;
        t.resize(v, sz);
        in.read(reinterpret_cast<char*>(v.data()), v.size() * sizeof(T));
    }
};

template<typename T>
struct deserialize_array_helper<false, T> {
    template<typename Input, typename Container>
    static void doit(Input& in, Container& v, size_t sz) {
        typename container_traits<Container>::back_emplacer be(v);
        while (sz--) {
            be(deserialize(in, boost::type<T>()));
        }
    }
};

template<typename T, typename Input, typename Container>
static inline void deserialize_array(Input& in, Container& v, size_t sz) {
    deserialize_array_helper<can_serialize_fast<T>(), T>::doit(in, v, sz);
}

template<typename T, typename Output>
inline void serialize(Output& out, const std::vector<T>& v) {
    safe_serialize_as_uint32(out, v.size());
    serialize_array<T>(out, v);
}
template<typename T, typename Input>
inline std::vector<T> deserialize(Input& in, boost::type<std::vector<T>>) {
    auto sz = deserialize(in, boost::type<uint32_t>());
    std::vector<T> v;
    v.reserve(sz);
    deserialize_array<T>(in, v, sz);
    return v;
}

template<typename T, typename Ratio, typename Output>
inline void serialize(Output& out, const std::chrono::duration<T, Ratio>& d) {
    serialize(out, d.count());
};

template<typename T, typename Ratio, typename Input>
inline std::chrono::duration<T, Ratio> deserialize(Input in, boost::type<std::chrono::duration<T, Ratio>>) {
    return std::chrono::duration<T, Ratio>(deserialize(in, boost::type<T>()));
};

template<size_t N, typename T, typename Output>
inline void serialize(Output& out, const std::array<T, N>& v) {
    serialize_array<T>(out, v);
}
template<size_t N, typename T, typename Input>
inline std::array<T, N> deserialize(Input& in, boost::type<std::array<T, N>>) {
    std::array<T, N> v;
    deserialize_array<T>(in, v, N);
    return v;
}

template<typename K, typename V, typename Output>
inline void serialize(Output& out, const std::map<K, V>& v) {
    safe_serialize_as_uint32(out, v.size());
    for (auto&& e : v) {
        serialize(out, e.first);
        serialize(out, e.second);
    }
}
template<typename K, typename V, typename Input>
inline std::map<K, V> deserialize(Input& in, boost::type<std::map<K, V>>) {
    auto sz = deserialize(in, boost::type<uint32_t>());
    std::map<K, V> m;
    while (sz--) {
        K k = deserialize(in, boost::type<K>());
        V v = deserialize(in, boost::type<V>());
        m[k] = v;
    }
    return m;
}

template<typename Output>
void serialize(Output& out, const bytes_view& v) {
    safe_serialize_as_uint32(out, uint32_t(v.size()));
    out.write(reinterpret_cast<const char*>(v.begin()), v.size());
}
template<typename Output>
void serialize(Output& out, const managed_bytes& v) {
    safe_serialize_as_uint32(out, uint32_t(v.size()));
    out.write(reinterpret_cast<const char*>(v.begin()), v.size());
}
template<typename Output>
void serialize(Output& out, const bytes& v) {
    safe_serialize_as_uint32(out, uint32_t(v.size()));
    out.write(reinterpret_cast<const char*>(v.begin()), v.size());
}
template<typename Input>
bytes deserialize(Input& in, boost::type<bytes>) {
    auto sz = deserialize(in, boost::type<uint32_t>());
    bytes v(bytes::initialized_later(), sz);
    in.read(reinterpret_cast<char*>(v.begin()), sz);
    return v;
}

template<typename Output>
void serialize(Output& out, const bytes_ostream& v) {
    safe_serialize_as_uint32(out, uint32_t(v.size()));
    for (bytes_view frag : v.fragments()) {
        out.write(reinterpret_cast<const char*>(frag.begin()), frag.size());
    }
}
template<typename Input>
bytes_ostream deserialize(Input& in, boost::type<bytes_ostream>) {
    bytes_ostream v;
    v.write(deserialize(in, boost::type<bytes>()));
    return v;
}

template<typename T, typename Output>
inline void serialize(Output& out, const std::experimental::optional<T>& v) {
    serialize(out, bool(v));
    if (v) {
        serialize(out, v.value());
    }
}
template<typename T, typename Input>
inline std::experimental::optional<T> deserialize(Input& in, boost::type<std::experimental::optional<T>>) {
    std::experimental::optional<T> v;
    auto b = deserialize(in, boost::type<bool>());
    if (b) {
        v = deserialize(in, boost::type<T>());
    }
    return v;
}

template<typename Output>
void serialize(Output& out, const sstring& v) {
    safe_serialize_as_uint32(out, uint32_t(v.size()));
    out.write(v.begin(), v.size());
}
template<typename Input>
sstring deserialize(Input& in, boost::type<sstring>) {
    auto sz = deserialize(in, boost::type<uint32_t>());
    sstring v(sstring::initialized_later(), sz);
    in.read(v.begin(), sz);
    return v;
}

template<typename T, typename Output>
inline void serialize(Output& out, const std::unique_ptr<T>& v) {
    serialize(out, bool(v));
    if (v) {
        serialize(out, *v);
    }
}
template<typename T, typename Input>
inline std::unique_ptr<T> deserialize(Input& in, boost::type<std::unique_ptr<T>>) {
    std::unique_ptr<T> v;
    auto b = deserialize(in, boost::type<bool>());
    if (b) {
        v = std::make_unique<T>(deserialize(in, boost::type<T>()));
    }
    return v;
}

template<typename Clock, typename Duration, typename Output>
inline void serialize(Output& out, const std::chrono::time_point<Clock, Duration>& v) {
    serialize(out, uint64_t(v.time_since_epoch().count()));
}
template<typename Clock, typename Duration, typename Input>
inline std::chrono::time_point<Clock, Duration> deserialize(Input& in, boost::type<std::chrono::time_point<Clock, Duration>>) {
    return typename Clock::time_point(Duration(deserialize(in, boost::type<uint64_t>())));
}

template<typename Enum, typename Output>
inline void serialize(Output& out, const enum_set<Enum>& v) {
    serialize(out, uint64_t(v.mask()));
}
template<typename Enum, typename Input>
inline enum_set<Enum> deserialize(Input& in, boost::type<enum_set<Enum>>) {
    return enum_set<Enum>::from_mask(deserialize(in, boost::type<uint64_t>()));
}

template<typename T>
size_type get_sizeof(const T& obj) {
    seastar::measuring_output_stream ms;
    serialize(ms, obj);
    auto size = ms.size();
    if (size > std::numeric_limits<size_type>::max()) {
        throw std::runtime_error("Object is too big for get_sizeof");
    }
    return size;
}

template<typename Buffer, typename T>
Buffer serialize_to_buffer(const T& v, size_t head_space) {
    seastar::measuring_output_stream measure;
    ser::serialize(measure, v);
    Buffer ret(typename Buffer::initialized_later(), measure.size() + head_space);
    seastar::simple_output_stream out(reinterpret_cast<char*>(ret.begin()), head_space);
    ser::serialize(out, v);
    return ret;
}

template<typename T, typename Buffer>
T deserialize_from_buffer(const Buffer& buf, boost::type<T> type, size_t head_space) {
    seastar::simple_input_stream in(reinterpret_cast<const char*>(buf.begin() + head_space), buf.size() - head_space);
    return deserialize(in, std::move(type));
}

template<typename Output, typename ...T>
void serialize(Output& out, const boost::variant<T...>& v) {}

template<typename Input, typename ...T>
boost::variant<T...> deserialize(Input& in, boost::type<boost::variant<T...>>) {
    return boost::variant<T...>();
}

template<typename Output>
void serialize(Output& out, const unknown_variant_type& v) {
    out.write(v.data.begin(), v.data.size());
}
template<typename Input>
unknown_variant_type deserialize(Input& in, boost::type<unknown_variant_type>) {
    auto size = deserialize(in, boost::type<size_type>());
    auto index = deserialize(in, boost::type<size_type>());
    auto sz = size - sizeof(size_type) * 2;
    sstring v(sstring::initialized_later(), sz);
    in.read(v.begin(), sz);
    return unknown_variant_type{index, std::move(v)};
}
}
<commit_msg>serializer: fix duration deserializer<commit_after>/*
 * Copyright 2016 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include "serializer.hh"
namespace ser {

template<typename T>
void set_size(seastar::simple_output_stream& os, const T& obj) {
    serialize(os, get_sizeof(obj));
}

template<typename T>
void set_size(seastar::measuring_output_stream& os, const T& obj) {
    serialize(os, uint32_t(0));
}

template<typename Output>
void safe_serialize_as_uint32(Output& out, uint64_t data) {
    if (data > std::numeric_limits<uint32_t>::max()) {
        throw std::runtime_error("Size is too big for serialization");
    }
    serialize(out, uint32_t(data));
}

template<typename T>
constexpr bool can_serialize_fast() {
    return !std::is_same<T, bool>::value && std::is_integral<T>::value && (sizeof(T) == 1 || __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__);
}

template<bool Fast, typename T>
struct serialize_array_helper;

template<typename T>
struct serialize_array_helper<true, T> {
    template<typename Container, typename Output>
    static void doit(Output& out, const Container& v) {
        out.write(reinterpret_cast<const char*>(v.data()), v.size() * sizeof(T));
    }
};

template<typename T>
struct serialize_array_helper<false, T> {
    template<typename Container, typename Output>
    static void doit(Output& out, const Container& v) {
        for (auto&& e : v) {
            serialize(out, e);
        }
    }
};

template<typename T, typename Container, typename Output>
static inline void serialize_array(Output& out, const Container& v) {
    serialize_array_helper<can_serialize_fast<T>(), T>::doit(out, v);
}

template<typename Container>
struct container_traits;

template<typename T>
struct container_traits<std::vector<T>> {
    struct back_emplacer {
        std::vector<T>& c;
        back_emplacer(std::vector<T>& c_) : c(c_) {}
        void operator()(T&& v) {
            c.emplace_back(std::move(v));
        }
    };
    void resize(std::vector<T>& c, size_t size) {
        c.resize(size);
    }
};

template<typename T, size_t N>
struct container_traits<std::array<T, N>> {
    struct back_emplacer {
        std::array<T, N>& c;
        size_t idx = 0;
        back_emplacer(std::array<T, N>& c_) : c(c_) {}
        void operator()(T&& v) {
            c[idx++] = std::move(v);
        }
    };
    void resize(std::array<T, N>& c, size_t size) {}
};

template<bool Fast, typename T>
struct deserialize_array_helper;

template<typename T>
struct deserialize_array_helper<true, T> {
    template<typename Input, typename Container>
    static void doit(Input& in, Container& v, size_t sz) {
        container_traits<Container> t;
        t.resize(v, sz);
        in.read(reinterpret_cast<char*>(v.data()), v.size() * sizeof(T));
    }
};

template<typename T>
struct deserialize_array_helper<false, T> {
    template<typename Input, typename Container>
    static void doit(Input& in, Container& v, size_t sz) {
        typename container_traits<Container>::back_emplacer be(v);
        while (sz--) {
            be(deserialize(in, boost::type<T>()));
        }
    }
};

template<typename T, typename Input, typename Container>
static inline void deserialize_array(Input& in, Container& v, size_t sz) {
    deserialize_array_helper<can_serialize_fast<T>(), T>::doit(in, v, sz);
}

template<typename T, typename Output>
inline void serialize(Output& out, const std::vector<T>& v) {
    safe_serialize_as_uint32(out, v.size());
    serialize_array<T>(out, v);
}
template<typename T, typename Input>
inline std::vector<T> deserialize(Input& in, boost::type<std::vector<T>>) {
    auto sz = deserialize(in, boost::type<uint32_t>());
    std::vector<T> v;
    v.reserve(sz);
    deserialize_array<T>(in, v, sz);
    return v;
}

template<typename T, typename Ratio, typename Output>
inline void serialize(Output& out, const std::chrono::duration<T, Ratio>& d) {
    serialize(out, d.count());
};

template<typename T, typename Ratio, typename Input>
inline std::chrono::duration<T, Ratio> deserialize(Input& in, boost::type<std::chrono::duration<T, Ratio>>) {
    return std::chrono::duration<T, Ratio>(deserialize(in, boost::type<T>()));
};

template<size_t N, typename T, typename Output>
inline void serialize(Output& out, const std::array<T, N>& v) {
    serialize_array<T>(out, v);
}
template<size_t N, typename T, typename Input>
inline std::array<T, N> deserialize(Input& in, boost::type<std::array<T, N>>) {
    std::array<T, N> v;
    deserialize_array<T>(in, v, N);
    return v;
}

template<typename K, typename V, typename Output>
inline void serialize(Output& out, const std::map<K, V>& v) {
    safe_serialize_as_uint32(out, v.size());
    for (auto&& e : v) {
        serialize(out, e.first);
        serialize(out, e.second);
    }
}
template<typename K, typename V, typename Input>
inline std::map<K, V> deserialize(Input& in, boost::type<std::map<K, V>>) {
    auto sz = deserialize(in, boost::type<uint32_t>());
    std::map<K, V> m;
    while (sz--) {
        K k = deserialize(in, boost::type<K>());
        V v = deserialize(in, boost::type<V>());
        m[k] = v;
    }
    return m;
}

template<typename Output>
void serialize(Output& out, const bytes_view& v) {
    safe_serialize_as_uint32(out, uint32_t(v.size()));
    out.write(reinterpret_cast<const char*>(v.begin()), v.size());
}
template<typename Output>
void serialize(Output& out, const managed_bytes& v) {
    safe_serialize_as_uint32(out, uint32_t(v.size()));
    out.write(reinterpret_cast<const char*>(v.begin()), v.size());
}
template<typename Output>
void serialize(Output& out, const bytes& v) {
    safe_serialize_as_uint32(out, uint32_t(v.size()));
    out.write(reinterpret_cast<const char*>(v.begin()), v.size());
}
template<typename Input>
bytes deserialize(Input& in, boost::type<bytes>) {
    auto sz = deserialize(in, boost::type<uint32_t>());
    bytes v(bytes::initialized_later(), sz);
    in.read(reinterpret_cast<char*>(v.begin()), sz);
    return v;
}

template<typename Output>
void serialize(Output& out, const bytes_ostream& v) {
    safe_serialize_as_uint32(out, uint32_t(v.size()));
    for (bytes_view frag : v.fragments()) {
        out.write(reinterpret_cast<const char*>(frag.begin()), frag.size());
    }
}
template<typename Input>
bytes_ostream deserialize(Input& in, boost::type<bytes_ostream>) {
    bytes_ostream v;
    v.write(deserialize(in, boost::type<bytes>()));
    return v;
}

template<typename T, typename Output>
inline void serialize(Output& out, const std::experimental::optional<T>& v) {
    serialize(out, bool(v));
    if (v) {
        serialize(out, v.value());
    }
}
template<typename T, typename Input>
inline std::experimental::optional<T> deserialize(Input& in, boost::type<std::experimental::optional<T>>) {
    std::experimental::optional<T> v;
    auto b = deserialize(in, boost::type<bool>());
    if (b) {
        v = deserialize(in, boost::type<T>());
    }
    return v;
}

template<typename Output>
void serialize(Output& out, const sstring& v) {
    safe_serialize_as_uint32(out, uint32_t(v.size()));
    out.write(v.begin(), v.size());
}
template<typename Input>
sstring deserialize(Input& in, boost::type<sstring>) {
    auto sz = deserialize(in, boost::type<uint32_t>());
    sstring v(sstring::initialized_later(), sz);
    in.read(v.begin(), sz);
    return v;
}

template<typename T, typename Output>
inline void serialize(Output& out, const std::unique_ptr<T>& v) {
    serialize(out, bool(v));
    if (v) {
        serialize(out, *v);
    }
}
template<typename T, typename Input>
inline std::unique_ptr<T> deserialize(Input& in, boost::type<std::unique_ptr<T>>) {
    std::unique_ptr<T> v;
    auto b = deserialize(in, boost::type<bool>());
    if (b) {
        v = std::make_unique<T>(deserialize(in, boost::type<T>()));
    }
    return v;
}

template<typename Clock, typename Duration, typename Output>
inline void serialize(Output& out, const std::chrono::time_point<Clock, Duration>& v) {
    serialize(out, uint64_t(v.time_since_epoch().count()));
}
template<typename Clock, typename Duration, typename Input>
inline std::chrono::time_point<Clock, Duration> deserialize(Input& in, boost::type<std::chrono::time_point<Clock, Duration>>) {
    return typename Clock::time_point(Duration(deserialize(in, boost::type<uint64_t>())));
}

template<typename Enum, typename Output>
inline void serialize(Output& out, const enum_set<Enum>& v) {
    serialize(out, uint64_t(v.mask()));
}
template<typename Enum, typename Input>
inline enum_set<Enum> deserialize(Input& in, boost::type<enum_set<Enum>>) {
    return enum_set<Enum>::from_mask(deserialize(in, boost::type<uint64_t>()));
}

template<typename T>
size_type get_sizeof(const T& obj) {
    seastar::measuring_output_stream ms;
    serialize(ms, obj);
    auto size = ms.size();
    if (size > std::numeric_limits<size_type>::max()) {
        throw std::runtime_error("Object is too big for get_sizeof");
    }
    return size;
}

template<typename Buffer, typename T>
Buffer serialize_to_buffer(const T& v, size_t head_space) {
    seastar::measuring_output_stream measure;
    ser::serialize(measure, v);
    Buffer ret(typename Buffer::initialized_later(), measure.size() + head_space);
    seastar::simple_output_stream out(reinterpret_cast<char*>(ret.begin()), head_space);
    ser::serialize(out, v);
    return ret;
}

template<typename T, typename Buffer>
T deserialize_from_buffer(const Buffer& buf, boost::type<T> type, size_t head_space) {
    seastar::simple_input_stream in(reinterpret_cast<const char*>(buf.begin() + head_space), buf.size() - head_space);
    return deserialize(in, std::move(type));
}

template<typename Output, typename ...T>
void serialize(Output& out, const boost::variant<T...>& v) {}

template<typename Input, typename ...T>
boost::variant<T...> deserialize(Input& in, boost::type<boost::variant<T...>>) {
    return boost::variant<T...>();
}

template<typename Output>
void serialize(Output& out, const unknown_variant_type& v) {
    out.write(v.data.begin(), v.data.size());
}
template<typename Input>
unknown_variant_type deserialize(Input& in, boost::type<unknown_variant_type>) {
    auto size = deserialize(in, boost::type<size_type>());
    auto index = deserialize(in, boost::type<size_type>());
    auto sz = size - sizeof(size_type) * 2;
    sstring v(sstring::initialized_later(), sz);
    in.read(v.begin(), sz);
    return unknown_variant_type{index, std::move(v)};
}
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015-2016 Nagisa Sekiguchi
 *
 * Licensed 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.
 */

#ifndef YDSH_MISC_HASH_HPP
#define YDSH_MISC_HASH_HPP

#include <cstring>
#include <unordered_map>

namespace ydsh {

struct CStringComparator {
    bool operator()(const char *x, const char *y) const {
        return strcmp(x, y) == 0;
    }
};

struct CStringHash {
    std::size_t operator()(const char *key) const {
        std::size_t hash = 1;
        for(std::size_t i = 0; key[i] != '\0'; i++) {
            hash = hash * 61 + key[i];
        }
        return hash;
    }
};

template <typename T>
using CStringHashMap = std::unordered_map<const char *, T, CStringHash, CStringComparator>;

} // namespace ydsh

#endif //YDSH_MISC_HASH_HPP
<commit_msg>fix cstring hash<commit_after>/*
 * Copyright (C) 2015-2017 Nagisa Sekiguchi
 *
 * Licensed 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.
 */

#ifndef YDSH_MISC_HASH_HPP
#define YDSH_MISC_HASH_HPP

#include <cstring>
#include <unordered_map>

namespace ydsh {

struct CStringComparator {
    bool operator()(const char *x, const char *y) const {
        return strcmp(x, y) == 0;
    }
};

struct CStringHash {
    std::size_t operator()(const char *key) const {
        std::size_t hash = 0;
        while(*key != '\0') {
            hash = hash * 31 + *(key++);
        }
        return hash;
    }
};

template <typename T>
using CStringHashMap = std::unordered_map<const char *, T, CStringHash, CStringComparator>;

} // namespace ydsh

#endif //YDSH_MISC_HASH_HPP
<|endoftext|>


<commit_before>#include "hext/capture-pattern.h"

namespace hext {

capture_pattern::capture_pattern(
  const std::string& result_name,
  const std::string& regex
)
: name(result_name)
, rx(regex.empty() ? nullptr : new boost::regex(regex))
{
}

capture_pattern::~capture_pattern()
{
}

std::string capture_pattern::regex_filter(const char * str) const
{
  assert(this->rx);
  if( !this->rx )
    return str;

  boost::match_results<const char *> mr;
  if( boost::regex_match(str, str + strlen(str), mr, *(this->rx)) )
  {
    // If there are no captures, return whole string (mr[0]), if there are
    // captures, then return the first one
    if( mr.size() > 1 )
      return mr[1];
    else
      return mr[0];
  }
  else
  {
    return "";
  }
}

} // namespace hext

<commit_msg>use regex_search to allow for shorter regexes when capturing values<commit_after>#include "hext/capture-pattern.h"

namespace hext {

capture_pattern::capture_pattern(
  const std::string& result_name,
  const std::string& regex
)
: name(result_name)
, rx(regex.empty() ? nullptr : new boost::regex(regex))
{
}

capture_pattern::~capture_pattern()
{
}

std::string capture_pattern::regex_filter(const char * str) const
{
  assert(this->rx);
  if( !this->rx )
    return str;

  boost::match_results<const char *> mr;
  if( boost::regex_search(str, str + strlen(str), mr, *(this->rx)) )
  {
    // If there are no captures, return whole string (mr[0]), if there are
    // captures, then return the first one
    if( mr.size() > 1 )
      return mr[1];
    else
      return mr[0];
  }
  else
  {
    return "";
  }
}

} // namespace hext

<|endoftext|>


<commit_before>int main(int argc, char *argv[])
{
   // Reads from the file build/version_number the new version
   // number and generates the header base/inc/RVersion.h.
   // To be executed as CINT script by build/unix/makeversion.sh.
   //
   // Author: Fons Rademakers   11/10/99

   const char *in  = "build/version_number";
   const char *inr = "etc/svninfo.txt";

   FILE *fp = fopen(in, "r");
   if (!fp) {
      printf("%s: can not open input file %s\n", argv[0], in);
      exit(1);
   }
   char vers[32];
   fgets(vers, sizeof(vers), fp);
   if (vers[strlen(vers)-1] == '\n') vers[strlen(vers)-1] = 0;
   fclose(fp);

   fp = fopen(inr, "r");
   if (!fp) {
      printf("%s: can not open input file %s\n", argv[0], in);
      exit(1);
   }
   char branch[2048];
   fgets(branch, sizeof(branch), fp);
   if (branch[strlen(branch)-1] == '\n') branch[strlen(branch)-1] = 0;
   char revs[32];
   fgets(revs, sizeof(revs), fp);
   if (revs[strlen(revs)-1] == '\n') revs[strlen(revs)-1] = 0;
   fclose(fp);

   const char *out = "core/base/inc/RVersion.h";
   fp = fopen(out, "w");
   if (!fp) {
      printf("%s: can not open output file %s\n", argv[0], out);
      exit(1);
   }

   fprintf(fp, "#ifndef ROOT_RVersion\n");
   fprintf(fp, "#define ROOT_RVersion\n\n");
   fprintf(fp, "/* Version information automatically generated by installer. */\n\n");
   fprintf(fp, "/*\n");
   fprintf(fp, " * These macros can be used in the following way:\n");
   fprintf(fp, " *\n");
   fprintf(fp, " *    #if ROOT_VERSION_CODE >= ROOT_VERSION(2,23,4)\n");
   fprintf(fp, " *       #include <newheader.h>\n");
   fprintf(fp, " *    #else\n");
   fprintf(fp, " *       #include <oldheader.h>\n");
   fprintf(fp, " *    #endif\n");
   fprintf(fp, " *\n");
   fprintf(fp, "*/\n\n");

   int xx, yy, zz, rev;
   sscanf(vers, "%d.%d/%d", &xx, &yy, &zz);
   int vers_code = (xx << 16) + (yy << 8) + zz;
   sscanf(revs, "%d", &rev);

   fprintf(fp, "#define ROOT_RELEASE \"%s\"\n", vers);
   fprintf(fp, "#define ROOT_RELEASE_DATE \"%s\"\n", __DATE__);
   fprintf(fp, "#define ROOT_RELEASE_TIME \"%s\"\n", __TIME__);
   fprintf(fp, "#define ROOT_SVN_REVISION %d\n", rev);
   fprintf(fp, "#define ROOT_SVN_BRANCH \"%s\"\n", branch);
   fprintf(fp, "#define ROOT_VERSION_CODE %d\n", vers_code);
   fprintf(fp, "#define ROOT_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))\n");
   fprintf(fp, "\n#endif\n");

   fclose(fp);

   exit(0);
}
<commit_msg>It might be in some extreme cases necessary to also check on the specific patch level of ROOT, for that we added to new macros to RVersion.h: #define ROOT_FULL_VERSION_CODE 85786880 #define ROOT_FULL_VERSION(a,b,c,p) (((a) << 24) + ((b) << 16) + ((c) << 8) + (p) To be used like: #if defined (ROOT_FULL_VERSION_CODE) && \     ROOT_FULL_VERSION_CODE >= ROOT_FULL_VERSION(5,28,0,a) #include <newheader.h> #else #include <oldheader.h> #endif<commit_after>int main(int argc, char *argv[])
{
   // Reads from the file build/version_number the new version
   // number and generates the header base/inc/RVersion.h.
   // To be executed as CINT script by build/unix/makeversion.sh.
   //
   // Author: Fons Rademakers   11/10/99

   const char *in  = "build/version_number";
   const char *inr = "etc/svninfo.txt";

   FILE *fp = fopen(in, "r");
   if (!fp) {
      printf("%s: can not open input file %s\n", argv[0], in);
      exit(1);
   }
   char vers[32];
   fgets(vers, sizeof(vers), fp);
   if (vers[strlen(vers)-1] == '\n') vers[strlen(vers)-1] = 0;
   fclose(fp);

   fp = fopen(inr, "r");
   if (!fp) {
      printf("%s: can not open input file %s\n", argv[0], in);
      exit(1);
   }
   char branch[2048];
   fgets(branch, sizeof(branch), fp);
   if (branch[strlen(branch)-1] == '\n') branch[strlen(branch)-1] = 0;
   char revs[32];
   fgets(revs, sizeof(revs), fp);
   if (revs[strlen(revs)-1] == '\n') revs[strlen(revs)-1] = 0;
   fclose(fp);

   const char *out = "core/base/inc/RVersion.h";
   fp = fopen(out, "w");
   if (!fp) {
      printf("%s: can not open output file %s\n", argv[0], out);
      exit(1);
   }

   fprintf(fp, "#ifndef ROOT_RVersion\n");
   fprintf(fp, "#define ROOT_RVersion\n\n");
   fprintf(fp, "/* Version information automatically generated by installer. */\n\n");
   fprintf(fp, "/*\n");
   fprintf(fp, " * These macros can be used in the following way:\n");
   fprintf(fp, " *\n");
   fprintf(fp, " *    #if ROOT_VERSION_CODE >= ROOT_VERSION(2,23,4)\n");
   fprintf(fp, " *       #include <newheader.h>\n");
   fprintf(fp, " *    #else\n");
   fprintf(fp, " *       #include <oldheader.h>\n");
   fprintf(fp, " *    #endif\n");
   fprintf(fp, " *\n");
   fprintf(fp, "*/\n\n");

   int xx, yy, zz, rev;
   char patch = '\0';
   sscanf(vers, "%d.%d/%d%c", &xx, &yy, &zz, &patch);
   int vers_code = (xx << 16) + (yy << 8) + zz;
   int full_vers_code = (xx << 24) + (yy << 16) + (zz << 8) + patch;
   sscanf(revs, "%d", &rev);

   fprintf(fp, "#define ROOT_RELEASE \"%s\"\n", vers);
   fprintf(fp, "#define ROOT_RELEASE_DATE \"%s\"\n", __DATE__);
   fprintf(fp, "#define ROOT_RELEASE_TIME \"%s\"\n", __TIME__);
   fprintf(fp, "#define ROOT_SVN_REVISION %d\n", rev);
   fprintf(fp, "#define ROOT_SVN_BRANCH \"%s\"\n", branch);
   fprintf(fp, "#define ROOT_VERSION_CODE %d\n", vers_code);
   fprintf(fp, "#define ROOT_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))\n");
   fprintf(fp, "#define ROOT_FULL_VERSION_CODE %d\n", full_vers_code);
   fprintf(fp, "#define ROOT_FULL_VERSION(a,b,c,p) (((a) << 24) + ((b) << 16) + ((c) << 8) + (p))\n");
   fprintf(fp, "\n#endif\n");

   fclose(fp);

   exit(0);
}
<|endoftext|>


<commit_before>#include "hext/program-options.h"

namespace hext {

program_options::program_options()
: desc("Options"),
  vm()
{
  namespace po = boost::program_options;
  this->desc.add_options()
    ("help,h", "This help message")
    ("hext-file,f", po::value<std::string>(), "Path to hext file")
    ("html-file,i", po::value<std::string>(), "Path to html file")
    ("lint,l", "Hext syntax check")
    ("print,p", "Print parsed hext")
    ("print-debug,d", "Print hext with match_count")
    ("keep-invalid,k", "Do not remove invalid matches")
    ("mt-graph,g", "Print match-tree as DOT")
  ;
}

void program_options::store(int argc, const char * argv[])
{
  namespace po = boost::program_options;
  po::store(po::parse_command_line(argc, argv, this->desc), this->vm);
  po::notify(this->vm);

  if( !this->contains("hext-file") )
    throw po::error("missing option --hext-file");

  if( this->contains("print") || this->contains("lint") )
    return;

  if( !this->contains("html-file") )
    throw po::error("missing option --html-file");
}

bool program_options::contains(const char * key) const
{
  return this->vm.count(key);
}

std::string program_options::get(const char * key) const
{
  return this->vm[key].as<std::string>();
}

void program_options::print(const char * program_name, std::ostream& out) const
{
  out << "Usage:\n"
      << program_name << " -h hext-file -i html-file\n";
  out << this->desc;
}

} // namespace hext

<commit_msg>don't validate arguments if --help was given<commit_after>#include "hext/program-options.h"

namespace hext {

program_options::program_options()
: desc("Options"),
  vm()
{
  namespace po = boost::program_options;
  this->desc.add_options()
    ("help,h", "This help message")
    ("hext-file,f", po::value<std::string>(), "Path to hext file")
    ("html-file,i", po::value<std::string>(), "Path to html file")
    ("lint,l", "Hext syntax check")
    ("print,p", "Print parsed hext")
    ("print-debug,d", "Print hext with match_count")
    ("keep-invalid,k", "Do not remove invalid matches")
    ("mt-graph,g", "Print match-tree as DOT")
  ;
}

void program_options::store(int argc, const char * argv[])
{
  namespace po = boost::program_options;
  po::store(po::parse_command_line(argc, argv, this->desc), this->vm);
  po::notify(this->vm);

  if( this->contains("help") )
    return;

  if( !this->contains("hext-file") )
    throw po::error("missing option --hext-file");

  if( this->contains("print") || this->contains("lint") )
    return;

  if( !this->contains("html-file") )
    throw po::error("missing option --html-file");
}

bool program_options::contains(const char * key) const
{
  return this->vm.count(key);
}

std::string program_options::get(const char * key) const
{
  return this->vm[key].as<std::string>();
}

void program_options::print(const char * program_name, std::ostream& out) const
{
  out << "Usage:\n"
      << program_name << " -h hext-file -i html-file\n";
  out << this->desc;
}

} // namespace hext

<|endoftext|>


<commit_before>//
// Copyright Copyright 2009-2021, AMT – The Association For Manufacturing Technology (“AMT”)
// All rights reserved.
//
//    Licensed 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.
//

#pragma once

#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast.hpp>
#include <boost/bind/bind.hpp>
#include <boost/beast/http/chunk_encode.hpp>
//#include <boost/beast/ssl.hpp>
//#include <boost/asio/ssl/stream.hpp>

#include <boost/beast/version.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>

#include "utilities.hpp"
#include <unordered_map>

#include <dlib/logger.h>
#include <dlib/md5.h>

#include <chrono>
#include <map>
#include <ostream>
#include <iostream>
#include <sstream>
#include <string>
#include <variant>

namespace mtconnect
{
  namespace http_server
  {

    namespace beast = boost::beast;         // from <boost/beast.hpp>
    namespace http = beast::http;           // from <boost/beast/http.hpp>
    namespace net = boost::asio;            // from <boost/asio.hpp>
    //namespace ssl = boost::asio::ssl;       // from <boost/asio/ssl.hpp>
    using tcp = boost::asio::ip::tcp;       // from <boost/asio/ip/tcp.hpp>

    enum ResponseCode
    {
      CONTINUE = 100,
      SWITCHING_PROTOCOLS = 101,
      OK = 200,
      CREATED = 201,
      ACCEPTED = 202,
      NON_AUTHORITATIVE_INFORMATION = 203,
      NO_CONTENT = 204,
      RESET_CONTENT = 205,
      PARTIAL_CONTENT = 206,
      MULTIPLE_CHOICES = 300,
      MOVED_PERMANENTLY = 301,
      FOUND = 302,
      SEE_OTHER = 303,
      NOT_MODIFIED = 304,
      USE_PROXY = 305,
      TEMPORARY_REDIRECT = 307,
      BAD_REQUEST = 400,
      UNAUTHORIZED = 401,
      PAYMENT_REQUIRED = 402,
      FORBIDDEN = 403,
      NOT_FOUND = 404,
      METHOD_NOT_ALLOWED = 405,
      NOT_ACCEPTABLE = 406,
      PROXY_AUTHENTICATION_REQUIRED = 407,
      REQUEST_TIMEOUT = 408,
      CONFLICT = 409,
      GONE = 410,
      LENGTH_REQUIRED = 411,
      PRECONDITION_FAILED = 412,
      PAYLOAD_TOO_LARGE = 413,
      URI_TOO_LONG = 414,
      UNSUPPORTED_MEDIA_TYPE = 415,
      RANGE_NOT_SATISFIABLE = 416,
      EXPECTATION_FAILED = 417,
      UPGRADE_REQUIRED = 426,
      INTERNAL_SERVER_ERROR = 500,
      NOT_IMPLEMENTED = 501,
      BAD_GATEWAY = 502,
      SERVICE_UNAVAILABLE = 503,
      GATEWAY_TIMEOUT = 504,
      HTTP_VERSION_NOT_SUPPORTED = 505
    };

    class Response
    {
    public:
      //Response(tcp::socket &socket, std::ostream &out, const StringList &fields = {}) : m_socket(std::move(socket)) ,m_out(out), m_fields(fields)
      Response(tcp::socket &socket,  const StringList &fields = {}) : m_socket(std::move(socket)), m_fields(fields)
      {
      }

      virtual ~Response() = default;

      virtual std::string getHeaderDate() { return getCurrentTime(HUM_READ); }

      bool good() const { return m_out.good(); }
      void setBad() { m_out.setstate(std::ios::badbit); }
      void flush() { m_out.flush(); }
      static std::string getStatus(uint16_t code)
      {
        auto cm = m_status.find(code);
        if (cm == m_status.end())
          return "Unknown";
        else
          return cm->second;
      }

      virtual void beginMultipartStream()
      {
        if (good())
        {
          m_boundary = dlib::md5(std::to_string(time(nullptr)));

          http::response<http::empty_body> res{http::status::ok, 11};
          res.set(http::field::server, "MTConnectAgent");
          res.set(http::field::date, getHeaderDate());
          res.set(http::field::connection, "close");
          res.set(http::field::expires, "-1");
          res.set(http::field::cache_control, "private, max-age=0\r\n");
          std::string content_type = "multipart/x-mixed-replace;boundary=";
          content_type.append(m_boundary);
          res.set(http::field::content_type, content_type);
          res.chunked(true);
          http::response_serializer<http::empty_body> sr{res};
          write_header(m_socket, sr);
        }
      }

      virtual void writeMultipartChunk(const std::string &body, const std::string &mimeType)
      {
        if (good())
        {
          using namespace http;
          using namespace std;
          using chunk_extensions = basic_chunk_extensions< std::allocator< char > >;
          char buffer[2048];
          ostringstream str;
          str << "--" << m_boundary
              << "\r\n"
                 "Content-type: "
              << mimeType
              << "\r\n"
                 "Content-length: "
              << body.length() << "\r\n\r\n"
              << body << "\r\n\r\n";

          net::const_buffers_1 buf(str.str().c_str(),str.str().size());
          chunk_body chunk(buf);
          net::write(m_socket,chunk,ec);
          if (ec)
          {
            string errorMsg = "Error writing chunk - ";
            errorMsg.append(ec.message());
            auto const textSize = errorMsg.length();
            http::response<http::string_body> errorRes{
                std::piecewise_construct, std::make_tuple(std::move(errorMsg.c_str())),
                std::make_tuple(http::status::ok, 11)  // m_req.version())
            };
            errorRes.set(http::field::server, "MTConnectAgent");
            errorRes.set(http::field::date, getHeaderDate());
            errorRes.set(http::field::connection, "close");
            errorRes.set(http::field::expires, "-1");
            errorRes.set(http::field::content_type, "text/xml");
            errorRes.content_length(textSize);
            http::serializer<false, http::string_body, http::fields> sr{errorRes};
            http::write(m_socket, sr, ec);
          }
        }
      }

      void writeResponse(const std::string &body, const std::string &mimeType = "text/plain",
                         const ResponseCode code = OK,
                         const std::chrono::seconds expires = std::chrono::seconds(0))
      {
        writeResponse(body.c_str(), body.size(), code, mimeType, expires);
      }

      void writeResponse(const std::string &body, const ResponseCode code,
                         const std::string &mimeType = "text/plain",
                         const std::chrono::seconds expires = std::chrono::seconds(0))
      {
        writeResponse(body.c_str(), body.size(), code, mimeType, expires);
      }

      virtual void writeResponse(const char *body, const size_t size, const ResponseCode code,
                                 const std::string &mimeType = "text/plain",
                                 const std::chrono::seconds expires = std::chrono::seconds(0))
      {
        if (good())
        {
          using namespace std;
          string expiry;
          if (expires == 0s)
          {
            expiry =
                "Expires: -1\r\n"
                "Cache-Control: private, max-age=0\r\n";
          }
          else
          {
            expiry = getCurrentTime(chrono::system_clock::now() + expires, HUM_READ);
          }
          m_out.write(body, size);
          auto const textSize = m_out.str().size();
          http::response<http::string_body> res
          {
              std::piecewise_construct,
              std::make_tuple(std::move(m_out.str().c_str())),
              std::make_tuple(http::status::ok, 11)// m_req.version())
          };
          res.set(http::field::server, "MTConnectAgent");
          res.set(http::field::date, getHeaderDate());
          res.set(http::field::connection, "close");
          res.set(http::field::expires, "-1");
          res.set(http::field::content_type, mimeType);
          //res.set(http::status::continue_, 100, 100);
          res.content_length(textSize);
          http::serializer<false, http::string_body , http::fields> sr{res};
          http::write(m_socket, sr, ec);
        }
      }

    protected:
      std::stringstream m_out;
      tcp::socket m_socket;
      std::string m_boundary;
      std::string content_type;
      http::response<http::buffer_body> res_c{http::status::ok, 11};
      static const std::unordered_map<uint16_t, std::string> m_status;
      static const std::unordered_map<std::string, uint16_t> m_codes;
      StringList m_fields;
      beast::error_code ec;
      bool close{false};
    };
  }  // namespace http_server
}  // namespace mtconnect
<commit_msg>Removed some unused lines of code<commit_after>//
// Copyright Copyright 2009-2021, AMT – The Association For Manufacturing Technology (“AMT”)
// All rights reserved.
//
//    Licensed 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.
//

#pragma once

#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast.hpp>
#include <boost/bind/bind.hpp>
#include <boost/beast/http/chunk_encode.hpp>
//#include <boost/beast/ssl.hpp>
//#include <boost/asio/ssl/stream.hpp>

#include <boost/beast/version.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>

#include "utilities.hpp"
#include <unordered_map>

#include <dlib/logger.h>
#include <dlib/md5.h>

#include <chrono>
#include <map>
#include <ostream>
#include <iostream>
#include <sstream>
#include <string>
#include <variant>

namespace mtconnect
{
  namespace http_server
  {

    namespace beast = boost::beast;         // from <boost/beast.hpp>
    namespace http = beast::http;           // from <boost/beast/http.hpp>
    namespace net = boost::asio;            // from <boost/asio.hpp>
    //namespace ssl = boost::asio::ssl;       // from <boost/asio/ssl.hpp>
    using tcp = boost::asio::ip::tcp;       // from <boost/asio/ip/tcp.hpp>

    enum ResponseCode
    {
      CONTINUE = 100,
      SWITCHING_PROTOCOLS = 101,
      OK = 200,
      CREATED = 201,
      ACCEPTED = 202,
      NON_AUTHORITATIVE_INFORMATION = 203,
      NO_CONTENT = 204,
      RESET_CONTENT = 205,
      PARTIAL_CONTENT = 206,
      MULTIPLE_CHOICES = 300,
      MOVED_PERMANENTLY = 301,
      FOUND = 302,
      SEE_OTHER = 303,
      NOT_MODIFIED = 304,
      USE_PROXY = 305,
      TEMPORARY_REDIRECT = 307,
      BAD_REQUEST = 400,
      UNAUTHORIZED = 401,
      PAYMENT_REQUIRED = 402,
      FORBIDDEN = 403,
      NOT_FOUND = 404,
      METHOD_NOT_ALLOWED = 405,
      NOT_ACCEPTABLE = 406,
      PROXY_AUTHENTICATION_REQUIRED = 407,
      REQUEST_TIMEOUT = 408,
      CONFLICT = 409,
      GONE = 410,
      LENGTH_REQUIRED = 411,
      PRECONDITION_FAILED = 412,
      PAYLOAD_TOO_LARGE = 413,
      URI_TOO_LONG = 414,
      UNSUPPORTED_MEDIA_TYPE = 415,
      RANGE_NOT_SATISFIABLE = 416,
      EXPECTATION_FAILED = 417,
      UPGRADE_REQUIRED = 426,
      INTERNAL_SERVER_ERROR = 500,
      NOT_IMPLEMENTED = 501,
      BAD_GATEWAY = 502,
      SERVICE_UNAVAILABLE = 503,
      GATEWAY_TIMEOUT = 504,
      HTTP_VERSION_NOT_SUPPORTED = 505
    };

    class Response
    {
    public:
      //Response(tcp::socket &socket, std::ostream &out, const StringList &fields = {}) : m_socket(std::move(socket)) ,m_out(out), m_fields(fields)
      Response(tcp::socket &socket,  const StringList &fields = {}) : m_socket(std::move(socket)), m_fields(fields)
      {
      }

      virtual ~Response() = default;

      virtual std::string getHeaderDate() { return getCurrentTime(HUM_READ); }

      bool good() const { return m_out.good(); }
      void setBad() { m_out.setstate(std::ios::badbit); }
      void flush() { m_out.flush(); }
      static std::string getStatus(uint16_t code)
      {
        auto cm = m_status.find(code);
        if (cm == m_status.end())
          return "Unknown";
        else
          return cm->second;
      }

      virtual void beginMultipartStream()
      {
        if (good())
        {
          m_boundary = dlib::md5(std::to_string(time(nullptr)));

          http::response<http::empty_body> res{http::status::ok, 11};
          res.set(http::field::server, "MTConnectAgent");
          res.set(http::field::date, getHeaderDate());
          res.set(http::field::connection, "close");
          res.set(http::field::expires, "-1");
          res.set(http::field::cache_control, "private, max-age=0\r\n");
          std::string content_type = "multipart/x-mixed-replace;boundary=";
          content_type.append(m_boundary);
          res.set(http::field::content_type, content_type);
          res.chunked(true);
          http::response_serializer<http::empty_body> sr{res};
          write_header(m_socket, sr);
        }
      }

      virtual void writeMultipartChunk(const std::string &body, const std::string &mimeType)
      {
        if (good())
        {
          using namespace std;
          ostringstream str;
          str << "--" << m_boundary
              << "\r\n"
                 "Content-type: "
              << mimeType
              << "\r\n"
                 "Content-length: "
              << body.length() << "\r\n\r\n"
              << body << "\r\n\r\n";

          net::const_buffers_1 buf(str.str().c_str(),str.str().size());
          http::chunk_body chunk(buf);
          net::write(m_socket,chunk,ec);
//          if (ec)
//          {
//            string errorMsg = "Error writing chunk - ";
//            errorMsg.append(ec.message());
//            auto const textSize = errorMsg.length();
//            http::response<http::string_body> errorRes{
//                std::piecewise_construct, std::make_tuple(std::move(errorMsg.c_str())),
//                std::make_tuple(http::status::ok, 11)  // m_req.version())
//            };
//            errorRes.set(http::field::server, "MTConnectAgent");
//            errorRes.set(http::field::date, getHeaderDate());
//            errorRes.set(http::field::connection, "close");
//            errorRes.set(http::field::expires, "-1");
//            errorRes.set(http::field::content_type, "text/xml");
//            errorRes.content_length(textSize);
//            http::serializer<false, http::string_body, http::fields> sr{errorRes};
//            http::write(m_socket, sr, ec);
//          }
        }
      }

      void writeResponse(const std::string &body, const std::string &mimeType = "text/plain",
                         const ResponseCode code = OK,
                         const std::chrono::seconds expires = std::chrono::seconds(0))
      {
        writeResponse(body.c_str(), body.size(), code, mimeType, expires);
      }

      void writeResponse(const std::string &body, const ResponseCode code,
                         const std::string &mimeType = "text/plain",
                         const std::chrono::seconds expires = std::chrono::seconds(0))
      {
        writeResponse(body.c_str(), body.size(), code, mimeType, expires);
      }

      virtual void writeResponse(const char *body, const size_t size, const ResponseCode code,
                                 const std::string &mimeType = "text/plain",
                                 const std::chrono::seconds expires = std::chrono::seconds(0))
      {
        if (good())
        {
          using namespace std;
          string expiry;
          if (expires == 0s)
          {
            expiry =
                "Expires: -1\r\n"
                "Cache-Control: private, max-age=0\r\n";
          }
          else
          {
            expiry = getCurrentTime(chrono::system_clock::now() + expires, HUM_READ);
          }
          m_out.write(body, size);
          auto const textSize = m_out.str().size();
          http::response<http::string_body> res
          {
              std::piecewise_construct,
              std::make_tuple(std::move(m_out.str().c_str())),
              std::make_tuple(http::status::ok, 11)// m_req.version())
          };
          res.set(http::field::server, "MTConnectAgent");
          res.set(http::field::date, getHeaderDate());
          res.set(http::field::connection, "close");
          res.set(http::field::expires, "-1");
          res.set(http::field::content_type, mimeType);
          //res.set(http::status::continue_, 100, 100);
          res.content_length(textSize);
          http::serializer<false, http::string_body , http::fields> sr{res};
          http::write(m_socket, sr, ec);
        }
      }

    protected:
      std::stringstream m_out;
      tcp::socket m_socket;
      std::string m_boundary;
      std::string content_type;
      static const std::unordered_map<uint16_t, std::string> m_status;
      static const std::unordered_map<std::string, uint16_t> m_codes;
      StringList m_fields;
      beast::error_code ec;
      bool close{false};
    };
  }  // namespace http_server
}  // namespace mtconnect
<|endoftext|>


<commit_before>// Copyright 2013 Duncan Smith

#include "service/logging.h"

#include <iostream>
#include <string>

#include "boost/filesystem.hpp"
#include "boost/filesystem/path.hpp"
#include "boost/log/attributes/attribute.hpp"
#include "boost/log/attributes/attribute_cast.hpp"
#include "boost/log/attributes/attribute_value.hpp"
#include "boost/log/attributes/current_process_name.hpp"
#include "boost/log/attributes/current_thread_id.hpp"
#include "boost/log/core.hpp"
#include "boost/log/expressions.hpp"
#include "boost/log/sinks/text_file_backend.hpp"
#include "boost/log/sources/global_logger_storage.hpp"
#include "boost/log/sources/logger.hpp"
#include "boost/log/sources/record_ostream.hpp"
#include "boost/log/sources/severity_feature.hpp"
#include "boost/log/sources/severity_logger.hpp"
#include "boost/log/trivial.hpp"
#include "boost/log/utility/setup/common_attributes.hpp"
#include "boost/log/utility/setup/console.hpp"
#include "boost/log/utility/setup/file.hpp"
#include "boost/move/utility.hpp"

#include "service/args.h"

namespace attrs = boost::log::attributes;
namespace expr = boost::log::expressions;
namespace fs = boost::filesystem;

namespace osoa {

namespace bl = boost::log;
namespace blt = boost::log::trivial;

Logging::Logging() :
  svc_logger_(new src::severity_logger_mt<blt::severity_level>()) {}

Logging::~Logging() {
}

int Logging::Initialize(const Args& args) {
  bl::add_common_attributes();

  bl::register_simple_formatter_factory<blt::severity_level, char>("Severity");

  fs::path full_path(args.module_path());
  fs::path leaf(full_path.filename().string());

  if (!args.log_dir().empty()) {
    full_path = args.log_dir();
    full_path = full_path / leaf;
  }

  if (!args.no_log_file())
    add_file_log(
      bl::keywords::file_name = full_path.string()
        + "_%Y-%m-%d_%H-%M-%S.%N.log",
      bl::keywords::rotation_size = args.rotation_size() * 1024 * 1024,
      bl::keywords::time_based_rotation =
        bl::sinks::file::rotation_at_time_point(0, 0, 0),
      bl::keywords::format =
        "[%TimeStamp%] [%Process%] [%Severity%] [%ThreadID%]: %Message%",
      bl::keywords::filter =
        expr::attr<blt::severity_level>("Severity") >= blt::debug);

  bl::add_console_log()->set_filter(
    blt::severity >= ((args.verbose()) ? blt::debug : blt::info));

  bl::core::get()->add_global_attribute(
    "ThreadID", attrs::current_thread_id());
  bl::core::get()->add_global_attribute(
    "Process", attrs::current_process_name());

  WriteLogHeader(args);

  return 0;
}

int Logging::WriteLogHeader(const Args& args) {
  char* user_name = getenv("USER");
  if (!user_name)
    user_name = getenv("USERNAME");

  if (user_name)
    BOOST_LOG_SEV(svc_logger(), blt::info) << "program started by : "
      << user_name;

  if (!args.config_file().empty())
    BOOST_LOG_SEV(svc_logger(), blt::info) << "using config file: "
      << args.config_file();
  else
    BOOST_LOG_SEV(svc_logger(), blt::info) << "not using config file";

  BOOST_LOG_SEV(svc_logger(), blt::info) << "log-dir: "
    << args.log_dir();

  BOOST_LOG_SEV(svc_logger(), blt::info) << "no-log-file: "
    << args.no_log_file();

  BOOST_LOG_SEV(svc_logger(), blt::info) << "verbose: "
    << args.verbose();

  BOOST_LOG_SEV(svc_logger(), blt::info) << "rotation-size: "
    << args.rotation_size();

  BOOST_LOG_SEV(svc_logger(), blt::info) << "async-log: "
    << args.async_log();

  BOOST_LOG_SEV(svc_logger(), blt::info) << "auto-flush-log: "
    << args.auto_flush_log();

  BOOST_LOG_SEV(svc_logger(), blt::info) << "auto-flush-log: " << std::endl;

  return 0;
}
}  // namespace osoa
<commit_msg>write log header<commit_after>// Copyright 2013 Duncan Smith

#include "service/logging.h"

#include <ctime>
#include <iomanip>
#include <iostream>
#include <string>

#include "boost/asio/ip/host_name.hpp"
#include "boost/filesystem.hpp"
#include "boost/filesystem/path.hpp"
#include "boost/log/attributes/attribute.hpp"
#include "boost/log/attributes/attribute_cast.hpp"
#include "boost/log/attributes/attribute_value.hpp"
#include "boost/log/attributes/current_process_name.hpp"
#include "boost/log/attributes/current_thread_id.hpp"
#include "boost/log/core.hpp"
#include "boost/log/expressions.hpp"
#include "boost/log/sinks/text_file_backend.hpp"
#include "boost/log/sources/global_logger_storage.hpp"
#include "boost/log/sources/logger.hpp"
#include "boost/log/sources/record_ostream.hpp"
#include "boost/log/sources/severity_feature.hpp"
#include "boost/log/sources/severity_logger.hpp"
#include "boost/log/trivial.hpp"
#include "boost/log/utility/setup/common_attributes.hpp"
#include "boost/log/utility/setup/console.hpp"
#include "boost/log/utility/setup/file.hpp"
#include "boost/move/utility.hpp"

#include "service/args.h"

namespace attrs = boost::log::attributes;
namespace expr = boost::log::expressions;
namespace fs = boost::filesystem;

namespace osoa {

namespace bl = boost::log;
namespace blt = boost::log::trivial;

Logging::Logging() :
  svc_logger_(new src::severity_logger_mt<blt::severity_level>()) {}

Logging::~Logging() {
}

int Logging::Initialize(const Args& args) {
  bl::add_common_attributes();

  bl::register_simple_formatter_factory<blt::severity_level, char>("Severity");

  fs::path full_path(args.module_path());
  fs::path leaf(full_path.filename().string());

  if (!args.log_dir().empty()) {
    full_path = args.log_dir();
    full_path = full_path / leaf;
  }

  if (!args.no_log_file())
    add_file_log(
      bl::keywords::file_name = full_path.string()
        + "_%Y-%m-%d_%H-%M-%S.%N.log",
      bl::keywords::rotation_size = args.rotation_size() * 1024 * 1024,
      bl::keywords::time_based_rotation =
        bl::sinks::file::rotation_at_time_point(0, 0, 0),
      bl::keywords::format =
        "[%TimeStamp%] [%Process%] [%Severity%] [%ThreadID%]: %Message%",
      bl::keywords::filter =
        expr::attr<blt::severity_level>("Severity") >= blt::debug);

  bl::add_console_log()->set_filter(
    blt::severity >= ((args.verbose()) ? blt::debug : blt::info));

  bl::core::get()->add_global_attribute(
    "ThreadID", attrs::current_thread_id());
  bl::core::get()->add_global_attribute(
    "Process", attrs::current_process_name());

  WriteLogHeader(args);

  return 0;
}

int Logging::WriteLogHeader(const Args& args) {
  char* user_name = getenv("USER");
  if (!user_name)
    user_name = getenv("USERNAME");

  std::string hostname = boost::asio::ip::host_name();

  std::time_t t = std::time(nullptr);
  std::tm tm = *std::localtime(&t);
  std::cout << std::put_time(&tm, "%c %Z") << '\n';

  if (user_name)
    BOOST_LOG_SEV(svc_logger(), blt::info) << "program started by : "
      << user_name << " on " << hostname;

  if (!args.config_file().empty())
    BOOST_LOG_SEV(svc_logger(), blt::info) << "using config-file: "
      << args.config_file();
  else
    BOOST_LOG_SEV(svc_logger(), blt::info) << "not using config-file";

  if (!args.log_dir().empty())
    BOOST_LOG_SEV(svc_logger(), blt::info) << "using log-dir: "
      << args.log_dir();
  else
    BOOST_LOG_SEV(svc_logger(), blt::info) << "not using log-dir";

  BOOST_LOG_SEV(svc_logger(), blt::info) << "no-log-file: "
    << args.no_log_file();

  BOOST_LOG_SEV(svc_logger(), blt::info) << "verbose: "
    << args.verbose();

  BOOST_LOG_SEV(svc_logger(), blt::info) << "log file rotation-size: "
    << args.rotation_size() << " MiB" ;

  BOOST_LOG_SEV(svc_logger(), blt::info) << "async-log: "
    << args.async_log();

  BOOST_LOG_SEV(svc_logger(), blt::info) << "auto-flush-log: "
    << args.auto_flush_log();

  BOOST_LOG_SEV(svc_logger(), blt::info) << "";

  return 0;
}
}  // namespace osoa
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2013 Fanout, Inc.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <unistd.h>
#include <QtTest/QtTest>
#include <QtCrypto>
#include "qzmqsocket.h"
#include "qzmqvalve.h"
#include "log.h"
#include "tnetstring.h"
#include "zhttprequestpacket.h"
#include "zhttpresponsepacket.h"
#include "engine.h"

class Wrapper : public QObject
{
	Q_OBJECT

public:
	QZmq::Socket *zhttpClientOutSock;
	QZmq::Socket *zhttpClientOutStreamSock;
	QZmq::Socket *zhttpClientInSock;
	QZmq::Valve *zhttpClientInValve;
	QZmq::Socket *zhttpServerInSock;
	QZmq::Valve *zhttpServerInValve;
	QZmq::Socket *zhttpServerInStreamSock;
	QZmq::Valve *zhttpServerInStreamValve;
	QZmq::Socket *zhttpServerOutSock;

	QZmq::Socket *handlerInspectSock;
	QZmq::Valve *handlerInspectValve;
	QZmq::Socket *handlerRetryOutSock;
	QZmq::Socket *handlerAcceptInSock;
	QZmq::Valve *handlerAcceptInValve;

	QByteArray in;
	bool finished;

	Wrapper(QObject *parent) :
		QObject(parent),
		finished(false)
	{
		// http sockets

		zhttpClientOutSock = new QZmq::Socket(QZmq::Socket::Push, this);

		zhttpClientOutStreamSock = new QZmq::Socket(QZmq::Socket::Router, this);

		zhttpClientInSock = new QZmq::Socket(QZmq::Socket::Sub, this);
		zhttpClientInValve = new QZmq::Valve(zhttpClientInSock, this);
		connect(zhttpClientInValve, SIGNAL(readyRead(const QList<QByteArray> &)), SLOT(zhttpClientIn_readyRead(const QList<QByteArray> &)));

		zhttpServerInSock = new QZmq::Socket(QZmq::Socket::Pull, this);
		zhttpServerInValve = new QZmq::Valve(zhttpServerInSock, this);
		connect(zhttpServerInValve, SIGNAL(readyRead(const QList<QByteArray> &)), SLOT(zhttpServerIn_readyRead(const QList<QByteArray> &)));

		zhttpServerInStreamSock = new QZmq::Socket(QZmq::Socket::Router, this);
		zhttpServerInStreamValve = new QZmq::Valve(zhttpServerInStreamSock, this);
		connect(zhttpServerInStreamValve, SIGNAL(readyRead(const QList<QByteArray> &)), SLOT(zhttpServerInStream_readyRead(const QList<QByteArray> &)));

		zhttpServerOutSock = new QZmq::Socket(QZmq::Socket::Pub, this);

		// handler sockets

		handlerInspectSock = new QZmq::Socket(QZmq::Socket::Router, this);

		handlerInspectValve = new QZmq::Valve(handlerInspectSock, this);
		connect(handlerInspectValve, SIGNAL(readyRead(const QList<QByteArray> &)), SLOT(handlerInspect_readyRead(const QList<QByteArray> &)));

		handlerRetryOutSock = new QZmq::Socket(QZmq::Socket::Push, this);

		handlerAcceptInSock = new QZmq::Socket(QZmq::Socket::Pull, this);
		handlerAcceptInValve = new QZmq::Valve(handlerAcceptInSock, this);
		connect(handlerAcceptInValve, SIGNAL(readyRead(const QList<QByteArray> &)), SLOT(handlerAcceptIn_readyRead(const QList<QByteArray> &)));
	}

	void startHttp()
	{
		zhttpClientOutSock->bind("ipc://client-out");
		zhttpClientOutStreamSock->bind("ipc://client-out-stream");
		zhttpClientInSock->bind("ipc://client-in");
		zhttpServerInSock->bind("ipc://server-in");
		zhttpServerInStreamSock->bind("ipc://server-in-stream");
		zhttpServerOutSock->bind("ipc://server-out");

		zhttpClientInSock->subscribe("test-client ");

		zhttpClientInValve->open();
		zhttpServerInValve->open();
		zhttpServerInStreamValve->open();
	}

	void startHandler()
	{
		handlerInspectSock->connectToAddress("ipc://inspect");
		handlerRetryOutSock->connectToAddress("ipc://retry-out");
		handlerAcceptInSock->connectToAddress("ipc://accept-in");

		handlerInspectValve->open();
		handlerAcceptInValve->open();
	}

private slots:
	void zhttpClientIn_readyRead(const QList<QByteArray> &message)
	{
		log_debug("client in");
		int at = message[0].indexOf(' ');
		QVariant v = TnetString::toVariant(message[0].mid(at + 2));
		ZhttpResponsePacket zresp;
		zresp.fromVariant(v);
		if(zresp.type == ZhttpResponsePacket::Data)
		{
			in += zresp.body;
			if(!zresp.more)
				finished = true;
		}
	}

	void zhttpServerIn_readyRead(const QList<QByteArray> &message)
	{
		log_debug("server in");
		QVariant v = TnetString::toVariant(message[0].mid(1));
		ZhttpRequestPacket zreq;
		zreq.fromVariant(v);

		ZhttpResponsePacket zresp;
		zresp.from = "test-client";
		zresp.id = zreq.id;
		zresp.seq = 0;
		zresp.code = 200;
		zresp.reason = "OK";
		zresp.body = "hello world";
		zresp.headers += HttpHeader("Content-Length", QByteArray::number(zresp.body.size()));
		QByteArray buf = zreq.from + " T" + TnetString::fromVariant(zresp.toVariant());
		zhttpServerOutSock->write(QList<QByteArray>() << buf);
	}

	void zhttpServerInStream_readyRead(const QList<QByteArray> &message)
	{
		Q_UNUSED(message);
	}

	void handlerInspect_readyRead(const QList<QByteArray> &message)
	{
		Q_UNUSED(message);
	}

	void handlerAcceptIn_readyRead(const QList<QByteArray> &message)
	{
		Q_UNUSED(message);
	}
};

class EngineTest : public QObject
{
	Q_OBJECT

private:
	QCA::Initializer *qcaInit;
	Engine *engine;
	Wrapper *wrapper;

private slots:
	void initTestCase()
	{
		qcaInit = new QCA::Initializer;
		QVERIFY(QCA::isSupported("hmac(sha256)"));

		log_setOutputLevel(LOG_LEVEL_INFO);

		wrapper = new Wrapper(this);
		wrapper->startHttp();

		engine = new Engine(this);

		Engine::Configuration config;
		config.clientId = "test";
		config.serverInSpecs = QStringList() << "ipc://client-out";
		config.serverInStreamSpecs = QStringList() << "ipc://client-out-stream";
		config.serverOutSpecs = QStringList() << "ipc://client-in";
		config.clientOutSpecs = QStringList() << "ipc://server-in";
		config.clientOutStreamSpecs = QStringList() << "ipc://server-in-stream";
		config.clientInSpecs = QStringList() << "ipc://server-out";
		config.inspectSpec = "ipc://inspect";
		config.retryInSpec = "ipc://retry-out";
		config.acceptOutSpec = "ipc://accept-in";
		config.inspectTimeout = 500;
		config.routesFile = "routes";
		config.sigIss = "pushpin";
		config.sigKey = "changeme";
		QVERIFY(engine->start(config));

		wrapper->startHandler();

		QTest::qWait(500);
	}

	void cleanupTestCase()
	{
		delete engine;
		delete wrapper;
		delete qcaInit;
	}

	void passthrough()
	{
		ZhttpRequestPacket zreq;
		zreq.from = "test-client";
		zreq.id = "1";
		zreq.uri = "http://example/path";
		zreq.method = "GET";
		zreq.stream = true;
		zreq.credits = 200000;
		QByteArray buf = 'T' + TnetString::fromVariant(zreq.toVariant());
		log_debug("writing: %s", buf.data());
		wrapper->zhttpClientOutSock->write(QList<QByteArray>() << buf);
		while(!wrapper->finished)
			QTest::qWait(10);

		QCOMPARE(wrapper->in, QByteArray("hello world"));
	}
};

QTEST_MAIN(EngineTest)
#include "enginetest.moc"
<commit_msg>some more rough integration tests<commit_after>/*
 * Copyright (C) 2013 Fanout, Inc.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <unistd.h>
#include <QtTest/QtTest>
#include <QtCrypto>
#include <qjson/parser.h>
#include "qzmqsocket.h"
#include "qzmqvalve.h"
#include "qzmqreqmessage.h"
#include "log.h"
#include "tnetstring.h"
#include "zhttprequestpacket.h"
#include "zhttpresponsepacket.h"
#include "engine.h"

class Wrapper : public QObject
{
	Q_OBJECT

public:
	QZmq::Socket *zhttpClientOutSock;
	QZmq::Socket *zhttpClientOutStreamSock;
	QZmq::Socket *zhttpClientInSock;
	QZmq::Valve *zhttpClientInValve;
	QZmq::Socket *zhttpServerInSock;
	QZmq::Valve *zhttpServerInValve;
	QZmq::Socket *zhttpServerInStreamSock;
	QZmq::Valve *zhttpServerInStreamValve;
	QZmq::Socket *zhttpServerOutSock;

	QZmq::Socket *handlerInspectSock;
	QZmq::Valve *handlerInspectValve;
	QZmq::Socket *handlerRetryOutSock;
	QZmq::Socket *handlerAcceptInSock;
	QZmq::Valve *handlerAcceptInValve;

	bool inspectEnabled;
	QByteArray in;
	QByteArray acceptIn;
	bool retried;
	bool finished;

	Wrapper(QObject *parent) :
		QObject(parent),
		inspectEnabled(true),
		retried(false),
		finished(false)
	{
		// http sockets

		zhttpClientOutSock = new QZmq::Socket(QZmq::Socket::Push, this);

		zhttpClientOutStreamSock = new QZmq::Socket(QZmq::Socket::Router, this);

		zhttpClientInSock = new QZmq::Socket(QZmq::Socket::Sub, this);
		zhttpClientInValve = new QZmq::Valve(zhttpClientInSock, this);
		connect(zhttpClientInValve, SIGNAL(readyRead(const QList<QByteArray> &)), SLOT(zhttpClientIn_readyRead(const QList<QByteArray> &)));

		zhttpServerInSock = new QZmq::Socket(QZmq::Socket::Pull, this);
		zhttpServerInValve = new QZmq::Valve(zhttpServerInSock, this);
		connect(zhttpServerInValve, SIGNAL(readyRead(const QList<QByteArray> &)), SLOT(zhttpServerIn_readyRead(const QList<QByteArray> &)));

		zhttpServerInStreamSock = new QZmq::Socket(QZmq::Socket::Router, this);
		zhttpServerInStreamValve = new QZmq::Valve(zhttpServerInStreamSock, this);
		connect(zhttpServerInStreamValve, SIGNAL(readyRead(const QList<QByteArray> &)), SLOT(zhttpServerInStream_readyRead(const QList<QByteArray> &)));

		zhttpServerOutSock = new QZmq::Socket(QZmq::Socket::Pub, this);

		// handler sockets

		handlerInspectSock = new QZmq::Socket(QZmq::Socket::Router, this);

		handlerInspectValve = new QZmq::Valve(handlerInspectSock, this);
		connect(handlerInspectValve, SIGNAL(readyRead(const QList<QByteArray> &)), SLOT(handlerInspect_readyRead(const QList<QByteArray> &)));

		handlerRetryOutSock = new QZmq::Socket(QZmq::Socket::Push, this);

		handlerAcceptInSock = new QZmq::Socket(QZmq::Socket::Pull, this);
		handlerAcceptInValve = new QZmq::Valve(handlerAcceptInSock, this);
		connect(handlerAcceptInValve, SIGNAL(readyRead(const QList<QByteArray> &)), SLOT(handlerAcceptIn_readyRead(const QList<QByteArray> &)));
	}

	void startHttp()
	{
		zhttpClientOutSock->bind("ipc://client-out");
		zhttpClientOutStreamSock->bind("ipc://client-out-stream");
		zhttpClientInSock->bind("ipc://client-in");
		zhttpServerInSock->bind("ipc://server-in");
		zhttpServerInStreamSock->bind("ipc://server-in-stream");
		zhttpServerOutSock->bind("ipc://server-out");

		zhttpClientInSock->subscribe("test-client ");

		zhttpClientInValve->open();
		zhttpServerInValve->open();
		zhttpServerInStreamValve->open();
	}

	void startHandler()
	{
		handlerInspectSock->connectToAddress("ipc://inspect");
		handlerRetryOutSock->connectToAddress("ipc://retry-out");
		handlerAcceptInSock->connectToAddress("ipc://accept-in");

		handlerInspectValve->open();
		handlerAcceptInValve->open();
	}

	void reset()
	{
		inspectEnabled = true;
		in.clear();
		acceptIn.clear();
		retried = false;
		finished = false;
	}

private slots:
	void zhttpClientIn_readyRead(const QList<QByteArray> &message)
	{
		log_debug("client in");
		int at = message[0].indexOf(' ');
		QVariant v = TnetString::toVariant(message[0].mid(at + 2));
		ZhttpResponsePacket zresp;
		zresp.fromVariant(v);
		if(zresp.type == ZhttpResponsePacket::Data)
		{
			in += zresp.body;
			if(!zresp.more)
				finished = true;
		}
		else if(zresp.type == ZhttpResponsePacket::HandoffStart)
		{
			ZhttpRequestPacket zreq;
			zreq.from = "test-client";
			zreq.id = zresp.id;
			zreq.type = ZhttpRequestPacket::HandoffProceed;
			zreq.seq = 1;
			QByteArray buf = 'T' + TnetString::fromVariant(zreq.toVariant());
			log_debug("writing: %s", buf.data());
			QList<QByteArray> msg;
			msg.append("proxy");
			msg.append(QByteArray());
			msg.append(buf);
			zhttpClientOutStreamSock->write(msg);
		}
	}

	void zhttpServerIn_readyRead(const QList<QByteArray> &message)
	{
		log_debug("server in");
		QVariant v = TnetString::toVariant(message[0].mid(1));
		ZhttpRequestPacket zreq;
		zreq.fromVariant(v);

		ZhttpResponsePacket zresp;
		zresp.from = "test-client";
		zresp.id = zreq.id;
		zresp.seq = 0;
		zresp.code = 200;
		zresp.reason = "OK";
		if(!retried && zreq.uri.encodedQuery().contains("wait=true"))
		{
			if(zreq.uri.encodedPath() == "/path2")
			{
				zresp.body = "{ \"hold\": { \"mode\": \"response\", \"channels\": [ { \"name\": \"test-channel\", \"prev-id\": \"1\" } ] } }";
				zresp.headers += HttpHeader("Content-Type", "application/grip-instruct");
			}
			else
			{
				zresp.body = "{ \"hold\": { \"mode\": \"response\", \"channels\": [ { \"name\": \"test-channel\" } ] } }";
				zresp.headers += HttpHeader("Content-Type", "application/grip-instruct");
			}
		}
		else
		{
			zresp.body = "hello world";
			zresp.headers += HttpHeader("Content-Type", "text/plain");
		}
		zresp.headers += HttpHeader("Content-Length", QByteArray::number(zresp.body.size()));
		QByteArray buf = zreq.from + " T" + TnetString::fromVariant(zresp.toVariant());
		zhttpServerOutSock->write(QList<QByteArray>() << buf);
	}

	void zhttpServerInStream_readyRead(const QList<QByteArray> &message)
	{
		Q_UNUSED(message);
	}

	void handlerInspect_readyRead(const QList<QByteArray> &_message)
	{
		QZmq::ReqMessage message(_message);
		QVariant v = TnetString::toVariant(message.content()[0]);
		log_debug("inspect: %s", qPrintable(TnetString::variantToString(v, -1)));
		if(inspectEnabled)
		{
			QVariantHash vreq = v.toHash();
			QVariantHash vresp;
			vresp["id"] = vreq["id"];
			vresp["no-proxy"] = false;
			handlerInspectSock->write(message.createReply(QList<QByteArray>() << TnetString::fromVariant(vresp)).toRawMessage());
		}
	}

	void handlerAcceptIn_readyRead(const QList<QByteArray> &message)
	{
		QVariant v = TnetString::toVariant(message[0]);
		log_debug("accept in: %s", qPrintable(TnetString::variantToString(v, -1)));
		QVariantHash vaccept = v.toHash();
		acceptIn = vaccept["response"].toHash()["body"].toByteArray();

		bool ok;
		QJson::Parser parser;
		log_debug("instruct: [%s]", acceptIn.data());
		QVariant vinstruct = parser.parse(acceptIn, &ok);
		QVERIFY(ok && vinstruct.type() == QVariant::Map);
		QVariantMap instruct = vinstruct.toMap();

		if(instruct["hold"].toMap()["channels"].toList()[0].toMap().contains("prev-id"))
		{
			retried = true;
			QVariantHash vretry;
			vretry["requests"] = vaccept["requests"];
			vretry["request-data"] = vaccept["request-data"];
			QByteArray buf = TnetString::fromVariant(vretry);
			log_debug("retrying: %s", buf.data());
			handlerRetryOutSock->write(QList<QByteArray>() << buf);
		}
		else
			finished = true;
	}
};

class EngineTest : public QObject
{
	Q_OBJECT

private:
	QCA::Initializer *qcaInit;
	Engine *engine;
	Wrapper *wrapper;

private slots:
	void initTestCase()
	{
		qcaInit = new QCA::Initializer;
		QVERIFY(QCA::isSupported("hmac(sha256)"));

		//log_setOutputLevel(LOG_LEVEL_DEBUG);
		log_setOutputLevel(LOG_LEVEL_WARNING);

		wrapper = new Wrapper(this);
		wrapper->startHttp();

		engine = new Engine(this);

		Engine::Configuration config;
		config.clientId = "proxy";
		config.serverInSpecs = QStringList() << "ipc://client-out";
		config.serverInStreamSpecs = QStringList() << "ipc://client-out-stream";
		config.serverOutSpecs = QStringList() << "ipc://client-in";
		config.clientOutSpecs = QStringList() << "ipc://server-in";
		config.clientOutStreamSpecs = QStringList() << "ipc://server-in-stream";
		config.clientInSpecs = QStringList() << "ipc://server-out";
		config.inspectSpec = "ipc://inspect";
		config.retryInSpec = "ipc://retry-out";
		config.acceptOutSpec = "ipc://accept-in";
		config.inspectTimeout = 500;
		config.routesFile = "routes";
		config.sigIss = "pushpin";
		config.sigKey = "changeme";
		QVERIFY(engine->start(config));

		wrapper->startHandler();

		QTest::qWait(500);
	}

	void cleanupTestCase()
	{
		delete engine;
		delete wrapper;
		delete qcaInit;
	}

	void passthrough()
	{
		wrapper->reset();

		ZhttpRequestPacket zreq;
		zreq.from = "test-client";
		zreq.id = "1";
		zreq.uri = "http://example/path";
		zreq.method = "GET";
		zreq.stream = true;
		zreq.credits = 200000;
		QByteArray buf = 'T' + TnetString::fromVariant(zreq.toVariant());
		log_debug("writing: %s", buf.data());
		wrapper->zhttpClientOutSock->write(QList<QByteArray>() << buf);
		while(!wrapper->finished)
			QTest::qWait(10);

		QCOMPARE(wrapper->in, QByteArray("hello world"));
	}

	void passthroughWithoutInspect()
	{
		wrapper->reset();
		wrapper->inspectEnabled = false;

		ZhttpRequestPacket zreq;
		zreq.from = "test-client";
		zreq.id = "2";
		zreq.uri = "http://example/path";
		zreq.method = "GET";
		zreq.stream = true;
		zreq.credits = 200000;
		QByteArray buf = 'T' + TnetString::fromVariant(zreq.toVariant());
		log_debug("writing: %s", buf.data());
		wrapper->zhttpClientOutSock->write(QList<QByteArray>() << buf);
		while(!wrapper->finished)
			QTest::qWait(10);

		QCOMPARE(wrapper->in, QByteArray("hello world"));
	}

	void accept()
	{
		wrapper->reset();

		ZhttpRequestPacket zreq;
		zreq.from = "test-client";
		zreq.id = "3";
		zreq.uri = "http://example/path?wait=true";
		zreq.method = "GET";
		zreq.stream = true;
		zreq.credits = 200000;
		QByteArray buf = 'T' + TnetString::fromVariant(zreq.toVariant());
		log_debug("writing: %s", buf.data());
		wrapper->zhttpClientOutSock->write(QList<QByteArray>() << buf);
		while(!wrapper->finished)
			QTest::qWait(10);

		QCOMPARE(wrapper->acceptIn, QByteArray("{ \"hold\": { \"mode\": \"response\", \"channels\": [ { \"name\": \"test-channel\" } ] } }"));
	}

	void acceptWithRetry()
	{
		wrapper->reset();

		ZhttpRequestPacket zreq;
		zreq.from = "test-client";
		zreq.id = "4";
		zreq.uri = "http://example/path2?wait=true";
		zreq.method = "GET";
		zreq.stream = true;
		zreq.credits = 200000;
		QByteArray buf = 'T' + TnetString::fromVariant(zreq.toVariant());
		log_debug("writing: %s", buf.data());
		wrapper->zhttpClientOutSock->write(QList<QByteArray>() << buf);
		while(!wrapper->finished)
			QTest::qWait(10);

		QCOMPARE(wrapper->in, QByteArray("hello world"));
	}
};

QTEST_MAIN(EngineTest)
#include "enginetest.moc"
<|endoftext|>