Skip to content

takei-yuya/libyu

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

default -std=c++11 -std=c++14 -std=c++17 -std=c++20 -std=c++23
gcc-4 - gcc4-11 gcc4-14 - - -
gcc-5 - gcc5-11 gcc5-14 gcc5-17 - -
gcc-6 gcc6 gcc6-11 gcc6-14 gcc6-17 - -
gcc-7 gcc7 gcc7-11 gcc7-14 gcc7-17 - -
gcc-8 gcc8 gcc8-11 gcc8-14 gcc8-17 - -
gcc-9 gcc9 gcc9-11 gcc9-14 gcc9-17 - -
gcc-10 gcc10 gcc10-11 gcc10-14 gcc10-17 gcc10-20 -
gcc-11 gcc11 gcc11-11 gcc11-14 gcc11-17 gcc11-20 gcc11-23
gcc-12 gcc12 gcc12-11 gcc12-14 gcc12-17 gcc12-20 gcc12-23
gcc-13 gcc13 gcc13-11 gcc13-14 gcc13-17 gcc13-20 gcc13-23
alpine3 gcc alpine-gcc alpine-gcc-11 alpine-gcc-14 alpine-gcc-17 alpine-gcc-20 alpine-gcc-23
alpine3 clang alpine-clang alpine-clang-11 alpine-clang-14 alpine-clang-17 alpine-clang-20 alpine-clang-23
macos-11 - macos-11-11 macos-11-14 macos-11-17 macos-11-20 -
macos-12 - macos-12-11 macos-12-14 macos-12-17 macos-12-20 -
macos-13 - macos-13-11 macos-13-14 macos-13-17 macos-13-20 -

misc libraries for C++

misc header only libiraries depend on only C++ standard libiraries.

Install

copy yu directory to your project, then include it.

usage

yu/utf8.hpp

namespace yu {
namespace utf8 {
std::string yu::utf8::encode(uint32_t);

class Decoder {
 public:
  class Error : public std::runtime_error {
   public:
    Error(const std::string& message);
  };

  explicit Decoder(std::istream& in, uint32_t invalid_char = 0xfffd);
  bool has_next();
  uint32_t next();  // decode UTF-8 next string and return code point, or invalid_char if the sequence is invalid as UTF-8

  const std::string& last_error() const;
  size_t num_processed_bytes();
  size_t num_processed_chars();  // num of code points returned
  size_t num_processed_errors();  // num of invalid_char returned
};

}
}

example: sample/utf8_sample.cpp

uint32_t code_point = 0x1f363;
std::string str = yu::utf8::encode(code_point);
std::cout << str << std::endl;

// invalid char '\xff' will be replaced by U+FFFD
std::istringstream iss("ABCあいう\xff" + str);
yu::utf8::Decoder decoder(iss);
while (decoder.has_next()) {
  code_point = decoder.next();
  std::cout << yu::utf8::encode(code_point) << " U+" << std::hex << std::uppercase << code_point << std::endl;
}
std::cout << std::dec
  << decoder.num_processed_bytes() << " bytes, "
  << decoder.num_processed_chars() << " chars, "
  << decoder.num_processed_errors() << " errors" << std::endl;
// 🍣
// A U+41
// B U+42
// C U+43
// あ U+3042
// い U+3044
// う U+3046
// � U+FFFD
// 🍣 U+1F363
// 17 bytes, 8 chars, 1 errors

yu/crypt/blowfish.hpp

