fix(stream): ensure transfer-encoding: chunked does not mess output@@ -0,0 +1,14 @@
+stages:
+ - name: build
+ image: alpine:3.18
+ workdir: .:/opt/app/
+ commands:
+ - git clone https://gitfoss.dev/ethicdevs/crystal-docker-api.git
+ - cd crystal-docker-api/
+ - shards install
+ - shards build
+ cached:
+ - ./lib
+ - ./shard.lock
+ # artifacts:
+ # - ./bin/crystal-docker-api
@@ -100,28 +100,8 @@ class Docker::Api::ApiClient
response
end
- # cleanup response body send over docker socket
- raw = res_str
- .gsub("10f1\r\n", "") # ?
- .gsub("10f", "") # ?
- .gsub("11\r\n", "") # ?
- .gsub("11c4\r\n", "") # ?
- .gsub("0\r\n", "") # ?
- .gsub("1321\r\n", "") # ?
- .gsub("1322\r\n", "") # ?
- .gsub("13\r\n", "") # ?
- .gsub("14\r\n", "") # ?
- .gsub("1d\r\n", "") # ?
- .gsub("4f\r\n", "") # ?
- .gsub("3a\r\n", "") # ?
- .gsub("3e\r\n", "") # ?
- .gsub("5e\r\n", "") # ?
-
- # pp path
- # pp raw
- # puts "\n"
-
# Parse status line, headers and body
+ raw = res_str
head, body = raw.split("\r\n\r\n", 2)
body = body.chomp("\r\n").chomp("\r\n")
status_line, *header_lines = head.split("\r\n")
@@ -140,6 +120,49 @@ class Docker::Api::ApiClient
headers["Content-Length"] = "0"
end
+ # Handle chunked response (Transfer-Encoding: chunked)
+ # (i.e. docker logs stream response)
+ if headers["Transfer-Encoding"]? && headers["Transfer-Encoding"] == "chunked"
+ json_payloads = 0
+ sb = [] of String
+ sb_chunk = [] of String
+ body.lines.each do |line|
+ begin
+ if line == "\0" || line == "0\r\n" || line == "0\n" # NUL-terminator
+ next
+ elsif (line.starts_with?('{') || line.starts_with?('[')) == false # size headers (json parse them...)
+ if headers["Content-Type"] == "application/vnd.docker.raw-stream"
+ sb_chunk << line
+ end
+ next
+ end
+ res = JSON.parse(line)
+ json_payloads += 1
+ sb << line
+ rescue
+ # this prevent header size to slip in result body
+ next
+ end
+ end
+
+ body = sb.join("\n")
+
+ if json_payloads > 1 && headers["Content-Type"] == "application/json"
+ begin
+ body = JSON.parse("["+sb.join(",\n")+"]").to_json # so json streaming works
+ rescue
+ body = sb.join("\n")
+ end
+ elsif headers["Content-Type"] == "application/vnd.docker.raw-stream"
+ body = sb_chunk[1..-2].join("\n").chomp("\n")
+ end
+ end
+
+ # puts "\n"
+ # pp path
+ # # pp headers
+ # pp body
+
HTTP::Client::Response.new(
status_code,
status_code != 204 ? body : nil,