При повторной итерации read, по идее, должен вернуть 0, так как все прочитано
Неа, не так это работает. 0 из read возвращается в одном единственном случае: если та сторона закрыла сокет на передачу и все посланные ею данные получены. В противном случае (сокет не закрыт) поведение зависит от настроек сокета: синхронный сокет при попытке чтения может заблокироваться в функции 
read или вернуть из неё -1 (и установить 
errno, например, в 
EINTR). Асинхронный сокет вернёт из 
read -1 и установит 
errno в 
EAGAIN или 
EWOULDBLOCK.
Ваш HTTP-сервер наверняка оставляет соединение открытым после того как прислал ответ на первый запрос, это можно понять по наличию заголовка 
Connection: Keep-Alive или отсутствию заголовка 
Connection: close в его ответе если это 
HTTP/1.1. Его можно попросить закрыть соединение после ответа, послав запрос с заголовком 
Connection: close или можно избежать блокировки в 
read прочитав только данные ответа размер которых прислан в заголовке ответа 
Content-Length.