namespace yu {
namespace crypt {

class Blowfish {
 public:
  Blowfish(bool decrypt, const std::string& key);
  void process(char *p, size_t size);
};

template <class ECB>
class CBC {
 public:
  CBC(bool decrypt, const std::string& key, const std::string& iv);
  void process(char *p, size_t size);
};

template <class ECB>
class CFB {
 public:
  CFB(bool decrypt, const std::string& key, const std::string& iv);
  void process(char *p, size_t size);
};

template <class ECB>
class OFB {
 public:
  OFB(bool /* decrypt */, const std::string& key, const std::string& iv);
  void process(char *p, size_t size);
};


template <class Cipher, class Padding = yu::crypt::PKCS7Padding>
class cipher_enc_ostream : public std::ostream {
 public:
  template <class... Args>
  cipher_enc_ostream(std::ostream& out, Args... args);
  void finish();
};

template <class Cipher, class Padding = yu::crypt::PKCS7Padding>
class cipher_dec_ostream : public std::ostream {
 public:
  template <class... Args>
  cipher_dec_ostream(std::ostream& out, Args... args);
  void finish();
};

template <class Cipher, class Padding = yu::crypt::PKCS7Padding>
class cipher_enc_istream : public std::istream {
 public:
  template <class... Args>
  cipher_enc_istream(std::istream& out, Args... args);
  void finish();
};

template <class Cipher, class Padding = yu::crypt::PKCS7Padding>
class cipher_dec_istream : public std::istream {
 public:
  template <class... Args>
  cipher_dec_istream(std::istream& out, Args... args);
  void finish();
};

using Blowfish_ECB = Blowfish;
using Blowfish_CBC = CBC<Blowfish>;
using Blowfish_OFB = OFB<Blowfish>;
using Blowfish_CFB = CFB<Blowfish>;

using blowfish_ecb_enc_ostream = cipher_enc_ostream<Blowfish_ECB, PKCS7Padding>;
using blowfish_ecb_dec_ostream = cipher_dec_ostream<Blowfish_ECB, PKCS7Padding>;
using blowfish_cbc_enc_ostream = cipher_enc_ostream<Blowfish_CBC, PKCS7Padding>;
using blowfish_cbc_dec_ostream = cipher_dec_ostream<Blowfish_CBC, PKCS7Padding>;
using blowfish_ofb_enc_ostream = cipher_enc_ostream<Blowfish_OFB, NoPadding>;
using blowfish_ofb_dec_ostream = cipher_dec_ostream<Blowfish_OFB, NoPadding>;
using blowfish_cfb_enc_ostream = cipher_enc_ostream<Blowfish_CFB, NoPadding>;
using blowfish_cfb_dec_ostream = cipher_dec_ostream<Blowfish_CFB, NoPadding>;

using blowfish_ecb_enc_istream = cipher_enc_istream<Blowfish_ECB, PKCS7Padding>;
using blowfish_ecb_dec_istream = cipher_dec_istream<Blowfish_ECB, PKCS7Padding>;
using blowfish_cbc_enc_istream = cipher_enc_istream<Blowfish_CBC, PKCS7Padding>;
using blowfish_cbc_dec_istream = cipher_dec_istream<Blowfish_CBC, PKCS7Padding>;
using blowfish_ofb_enc_istream = cipher_enc_istream<Blowfish_OFB, NoPadding>;
using blowfish_ofb_dec_istream = cipher_dec_istream<Blowfish_OFB, NoPadding>;
using blowfish_cfb_enc_istream = cipher_enc_istream<Blowfish_CFB, NoPadding>;
using blowfish_cfb_dec_istream = cipher_dec_istream<Blowfish_CFB, NoPadding>;

// see sample for usage

}
}

example: sample/crypt_blowfish_sample.cpp

{  // ECB
  std::ostringstream oss;
  yu::crypt::blowfish_ecb_dec_ostream ds(oss, "key");
  yu::crypt::blowfish_ecb_enc_ostream es(ds, "key");
  es << "Hello blowfish!!";
  es.finish();
  ds.finish();
  std::cout << oss.str() << std::endl;
}
{ // CBC, CFB and OFB
  std::ostringstream oss;
  yu::crypt::blowfish_cbc_dec_ostream ds(oss, "key", "Init Vec");
  yu::crypt::blowfish_cbc_enc_ostream es(ds, "key", "Init Vec");
  es << "Hello blowfish!!";
  es.finish();
  ds.finish();
  std::cout << oss.str() << std::endl;
}

