static void sendFileAsResponse(TSession *const sessionP, TFile *const fileP, const char *const fileName, time_t const fileModTime, MIMEType *const mimeTypeP) { uint64_t const filesize = FileSize(fileP); const char *const mediatype = MIMETypeGuessFromFile2(mimeTypeP, fileName); uint64_t start; /* Defined only if session has one range */ uint64_t end; /* Defined only if session has one range */ switch (sessionP->ranges.size) { case 0: ResponseStatus(sessionP, 200); break; case 1: { bool decoded; decoded = RangeDecode((char *) (sessionP->ranges.item[0]), filesize, &start, &end); if (!decoded) { ListFree(&sessionP->ranges); ResponseStatus(sessionP, 200); } else { const char *contentRange; xmlrpc_asprintf(&contentRange, "bytes %" PRIu64 "-%" PRIu64 "/%" PRIu64, start, end, filesize); ResponseAddField(sessionP, "Content-range", contentRange); xmlrpc_strfree(contentRange); ResponseContentLength(sessionP, end - start + 1); ResponseStatus(sessionP, 206); } } break; default: ResponseContentType(sessionP, "multipart/ranges; boundary=" BOUNDARY); ResponseStatus(sessionP, 206); break; } if (sessionP->ranges.size == 0) { ResponseContentLength(sessionP, filesize); ResponseContentType(sessionP, mediatype); } addLastModifiedHeader(sessionP, fileModTime); ResponseWriteStart(sessionP); if (sessionP->requestInfo.method != m_head) sendBody(sessionP, fileP, filesize, mediatype, start, end); }
static void sendBody(TSession *const sessionP, const TFile *const fileP, uint64_t const filesize, const char *const mediatype, uint64_t const start0, uint64_t const end0) { /*---------------------------------------------------------------------------- 'start0' and 'end0' are meaningful only if the session has ranges. -----------------------------------------------------------------------------*/ char buffer[4096]; if (sessionP->ranges.size == 0) ConnWriteFromFile(sessionP->connP, fileP, 0, filesize - 1, buffer, sizeof(buffer), 0); else if (sessionP->ranges.size == 1) ConnWriteFromFile(sessionP->connP, fileP, start0, end0, buffer, sizeof(buffer), 0); else { uint64_t i; for (i = 0; i <= sessionP->ranges.size; ++i) { ConnWrite(sessionP->connP, "--", 2); ConnWrite(sessionP->connP, BOUNDARY, strlen(BOUNDARY)); ConnWrite(sessionP->connP, CRLF, 2); if (i < sessionP->ranges.size) { uint64_t start; uint64_t end; bool decoded; decoded = RangeDecode((char *) (sessionP->ranges.item[i]), filesize, &start, &end); if (decoded) { /* Entity header, not response header */ const char *entityHeader; composeEntityHeader(&entityHeader, mediatype, start, end, filesize); ConnWrite(sessionP->connP, entityHeader, strlen(entityHeader)); xmlrpc_strfree(entityHeader); ConnWriteFromFile(sessionP->connP, fileP, start, end, buffer, sizeof(buffer), 0); } } } } }
static void sendBody(TSession * const sessionP, TFile * const fileP, uint64_t const filesize, const char * const mediatype, uint64_t const start0, uint64_t const end0, char * const z) { if (sessionP->ranges.size == 0) ConnWriteFromFile(sessionP->conn, fileP, 0, filesize - 1, z, 4096, 0); else if (sessionP->ranges.size == 1) ConnWriteFromFile(sessionP->conn, fileP, start0, end0, z, 4096, 0); else { uint64_t i; for (i = 0; i <= sessionP->ranges.size; ++i) { ConnWrite(sessionP->conn,"--", 2); ConnWrite(sessionP->conn, BOUNDARY, strlen(BOUNDARY)); ConnWrite(sessionP->conn, CRLF, 2); if (i < sessionP->ranges.size) { uint64_t start; uint64_t end; abyss_bool decoded; decoded = RangeDecode((char *)(sessionP->ranges.item[i]), filesize, &start, &end); if (decoded) { /* Entity header, not response header */ sprintf(z, "Content-type: %s" CRLF "Content-range: bytes %llu-%llu/%llu" CRLF "Content-length: %llu" CRLF CRLF, mediatype, (long long unsigned int)start, (long long unsigned int)end, (long long unsigned int)filesize, (long long unsigned int)(end-start+1)); ConnWrite(sessionP->conn, z, strlen(z)); ConnWriteFromFile(sessionP->conn, fileP, start, end, z, 4096, 0); } } } } }
static abyss_bool ServerFileHandler(TSession * const r, char * const z, time_t const fileModTime, MIMEType * const mimeTypeP) { const char * mediatype; TFile file; uint64_t filesize; uint64_t start; uint64_t end; TDate date; char * p; TDate filedate; mediatype = MIMETypeGuessFromFile2(mimeTypeP, z); if (!FileOpen(&file,z,O_BINARY | O_RDONLY)) { ResponseStatusErrno(r); return TRUE; } fileDate(r, fileModTime, &filedate); p = RequestHeaderValue(r, "if-modified-since"); if (p) { if (DateDecode(p,&date)) { if (DateCompare(&filedate, &date) <= 0) { ResponseStatus(r, 304); ResponseWrite(r); return TRUE; } else r->ranges.size = 0; } } filesize = FileSize(&file); switch (r->ranges.size) { case 0: ResponseStatus(r, 200); break; case 1: { abyss_bool decoded; decoded = RangeDecode((char *)(r->ranges.item[0]), filesize, &start, &end); if (!decoded) { ListFree(&(r->ranges)); ResponseStatus(r, 200); break; } sprintf(z, "bytes %llu-%llu/%llu", start, end, filesize); ResponseAddField(r, "Content-range", z); ResponseContentLength(r, end - start + 1); ResponseStatus(r, 206); } break; default: ResponseContentType(r, "multipart/ranges; boundary=" BOUNDARY); ResponseStatus(r, 206); break; } if (r->ranges.size == 0) { ResponseContentLength(r, filesize); ResponseContentType(r, mediatype); } if (DateToString(&filedate, z)) ResponseAddField(r, "Last-Modified", z); ResponseWrite(r); if (r->request_info.method != m_head) sendBody(r, &file, filesize, mediatype, start, end, z); FileClose(&file); return TRUE; }