split protocol handler out into pure fn