{  // istream and ostream
  std::ostringstream oss;
  {
    yu::crypt::blowfish_cfb_enc_ostream oes(oss, "key", "Init Vec");
    oes << "Hello blowfish!!";
    oes.finish();
  }

  std::istringstream iss(oss.str());
  {
    yu::crypt::blowfish_cfb_dec_istream ids(iss, "key", "Init Vec");
    std::vector<char> buffer(1024);
    ids.read(buffer.data(), static_cast<std::streamsize>(buffer.size()));
    std::string message(buffer.data(), static_cast<size_t>(ids.gcount()));
    std::cout << message << std::endl;
  }
}
// Hello blowfish!!
// Hello blowfish!!
// Hello blowfish!!

yu/data/heap.hpp

namespace yu {
namespace data {

template <typename T, typename Compare = std::less<T>, typename Allocator = std::allocator<T>>
class Heap {
 public:
  Heap();
  explicit Heap(const std::vector<T>& values);
  explicit Heap(std::vector<T>&& values);

  Heap(const Heap&) = delete;
  Heap& operator=(const Heap&) = delete;

  Heap(Heap&&) = default;
  Heap& operator=(Heap&&) = default;

  void push(const T& value);
  void push(T&& value);

  template <class... Args>
  void emplace(Args&&... args);

  const T& top() const;

  T pop();

  void drain(std::vector<T>& values);

  void clear();
  size_t size() noexcept const;
  bool empty() noexcept const;
  size_t capacity() noexcept const;
  void reserve(size_t size);
};

}  // namespace data
}  // namespace yu

example: sample/data_heap_sample.cpp

std::vector<int> vec{5, 2, 6, 1, 3, 4};
{
  yu::data::Heap<int> heap;
  for (auto i : vec) {
    heap.push(i);
  }
  std::cout << "asc: ";
  while (!heap.empty()) {
    std::cout << " " << heap.pop();
  }
  std::cout << std::endl;
}
{
  yu::data::Heap<int, std::greater<int>> heap;
  for (auto i : vec) {
    heap.push(i);
  }
  std::cout << "desc:";
  while (!heap.empty()) {
    std::cout << " " << heap.pop();
  }
  std::cout << std::endl;
}
// asc:  1 2 3 4 5 6
// desc: 6 5 4 3 2 1

yu/digest/sha2.hpp

namespace yu {
namespace digest {
class sha256_stream : public std::ostream {
 public:
  sha256_stream();
  std::string hash_hex();
  std::string hash_bin();
  std::string hash_base64();
  void reset();
};

std::string sha256_hex(const std::string& str);
std::string sha256_bin(const std::string& str);
std::string sha256_base64(const std::string& str);

// 224, 512, 384, 512_224 and 512_256 are same as 256
}
}

example: sample/digest_sha2_sample.cpp

std::string message = "Hello World.";
std::cout << yu::digest::sha256_hex(message) << std::endl;
std::cout << yu::digest::sha224_hex(message) << std::endl;
std::cout << yu::digest::sha512_hex(message) << std::endl;
std::cout << yu::digest::sha384_hex(message) << std::endl;
std::cout << yu::digest::sha512_256_hex(message) << std::endl;
std::cout << yu::digest::sha512_224_hex(message) << std::endl;
// f4bb1975bf1f81f76ce824f7536c1e101a8060a632a52289d530a6f600d52c92
// f871ab68ccdf47a7afb935f9f2f05365a61dee3aa6ebb7ef22be5de1
// fee4e02329c0e1c9005d0590f4773d8e519e0cda859775ac9c83641e3a960c57e7ad461354e4860722b6e3c161e493e04f5ef07d9169ff7bdab659d6a57cc316
// ded020e0ea23fd2d983f7d833c44811f9e3fa96e412f84f7427250af07a5630e26366a69c44bac94fd31ec73b1b847d1
// cc296ed308cbe384e0de66c8580b3373ac2ae88dd53a9bd8542df1431e87f01d
// 53a8f45fd2b7631b90d2c84b5dd223389b90ef503059f4c86fe6857d

