• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

haraka / Haraka / 20669108801

02 Jan 2026 11:52PM UTC coverage: 62.769%. First build
20669108801

Pull #3513

github

web-flow
Merge d5edf4cf9 into 4cd76b8ca
Pull Request #3513: Release v3.1.2

927 of 1409 branches covered (65.79%)

1 of 11 new or added lines in 2 files covered. (9.09%)

5400 of 8603 relevant lines covered (62.77%)

27.48 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.54
/outbound/config.js
1
'use strict'
1✔
2

1✔
3
const config = require('haraka-config')
1✔
4
const logger = require('../logger')
1✔
5

1✔
6
exports.name = 'outbound/config'
1✔
7

1✔
8
function load_config() {
1✔
9
    const cfg = (exports.cfg = config.get(
1✔
10
        'outbound.ini',
1✔
11
        {
1✔
12
            booleans: ['-disabled', '-always_split', '+enable_tls', '-local_mx_ok'],
1✔
13
        },
1✔
14
        () => {
1✔
15
            load_config()
×
16
        },
1✔
17
    ).main)
1✔
18

1✔
19
    if (!cfg.inet_prefer) cfg.inet_prefer = 'default'
1✔
20
    if (!cfg.inet_prefer.match(/^(v4|v6|default)$/)) {
1!
NEW
21
        logger.warn(exports, `inet_prefer is set to an invalid value: ${cfg.inet_prefer}`)
×
NEW
22
        cfg.inet_prefer = 'default'
×
23
    }
×
24

1✔
25
    // legacy config file support. Remove in Haraka 4.0
1✔
26
    if (!cfg.disabled && config.get('outbound.disabled')) {
1!
27
        cfg.disabled = true
×
28
    }
×
29
    if (!cfg.enable_tls && config.get('outbound.enable_tls')) {
1!
30
        cfg.enable_tls = true
×
31
    }
×
32
    if (!cfg.temp_fail_intervals) {
1✔
33
        cfg.temp_fail_intervals = config.get('outbound.temp_fail_intervals')
1✔
34
    }
1✔
35
    if (!cfg.maxTempFailures) {
1✔
36
        cfg.maxTempFailures = config.get('outbound.maxTempFailures') || 13
1✔
37
    }
1✔
38
    if (!cfg.concurrency_max) {
1✔
39
        cfg.concurrency_max = config.get('outbound.concurrency_max') || 10000
1✔
40
    }
1✔
41
    if (!cfg.connect_timeout) {
1✔
42
        cfg.connect_timeout = 30
1✔
43
    }
1✔
44
    if (!cfg.received_header) {
1✔
45
        cfg.received_header = config.get('outbound.received_header') || 'Haraka outbound'
1✔
46
    }
1✔
47

1✔
48
    exports.set_temp_fail_intervals()
1✔
49
}
1✔
50

1✔
51
exports.set_temp_fail_intervals = function () {
1✔
52
    // Set the outbound temp fail intervals (retry times) using the following rules:
5✔
53
    //   1) temp_fail_intervals takes precedence over maxTempFailures if both are specified
5✔
54
    //   2) if temp_fail_intervals is not specified or is illegally specified, then initialize
5✔
55
    //      it with the equivalent times of maxTempFailures using the original 2^N formula
5✔
56
    //   3) the word "none" can be specified if you do not want to retry a temp failure,
5✔
57
    //      equivalent behavior of specifying maxTempFailures=1
5✔
58
    const { cfg } = this
5✔
59

5✔
60
    // Fallback function to create an array of the original retry times
5✔
61
    function set_old_defaults() {
5✔
62
        cfg.temp_fail_intervals = []
2✔
63
        for (let i = 1; i < cfg.maxTempFailures; i++) {
2✔
64
            cfg.temp_fail_intervals.push(2 ** (i + 5))
24✔
65
        }
24✔
66
    }
2✔
67

5✔
68
    // Helpful error function in case of parsing failure
5✔
69
    function error(i, msg) {
5✔
70
        logger.error(exports, `temp_fail_intervals syntax error parsing element ${i}: ${msg}`)
1✔
71
        logger.warn(exports, 'Setting outbound temp_fail_intervals to old defaults')
1✔
72
        set_old_defaults()
1✔
73
    }
1✔
74

5✔
75
    // If the new value isn't specified, then create the old defaults
5✔
76
    if (!cfg.temp_fail_intervals) {
5✔
77
        return set_old_defaults()
1✔
78
    }
1✔
79

4✔
80
    // If here then turn the text input into an expanded array of intervals (in seconds)
4✔
81
    // i.e, turn "1m,5m*2,1h*3" into [60,300,300,3600,3600,3600]
4✔
82
    // Parse manually to do better syntax checking and provide better failure messages
4✔
83
    const times = []
4✔
84
    let input = cfg.temp_fail_intervals.replace(/\s+/g, '').toLowerCase()
4✔
85
    if (input.length === 0) return error(0, 'nothing specified')
5!
86
    if (input === 'none') {
5✔
87
        cfg.temp_fail_intervals = []
1✔
88
        return
1✔
89
    }
1✔
90
    input = input.split(',')
3✔
91

3✔
92
    for (let i = 0; i < input.length; i++) {
5✔
93
        const delay = input[i].split('*')
12✔
94
        if (delay.length === 1) delay.push(1)
12✔
95
        else if (delay.length === 2) delay[1] = Number(delay[1])
5✔
96
        else return error(i, 'too many *')
×
97
        if (!Number.isInteger(delay[1])) return error(i, 'multiplier is not an integer')
12!
98

12✔
99
        if (delay[0].length < 2) error(i, 'invalid time span')
12!
100
        const symbol = delay[0].charAt(delay[0].length - 1)
12✔
101
        let num = Number(delay[0].slice(0, -1))
12✔
102
        if (isNaN(num)) return error(i, 'invalid number or symbol')
12✔
103

11✔
104
        switch (symbol) {
11✔
105
            case 's':
12✔
106
                // do nothing, this is the base unit
2✔
107
                break
2✔
108
            case 'm':
12✔
109
                num *= 60
6✔
110
                break
6✔
111
            case 'h':
12✔
112
                num *= 3600
2✔
113
                break
2✔
114
            case 'd':
12✔
115
                num *= 86400
1✔
116
                break
1✔
117
            default:
12!
118
                return error(i, 'invalid time span symbol')
×
119
        }
12✔
120
        // Sanity check (what should this number be?)
11✔
121
        if (num < 5) return error(i, 'delay time too small, should be >=5 seconds')
12!
122
        for (let j = 0; j < delay[1]; j++) {
12✔
123
            times.push(num)
19✔
124
        }
19✔
125
    }
11✔
126

2✔
127
    // One last check, just in case...should never be true
2✔
128
    if (times.length === 0) return error(0, 'unexpected parsing result')
5!
129

2✔
130
    // If here, success, so actually store the calculated array in the config
2✔
131
    cfg.temp_fail_intervals = times
2✔
132
}
5✔
133

1✔
134
load_config()
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc