| 1587 | |
| 1588 | |
| 1589 | Future<Nothing> sendfile( |
| 1590 | network::Socket socket, |
| 1591 | Response response, |
| 1592 | Request* request) |
| 1593 | { |
| 1594 | CHECK(response.type == Response::PATH); |
| 1595 | |
| 1596 | // Make sure no body is sent (this is really an error and |
| 1597 | // should be reported and no response sent. |
| 1598 | response.body.clear(); |
| 1599 | |
| 1600 | Try<int_fd> fd = os::open(response.path, O_CLOEXEC | O_NONBLOCK | O_RDONLY); |
| 1601 | |
| 1602 | if (fd.isError()) { |
| 1603 | const string body = "Failed to open '" + response.path + "': " + fd.error(); |
| 1604 | // TODO(benh): VLOG(1)? |
| 1605 | // TODO(benh): Don't send error back as part of InternalServiceError? |
| 1606 | // TODO(benh): Copy headers from `response`? |
| 1607 | return send(socket, InternalServerError(body), request); |
| 1608 | } |
| 1609 | |
| 1610 | const Try<Bytes> size = os::stat::size(fd.get()); |
| 1611 | if (size.isError()) { |
| 1612 | const string body = |
| 1613 | "Failed to fstat '" + response.path + "': " + size.error(); |
| 1614 | // TODO(benh): VLOG(1)? |
| 1615 | // TODO(benh): Don't send error back as part of InternalServiceError? |
| 1616 | // TODO(benh): Copy headers from `response`? |
| 1617 | os::close(fd.get()); |
| 1618 | return send(socket, InternalServerError(body), request); |
| 1619 | } else if (os::stat::isdir(fd.get())) { |
| 1620 | const string body = "'" + response.path + "' is a directory"; |
| 1621 | // TODO(benh): VLOG(1)? |
| 1622 | // TODO(benh): Don't send error back as part of InternalServiceError? |
| 1623 | // TODO(benh): Copy headers from `response`? |
| 1624 | os::close(fd.get()); |
| 1625 | return send(socket, InternalServerError(body), request); |
| 1626 | } |
| 1627 | |
| 1628 | // While the user is expected to properly set a 'Content-Type' |
| 1629 | // header, we'll fill in (or overwrite) 'Content-Length' header. |
| 1630 | response.headers["Content-Length"] = stringify(size->bytes()); |
| 1631 | |
| 1632 | // TODO(benh): If this is a TCP socket consider turning on TCP_CORK |
| 1633 | // for both sends and then turning it off. |
| 1634 | Encoder* encoder = new HttpResponseEncoder(response, *request); |
| 1635 | |
| 1636 | return send(socket, encoder) |
| 1637 | .onAny([=](const Future<Nothing>& future) { |
| 1638 | delete encoder; |
| 1639 | |
| 1640 | // Close file descriptor if we aren't doing any more sending. |
| 1641 | if (future.isDiscarded() || future.isFailed()) { |
| 1642 | os::close(fd.get()); |
| 1643 | } |
| 1644 | }) |
| 1645 | .then([=]() mutable -> Future<Nothing> { |
| 1646 | // NOTE: the file descriptor gets closed by FileEncoder. |
no test coverage detected