yu/digest/bcrypt.hpp

TODO: $1$ $2b$

namespace yu {
namespace digest {
std::string bcrypt_sha256(const std::string& salt, const std::string& password);
std::string bcrypt_sha512(const std::string& salt, const std::string& password);
bool bcrypt_check(const std::string& digest, const std::string& password);
}
}

example: sample/digest_bcrypt_sample.cpp

std::cout << yu::digest::bcrypt_sha256("salt", "password") << std::endl;
std::cout << yu::digest::bcrypt_sha512("salt", "password") << std::endl;
std::string digest = yu::digest::bcrypt_sha512("", "password");  // random salt
std::cout << yu::digest::bcrypt_check(digest, "password") << std::endl;
std::cout << yu::digest::bcrypt_check(digest, "Password") << std::endl;
// $5$salt$Gcm6FsVtF/Qa77ZKD.iwsJlCVPY0XSMgLJL0Hnww/c1
// $6$salt$IxDD3jeSOb5eB1CX5LBsqZFVkJdido3OUILO5Ifz5iwMuTS4XMS130MTSuDDl3aCI6WouIL9AjRbLCelDCy.g.
// 1
// 0

yu/lang/lexical_cast.hpp

template <typename T1, typename T2>
T1 lexical_cast(const T2& val);

example: sample/lexical_cast_sample.cpp

std::cout << (yu::lang::lexical_cast<int>("42") * 10) << std::endl;
std::cout << (yu::lang::lexical_cast<std::string>(42) + "0") << std::endl;
// 420
// 420

yu/stream/fdstream.hpp

namespace yu {
namespace stream {
class fdstream : public std::iostream {
 public:
  explicit fdstream(int fd);
};
}
}

example: sample/stream_fdstream_sample.cpp

yu::stream::fdstream fds(1);
fds << "Hello World." << std::endl;
// Hello World.

yu/stream/repeatstream.hpp

namespace yu {
namespace stream {
class repeatstream : public std::iostream {
 public:
  explicit repeatstream(const std::string&);
};
}
}

example: sample/stream_repeatstream_sample.cpp

yu::stream::repeatstream repeat("yes\n");
std::string line;
for (size_t i = 0; i < 10; ++i) {
  std::getline(repeat, line);
  std::cout << line << std::endl;
}
// yes
// yes
// yes
// yes
// yes
// yes
// yes
// yes
// yes
// yes

yu/stream/nullstream.hpp

namespace yu {
namespace stream {
class nullstream : public std::iostream {
 public:
  nullstream();
};
}
}

example: sample/stream_nullstream_sample.cpp

yu::stream::nullstream ns;
std::vector<char> buffer(1024);

// empty read
ns.read(buffer.data(), static_cast<std::streamsize>(buffer.size()));
std::cout << "read = " << ns.gcount() << " bytes, eof = " << ns.eof() << std::endl;

// infinity write
size_t write_count = 0;
for (size_t i = 0; i < 1024 * 1024; ++i) {
  ns.write(buffer.data(), static_cast<std::streamsize>(buffer.size()));
  write_count += buffer.size();
}
std::cout << "write = " << write_count << " bytes" << std::endl;
// read = 0 bytes, eof = 1
// write = 1073741824 bytes

yu/stream/teestream.hpp

namespace yu {
namespace stream {
class oteestream : public std::ostream {
 public:
  template<class.. Args>
  oteestream(Args&&... ostreams);
};

class iteestream : public std::ostream {
 public:
  oteestream(std::istream& in, std::ostream& out);
};
}
}

example: sample/stream_teestream_sample.cpp

