ld-eventsource 1.0.2 → 2.0.1
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 +24 -0
- data/README.md +2 -2
- data/ld-eventsource.gemspec +3 -3
- data/lib/ld-eventsource/client.rb +86 -21
- data/lib/ld-eventsource/impl/backoff.rb +0 -4
- data/lib/ld-eventsource/version.rb +1 -1
- data/spec/backoff_spec.rb +52 -0
- data/spec/client_spec.rb +68 -8
- data/spec/http_stub.rb +2 -0
- metadata +36 -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: 4e0485d46aab9fb8bb9ac32ae49c013dde072d0695372089989a2f5e8799b6de
|
4
|
+
data.tar.gz: 7ca30c7591596b56925da6f157bb2ba39a41eaf8a23679bfd823c05ac83edb81
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8a41b9daef952bc55ada55788415d25da1bf5bbfcbd071b52d8f39693132281a654c59cbfb05165af49f189be52bdf579ae130b21a974a67a0d96e239b67861e
|
7
|
+
data.tar.gz: 650358cf13547c6db91e0a793ed45f32bdd6221f0d96224121132e6055453fb88d1977c13657c1c4230894873c90a0c66761865ae8db232bc355326d34fdbd3b
|
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.0.1"
|
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,30 @@
|
|
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
|
+
|
24
|
+
## [1.0.2] - 2020-03-10
|
25
|
+
### Removed:
|
26
|
+
- Removed an unused dependency on `rake`. There are no other changes in this release.
|
27
|
+
|
28
|
+
|
5
29
|
## [1.0.1] - 2019-07-10
|
6
30
|
### Fixed:
|
7
31
|
- Calling `close` on the client could cause a misleading warning message in the log, such as `Unexpected error from event source: #<IOError: stream closed in another thread>`.
|
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,56 @@ 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
|
|
171
193
|
private
|
194
|
+
|
195
|
+
def reset_http
|
196
|
+
@http_client.close if !@http_client.nil?
|
197
|
+
@cxn = nil
|
198
|
+
@buffer = ""
|
199
|
+
end
|
200
|
+
|
201
|
+
def read_lines
|
202
|
+
Enumerator.new do |gen|
|
203
|
+
loop do
|
204
|
+
line = read_line
|
205
|
+
break if line.nil?
|
206
|
+
gen.yield line
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def read_line
|
212
|
+
loop do
|
213
|
+
@lock.synchronize do
|
214
|
+
i = @buffer.index(/[\r\n]/)
|
215
|
+
if !i.nil? && !(i == @buffer.length - 1 && @buffer[i] == "\r")
|
216
|
+
i += 1 if (@buffer[i] == "\r" && @buffer[i + 1] == "\n")
|
217
|
+
return @buffer.slice!(0, i + 1).force_encoding(Encoding::UTF_8)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
return nil if !read_chunk_into_buffer
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def read_chunk_into_buffer
|
225
|
+
# If @done is set, it means the Parser has signaled end of response body
|
226
|
+
@lock.synchronize { return false if @done }
|
227
|
+
begin
|
228
|
+
data = @cxn.readpartial
|
229
|
+
rescue HTTP::TimeoutError
|
230
|
+
# We rethrow this as our own type so the caller doesn't have to know the httprb API
|
231
|
+
raise Errors::ReadTimeoutError.new(@read_timeout)
|
232
|
+
end
|
233
|
+
return false if data == nil
|
234
|
+
@buffer << data
|
235
|
+
# We are piping the content through the parser so that it can handle things like chunked
|
236
|
+
# encoding for us. The content ends up being appended to @buffer via our callback.
|
237
|
+
true
|
238
|
+
end
|
172
239
|
|
173
240
|
def default_logger
|
174
241
|
log = ::Logger.new($stdout)
|
@@ -196,7 +263,7 @@ module SSE
|
|
196
263
|
end
|
197
264
|
end
|
198
265
|
begin
|
199
|
-
|
266
|
+
reset_http
|
200
267
|
rescue StandardError => e
|
201
268
|
log_and_dispatch_error(e, "Unexpected error while closing stream")
|
202
269
|
end
|
@@ -215,31 +282,28 @@ module SSE
|
|
215
282
|
cxn = nil
|
216
283
|
begin
|
217
284
|
@logger.info { "Connecting to event stream at #{@uri}" }
|
218
|
-
cxn =
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
read_timeout: @read_timeout
|
223
|
-
)
|
224
|
-
if cxn.status == 200
|
285
|
+
cxn = @http_client.request("GET", @uri, {
|
286
|
+
headers: build_headers
|
287
|
+
})
|
288
|
+
if cxn.status.code == 200
|
225
289
|
content_type = cxn.headers["content-type"]
|
226
290
|
if content_type && content_type.start_with?("text/event-stream")
|
227
291
|
return cxn # we're good to proceed
|
228
292
|
else
|
229
|
-
|
293
|
+
reset_http
|
230
294
|
err = Errors::HTTPContentTypeError.new(cxn.headers["content-type"])
|
231
295
|
@on[:error].call(err)
|
232
296
|
@logger.warn { "Event source returned unexpected content type '#{cxn.headers["content-type"]}'" }
|
233
297
|
end
|
234
298
|
else
|
235
|
-
body = cxn.
|
236
|
-
|
237
|
-
@logger.info { "Server returned error status #{cxn.status}" }
|
238
|
-
err = Errors::HTTPStatusError.new(cxn.status, body)
|
299
|
+
body = cxn.to_s # grab the whole response body in case it has error details
|
300
|
+
reset_http
|
301
|
+
@logger.info { "Server returned error status #{cxn.status.code}" }
|
302
|
+
err = Errors::HTTPStatusError.new(cxn.status.code, body)
|
239
303
|
@on[:error].call(err)
|
240
304
|
end
|
241
305
|
rescue
|
242
|
-
|
306
|
+
reset_http
|
243
307
|
raise # will be handled in run_stream
|
244
308
|
end
|
245
309
|
# if unsuccessful, continue the loop to connect again
|
@@ -253,7 +317,7 @@ module SSE
|
|
253
317
|
# it can automatically reset itself if enough time passes between failures.
|
254
318
|
@backoff.mark_success
|
255
319
|
|
256
|
-
event_parser = Impl::EventParser.new(
|
320
|
+
event_parser = Impl::EventParser.new(read_lines)
|
257
321
|
event_parser.items.each do |item|
|
258
322
|
return if @stopped.value
|
259
323
|
case item
|
@@ -288,7 +352,8 @@ module SSE
|
|
288
352
|
def build_headers
|
289
353
|
h = {
|
290
354
|
'Accept' => 'text/event-stream',
|
291
|
-
'Cache-Control' => 'no-cache'
|
355
|
+
'Cache-Control' => 'no-cache',
|
356
|
+
'User-Agent' => 'ruby-eventsource'
|
292
357
|
}
|
293
358
|
h['Last-Event-Id'] = @last_id if !@last_id.nil?
|
294
359
|
h.merge(@headers)
|