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

grpc / grpc-java / #19449

05 Sep 2024 10:32PM CUT coverage: 84.488% (-0.01%) from 84.498%
#19449

push

github

ejona86
core: touch() buffer when detach()ing

Detachable lets a buffer outlive its original lifetime. The new lifetime
is application-controlled. If the application fails to read/close the
stream, then the leak detector wouldn't make clear what code was
responsible for the buffer's lifetime. With this touch, we'll be able to
see detach() was called and thus know the application needs debugging.

Realized when looking at b/364531464, although I think the issue is
unrelated.

33251 of 39356 relevant lines covered (84.49%)

0.84 hits per line

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

83.16
/../okhttp/src/main/java/io/grpc/okhttp/ExceptionHandlingFrameWriter.java
1
/*
2
 * Copyright 2018 The gRPC Authors
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16

17
package io.grpc.okhttp;
18

19
import static com.google.common.base.Preconditions.checkNotNull;
20

21
import com.google.common.annotations.VisibleForTesting;
22
import com.google.common.base.Preconditions;
23
import io.grpc.okhttp.internal.framed.ErrorCode;
24
import io.grpc.okhttp.internal.framed.FrameWriter;
25
import io.grpc.okhttp.internal.framed.Header;
26
import io.grpc.okhttp.internal.framed.Settings;
27
import java.io.IOException;
28
import java.util.List;
29
import java.util.logging.Level;
30
import java.util.logging.Logger;
31
import okio.Buffer;
32
import okio.ByteString;
33

34
/**
35
 * FrameWriter that propagates IOExceptions via callback instead of throwing. This allows
36
 * centralized handling of errors. Exceptions only impact the single call that throws them; callers
37
 * should be sure to kill the connection after an exception (potentially after sending a GOAWAY) as
38
 * otherwise additional frames after the failed/omitted one could cause HTTP/2 confusion.
39
 */
