OSingh commented on code in PR #1090: URL: https://github.com/apache/yunikorn-core/pull/1090#discussion_r3380158360
########## pkg/webservice/gzip.go: ########## @@ -0,0 +1,170 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package webservice + +import ( + "bytes" + "compress/gzip" + "net/http" + "strings" + + "go.uber.org/zap" + + "github.com/apache/yunikorn-core/pkg/log" +) + +// minCompressionSize is the minimum response body size in bytes required before gzip +// compression is applied. Responses smaller than this threshold are sent uncompressed +// because the gzip framing overhead would exceed any size savings. The value is set +// below the typical Ethernet MTU (1500 bytes) to account for TCP/IP and HTTP header +// overhead, ensuring a small response body fits within a single network packet. +var minCompressionSize = 1400 + +// deferredGzipResponseWriter buffers the first minCompressionSize bytes of the +// response body and defers the compression decision until either the buffer is full +// (switch to gzip streaming) or the handler returns (send raw if still under threshold). +// This avoids the memory cost of buffering the entire response while still skipping +// gzip for small payloads where the overhead outweighs the savings. +type deferredGzipResponseWriter struct { + http.ResponseWriter + buf bytes.Buffer + gz *gzip.Writer + statusCode int + decided bool + useGzip bool +} + +// WriteHeader captures the status code until the compression decision is made, then +// forwards it to the underlying ResponseWriter. +func (d *deferredGzipResponseWriter) WriteHeader(code int) { + if d.decided { + d.ResponseWriter.WriteHeader(code) + return + } + d.statusCode = code +} + +// Write buffers incoming bytes until the threshold is reached, at which point it +// commits to gzip streaming. After the decision is made, writes go directly to the +// chosen writer. +func (d *deferredGzipResponseWriter) Write(b []byte) (int, error) { + if d.decided { + if d.useGzip { + return d.gz.Write(b) + } + return d.ResponseWriter.Write(b) + } + + n, err := d.buf.Write(b) + if d.buf.Len() >= minCompressionSize { + d.switchToGzip() + } + return n, err +} + +// switchToGzip commits to gzip encoding: sets response headers, flushes the buffered +// bytes through the gzip writer, and marks the decision as final. +func (d *deferredGzipResponseWriter) switchToGzip() { + d.decided = true + d.useGzip = true + d.ResponseWriter.Header().Set("Content-Encoding", "gzip") + d.ResponseWriter.Header().Add("Vary", "Accept-Encoding") + if d.statusCode != 0 { + d.ResponseWriter.WriteHeader(d.statusCode) + } + _, _ = d.gz.Write(d.buf.Bytes()) Review Comment: Now I have logged the warning message for this. ########## pkg/webservice/gzip.go: ########## @@ -0,0 +1,170 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package webservice + +import ( + "bytes" + "compress/gzip" + "net/http" + "strings" + + "go.uber.org/zap" + + "github.com/apache/yunikorn-core/pkg/log" +) + +// minCompressionSize is the minimum response body size in bytes required before gzip +// compression is applied. Responses smaller than this threshold are sent uncompressed +// because the gzip framing overhead would exceed any size savings. The value is set +// below the typical Ethernet MTU (1500 bytes) to account for TCP/IP and HTTP header +// overhead, ensuring a small response body fits within a single network packet. +var minCompressionSize = 1400 + +// deferredGzipResponseWriter buffers the first minCompressionSize bytes of the +// response body and defers the compression decision until either the buffer is full +// (switch to gzip streaming) or the handler returns (send raw if still under threshold). +// This avoids the memory cost of buffering the entire response while still skipping +// gzip for small payloads where the overhead outweighs the savings. +type deferredGzipResponseWriter struct { + http.ResponseWriter + buf bytes.Buffer + gz *gzip.Writer + statusCode int + decided bool + useGzip bool +} + +// WriteHeader captures the status code until the compression decision is made, then +// forwards it to the underlying ResponseWriter. +func (d *deferredGzipResponseWriter) WriteHeader(code int) { + if d.decided { + d.ResponseWriter.WriteHeader(code) + return + } + d.statusCode = code +} + +// Write buffers incoming bytes until the threshold is reached, at which point it +// commits to gzip streaming. After the decision is made, writes go directly to the +// chosen writer. +func (d *deferredGzipResponseWriter) Write(b []byte) (int, error) { + if d.decided { + if d.useGzip { + return d.gz.Write(b) + } + return d.ResponseWriter.Write(b) + } + + n, err := d.buf.Write(b) + if d.buf.Len() >= minCompressionSize { + d.switchToGzip() + } + return n, err +} + +// switchToGzip commits to gzip encoding: sets response headers, flushes the buffered +// bytes through the gzip writer, and marks the decision as final. +func (d *deferredGzipResponseWriter) switchToGzip() { + d.decided = true + d.useGzip = true + d.ResponseWriter.Header().Set("Content-Encoding", "gzip") + d.ResponseWriter.Header().Add("Vary", "Accept-Encoding") + if d.statusCode != 0 { + d.ResponseWriter.WriteHeader(d.statusCode) + } + _, _ = d.gz.Write(d.buf.Bytes()) + d.buf.Reset() +} + +// finalize is called via defer after the handler returns. If no compression decision +// was made yet (response stayed below threshold), the buffered bytes are written raw. +// The gzip writer is closed if compression was used. +func (d *deferredGzipResponseWriter) finalize() { + if !d.decided { + d.decided = true + if d.statusCode != 0 { + d.ResponseWriter.WriteHeader(d.statusCode) + } + _, _ = d.ResponseWriter.Write(d.buf.Bytes()) Review Comment: Now I have logged the warning message for this. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