{
  std::ostringstream out1;
  std::ostringstream out2;
  std::ostringstream out3;
  yu::stream::oteestream ots(out1, out2, out3);
  ots << "Hello World!";
  std::cout << "out1 = " << out1.str() << std::endl;
  std::cout << "out2 = " << out2.str() << std::endl;
  std::cout << "out3 = " << out3.str() << std::endl;
}
{
  std::istringstream iss("Hello World!");
  std::ostringstream oss;
  yu::stream::iteestream its(iss, oss);
  std::string str;
  its >> str;
  std::cout << "str = " << str << std::endl;
  std::cout << "oss = " << oss.str() << std::endl;  // ostream recieve whole buffer
  its >> str;
  std::cout << "str = " << str << std::endl;
}
// out1 = Hello World!
// out2 = Hello World!
// out3 = Hello World!
// str = Hello
// oss = Hello World!
// str = World!

see also: app/sha2.cpp

yu/string/utils.hpp

namespace yu {
namespace string {


inline std::string lstrip(const std::string& str, const std::string& spaces = "\r\n\t ");
inline std::string rstrip(const std::string& str, const std::string& spaces = "\r\n\t ");
inline std::string strip(const std::string& str, const std::string& spaces = "\r\n\t ");

inline std::vector<std::string> split(const std::string& str, char delim = ',', bool strip_token = false, size_t max_tokens = 0);

inline std::string join(const std::vector<std::string>& strs, const std::string& delim = ",");
template <typename T>
inline std::string join(const std::vector<T>& strs, const std::string& delim = ",");

inline bool starts_with(const std::string& str, const std::string& prefix);
inline bool ends_with(const std::string& str, const std::string& suffix);

inline std::string remove_prefix(const std::string& str, const std::string& prefix);
inline std::string remove_suffix(const std::string& str, const std::string& suffix);

inline int icompare(const std::string& lhs, const std::string& rhs);

inline bool iless(const std::string& lhs, const std::string& rhs);
class iLess {
 public:
  bool operator()(const std::string& lhs, const std::string& rhs) const { return iless(lhs, rhs); }
  using first_argument_type  = std::string;
  using second_argument_type = std::string;
  using result_type          = bool;
};

inline bool igreater(const std::string& lhs, const std::string& rhs);
class iGreater {
 public:
  bool operator()(const std::string& lhs, const std::string& rhs) const { return igreater(lhs, rhs); }
  using first_argument_type  = std::string;
  using second_argument_type = std::string;
  using result_type          = bool;
};

}  // namespace string
}  // namespace yu

example: sample/string_utils_sample.cpp

std::cout << "lstrip: [" << yu::string::lstrip("  hello world  ") << "]" << std::endl;
std::cout << "rstrip: [" << yu::string::rstrip("  hello world  ") << "]" << std::endl;
std::cout << " strip: [" << yu::string::strip("  hello world  ") << "]" << std::endl;
std::vector<std::string> strs = yu::string::split("hello world", ' ');
for (const auto& str : strs) {
  std::cout << "split: [" << str << "]" << std::endl;
}
std::cout << "join: [" << yu::string::join(strs, ",") << "]" << std::endl;

std::cout << "foobar is " << (yu::string::starts_with("foobar", "foo") ? "" : "not ") << "starts with foo" << std::endl;
std::cout << "foobar is " << (yu::string::starts_with("foobar", "bar") ? "" : "not ") << "starts with bar" << std::endl;
std::cout << "foobar is " << (yu::string::ends_with("foobar", "foo") ? "" : "not ") << "ends with foo" << std::endl;
std::cout << "foobar is " << (yu::string::ends_with("foobar", "bar") ? "" : "not ") << "ends with bar" << std::endl;

std::cout << "remove prefix foo from foobar: " << yu::string::remove_prefix("foobar", "foo") << std::endl;
std::cout << "remove prefix bar from foobar: " << yu::string::remove_prefix("foobar", "bar") << std::endl;
std::cout << "remove suffix foo from foobar: " << yu::string::remove_suffix("foobar", "foo") << std::endl;
std::cout << "remove suffix bar from foobar: " << yu::string::remove_suffix("foobar", "bar") << std::endl;