40
final class ExceptionHandlingFrameWriter implements FrameWriter {
41

42
  private static final Logger log = Logger.getLogger(OkHttpClientTransport.class.getName());
1✔
43

44
  private final TransportExceptionHandler transportExceptionHandler;
45

46
  private final FrameWriter frameWriter;
47

48
  private final OkHttpFrameLogger frameLogger =
1✔
49
      new OkHttpFrameLogger(Level.FINE, OkHttpClientTransport.class);
50

51
  ExceptionHandlingFrameWriter(
52
      TransportExceptionHandler transportExceptionHandler, FrameWriter frameWriter) {
1✔
53
    this.transportExceptionHandler =
1✔
54
        checkNotNull(transportExceptionHandler, "transportExceptionHandler");
1✔
55
    this.frameWriter = Preconditions.checkNotNull(frameWriter, "frameWriter");
1✔
56
  }
1✔
57

58
  @Override
59
  public void connectionPreface() {
60
    try {
61
      frameWriter.connectionPreface();
1✔
62
    } catch (IOException e) {
×
63
      transportExceptionHandler.onException(e);
×
64
    }
1✔
65
  }
1✔
66

67
  @Override
68
  public void ackSettings(Settings peerSettings) {
69
    frameLogger.logSettingsAck(OkHttpFrameLogger.Direction.OUTBOUND);
1✔
70
    try {
71
      frameWriter.ackSettings(peerSettings);
1✔
72
    } catch (IOException e) {
×
73
      transportExceptionHandler.onException(e);
×
74
    }
1✔
75
  }
1✔
76

77
  @Override
78
  public void pushPromise(int streamId, int promisedStreamId, List<Header> requestHeaders) {
79
    frameLogger.logPushPromise(OkHttpFrameLogger.Direction.OUTBOUND,
1✔
80
        streamId, promisedStreamId, requestHeaders);
81
    try {
82
      frameWriter.pushPromise(streamId, promisedStreamId, requestHeaders);
1✔
83
    } catch (IOException e) {
×
84
      transportExceptionHandler.onException(e);
×
85
    }
1✔
86
  }
1✔
87

88
  @Override
89
  public void flush() {
90
    try {
91
      frameWriter.flush();
1✔
92
    } catch (IOException e) {
1✔
93
      transportExceptionHandler.onException(e);
1✔
94
    }
1✔
95
  }
1✔
96

97
  @Override
98
  public void synStream(
99
      boolean outFinished,
100
      boolean inFinished,
101
      int streamId,
102
      int associatedStreamId,
103
      List<Header> headerBlock) {
104
    try {
105
      frameWriter.synStream(outFinished, inFinished, streamId, associatedStreamId, headerBlock);
1✔
106
    } catch (IOException e) {
×
107
      transportExceptionHandler.onException(e);
×
108
    }
1✔
109
  }
1✔
110

111
  @Override
112
  public void synReply(boolean outFinished, int streamId,
113
      List<Header> headerBlock) {
114
    try {
115
      frameWriter.synReply(outFinished, streamId, headerBlock);
1✔
116
    } catch (IOException e) {
1✔
117
      transportExceptionHandler.onException(e);
1✔
118
    }
1✔
119
  }
1✔
120

121
  @Override
122
  public void headers(int streamId, List<Header> headerBlock) {
123
    frameLogger.logHeaders(OkHttpFrameLogger.Direction.OUTBOUND, streamId, headerBlock, false);
1✔
124
    try {
125
      frameWriter.headers(streamId, headerBlock);
1✔
126
    } catch (IOException e) {
×
127
      transportExceptionHandler.onException(e);
×
128
    }
1✔
129
  }
1✔
130

131
  @Override
132
  public void rstStream(int streamId, ErrorCode errorCode) {
133
    frameLogger.logRstStream(OkHttpFrameLogger.Direction.OUTBOUND, streamId, errorCode);
1✔
134
    try {
135
      frameWriter.rstStream(streamId, errorCode);
1✔
136
    } catch (IOException e) {
1✔
137
      transportExceptionHandler.onException(e);
1✔
138
    }
1✔
139
  }
1✔
140

141
  @Override
142
  public int maxDataLength() {
143
    return frameWriter.maxDataLength();
1✔
144
  }
145

146
  @Override
147
  public void data(boolean outFinished, int streamId, Buffer source, int byteCount) {
148
    frameLogger.logData(OkHttpFrameLogger.Direction.OUTBOUND,
1✔
149
        streamId, source.buffer(), byteCount, outFinished);
1✔
150
    try {
151
      frameWriter.data(outFinished, streamId, source, byteCount);
1✔
152
    } catch (IOException e) {
×
153
      transportExceptionHandler.onException(e);
×
154
    }
1✔
155
  }
1✔
156

157
  @Override
158
  public void settings(Settings okHttpSettings) {
159
    frameLogger.logSettings(OkHttpFrameLogger.Direction.OUTBOUND, okHttpSettings);
1✔
160
    try {
161
      frameWriter.settings(okHttpSettings);
1✔
162
    } catch (IOException e) {
×
163
      transportExceptionHandler.onException(e);
×
164
    }
1✔
165
  }
1✔
166

167
  @Override
168
  public void ping(boolean ack, int payload1, int payload2) {
169
    if (ack) {
1✔
170
      frameLogger.logPingAck(OkHttpFrameLogger.Direction.OUTBOUND,
1✔
171
          ((long) payload1 << 32) | (payload2 & 0xFFFFFFFFL));
172
    } else {
173
      frameLogger.logPing(OkHttpFrameLogger.Direction.OUTBOUND,
1✔
174
          ((long) payload1 << 32) | (payload2 & 0xFFFFFFFFL));
175
    }
176
    try {
177
      frameWriter.ping(ack, payload1, payload2);
1✔
178
    } catch (IOException e) {
1✔
179
      transportExceptionHandler.onException(e);
1✔
180
    }
1✔
181
  }
1✔
182

183
  @Override
184
  public void goAway(int lastGoodStreamId, ErrorCode errorCode,
185
      byte[] debugData) {
186
    frameLogger.logGoAway(OkHttpFrameLogger.Direction.OUTBOUND,
1✔
187
        lastGoodStreamId, errorCode, ByteString.of(debugData));
1✔
188
    try {
189
      frameWriter.goAway(lastGoodStreamId, errorCode, debugData);
1✔
190
      // Flush it since after goAway, we are likely to close this writer.
191
      frameWriter.flush();
1✔
192
    } catch (IOException e) {
1✔
193
      transportExceptionHandler.onException(e);
1✔
194
    }
1✔
195
  }
1✔
196

197
  @Override
198
  public void windowUpdate(int streamId, long windowSizeIncrement) {
199
    frameLogger.logWindowsUpdate(OkHttpFrameLogger.Direction.OUTBOUND,
1✔
200
        streamId, windowSizeIncrement);
201
    try {
202
      frameWriter.windowUpdate(streamId, windowSizeIncrement);
1✔
203
    } catch (IOException e) {
1✔
204
      transportExceptionHandler.onException(e);
1✔
205
    }
1✔
206
  }
1✔
207

208
  @Override
209
  public void close() {
210
    try {
211
      frameWriter.close();
1✔
212
    } catch (IOException e) {
×
213
      log.log(getLogLevel(e), "Failed closing connection", e);
×
214
    }
1✔
215
  }
1✔
216

217
  /**
218
   * Accepts a throwable and returns the appropriate logging level. Uninteresting exceptions
219
   * should not clutter the log.
220
   */
221
  @VisibleForTesting
222
  static Level getLogLevel(Throwable t) {
223
    if (t.getClass().equals(IOException.class)) {
1✔
224
      return Level.FINE;
1✔
225
    }
226
    return Level.INFO;
1✔
227
  }
228

229
  /** A class that handles transport exception. */
230
  interface TransportExceptionHandler {
231
    /** Handles exception. */
232
    void onException(Throwable throwable);
233
  }
234
}
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

© 2025 Coveralls, Inc