| 1700 | |
| 1701 | |
| 1702 | Future<Nothing> sendfile( |
| 1703 | network::Socket socket, |
| 1704 | Response response, |
| 1705 | Request* request) |
| 1706 | { |
| 1707 | CHECK(response.type == Response::PATH); |
| 1708 | |
| 1709 | // Make sure no body is sent (this is really an error and |
| 1710 | // should be reported and no response sent. |
| 1711 | response.body.clear(); |
| 1712 | |
| 1713 | Try<int_fd> fd = os::open(response.path, O_CLOEXEC | O_NONBLOCK | O_RDONLY); |
| 1714 | |
| 1715 | if (fd.isError()) { |
| 1716 | const string body = "Failed to open '" + response.path + "': " + fd.error(); |
| 1717 | // TODO(benh): VLOG(1)? |
| 1718 | // TODO(benh): Don't send error back as part of InternalServiceError? |
| 1719 | // TODO(benh): Copy headers from `response`? |
| 1720 | return send(socket, InternalServerError(body), request); |
| 1721 | } |
| 1722 | |
| 1723 | const Try<Bytes> size = os::stat::size(fd.get()); |
| 1724 | if (size.isError()) { |
| 1725 | const string body = |
| 1726 | "Failed to fstat '" + response.path + "': " + size.error(); |
| 1727 | // TODO(benh): VLOG(1)? |
| 1728 | // TODO(benh): Don't send error back as part of InternalServiceError? |
| 1729 | // TODO(benh): Copy headers from `response`? |
| 1730 | os::close(fd.get()); |
| 1731 | return send(socket, InternalServerError(body), request); |
| 1732 | } else if (os::stat::isdir(fd.get())) { |
| 1733 | const string body = "'" + response.path + "' is a directory"; |
| 1734 | // TODO(benh): VLOG(1)? |
| 1735 | // TODO(benh): Don't send error back as part of InternalServiceError? |
| 1736 | // TODO(benh): Copy headers from `response`? |
| 1737 | os::close(fd.get()); |
| 1738 | return send(socket, InternalServerError(body), request); |
| 1739 | } |
| 1740 | |
| 1741 | // While the user is expected to properly set a 'Content-Type' |
| 1742 | // header, we'll fill in (or overwrite) 'Content-Length' header. |
| 1743 | response.headers["Content-Length"] = stringify(size->bytes()); |
| 1744 | |
| 1745 | // TODO(benh): If this is a TCP socket consider turning on TCP_CORK |
| 1746 | // for both sends and then turning it off. |
| 1747 | Encoder* encoder = new HttpResponseEncoder(response, *request); |
| 1748 | |
| 1749 | return send(socket, encoder) |
| 1750 | .onAny([=](const Future<Nothing>& future) { |
| 1751 | delete encoder; |
| 1752 | |
| 1753 | // Close file descriptor if we aren't doing any more sending. |
| 1754 | if (future.isDiscarded() || future.isFailed()) { |
| 1755 | os::close(fd.get()); |
| 1756 | } |
| 1757 | }) |
| 1758 | .then([=]() mutable -> Future<Nothing> { |
| 1759 | // NOTE: the file descriptor gets closed by FileEncoder. |