body.Close() 는 TCP 연결을 재홣용하기 위해 현재 연결의 데이터를 모두 소비하도록 한다.
이때 예를들어, 대용량 파일을 다운로드 받는 Get 요청의 Body를 Close 하게되면, net/http 클라이언트는 그 응답 body를 닫기 위해 대용량으 파일 다운로드를 모두 기다렸다가 데이터를 소비한 뒤 연결을 닫을 것이다. 불필요해서 닫았겠지만 결국 모든 데이터를 소비해야한다는 뜻이다.
이를 위해 io.Copy라는 함수와 ioutil.Discard라는 Writer 객체를 쓸 수 있다.
_, _ = io.Copy(ioutil.Discard, resp.Body)
_ = resp.Body.Close()
io.Copy는 resp.Body의 내용을 모두 ioutil.Discard 라는 writer로 복사하면서 데이터를 소비시키는데, Discard라는 의미 그대로 그냥 버려버리기 때문에 안전하게 소비할 수 있다.
이렇게 된 이유는 다음과 같다. keep-alive로 하나의 TCP 연결에서 여러 요청을 처리하려면 클라이어트가 이전 응답으로부터 읽지 않은 바이트가 없어야 한다. 그 데이터를 사용하지 않더라도 명시적으로 응답 바디를 닫음으로써 모든 바이트를 읽은 것으로 처리할 수 있게 되기 때문이다.
바운더리는 여러 파트로 나뉘어진 폼 데이터를 구분하는데 쓰임
description=hello
, date=2022-12-31T22:01:45+09:00
라는 key, value 와 hello\n 란 내용을 담고있는 hello.txt
, goodbye 란 내용을 담고있는 goodbye.txt
두개의 파일을 요청하는 form data 의 request body를 보면 다음과 같다.
--2d8d9d844f250a95bb200b3ada6464cb6210ddc8e8bca61156151f0a3314
Content-Disposition: form-data; name="description"
hello
--2d8d9d844f250a95bb200b3ada6464cb6210ddc8e8bca61156151f0a3314
Content-Disposition: form-data; name="date"
2022-12-31T22:01:45+09:00
--2d8d9d844f250a95bb200b3ada6464cb6210ddc8e8bca61156151f0a3314
Content-Disposition: form-data; name="file1"; filename="hello.txt"
Content-Type: application/octet-stream
hello
--2d8d9d844f250a95bb200b3ada6464cb6210ddc8e8bca61156151f0a3314
Content-Disposition: form-data; name="file2"; filename="goodbye.txt"
Content-Type: application/octet-stream
goodbye
--2d8d9d844f250a95bb200b3ada6464cb6210ddc8e8bca61156151f0a3314--
서버에서는 2d8d9d844f250a95bb200b3ada6464cb6210ddc8e8bca61156151f0a3314
라는 바운더리 데이터로 각 파트를 나눠서 처리할 수 있게 된다.
그리고 이 boundary 값은 요청 헤더의 Content-Type
필드에 포함된다.