closeyourit-ruby 0.9.3 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +11 -3
- data/lib/closeyourit/background_worker.rb +21 -2
- data/lib/closeyourit/client.rb +25 -12
- data/lib/closeyourit/configuration.rb +5 -1
- data/lib/closeyourit/instrumenter.rb +10 -2
- data/lib/closeyourit/scrubber.rb +54 -1
- data/lib/closeyourit/transport.rb +24 -3
- data/lib/closeyourit/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0bc659e436ad9b08b9861d4898e6876a3b753ec88ebb7f8e81075729d958fcb4
|
|
4
|
+
data.tar.gz: 2bb509651ce4aa880a40f2d49f29c9584bb1bfde984b6fba3065c1a954800f82
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d3cfd52ac4b7ddda5a5a92485ebe1fb4be25b808e706b11af7c5e0c6767fff19af3aa3958a8a3d9e6d94ec6ec36162a942fa439700bd16d07d52987d34a4328e
|
|
7
|
+
data.tar.gz: fe22c4cae5a0e94f4eeaf8284d00c0a42d51a0d8c3830dcd703abcf9774eaf31c4e5e81c0c4b8ba6331a1e3d331078136b2cf05e09316325c5f545acaa8b9c17
|
data/README.md
CHANGED
|
@@ -79,6 +79,7 @@ end
|
|
|
79
79
|
| `before_send` | `nil` | `->(payload) { ... }` — scrub finale; ritorna payload o `nil` per scartare |
|
|
80
80
|
| `sample_rate` | `1.0` | Frazione di errori/messaggi inviata (`1.0` tutto, `0.0` niente) |
|
|
81
81
|
| `async_threads` | `cpu/2` | Thread di invio; `0` = sincrono (test) |
|
|
82
|
+
| `shutdown_timeout` | `15` | Secondi di attesa, alla chiusura, perché gli invii in volo completino; scaduto il tempo gli eventi residui sono contati come `dropped` (vedi [Flush di fine-vita](#flush-di-fine-vita)) |
|
|
82
83
|
| `trap_signals` | `false` | Intercetta `SIGTERM` per garantire il flush di fine-vita (deploy/Kamal); opt-in perché sovrascrive un eventuale handler `TERM` dell'app ospite (vedi [Flush di fine-vita](#flush-di-fine-vita)) |
|
|
83
84
|
| `slow_query_threshold_ms` | `100` | Soglia query lente |
|
|
84
85
|
| `excluded_query_patterns` | `solid_queue_`, `solid_cache_`, `solid_cable_` | Query da NON misurare come rallentamento — **Regexp** (pattern) o **String** (testo letterale). Vale solo per la misura: breadcrumb e profiling N+1 vedono tutto |
|
|
@@ -92,7 +93,7 @@ end
|
|
|
92
93
|
| `send_pii` | `false` | Master switch PII |
|
|
93
94
|
| `obfuscate_sql` | `true` | Maschera i literal nello SQL |
|
|
94
95
|
| `filter_parameters` | `[]` | Chiavi extra da redarre (mergiate con quelle di Rails) |
|
|
95
|
-
| `scrub_message_patterns` | `[]` | Regexp da redarre da messaggi d'eccezione **e da `message` dei log
|
|
96
|
+
| `scrub_message_patterns` | `[]` | Regexp **aggiuntive** da redarre da messaggi d'eccezione **e da `message` dei log**; le credenziali `Bearer`/`Basic`/`Token` sono già redatte di serie |
|
|
96
97
|
| `logs_enabled` | `true` | Master switch dello stream di log strutturato |
|
|
97
98
|
| `logs_min_level` | `:info` | Soglia minima: i log sotto (es. `debug`) non vengono inviati |
|
|
98
99
|
| `logs_sample_rate` | `1.0` | Frazione di log inviata (`1.0` tutto, `0.0` niente) |
|
|
@@ -227,6 +228,11 @@ buffer), così i log sotto-batch di rake/CLI non vanno persi all'uscita. `SIGTER
|
|
|
227
228
|
termina il processo **senza** eseguire gli `at_exit`: imposta `trap_signals = true` per intercettarlo e
|
|
228
229
|
convertirlo in un exit pulito, oppure chiama `CloseYourIt.shutdown` esplicitamente prima di terminare.
|
|
229
230
|
|
|
231
|
+
Il drain attende fino a `shutdown_timeout` secondi (default `15`, il tetto di durata di una singola
|
|
232
|
+
POST verso l'ingest). Gli eventi ancora in coda quando il tempo scade sono contati come `dropped`
|
|
233
|
+
nello snapshot diagnostico di fine-vita: nessuna perdita silenziosa. Abbassa il valore se l'uscita
|
|
234
|
+
del processo deve essere più rapida della consegna.
|
|
235
|
+
|
|
230
236
|
#### Server che forkano (Puma cluster, Sidekiq, Unicorn)
|
|
231
237
|
|
|
232
238
|
I server che precaricano l'app (`preload_app!`) inizializzano la gemma nel processo **master**, poi
|
|
@@ -381,8 +387,10 @@ Privacy-by-default (`send_pii = false`). In sintesi:
|
|
|
381
387
|
- **SQL**: inviato il template, **mai** i bind values; `obfuscate_sql` maschera anche i literal inline.
|
|
382
388
|
- **Chiavi sensibili** (`password`, `token`, `authorization`, `cookie`, `secret`, `api_key`, `csrf`,
|
|
383
389
|
`credit_card`, `cvv`, `ssn`, `iban`, …) → `[FILTERED]`; estendibili con `filter_parameters`.
|
|
384
|
-
- **Messaggi d'eccezione**: inviati per il debug
|
|
385
|
-
`
|
|
390
|
+
- **Messaggi d'eccezione**: inviati per il debug. Le credenziali di autenticazione finite nel testo
|
|
391
|
+
(`Authorization: Bearer …`, `Basic …`, `Token …`) sono redatte **di serie**, senza configurazione:
|
|
392
|
+
chiave e schema restano leggibili, sparisce il valore → `Authorization: Bearer [FILTERED]`. Il resto
|
|
393
|
+
è redigibile con `scrub_message_patterns` / `before_send`.
|
|
386
394
|
- **Mai inviati**: variabili locali dei frame, argomenti dei metodi, IP/cookie/Authorization, token (che
|
|
387
395
|
viaggia solo nell'header su HTTPS).
|
|
388
396
|
|
|
@@ -32,15 +32,34 @@ module CloseYourIt
|
|
|
32
32
|
accepted
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
# Drena la coda attendendo fino a `timeout` secondi. Il default è il tetto di durata di UNA POST
|
|
36
|
+
# (Transport::MAX_REQUEST_SECONDS): aspettare meno abbandona invii ancora sani, ed è quello che
|
|
37
|
+
# succedeva con il vecchio secondo fisso (CYRB-24). Se il drain scade, i task rimasti in coda sono
|
|
38
|
+
# persi: contali come `dropped`, altrimenti lo snapshot di fine vita dichiara zero perdite.
|
|
39
|
+
def shutdown(timeout = Transport::MAX_REQUEST_SECONDS)
|
|
36
40
|
return unless @executor.respond_to?(:shutdown)
|
|
37
41
|
|
|
38
42
|
@executor.shutdown
|
|
39
|
-
@executor.wait_for_termination(timeout)
|
|
43
|
+
terminated = @executor.wait_for_termination(timeout)
|
|
44
|
+
report_abandoned(timeout) unless terminated
|
|
45
|
+
terminated
|
|
40
46
|
end
|
|
41
47
|
|
|
42
48
|
private
|
|
43
49
|
|
|
50
|
+
# Task accodati e mai eseguiti al momento della scadenza del drain. `queue_length` non esiste
|
|
51
|
+
# sull'ImmediateExecutor (sincrono, niente coda) → zero residui.
|
|
52
|
+
def report_abandoned(timeout)
|
|
53
|
+
abandoned = @executor.respond_to?(:queue_length) ? @executor.queue_length.to_i : 0
|
|
54
|
+
CloseYourIt.internal_logger.warn(
|
|
55
|
+
"CloseYourIt background worker: drain scaduto dopo #{timeout}s, #{abandoned} eventi abbandonati"
|
|
56
|
+
)
|
|
57
|
+
abandoned.times do
|
|
58
|
+
CloseYourIt.stats.increment(:dropped)
|
|
59
|
+
CloseYourIt.notify_diagnostic(:drop, reason: :shutdown_timeout)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
44
63
|
def build_executor(threads, max_queue)
|
|
45
64
|
return Concurrent::ImmediateExecutor.new if threads <= 0
|
|
46
65
|
|
data/lib/closeyourit/client.rb
CHANGED
|
@@ -54,17 +54,7 @@ module CloseYourIt
|
|
|
54
54
|
def flush_logs(events)
|
|
55
55
|
return nil if events.nil? || events.empty?
|
|
56
56
|
|
|
57
|
-
payloads = events.
|
|
58
|
-
if @configuration.before_send
|
|
59
|
-
kept = payloads.filter_map { |payload| @configuration.before_send.call(payload) }
|
|
60
|
-
# I log che before_send porta a nil sono scarti voluti: contabilizzali come gli errori/metriche
|
|
61
|
-
# (parità con #capture_event), altrimenti sparirebbero silenziosamente dai contatori (CYRB-12).
|
|
62
|
-
(payloads.size - kept.size).times do
|
|
63
|
-
CloseYourIt.stats.increment(:dropped)
|
|
64
|
-
CloseYourIt.notify_diagnostic(:drop, reason: :before_send)
|
|
65
|
-
end
|
|
66
|
-
payloads = kept
|
|
67
|
-
end
|
|
57
|
+
payloads = events.filter_map { |event| build_log_payload(event) }
|
|
68
58
|
return nil if payloads.empty?
|
|
69
59
|
|
|
70
60
|
path = events.first.ingest_path(@configuration.project_id)
|
|
@@ -95,7 +85,30 @@ module CloseYourIt
|
|
|
95
85
|
end
|
|
96
86
|
|
|
97
87
|
def shutdown
|
|
98
|
-
@worker.shutdown
|
|
88
|
+
@worker.shutdown(@configuration.shutdown_timeout)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
private
|
|
92
|
+
|
|
93
|
+
# Costruisce il payload di UNA voce di log. `to_h` (scrubber su un byte non UTF-8) e `before_send`
|
|
94
|
+
# possono sollevare: qui il buffer è già stato drenato, quindi una sola voce rotta valutata
|
|
95
|
+
# insieme alle altre porterebbe via l'intero batch di voci sane. Ogni voce è isolata e scartata
|
|
96
|
+
# da sola, come fa il client JS (CYRB-24). `nil` = voce da non spedire, già contabilizzata.
|
|
97
|
+
def build_log_payload(event)
|
|
98
|
+
payload = event.to_h
|
|
99
|
+
payload = @configuration.before_send.call(payload) if @configuration.before_send
|
|
100
|
+
return payload unless payload.nil?
|
|
101
|
+
|
|
102
|
+
# Scarto voluto di before_send: contabilizzato come in #capture_event, altrimenti sparirebbe
|
|
103
|
+
# silenziosamente dai contatori (CYRB-12).
|
|
104
|
+
CloseYourIt.stats.increment(:dropped)
|
|
105
|
+
CloseYourIt.notify_diagnostic(:drop, reason: :before_send)
|
|
106
|
+
nil
|
|
107
|
+
rescue StandardError => e
|
|
108
|
+
CloseYourIt.internal_logger.error("CloseYourIt client: log scartato — #{e.class}: #{e.message}")
|
|
109
|
+
CloseYourIt.stats.increment(:dropped)
|
|
110
|
+
CloseYourIt.notify_diagnostic(:drop, reason: :error, error: e.class.name)
|
|
111
|
+
nil
|
|
99
112
|
end
|
|
100
113
|
end
|
|
101
114
|
end
|
|
@@ -27,7 +27,7 @@ module CloseYourIt
|
|
|
27
27
|
].freeze
|
|
28
28
|
|
|
29
29
|
attr_accessor :endpoint_url, :token, :project_id, :environment, :before_send, :on_diagnostic,
|
|
30
|
-
:async_threads, :background_worker_max_queue,
|
|
30
|
+
:async_threads, :background_worker_max_queue, :shutdown_timeout,
|
|
31
31
|
:slow_query_threshold_ms, :slow_method_threshold_ms,
|
|
32
32
|
:send_pii, :obfuscate_sql, :send_server_name,
|
|
33
33
|
:capture_query_bindings, :capture_method_arguments,
|
|
@@ -65,6 +65,10 @@ module CloseYourIt
|
|
|
65
65
|
|
|
66
66
|
@async_threads = default_threads
|
|
67
67
|
@background_worker_max_queue = 30
|
|
68
|
+
# Quanto attendere, alla chiusura del processo, che gli invii in volo completino. Default: il
|
|
69
|
+
# tetto di durata di UNA POST, così l'ultimo batch non viene abbandonato a metà (CYRB-24).
|
|
70
|
+
# Abbassalo se l'uscita del processo deve essere più rapida della consegna.
|
|
71
|
+
@shutdown_timeout = Transport::MAX_REQUEST_SECONDS
|
|
68
72
|
|
|
69
73
|
# Intercetta SIGTERM per garantire il flush di fine-vita (deploy/Kamal): SIGTERM di default
|
|
70
74
|
# termina il processo SENZA eseguire gli at_exit. OPT-IN perché sovrascrive un eventuale handler
|
|
@@ -13,8 +13,16 @@ module CloseYourIt
|
|
|
13
13
|
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
14
14
|
yield
|
|
15
15
|
ensure
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
# `measure` gira DENTRO il metodo dell'app (Monitor fa prepend): un guasto qui — soglia non
|
|
17
|
+
# configurata, ingest irraggiungibile — solleverebbe in un chiamante che aveva già il suo
|
|
18
|
+
# risultato. È l'unico ingresso della gemma che era scoperto: ora è come tutti gli altri
|
|
19
|
+
# (CYRB-24). Un'eccezione del blocco misurato continua invece a propagare intatta.
|
|
20
|
+
begin
|
|
21
|
+
duration_ms = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start) * 1000.0
|
|
22
|
+
report(label, duration_ms, location, args: args, kwargs: kwargs)
|
|
23
|
+
rescue StandardError => e
|
|
24
|
+
CloseYourIt.internal_logger.error("CloseYourIt instrumenter: #{e.class}: #{e.message}")
|
|
25
|
+
end
|
|
18
26
|
end
|
|
19
27
|
|
|
20
28
|
def report(label, duration_ms, location = nil, args: nil, kwargs: nil)
|
data/lib/closeyourit/scrubber.rb
CHANGED
|
@@ -23,6 +23,53 @@ module CloseYourIt
|
|
|
23
23
|
email phone telephone mobile dob birth passport bearer session pin pan
|
|
24
24
|
].freeze
|
|
25
25
|
|
|
26
|
+
# CYRB-23 — redazione INTEGRATA delle credenziali di autenticazione nel testo libero.
|
|
27
|
+
# `filter_params`/`filter_value` coprono gli header STRUTTURATI (la chiave `authorization` è in
|
|
28
|
+
# DENYLIST); un `Authorization: Bearer <token>` incollato dentro `exception.message` o dentro la
|
|
29
|
+
# riga di un log è testo, non una chiave, e prima di questo ticket partiva in chiaro perché
|
|
30
|
+
# `scrub_message` applicava solo i pattern configurati dall'utente (default `[]`).
|
|
31
|
+
#
|
|
32
|
+
# Forma del testo redatto (decisa qui una volta per tutti gli SDK — gemelli CYDA-26/CYJS-38/
|
|
33
|
+
# CYPY-15): chiave e schema restano leggibili, sparisce SOLO la credenziale →
|
|
34
|
+
# `Authorization: Bearer [FILTERED]`. Il messaggio resta diagnosticamente utile (si vede CHE
|
|
35
|
+
# c'era un header e con quale schema) senza trasportare il segreto.
|
|
36
|
+
AUTH_SCHEME = /(?i:Bearer|Basic|Token)/
|
|
37
|
+
|
|
38
|
+
# `token68` (RFC 7235) con padding `=` finale: la forma di una credenziale reale. Minimo 4
|
|
39
|
+
# caratteri — un `Basic` di credenziali brevi è corto (`dTpw` = `u:p`); a filtrare la prosa
|
|
40
|
+
# pensa NOT_PROSE, non la lunghezza.
|
|
41
|
+
CREDENTIAL_TOKEN = %r{[A-Za-z0-9\-._~+/]{4,}={0,2}}
|
|
42
|
+
|
|
43
|
+
# Guardia anti-falso-positivo per lo schema NUDO: quello che segue NON è una credenziale se ha
|
|
44
|
+
# la forma di una parola scritta da un umano, cioè sole lettere in UNA di tre forme —
|
|
45
|
+
# `minuscole`, `Capitalizzata`, `MAIUSCOLE` — e più corta di 20 caratteri. Salva "the bearer of
|
|
46
|
+
# bad news", "invalid bearer credentials", "Basic HTTP authentication", "Bearer Token expired".
|
|
47
|
+
# Tutto il resto è credenziale: entropia (cifre, `_`, `-`, `.`), maiuscole interne miste
|
|
48
|
+
# (`dTpw`, base64) o lunghezza ≥ 20 (nessuna parola è così lunga, mentre un token opaco di sole
|
|
49
|
+
# minuscole sì).
|
|
50
|
+
# Trade-off accettato: un CamelCase subito dopo lo schema (`Token MyAppName`) viene redatto.
|
|
51
|
+
PROSE_WORD = /(?:[a-z]{1,19}|[A-Z][a-z]{0,18}|[A-Z]{1,19})/
|
|
52
|
+
NOT_PROSE = %r{(?!\[FILTERED\])(?!#{PROSE_WORD}(?![A-Za-z0-9\-._~+/=]))}
|
|
53
|
+
|
|
54
|
+
# Valore di un header incollato nel testo: sequenza di pezzi non delimitatori, dove una stringa
|
|
55
|
+
# fra virgolette conta come UN pezzo. NON `\S+`, che si mangerebbe la `)` di
|
|
56
|
+
# "(Authorization: Bearer <token>)"; ma nemmeno una classe secca, che si fermerebbe alla prima
|
|
57
|
+
# virgoletta lasciando in chiaro il segreto di `Token token="<token>"` (sintassi RFC 7235 e
|
|
58
|
+
# `authenticate_with_http_token` di Rails) e di un frammento JSON incollato nel messaggio.
|
|
59
|
+
QUOTED = /"[^"\n]*"|'[^'\n]*'/
|
|
60
|
+
HEADER_VALUE = /(?:#{QUOTED}|[^\s,;)\]}"'<>])+/
|
|
61
|
+
|
|
62
|
+
# `Authorization:`/`authorization=` (anche `Proxy-Authorization`, anche `authorization_header`)
|
|
63
|
+
# seguito dal valore: qui la chiave è esplicita, quindi si redige senza euristica di prosa —
|
|
64
|
+
# over-redaction voluta, coerente con la DENYLIST. `[ \t]` e non `\s`: mai oltre il newline.
|
|
65
|
+
# Il lookahead copre lo schema OPZIONALE: senza, su un testo già redatto il motore farebbe
|
|
66
|
+
# backtracking sul ramo "senza schema" e redigerebbe la parola `Bearer` stessa (non idempotente).
|
|
67
|
+
AUTH_HEADER = /((?i:(?:proxy[_-]?)?authorization)[\w-]*["']?[ \t]*[:=][ \t]*)(?!(?:#{AUTH_SCHEME}[ \t]+)?\[FILTERED\])(#{AUTH_SCHEME}[ \t]+)?#{HEADER_VALUE}/
|
|
68
|
+
|
|
69
|
+
# Schema nudo (`Bearer <token>` senza il nome dell'header) seguito da qualcosa che ha la forma
|
|
70
|
+
# di una credenziale: qui la guardia di prosa serve, la parola "bearer" ricorre nei messaggi.
|
|
71
|
+
AUTH_CREDENTIAL = /(\b#{AUTH_SCHEME}[ \t]+)#{NOT_PROSE}#{CREDENTIAL_TOKEN}/
|
|
72
|
+
|
|
26
73
|
STRING_LITERAL = /'(?:[^']|'')*'/
|
|
27
74
|
NUMERIC_LITERAL = /\b\d+(?:\.\d+)?\b/
|
|
28
75
|
|
|
@@ -51,10 +98,16 @@ module CloseYourIt
|
|
|
51
98
|
sql.to_s.gsub(STRING_LITERAL, "?").gsub(NUMERIC_LITERAL, "?")
|
|
52
99
|
end
|
|
53
100
|
|
|
101
|
+
# Regola integrata (schemi di autenticazione) PRIMA dei pattern utente: le due protezioni si
|
|
102
|
+
# sommano, la prima non è disattivabile — privacy-by-default (CYRB-23).
|
|
54
103
|
def scrub_message(message)
|
|
55
104
|
return message if message.nil?
|
|
56
105
|
|
|
57
|
-
|
|
106
|
+
redacted = message.to_s
|
|
107
|
+
.gsub(AUTH_HEADER, "\\1\\2#{FILTERED}")
|
|
108
|
+
.gsub(AUTH_CREDENTIAL, "\\1#{FILTERED}")
|
|
109
|
+
|
|
110
|
+
@configuration.scrub_message_patterns.reduce(redacted) do |acc, pattern|
|
|
58
111
|
acc.gsub(pattern, FILTERED)
|
|
59
112
|
end
|
|
60
113
|
end
|
|
@@ -15,6 +15,11 @@ module CloseYourIt
|
|
|
15
15
|
# Ri-POSTiamo a Location preservando metodo + body, così l'evento non si perde in silenzio.
|
|
16
16
|
MAX_REDIRECTS = 2
|
|
17
17
|
|
|
18
|
+
# Durata massima di UNA send_event: apertura + lettura, ripetute a ogni redirect seguito. È il
|
|
19
|
+
# tetto che il drain di fine vita deve poter aspettare, altrimenti abbandona un invio ancora sano
|
|
20
|
+
# (CYRB-24).
|
|
21
|
+
MAX_REQUEST_SECONDS = (OPEN_TIMEOUT + READ_TIMEOUT) * (MAX_REDIRECTS + 1)
|
|
22
|
+
|
|
18
23
|
# Un timeout di rete (apertura o lettura) è un fallimento d'invio speciale: lo isoliamo dai non-2xx
|
|
19
24
|
# e dagli altri errori di rete perché segnala tipicamente problemi di connettività (CYRB-12).
|
|
20
25
|
TIMEOUT_ERRORS = [ Net::OpenTimeout, Net::ReadTimeout, Timeout::Error ].freeze
|
|
@@ -91,12 +96,28 @@ module CloseYourIt
|
|
|
91
96
|
target.relative? ? current + target : target
|
|
92
97
|
end
|
|
93
98
|
|
|
94
|
-
# Il Bearer va ri-inviato solo se il redirect resta
|
|
95
|
-
#
|
|
99
|
+
# Il Bearer va ri-inviato solo se il redirect resta sull'autorità dell'endpoint configurato:
|
|
100
|
+
# stesso host (o la sua variante www), stessa porta effettiva e senza downgrade https→http.
|
|
101
|
+
# La porta fa parte del confine di fiducia (CYRB-22): sullo stesso server una porta diversa è
|
|
102
|
+
# un servizio distinto, che non deve ricevere il segreto d'ingest.
|
|
96
103
|
def same_authority?(origin, target)
|
|
97
104
|
return false if origin.scheme == "https" && target.scheme != "https"
|
|
105
|
+
return false unless canonical_host(origin) == canonical_host(target)
|
|
106
|
+
|
|
107
|
+
origin.port == target.port || canonical_upgrade?(origin, target)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Un endpoint configurato in `http` che redirige a `https` resta fidato: la porta cambia solo
|
|
111
|
+
# perché cambia lo schema (80 → 443), non perché punti a un servizio su porta non standard.
|
|
112
|
+
def canonical_upgrade?(origin, target)
|
|
113
|
+
origin.scheme == "http" && target.scheme == "https" &&
|
|
114
|
+
origin.port == origin.default_port && target.port == target.default_port
|
|
115
|
+
end
|
|
98
116
|
|
|
99
|
-
|
|
117
|
+
# Una porta esplicita pari alla default (`:443` su https) equivale a ometterla: `URI#port` la
|
|
118
|
+
# normalizza già, qui resta solo da neutralizzare la variante www dell'host canonico.
|
|
119
|
+
def canonical_host(uri)
|
|
120
|
+
uri.host.to_s.sub(/\Awww\./, "")
|
|
100
121
|
end
|
|
101
122
|
|
|
102
123
|
# Estrae " R…: messaggio" dall'envelope d'errore del backend, così il dev vede il codice d'errore
|
data/lib/closeyourit/version.rb
CHANGED