std::vector<std::string> vs = { "a", "B", "c", "D" };

std::sort(vs.begin(), vs.end());

std::cout << "asc (case   sensitive):";
for (const auto& v : vs) { std::cout << " " << v; } std::cout << std::endl;

std::sort(vs.begin(), vs.end(), yu::string::iLess());
std::cout << "asc (case insensitive):";
for (const auto& v : vs) { std::cout << " " << v; } std::cout << std::endl;

std::sort(vs.begin(), vs.end(), std::greater<std::string>());
std::cout << "desc(case   sensitive):";
for (const auto& v : vs) { std::cout << " " << v; } std::cout << std::endl;

std::sort(vs.begin(), vs.end(), yu::string::iGreater());
std::cout << "desc(case insensitive):";
for (const auto& v : vs) { std::cout << " " << v; } std::cout << std::endl;
// lstrip: [hello world  ]
// rstrip: [  hello world]
//  strip: [hello world]
// split: [hello]
// split: [world]
// join: [hello,world]
// foobar is starts with foo
// foobar is not starts with bar
// foobar is not ends with foo
// foobar is ends with bar
// remove prefix foo from foobar: bar
// remove prefix bar from foobar: foobar
// remove suffix foo from foobar: foobar
// remove suffix bar from foobar: foo
// asc (case   sensitive): B D a c
// asc (case insensitive): a B c D
// desc(case   sensitive): c a D B
// desc(case insensitive): D c B a

yu/base64.hpp

namespace yu {
namespace base64 {
class Encoder {
 public:
  Encoder(size_t wrap_width = 76, char char62 = '+', char char63 = '/', char pad = '=', const std::string& newline = "\n");

  // read from in, encode, and write to out. return CRC24 for Radix-64
  uint32_t encode(std::istream& in, std::ostream& out) const;
};

// base64 encode
std::string encode(const std::string& str, size_t wrap_width = 76);
// base64 encode using '-' and '_' instead of '+' and '/'
std::string encodeURL(const std::string& str);

class Decoder {
 public:
  Decoder(char char62 = '+', char char63 = '/', char pad = '=', const std::string& allowed_white_space = " \r\n");

  // read from in, decode, and write to out. return CRC24 for Radix-64
  uint32_t decode(std::istream& in, std::ostream& out) const;
};

// base64 decode
std::string decode(const std::string& str);
// base64 decode using '-' and '_' instead of '+' and '/'
std::string decodeURL(const std::string& str);
}
}

example: sample/base64_sample.cpp

std::cout << yu::base64::encode("Hello World.") << std::endl;;
std::cout << yu::base64::decode("SGVsbG8gV29ybGQu") << std::endl;
// SGVsbG8gV29ybGQu
// Hello World.

yu/json.hpp

class Stringifier {
 public:
  explicit Stringifier(std::ostream& out, bool pretty = false);

  template <typename T>
  void stringify(const T& value);
}

class Parser {
 public:
  explicit Parser(std::istream& in) : in_(in) {}

  template <typename T>
  void parse(std::ostream& out, const T& v);
};

// stream interface
template <typename T>
inline void stringify(std::ostream& out, const T& val);

template <typename T>
inline void parse(std::istream& in, T& val);

// string interface
template <typename T>
inline std::string to_json(const T& v);

template <typename T>
inline T from_json(const std::string& str);

// Macros: Object mappring
// Usage
class Klass {
 private:
  std::string str;
  int num;

 public:
  void stringifyJson(yu::json::Stringifier& stringifier) {
    JSON_MEMBER_STRINGIFIER(stringifier)
      << JSON_GETTER(str)
      << JSON_NAMED_GETTER("number", num)
      << JSON_STRINGIFY;
  }
  void parseJson(yu::json::Parser& parser) {
    JSON_MEMBER_PARSER(parser)
      << JSON_SETTER(str)
      << JSON_NAMED_SETTER("number", num)
      << JSON_PARSE;
  }
}

