ld-eventsource 1.0.3 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.circleci/config.yml +28 -81
- data/.gitignore +2 -0
- data/.ldrelease/circleci/linux/execute.sh +18 -0
- data/.ldrelease/circleci/mac/execute.sh +18 -0
- data/.ldrelease/circleci/template/build.sh +19 -0
- data/.ldrelease/circleci/template/gems-setup.sh +16 -0
- data/.ldrelease/circleci/template/prepare.sh +17 -0
- data/.ldrelease/circleci/template/publish.sh +19 -0
- data/.ldrelease/circleci/template/test.sh +10 -0
- data/.ldrelease/circleci/template/update-version.sh +8 -0
- data/.ldrelease/circleci/windows/execute.ps1 +19 -0
- data/.ldrelease/config.yml +13 -0
- data/CHANGELOG.md +19 -0
- data/README.md +2 -2
- data/ld-eventsource.gemspec +3 -3
- data/lib/ld-eventsource/client.rb +95 -21
- data/lib/ld-eventsource/version.rb +1 -1
- data/spec/backoff_spec.rb +2 -0
- data/spec/client_spec.rb +81 -6
- data/spec/http_stub.rb +2 -0
- metadata +34 -24
- data/Gemfile.lock +0 -46
- data/lib/ld-eventsource/impl/streaming_http.rb +0 -222
- data/spec/streaming_http_spec.rb +0 -263
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e62fdcf615bb0c699746e63a929e5eb582b8680cf873659d2be9e7e018fb0a89
|
4
|
+
data.tar.gz: f5f2b5694a3049e4f5dc2fd5f49de948ab6a8d040151d506a5589b0da4bca0f6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dd8ec3f6a4719860f1bf32bef767917a6b97753b8c9eb060c5478fa93b9115c3090313d5cfe4d00137dd1f764cb4bdfb3af000246718dd446691912f4c17ee1d
|
7
|
+
data.tar.gz: 23a6178d006572791caddbfb372bd9a4c68c5e9f32c272c7b3230008698744e973fcecee8d51b2bfc5471ba99f18190b95e0f074307b77dee81d1274b0416d07
|
data/.circleci/config.yml
CHANGED
@@ -1,92 +1,39 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# This CircleCI configuration was generated by Releaser for a specific release. It is not to be used
|
2
|
+
# for regular CI builds. Be aware that rerunning this build may cause it to repeat release actions
|
3
|
+
# such as publishing to a package manager. However, it will not perform any Git actions other than
|
4
|
+
# reading the repository.
|
5
|
+
version: 2.1
|
3
6
|
workflows:
|
4
|
-
version: 2
|
5
7
|
test:
|
6
8
|
jobs:
|
7
|
-
-
|
8
|
-
|
9
|
-
- test-2.3
|
10
|
-
- test-2.4
|
11
|
-
- test-2.5
|
12
|
-
- test-jruby-9.2
|
13
|
-
|
14
|
-
ruby-docker-template: &ruby-docker-template
|
15
|
-
steps:
|
16
|
-
- checkout
|
17
|
-
- run: |
|
18
|
-
if [[ $CIRCLE_JOB == test-jruby* ]]; then
|
19
|
-
gem install jruby-openssl; # required by bundler, no effect on Ruby MRI
|
20
|
-
fi
|
21
|
-
- run: ruby -v
|
22
|
-
- run: gem install bundler -v "~> 1.17"
|
23
|
-
- run: bundle install
|
24
|
-
- run: mkdir ./rspec
|
25
|
-
- run: bundle exec rspec --format progress --format RspecJunitFormatter -o ./rspec/rspec.xml spec
|
26
|
-
- store_test_results:
|
27
|
-
path: ./rspec
|
28
|
-
- store_artifacts:
|
29
|
-
path: ./rspec
|
30
|
-
|
9
|
+
- release_linux:
|
10
|
+
context: org-global
|
31
11
|
jobs:
|
32
|
-
|
33
|
-
<<: *ruby-docker-template
|
12
|
+
release_linux:
|
34
13
|
docker:
|
35
|
-
- image:
|
36
|
-
test-2.3:
|
37
|
-
<<: *ruby-docker-template
|
38
|
-
docker:
|
39
|
-
- image: circleci/ruby:2.3.7-jessie
|
40
|
-
test-2.4:
|
41
|
-
<<: *ruby-docker-template
|
42
|
-
docker:
|
43
|
-
- image: circleci/ruby:2.4.5-stretch
|
44
|
-
test-2.5:
|
45
|
-
<<: *ruby-docker-template
|
46
|
-
docker:
|
47
|
-
- image: circleci/ruby:2.5.3-stretch
|
48
|
-
test-jruby-9.2:
|
49
|
-
<<: *ruby-docker-template
|
50
|
-
docker:
|
51
|
-
- image: circleci/jruby:9-jdk
|
52
|
-
|
53
|
-
# The following very slow job uses an Ubuntu container to run the Ruby versions that
|
54
|
-
# CircleCI doesn't provide Docker images for.
|
55
|
-
test-misc-rubies:
|
56
|
-
machine:
|
57
|
-
image: circleci/classic:latest
|
14
|
+
- image: cimg/ruby:2.5
|
58
15
|
environment:
|
59
|
-
|
16
|
+
LD_RELEASE_CIRCLECI_TYPE: linux
|
17
|
+
LD_RELEASE_BRANCH: "master"
|
18
|
+
LD_RELEASE_CIRCLECI_BRANCH: ""
|
19
|
+
LD_RELEASE_DOCS_GITHUB_PAGES: ""
|
20
|
+
LD_RELEASE_DOCS_TITLE: ""
|
21
|
+
LD_RELEASE_PROJECT: "ruby-eventsource"
|
22
|
+
LD_RELEASE_PROJECT_TEMPLATE: "ruby"
|
23
|
+
LD_RELEASE_VERSION: "2.1.0"
|
60
24
|
steps:
|
61
25
|
- checkout
|
62
26
|
- run:
|
63
|
-
name:
|
64
|
-
command:
|
27
|
+
name: "Releaser: prepare"
|
28
|
+
command: .ldrelease/circleci/mac/execute.sh prepare .ldrelease/circleci/template/prepare.sh
|
29
|
+
- run:
|
30
|
+
name: "Releaser: build"
|
31
|
+
command: .ldrelease/circleci/mac/execute.sh build .ldrelease/circleci/template/build.sh
|
65
32
|
- run:
|
66
|
-
name:
|
67
|
-
|
68
|
-
command: |
|
69
|
-
set -e;
|
70
|
-
for i in $RUBIES;
|
71
|
-
do
|
72
|
-
rvm use $i;
|
73
|
-
if [[ $i == jruby* ]]; then
|
74
|
-
gem install jruby-openssl; # required by bundler, no effect on Ruby MRI
|
75
|
-
fi;
|
76
|
-
# bundler 2.0 may be preinstalled, we need to remove it if so
|
77
|
-
yes | gem uninstall bundler --version '>=2.0' || true;
|
78
|
-
gem install bundler -v "~> 1.17";
|
79
|
-
bundle install;
|
80
|
-
mv Gemfile.lock "Gemfile.lock.$i"
|
81
|
-
done
|
33
|
+
name: "Releaser: test"
|
34
|
+
command: .ldrelease/circleci/mac/execute.sh test .ldrelease/circleci/template/test.sh
|
82
35
|
- run:
|
83
|
-
name:
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
for i in $RUBIES;
|
88
|
-
do
|
89
|
-
rvm use $i;
|
90
|
-
cp "Gemfile.lock.$i" Gemfile.lock;
|
91
|
-
bundle exec rspec spec;
|
92
|
-
done
|
36
|
+
name: "Releaser: publish"
|
37
|
+
command: .ldrelease/circleci/mac/execute.sh publish .ldrelease/circleci/template/publish.sh
|
38
|
+
- store_artifacts:
|
39
|
+
path: artifacts
|
data/.gitignore
CHANGED
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
set -ue
|
4
|
+
|
5
|
+
# Performs a delegated release step in a CircleCI Linux container. This mechanism is described
|
6
|
+
# in scripts/circleci/README.md. All of the necessary environment variables should already be
|
7
|
+
# in the generated CircleCI configuration.
|
8
|
+
|
9
|
+
mkdir -p artifacts
|
10
|
+
|
11
|
+
export LD_RELEASE_TEMP_DIR=/tmp/project-releaser-temp
|
12
|
+
mkdir -p ${LD_RELEASE_TEMP_DIR}
|
13
|
+
|
14
|
+
STEP="$1"
|
15
|
+
SCRIPT="$2"
|
16
|
+
echo
|
17
|
+
echo "[${STEP}] executing ${SCRIPT}"
|
18
|
+
"./${SCRIPT}"
|
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
set -ue
|
4
|
+
|
5
|
+
# Performs a delegated release step in a CircleCI Mac container. This mechanism is described
|
6
|
+
# in scripts/circleci/README.md. All of the necessary environment variables should already be
|
7
|
+
# in the generated CircleCI configuration.
|
8
|
+
|
9
|
+
mkdir -p artifacts
|
10
|
+
|
11
|
+
export LD_RELEASE_TEMP_DIR=/tmp/project-releaser-temp
|
12
|
+
mkdir -p ${LD_RELEASE_TEMP_DIR}
|
13
|
+
|
14
|
+
STEP="$1"
|
15
|
+
SCRIPT="$2"
|
16
|
+
echo
|
17
|
+
echo "[${STEP}] executing ${SCRIPT}"
|
18
|
+
"./${SCRIPT}"
|
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
set -ue
|
4
|
+
|
5
|
+
# Standard build.sh for Ruby-based projects that publish a gem
|
6
|
+
|
7
|
+
echo "Using gem $(gem --version)"
|
8
|
+
|
9
|
+
#shellcheck source=/dev/null
|
10
|
+
source "$(dirname "$0")/gems-setup.sh"
|
11
|
+
|
12
|
+
echo; echo "Installing dependencies"
|
13
|
+
${BUNDLER_COMMAND} install
|
14
|
+
|
15
|
+
# Build Ruby Gem - this assumes there is a single .gemspec file in the main project directory
|
16
|
+
# Note that the gemspec must be able to get the project version either from $LD_RELEASE_VERSION,
|
17
|
+
# or from somewhere in the source code that the project-specific update-version.sh has updated.
|
18
|
+
echo "Running gem build"
|
19
|
+
gem build ./*.gemspec || { echo "gem build failed" >&2; exit 1; }
|
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
# helper script to set GEM_HOME, PATH, and BUNDLER_COMMAND for Ruby - must be sourced, not executed
|
4
|
+
|
5
|
+
mkdir -p "${LD_RELEASE_TEMP_DIR}/gems"
|
6
|
+
export GEM_HOME="${LD_RELEASE_TEMP_DIR}/gems"
|
7
|
+
export PATH="${GEM_HOME}/bin:${PATH}"
|
8
|
+
|
9
|
+
# also, determine whether we'll need to run a specific version of Bundler
|
10
|
+
|
11
|
+
GEMSPEC_BUNDLER_VERSION=$(sed -n -e "s/.*['\"]bundler['\"], *['\"]\([^'\"]*\)['\"]/\1/p" ./*.gemspec | tr -d ' ')
|
12
|
+
if [ -n "${GEMSPEC_BUNDLER_VERSION}" ]; then
|
13
|
+
BUNDLER_COMMAND="bundler _${GEMSPEC_BUNDLER_VERSION}_"
|
14
|
+
else
|
15
|
+
BUNDLER_COMMAND="bundler"
|
16
|
+
fi
|
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
set -ue
|
4
|
+
|
5
|
+
echo "Using gem $(gem --version)"
|
6
|
+
|
7
|
+
#shellcheck source=/dev/null
|
8
|
+
source "$(dirname "$0")/gems-setup.sh"
|
9
|
+
|
10
|
+
# If the gemspec specifies a certain version of bundler, we need to make sure we install that version.
|
11
|
+
echo "Installing bundler"
|
12
|
+
if [ -n "${GEMSPEC_BUNDLER_VERSION:-}" ]; then
|
13
|
+
GEMSPEC_OPTIONS="-v ${GEMSPEC_BUNDLER_VERSION}"
|
14
|
+
else
|
15
|
+
GEMSPEC_OPTIONS=""
|
16
|
+
fi
|
17
|
+
gem install bundler ${GEMSPEC_OPTIONS} || { echo "installing bundler failed" >&2; exit 1; }
|
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
set -ue
|
4
|
+
|
5
|
+
# Standard publish.sh for Ruby-based projects - we can assume build.sh has already been run
|
6
|
+
|
7
|
+
#shellcheck source=/dev/null
|
8
|
+
source "$(dirname "$0")/gems-setup.sh"
|
9
|
+
|
10
|
+
# If we're running in CircleCI, the RubyGems credentials will be in an environment
|
11
|
+
# variable and should be copied to the variable the gem command expects
|
12
|
+
if [ -n "${LD_RELEASE_RUBYGEMS_API_KEY:-}" ]; then
|
13
|
+
export GEM_HOST_API_KEY="${LD_RELEASE_RUBYGEMS_API_KEY}"
|
14
|
+
fi
|
15
|
+
|
16
|
+
# Since all Releaser builds are clean builds, we can assume that the only .gem file here
|
17
|
+
# is the one we just built
|
18
|
+
echo "Running gem push"
|
19
|
+
gem push ./*.gem || { echo "gem push failed" >&2; exit 1; }
|
@@ -0,0 +1,8 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
set -ue
|
4
|
+
|
5
|
+
# Standard update-version.sh for Ruby-based projects - this will work only if the version string
|
6
|
+
# is in a source file under lib/ that has a line like his: VERSION = "2.0.0"
|
7
|
+
|
8
|
+
"$(dirname "$0")/../update-version-constant.sh" lib '*.rb'
|
@@ -0,0 +1,19 @@
|
|
1
|
+
param(
|
2
|
+
[string]$step,
|
3
|
+
[string]$script
|
4
|
+
)
|
5
|
+
|
6
|
+
# Performs a delegated release step in a CircleCI Windows container using PowerShell. This
|
7
|
+
# mechanism is described in scripts/circleci/README.md. All of the necessary environment
|
8
|
+
# variables should already be in the generated CircleCI configuration.
|
9
|
+
|
10
|
+
$ErrorActionPreference = "Stop"
|
11
|
+
|
12
|
+
New-Item -Path "./artifacts" -ItemType "directory" -Force | Out-Null
|
13
|
+
|
14
|
+
$env:LD_RELEASE_TEMP_DIR = "$env:TEMP\project-releaser-temp"
|
15
|
+
New-Item -Path $env:LD_RELEASE_TEMP_DIR -ItemType "directory" -Force | Out-Null
|
16
|
+
|
17
|
+
Write-Host
|
18
|
+
Write-Host "[$step] executing $script"
|
19
|
+
& "./$script"
|
data/.ldrelease/config.yml
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
repo:
|
2
|
+
public: ruby-eventsource
|
1
3
|
|
2
4
|
publications:
|
3
5
|
- url: https://rubygems.org/gems/ld-eventsource
|
@@ -5,5 +7,16 @@ publications:
|
|
5
7
|
- url: https://www.rubydoc.info/gems/ld-eventsource
|
6
8
|
description: documentation
|
7
9
|
|
10
|
+
releasableBranches:
|
11
|
+
- name: master
|
12
|
+
description: 2.x - based on the http gem
|
13
|
+
- name: 1.x
|
14
|
+
description: 1.x - based on the socketry gem
|
15
|
+
|
16
|
+
circleci:
|
17
|
+
linux:
|
18
|
+
image: cimg/ruby:2.5
|
19
|
+
context: org-global
|
20
|
+
|
8
21
|
template:
|
9
22
|
name: ruby
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,25 @@
|
|
2
2
|
|
3
3
|
All notable changes to the LaunchDarkly SSE Client for Ruby will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org).
|
4
4
|
|
5
|
+
## [2.0.1] - 2021-08-10
|
6
|
+
### Changed:
|
7
|
+
- The dependency version constraint for the `http` gem is now looser: it allows 5.x versions as well as 4.x. The breaking changes in `http` v5.0.0 do not affect `ld-eventsource`.
|
8
|
+
- The project's build now uses v2.2.10 of `bundler` due to known vulnerabilities in other versions.
|
9
|
+
- `Gemfile.lock` has been removed from source control. As this is a library project, the lockfile never affected application code that used this gem, but only affected the gem's own CI build. It is preferable for the CI build to refer only to the gemspec so that it resolves dependencies the same way an application using this gem would, rather than using pinned dependencies that an application would not use.
|
10
|
+
|
11
|
+
## [2.0.0] - 2021-01-26
|
12
|
+
### Added:
|
13
|
+
- Added a `socket_factory` configuration option which can be used for socket creation by the HTTP client if provided. The value of `socket_factory` must be an object providing an `open(uri, timeout)` method and returning a connected socket.
|
14
|
+
|
15
|
+
### Changed:
|
16
|
+
- Switched to the `http` gem instead of `socketry` and a custom HTTP client.
|
17
|
+
- Dropped support for Ruby < version 2.5
|
18
|
+
- Dropped support for JRuby < version 9.2
|
19
|
+
|
20
|
+
## [1.0.3] - 2020-03-17
|
21
|
+
### Fixed:
|
22
|
+
- The backoff delay logic for reconnecting after a stream failure was broken so that if a failure occurred after a stream had been active for at least `reconnect_reset_interval` (default 60 seconds), retries would use _no_ delay, potentially causing a flood of requests and a spike in CPU usage.
|
23
|
+
|
5
24
|
## [1.0.2] - 2020-03-10
|
6
25
|
### Removed:
|
7
26
|
- Removed an unused dependency on `rake`. There are no other changes in this release.
|
data/README.md
CHANGED
@@ -3,14 +3,14 @@ LaunchDarkly SSE Client for Ruby
|
|
3
3
|
|
4
4
|
[![Gem Version](https://badge.fury.io/rb/ld-eventsource.svg)](http://badge.fury.io/rb/ld-eventsource) [![Circle CI](https://circleci.com/gh/launchdarkly/ruby-eventsource/tree/master.svg?style=svg)](https://circleci.com/gh/launchdarkly/ruby-eventsource/tree/master)
|
5
5
|
|
6
|
-
A client for the [Server-Sent Events](https://www.w3.org/TR/eventsource/) protocol. This implementation runs on a worker thread, and uses the [`
|
6
|
+
A client for the [Server-Sent Events](https://www.w3.org/TR/eventsource/) protocol. This implementation runs on a worker thread, and uses the [`http`](https://rubygems.org/gems/http) gem to manage a persistent connection. Its primary purpose is to support the [LaunchDarkly SDK for Ruby](https://github.com/launchdarkly/ruby-client), but it can be used independently.
|
7
7
|
|
8
8
|
Parts of this code are based on https://github.com/Tonkpils/celluloid-eventsource, but it does not use Celluloid.
|
9
9
|
|
10
10
|
Supported Ruby versions
|
11
11
|
-----------------------
|
12
12
|
|
13
|
-
This gem has a minimum Ruby version of 2.
|
13
|
+
This gem has a minimum Ruby version of 2.5, or 9.2 for JRuby.
|
14
14
|
|
15
15
|
Quick setup
|
16
16
|
-----------
|
data/ld-eventsource.gemspec
CHANGED
@@ -20,11 +20,11 @@ Gem::Specification.new do |spec|
|
|
20
20
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
21
21
|
spec.require_paths = ["lib"]
|
22
22
|
|
23
|
-
spec.add_development_dependency "bundler", "
|
23
|
+
spec.add_development_dependency "bundler", "2.2.10"
|
24
24
|
spec.add_development_dependency "rspec", "~> 3.2"
|
25
25
|
spec.add_development_dependency "rspec_junit_formatter", "~> 0.3.0"
|
26
|
+
spec.add_development_dependency "webrick", "~> 1.7"
|
26
27
|
|
27
28
|
spec.add_runtime_dependency "concurrent-ruby", "~> 1.0"
|
28
|
-
spec.add_runtime_dependency "
|
29
|
-
spec.add_runtime_dependency "socketry", "~> 0.5.1"
|
29
|
+
spec.add_runtime_dependency "http", ">= 4.4.1", "< 6.0.0"
|
30
30
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
require "ld-eventsource/impl/backoff"
|
2
2
|
require "ld-eventsource/impl/event_parser"
|
3
|
-
require "ld-eventsource/impl/streaming_http"
|
4
3
|
require "ld-eventsource/events"
|
5
4
|
require "ld-eventsource/errors"
|
6
5
|
|
@@ -8,6 +7,7 @@ require "concurrent/atomics"
|
|
8
7
|
require "logger"
|
9
8
|
require "thread"
|
10
9
|
require "uri"
|
10
|
+
require "http"
|
11
11
|
|
12
12
|
module SSE
|
13
13
|
#
|
@@ -80,6 +80,9 @@ module SSE
|
|
80
80
|
# proxy with the `HTTP_PROXY` or `HTTPS_PROXY` environment variable)
|
81
81
|
# @param logger [Logger] a Logger instance for the client to use for diagnostic output;
|
82
82
|
# defaults to a logger with WARN level that goes to standard output
|
83
|
+
# @param socket_factory [#open] (nil) an optional factory object for creating sockets,
|
84
|
+
# if you want to use something other than the default `TCPSocket`; it must implement
|
85
|
+
# `open(uri, timeout)` to return a connected `Socket`
|
83
86
|
# @yieldparam [Client] client the new client instance, before opening the connection
|
84
87
|
#
|
85
88
|
def initialize(uri,
|
@@ -90,7 +93,8 @@ module SSE
|
|
90
93
|
reconnect_reset_interval: DEFAULT_RECONNECT_RESET_INTERVAL,
|
91
94
|
last_event_id: nil,
|
92
95
|
proxy: nil,
|
93
|
-
logger: nil
|
96
|
+
logger: nil,
|
97
|
+
socket_factory: nil)
|
94
98
|
@uri = URI(uri)
|
95
99
|
@stopped = Concurrent::AtomicBoolean.new(false)
|
96
100
|
|
@@ -98,7 +102,11 @@ module SSE
|
|
98
102
|
@connect_timeout = connect_timeout
|
99
103
|
@read_timeout = read_timeout
|
100
104
|
@logger = logger || default_logger
|
101
|
-
|
105
|
+
http_client_options = {}
|
106
|
+
if socket_factory
|
107
|
+
http_client_options["socket_class"] = socket_factory
|
108
|
+
end
|
109
|
+
|
102
110
|
if proxy
|
103
111
|
@proxy = proxy
|
104
112
|
else
|
@@ -108,6 +116,21 @@ module SSE
|
|
108
116
|
end
|
109
117
|
end
|
110
118
|
|
119
|
+
if @proxy
|
120
|
+
http_client_options["proxy"] = {
|
121
|
+
:proxy_address => @proxy.host,
|
122
|
+
:proxy_port => @proxy.port
|
123
|
+
}
|
124
|
+
end
|
125
|
+
|
126
|
+
@http_client = HTTP::Client.new(http_client_options)
|
127
|
+
.timeout({
|
128
|
+
read: read_timeout,
|
129
|
+
connect: connect_timeout
|
130
|
+
})
|
131
|
+
@buffer = ""
|
132
|
+
@lock = Mutex.new
|
133
|
+
|
111
134
|
@backoff = Impl::Backoff.new(reconnect_time || DEFAULT_RECONNECT_TIME, MAX_RECONNECT_TIME,
|
112
135
|
reconnect_reset_interval: reconnect_reset_interval)
|
113
136
|
|
@@ -163,12 +186,65 @@ module SSE
|
|
163
186
|
#
|
164
187
|
def close
|
165
188
|
if @stopped.make_true
|
166
|
-
|
167
|
-
@cxn = nil
|
189
|
+
reset_http
|
168
190
|
end
|
169
191
|
end
|
170
192
|
|
193
|
+
#
|
194
|
+
# Tests whether the client has been shut down by a call to {Client#close}.
|
195
|
+
#
|
196
|
+
# @return [Boolean] true if the client has been shut down
|
197
|
+
#
|
198
|
+
def closed?
|
199
|
+
@stopped.value
|
200
|
+
end
|
201
|
+
|
171
202
|
private
|
203
|
+
|
204
|
+
def reset_http
|
205
|
+
@http_client.close if !@http_client.nil?
|
206
|
+
@cxn = nil
|
207
|
+
@buffer = ""
|
208
|
+
end
|
209
|
+
|
210
|
+
def read_lines
|
211
|
+
Enumerator.new do |gen|
|
212
|
+
loop do
|
213
|
+
line = read_line
|
214
|
+
break if line.nil?
|
215
|
+
gen.yield line
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def read_line
|
221
|
+
loop do
|
222
|
+
@lock.synchronize do
|
223
|
+
i = @buffer.index(/[\r\n]/)
|
224
|
+
if !i.nil? && !(i == @buffer.length - 1 && @buffer[i] == "\r")
|
225
|
+
i += 1 if (@buffer[i] == "\r" && @buffer[i + 1] == "\n")
|
226
|
+
return @buffer.slice!(0, i + 1).force_encoding(Encoding::UTF_8)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
return nil if !read_chunk_into_buffer
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def read_chunk_into_buffer
|
234
|
+
# If @done is set, it means the Parser has signaled end of response body
|
235
|
+
@lock.synchronize { return false if @done }
|
236
|
+
begin
|
237
|
+
data = @cxn.readpartial
|
238
|
+
rescue HTTP::TimeoutError
|
239
|
+
# We rethrow this as our own type so the caller doesn't have to know the httprb API
|
240
|
+
raise Errors::ReadTimeoutError.new(@read_timeout)
|
241
|
+
end
|
242
|
+
return false if data == nil
|
243
|
+
@buffer << data
|
244
|
+
# We are piping the content through the parser so that it can handle things like chunked
|
245
|
+
# encoding for us. The content ends up being appended to @buffer via our callback.
|
246
|
+
true
|
247
|
+
end
|
172
248
|
|
173
249
|
def default_logger
|
174
250
|
log = ::Logger.new($stdout)
|
@@ -196,7 +272,7 @@ module SSE
|
|
196
272
|
end
|
197
273
|
end
|
198
274
|
begin
|
199
|
-
|
275
|
+
reset_http
|
200
276
|
rescue StandardError => e
|
201
277
|
log_and_dispatch_error(e, "Unexpected error while closing stream")
|
202
278
|
end
|
@@ -215,31 +291,28 @@ module SSE
|
|
215
291
|
cxn = nil
|
216
292
|
begin
|
217
293
|
@logger.info { "Connecting to event stream at #{@uri}" }
|
218
|
-
cxn =
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
read_timeout: @read_timeout
|
223
|
-
)
|
224
|
-
if cxn.status == 200
|
294
|
+
cxn = @http_client.request("GET", @uri, {
|
295
|
+
headers: build_headers
|
296
|
+
})
|
297
|
+
if cxn.status.code == 200
|
225
298
|
content_type = cxn.headers["content-type"]
|
226
299
|
if content_type && content_type.start_with?("text/event-stream")
|
227
300
|
return cxn # we're good to proceed
|
228
301
|
else
|
229
|
-
|
302
|
+
reset_http
|
230
303
|
err = Errors::HTTPContentTypeError.new(cxn.headers["content-type"])
|
231
304
|
@on[:error].call(err)
|
232
305
|
@logger.warn { "Event source returned unexpected content type '#{cxn.headers["content-type"]}'" }
|
233
306
|
end
|
234
307
|
else
|
235
|
-
body = cxn.
|
236
|
-
|
237
|
-
@logger.info { "Server returned error status #{cxn.status}" }
|
238
|
-
err = Errors::HTTPStatusError.new(cxn.status, body)
|
308
|
+
body = cxn.to_s # grab the whole response body in case it has error details
|
309
|
+
reset_http
|
310
|
+
@logger.info { "Server returned error status #{cxn.status.code}" }
|
311
|
+
err = Errors::HTTPStatusError.new(cxn.status.code, body)
|
239
312
|
@on[:error].call(err)
|
240
313
|
end
|
241
314
|
rescue
|
242
|
-
|
315
|
+
reset_http
|
243
316
|
raise # will be handled in run_stream
|
244
317
|
end
|
245
318
|
# if unsuccessful, continue the loop to connect again
|
@@ -253,7 +326,7 @@ module SSE
|
|
253
326
|
# it can automatically reset itself if enough time passes between failures.
|
254
327
|
@backoff.mark_success
|
255
328
|
|
256
|
-
event_parser = Impl::EventParser.new(
|
329
|
+
event_parser = Impl::EventParser.new(read_lines)
|
257
330
|
event_parser.items.each do |item|
|
258
331
|
return if @stopped.value
|
259
332
|
case item
|
@@ -288,7 +361,8 @@ module SSE
|
|
288
361
|
def build_headers
|
289
362
|
h = {
|
290
363
|
'Accept' => 'text/event-stream',
|
291
|
-
'Cache-Control' => 'no-cache'
|
364
|
+
'Cache-Control' => 'no-cache',
|
365
|
+
'User-Agent' => 'ruby-eventsource'
|
292
366
|
}
|
293
367
|
h['Last-Event-Id'] = @last_id if !@last_id.nil?
|
294
368
|
h.merge(@headers)
|