From 86cc76388e2da507f690dfcb85c0ebadba672554 Mon Sep 17 00:00:00 2001 From: Benjamin Sergeant Date: Sat, 2 Mar 2019 11:01:51 -0800 Subject: [PATCH] create a blocking + cancellable Socket::readBytes method --- README.md | 7 ++++++ ixwebsocket/IXHttpClient.cpp | 40 +++++++++++++-------------------- ixwebsocket/IXSocket.cpp | 25 ++++++++++++++++++++- ixwebsocket/IXSocket.h | 7 +++++- third_party/homebrew_formula.rb | 20 +++++++++++++++++ 5 files changed, 73 insertions(+), 26 deletions(-) create mode 100644 third_party/homebrew_formula.rb diff --git a/README.md b/README.md index f9a604ed..1fa20407 100644 --- a/README.md +++ b/README.md @@ -178,6 +178,13 @@ CMakefiles for the library and the examples are available. This library has few There is a Dockerfile for running some code on Linux, and a unittest which can be executed by typing `make test`. +You can build and install the ws command line tool with Homebrew. + +``` +brew create --cmake https://github.com/machinezone/IXWebSocket/archive/v1.1.0.tar.gz +brew install IXWebSocket +``` + ## Implementation details ### Per Message Deflate compression. diff --git a/ixwebsocket/IXHttpClient.cpp b/ixwebsocket/IXHttpClient.cpp index 28945b76..d27882c0 100644 --- a/ixwebsocket/IXHttpClient.cpp +++ b/ixwebsocket/IXHttpClient.cpp @@ -231,19 +231,15 @@ namespace ix payload.reserve(contentLength); - // FIXME: very inefficient way to read bytes, but it works... - for (int i = 0; i < contentLength; ++i) + auto chunkResult = _socket->readBytes(contentLength, isCancellationRequested); + if (!chunkResult.first) { - char c; - if (!_socket->readByte(&c, isCancellationRequested)) - { - return std::make_tuple(code, HttpErrorCode_ReadError, - headers, payload, "Cannot read byte", - uploadSize, downloadSize); - } - - payload += c; + errorMsg = "Cannot read chunk"; + return std::make_tuple(code, HttpErrorCode_ChunkReadError, + headers, payload, errorMsg, + uploadSize, downloadSize); } + payload += chunkResult.second; } else if (headers.find("Transfer-Encoding") != headers.end() && headers["Transfer-Encoding"] == "chunked") @@ -277,22 +273,18 @@ namespace ix payload.reserve(payload.size() + chunkSize); - // Read another line - - for (uint64_t i = 0; i < chunkSize; ++i) + // Read a chunk + auto chunkResult = _socket->readBytes(chunkSize, isCancellationRequested); + if (!chunkResult.first) { - char c; - if (!_socket->readByte(&c, isCancellationRequested)) - { - errorMsg = "Cannot read byte"; - return std::make_tuple(code, HttpErrorCode_ChunkReadError, - headers, payload, errorMsg, - uploadSize, downloadSize); - } - - payload += c; + errorMsg = "Cannot read chunk"; + return std::make_tuple(code, HttpErrorCode_ChunkReadError, + headers, payload, errorMsg, + uploadSize, downloadSize); } + payload += chunkResult.second; + // Read the line that terminates the chunk (\r\n) lineResult = _socket->readLine(isCancellationRequested); if (!lineResult.first) diff --git a/ixwebsocket/IXSocket.cpp b/ixwebsocket/IXSocket.cpp index ef5f1e3f..601f76bd 100644 --- a/ixwebsocket/IXSocket.cpp +++ b/ixwebsocket/IXSocket.cpp @@ -251,7 +251,8 @@ namespace ix { if (!readByte(&c, isCancellationRequested)) { - return std::make_pair(false, std::string()); + // Return what we were able to read + return std::make_pair(false, line); } line += c; @@ -259,4 +260,26 @@ namespace ix return std::make_pair(true, line); } + + std::pair Socket::readBytes( + size_t length, + const CancellationRequest& isCancellationRequested) + { + char c; + std::string buffer; + buffer.reserve(length); + + for (size_t i = 0; i < length; ++i) + { + if (!readByte(&c, isCancellationRequested)) + { + // Return what we were able to read + return std::make_pair(false, buffer); + } + + buffer += c; + } + + return std::make_pair(true, buffer); + } } diff --git a/ixwebsocket/IXSocket.h b/ixwebsocket/IXSocket.h index 80b62cca..fbb7e8dd 100644 --- a/ixwebsocket/IXSocket.h +++ b/ixwebsocket/IXSocket.h @@ -58,7 +58,12 @@ namespace ix const CancellationRequest& isCancellationRequested); bool writeBytes(const std::string& str, const CancellationRequest& isCancellationRequested); - std::pair readLine(const CancellationRequest& isCancellationRequested); + + std::pair readLine( + const CancellationRequest& isCancellationRequested); + std::pair readBytes( + size_t length, + const CancellationRequest& isCancellationRequested); static int getErrno(); static bool init(); // Required on Windows to initialize WinSocket diff --git a/third_party/homebrew_formula.rb b/third_party/homebrew_formula.rb new file mode 100644 index 00000000..7b552f33 --- /dev/null +++ b/third_party/homebrew_formula.rb @@ -0,0 +1,20 @@ +class Ixwebsocket < Formula + desc "WebSocket client and server, and HTTP client command-line tool" + homepage "https://github.com/machinezone/IXWebSocket" + url "https://github.com/machinezone/IXWebSocket/archive/v1.1.0.tar.gz" + sha256 "52592ce3d0a67ad0f90ac9e8a458f61724175d95a01a38d1bad3fcdc5c7b6666" + depends_on "cmake" => :build + + def install + system "cmake", ".", *std_cmake_args + system "make", "install" + end + + test do + system "#{bin}/ws", "--help" + system "#{bin}/ws", "send", "--help" + system "#{bin}/ws", "receive", "--help" + system "#{bin}/ws", "transfer", "--help" + system "#{bin}/ws", "curl", "--help" + end +end