example: sample/json_sample.cpp

class Klass {
 public:
  Klass() : str(), num(), map(), vec() {}
 private:
  std::string str;
  int num;
  std::map<std::string, float> map;
  std::vector<bool> vec;
 public:
  void stringifyJson(yu::json::Stringifier& stringifier) const {
    JSON_MEMBER_STRINGIFIER(stringifier)
      << JSON_GETTER(str) << JSON_GETTER(num)
      << JSON_GETTER(map) << JSON_GETTER(vec)
      << JSON_STRINGIFY;
  }
  void parseJson(yu::json::Parser& parser) {
    JSON_MEMBER_PARSER(parser)
      << JSON_SETTER(str) << JSON_SETTER(num)
      << JSON_SETTER(map) << JSON_SETTER(vec)
      << JSON_PARSE;
  }
};
std::string json = R"(
  {
    "str": "Hello World.", "num": 42,
    "map": { "a": 3.14 }, "vec": [ true, false ]
  }
)";
Klass obj = yu::json::from_json<Klass>(json);
std::cout << yu::json::to_json(obj) << std::endl;
// {"str":"Hello World.","num":42,"map":{"a":3.14},"vec":[true,false]}

yu/http/client_stream.hpp

namespace yu {
namespace http {
class ClientStream {
 public:
  explicit ClientStream(std::iostream& stream);

  void set_header(const std::string& key, const std::string& value);
  std::unique_ptr<std::ostream> request(const std::string& request_method,
                                        const std::string& request_target,
                                        const std::string& request_version = "HTTP/1.1");

  std::unique_ptr<std::istream> parse_respose();
  const std::string& response_version() const;
  int response_code() const;
  const std::string& response_code_message() const;
  const Header& response_header() const;
};
}
}

example: sample/http_client_stream_sample.cpp

int fds[2];
::socketpair(AF_UNIX, SOCK_STREAM, 0, fds);

yu::stream::fdstream user_agent(fds[0]);
yu::stream::fdstream server(fds[1]);

std::cout << "* Send request" << std::endl;
yu::http::ClientStream cs(user_agent);
cs.set_header("Host", "example.com");
{
  std::unique_ptr<std::ostream> request_body_stream = cs.request("POST", "/");
  *request_body_stream << "Hello World!!";
}

std::cout << "* Recieve request" << std::endl;
std::string line;
// header
while (std::getline(server, line)) {
  line = yu::string::rstrip(line, "\r");
  if (line.empty()) break;
  std::cout << line << std::endl;
}
std::cout << std::endl;
// body
while (std::getline(server, line)) {
  line = yu::string::rstrip(line, "\r");
  if (line.empty()) break;
  std::cout << line << std::endl;
}

std::cout << "* Send response" << std::endl;
server
  << "HTTP/1.1 200 OK\r\n"
  << "Server: test\r\n"
  << "Set-Cookie: foo=1; path=/\r\n"
  << "Transfer-Encoding: chunked\r\n"
  << "\r\n"
  << "6\r\n"
  << "Hello \r\n"
  << "7\r\n"
  << "World!!\r\n"
  << "0\r\n"
  << "\r\n";
server.flush();

std::cout << "* Recieve response" << std::endl;
{
  std::unique_ptr<std::istream> response_body_stream = cs.parse_respose();
  std::cout << cs.response_version() << " " << cs.response_code() << " " << cs.response_code_message() << std::endl;
  cs.response_header().write(std::cout);

  std::vector<char> buffer(1024);
  response_body_stream->read(buffer.data(), static_cast<std::streamsize>(buffer.size()));
  std::string response_body(buffer.data(), buffer.data() + response_body_stream->gcount());
  std::cout << response_body << std::endl;
}
// * Send request
// * Recieve request
// POST / HTTP/1.1
// Host: example.com
// Transfer-Encoding: chunked
// 
// D
// Hello World!!
// 0
// * Send response
// * Recieve response
// HTTP/1.1 200 OK
// Set-Cookie: foo=1; path=/
// Server: test
// Transfer-Encoding: chunked
// 
// Hello World!!

