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

mizosoft / methanol / #590

09 Sep 2025 03:38PM UTC coverage: 88.988% (-0.07%) from 89.053%
#590

Pull #133

github

mizosoft
Try increasing timeout
Pull Request #133: Bound WritableBodyPublisher memory usage

2345 of 2822 branches covered (83.1%)

Branch coverage included in aggregate %.

144 of 160 new or added lines in 12 files covered. (90.0%)

7 existing lines in 3 files now uncovered.

7700 of 8466 relevant lines covered (90.95%)

0.91 hits per line

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

89.09
/methanol/src/main/java/com/github/mizosoft/methanol/internal/cache/HttpDates.java
1
/*
2
 * Copyright (c) 2025 Moataz Hussein
3
 *
4
 * Permission is hereby granted, free of charge, to any person obtaining a copy
5
 * of this software and associated documentation files (the "Software"), to deal
6
 * in the Software without restriction, including without limitation the rights
7
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
 * copies of the Software, and to permit persons to whom the Software is
9
 * furnished to do so, subject to the following conditions:
10
 *
11
 * The above copyright notice and this permission notice shall be included in all
12
 * copies or substantial portions of the Software.
13
 *
14
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
 * SOFTWARE.
21
 */
22

23
package com.github.mizosoft.methanol.internal.cache;
24

25
import static com.github.mizosoft.methanol.internal.Validate.requireArgument;
26

27
import java.lang.System.Logger;
28
import java.lang.System.Logger.Level;
29
import java.time.DateTimeException;
30
import java.time.Duration;
31
import java.time.Instant;
32
import java.time.LocalDateTime;
33
import java.time.ZoneOffset;
34
import java.time.format.DateTimeFormatter;
35
import java.time.temporal.TemporalAccessor;
36
import java.time.temporal.TemporalQueries;
37
import java.util.List;
38
import java.util.Locale;
39
import java.util.Optional;
40

41
/** Helpers for parsing/formatting HTTP dates. */
42
public class HttpDates {
43
  private static final Logger logger = System.getLogger(HttpDates.class.getName());
1✔
44

45
  /** A formatter for the preferred format specified by rfc7231 Section 7.1.1.1. */
46
  private static final DateTimeFormatter PREFERRED_FORMATTER;
47

48
  /** A list of formatters to try in sequence till one succeeds. */
49
  private static final List<DateTimeFormatter> FORMATTERS;
50

51
  static {
52
    // Note that we need to create formatters with the US locale explicitly as they'll use the
53
    // default locale (e.g. system language) otherwise, which is not what we want (see
54
    // https://github.com/mizosoft/methanol/issues/134).
55

56
    PREFERRED_FORMATTER =
1✔
57
        DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss 'GMT'").withLocale(Locale.US);
1✔
58

59
    // These are the formats specified by rfc7231 Section 7.1.1.1 for acceptable HTTP dates.
60
    FORMATTERS =
1✔
61
        List.of(
1✔
62
            // The preferred format is tried first.
63
            PREFERRED_FORMATTER,
64

65
            // Obsolete formats, but allowed by rfc7231.
66
            DateTimeFormatter.ofPattern("EEEE, dd-MMM-uu HH:mm:ss 'GMT'")
1✔
67
                .withLocale(Locale.US), // rfc850
1✔
68
            DateTimeFormatter.ofPattern("EEE MMM ppd HH:mm:ss uuuu")
1✔
69
                .withLocale(Locale.US), // C's asctime()
1✔
70

71
            // A lenient version of the preferred format, with the possibility of zone offsets.
72
            DateTimeFormatter.RFC_1123_DATE_TIME.withLocale(Locale.US),
1✔
73

74
            // The preferred format but with UTC instead of GMT.
75
            DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss 'UTC'").withLocale(Locale.US));
1✔
76
  }
1✔
77

78
  private HttpDates() {}
79

80
  public static String formatHttpDate(LocalDateTime dateTime) {
81
    return PREFERRED_FORMATTER.format(dateTime);
1✔
82
  }
83

84
  public static boolean isHttpDate(String value) {
UNCOV
85
    return tryParseHttpDate0(value, false).isPresent();
×
86
  }
87

88
  static Optional<LocalDateTime> tryParseHttpDate(String value) {
89
    return tryParseHttpDate0(value, true);
1✔
90
  }
91

92
  private static Optional<LocalDateTime> tryParseHttpDate0(String value, boolean logFailure) {
93
    TemporalAccessor parsedTemporal = null;
1✔
94
    for (var formatter : FORMATTERS) {
1✔
95
      try {
96
        parsedTemporal = formatter.parse(value);
1✔
97
        break;
1✔
98
      } catch (DateTimeException ignored) {
1✔
99
        // Try next formatter.
100
      }
101
    }
1✔
102

103
    DateTimeException malformedHttpDate = null;
1✔
104
    if (parsedTemporal != null) {
1✔
105
      try {
106
        var dateTime = LocalDateTime.from(parsedTemporal);
1✔
107
        var offset = parsedTemporal.query(TemporalQueries.offset());
1✔
108
        return Optional.of(
1✔
109
            (offset == null || offset.equals(ZoneOffset.UTC))
1✔
110
                ? dateTime
1✔
111
                : toUtcDateTime(dateTime.toInstant(offset)));
1✔
UNCOV
112
      } catch (DateTimeException e) {
×
UNCOV
113
        malformedHttpDate = e;
×
114
      }
115
    }
116

117
    if (logFailure) {
1!
118
      logger.log(
1✔
119
          Level.WARNING, () -> "Malformed or unrecognized HTTP date: " + value, malformedHttpDate);
1✔
120
    }
121
    return Optional.empty();
1✔
122
  }
123

124
  public static Duration parseDeltaSeconds(String value) {
125
    long secondsLong = Long.parseLong(value);
1✔
126
    requireArgument(secondsLong >= 0, "Delta seconds can't be negative");
1✔
127

128
    // Truncate to Integer.MAX_VALUE to avoid overflows on further calculations.
129
    int secondsInt = (int) Math.min(secondsLong, Integer.MAX_VALUE);
1✔
130
    return Duration.ofSeconds(secondsInt);
1✔
131
  }
132

133
  static Optional<Duration> tryParseDeltaSeconds(String value) {
134
    try {
135
      return Optional.of(parseDeltaSeconds(value));
1✔
UNCOV
136
    } catch (NumberFormatException ignored) {
×
UNCOV
137
      return Optional.empty();
×
138
    }
139
  }
140

141
  static LocalDateTime toUtcDateTime(Instant instant) {
142
    return LocalDateTime.ofInstant(instant, ZoneOffset.UTC);
1✔
143
  }
144
}
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