yu/http/server_stream.hpp

namespace yu {
namespace http {
class ServerStream {
 public:
  explicit ServerStream(std::iostream& stream);

  std::unique_ptr<std::istream> parse_request();
  const std::string& request_method() const;
  const std::string& request_target() const;
  const std::string& request_version() const;
  const std::unordered_map<std::string, std::string>& request_headers() const;

  void set_status(int status, const std::string& messgae = "");
  void set_header(const std::string& key, const std::string& value);
  std::unique_ptr<chunked_ostream> send_header();
};
}
}

example: sample/http_server_stream_sample.cpp

int fds[2];
::socketpair(AF_UNIX, SOCK_STREAM, 0, fds);

yu::stream::fdstream user_agent(fds[0]);
yu::stream::fdstream server(fds[1]);

yu::http::ServerStream ss(server);

std::cout << "* Send request" << std::endl;
user_agent
  << "POST / HTTP/1.1\r\n"
  << "Host: example.com\r\n"
  << "User-Agent: test\r\n"
  << "Transfer-Encoding: chunked\r\n"
  << "\r\n"
  << "6\r\n"
  << "Hello \r\n"
  << "6\r\n"
  << "World!\r\n"
  << "0\r\n"
  << "\r\n";
user_agent.flush();

std::cout << "* Recieve request" << std::endl;
{
  std::unique_ptr<std::istream> request_body_stream = ss.parse_request();
  std::cout << ss.request_method() << " " << ss.request_target() << " " << ss.request_version() << std::endl;
  ss.request_header().write(std::cout);

  std::vector<char> buffer(1024);
  request_body_stream->read(buffer.data(), static_cast<std::streamsize>(buffer.size()));
  std::string request_body(buffer.data(), buffer.data() + request_body_stream->gcount());
  std::cout << request_body << std::endl;
}

std::cout << "* Send response" << std::endl;
ss.set_status(200);
ss.set_header("Server", "libyu http::ServerStream");
ss.set_header("Set-Cookie", "foo=1; path=/");
ss.set_header("Server", "version 0.0.0");
ss.set_header("Set-Cookie", "bar=2; path=/");
{
  std::unique_ptr<std::ostream> response_body_stream = ss.send_header();
  *response_body_stream << "It's ";
  *response_body_stream << "wonderful ";
  *response_body_stream << "wordl!!";
}

std::cout << "* Recieve response" << std::endl;
std::string line;
// header
while (std::getline(user_agent, line)) {
  line = yu::string::rstrip(line, "\r");
  if (line.empty()) break;
  std::cout << line << std::endl;
}
std::cout << std::endl;
// body
while (std::getline(user_agent, line)) {
  line = yu::string::rstrip(line, "\r");
  if (line.empty()) break;
  std::cout << line << std::endl;
}

close(fds[0]);
close(fds[1]);
// * Send request
// * Recieve request
// POST / HTTP/1.1
// Host: example.com
// Transfer-Encoding: chunked
// User-Agent: test
// 
// Hello World!
// * Send response
// * Recieve response
// HTTP/1.1 200 OK
// Set-Cookie: foo=1; path=/
// Set-Cookie: bar=2; path=/
// Server: libyu http::ServerStream,version 0.0.0
// Transfer-Encoding: chunked
// 
// 16
// It's wonderful wordl!!
// 0

yu/test.hpp

see test/*_test.cpp

Q&A

  • なんで今更C++?
    • 書きたかったから
  • base64とかOpenSSLとかにあるよ?
    • 書きたかったから書いた
  • 実装の優先度は?
    • 書きたくなった順
  • バグを見つけた/実装がいけていない
    • つ Pull Request

LICENSE

see LICENSE file

Author

Copyright 2023 TAKEI Yuya (https://github.com/takei-yuya)

About

mics libraries for C++

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages