static magic_t cookie = NULL;
-static void passdata(int in, int out)
+static void passdata(int in, int out, off_t maxlen)
{
int ret, len, off;
char *buf;
buf = smalloc(65536);
while(1) {
- len = read(in, buf, 65536);
+ len = 65536;
+ if((maxlen > 0) && (len > maxlen))
+ len = maxlen;
+ len = read(in, buf, len);
if(len < 0) {
flog(LOG_ERR, "sendfile: could not read input: %s", strerror(errno));
break;
break;
}
}
+ if(maxlen > 0) {
+ if((maxlen -= len) <= 0)
+ break;
+ }
}
free(buf);
}
return(NULL);
found:
for(i = 0; i < sz; i++) {
- if((buf[sz] < 32) || (buf[sz] >= 128))
+ if((buf[i] < 32) || (buf[i] >= 128))
return(NULL);
}
buf[sz] = 0;
}
}
+static void sendwhole(int fd, struct stat *sb, const char *contype, int head)
+{
+ printf("HTTP/1.1 200 OK\n");
+ printf("Content-Type: %s\n", contype);
+ printf("Content-Length: %ji\n", (intmax_t)sb->st_size);
+ printf("Last-Modified: %s\n", fmthttpdate(sb->st_mtime));
+ printf("Date: %s\n", fmthttpdate(time(NULL)));
+ printf("\n");
+ fflush(stdout);
+ if(!head)
+ passdata(fd, 1, -1);
+}
+
+static void sendrange(int fd, struct stat *sb, const char *contype, char *spec, int head)
+{
+ char buf[strlen(spec) + 1];
+ char *p, *e;
+ off_t start, end;
+
+ if(strncmp(spec, "bytes=", 6))
+ goto error;
+ strcpy(buf, spec + 6);
+ if((p = strchr(buf, '-')) == NULL)
+ goto error;
+ if(p == buf) {
+ if(!p[1])
+ goto error;
+ end = sb->st_size;
+ start = end - strtoll(p + 1, &e, 10);
+ if(*e)
+ goto error;
+ if(start < 0)
+ start = 0;
+ } else {
+ *(p++) = 0;
+ start = strtoll(buf, &e, 10);
+ if(*e)
+ goto error;
+ if(*p) {
+ end = strtoll(p, &e, 10) + 1;
+ if(*e)
+ goto error;
+ } else {
+ end = sb->st_size;
+ }
+ }
+ if(start >= sb->st_size) {
+ printf("HTTP/1.1 416 Not satisfiable\n");
+ printf("Content-Range: */%ji\n", (intmax_t)sb->st_size);
+ printf("Content-Length: 0\n");
+ printf("Last-Modified: %s\n", fmthttpdate(sb->st_mtime));
+ printf("Date: %s\n", fmthttpdate(time(NULL)));
+ printf("\n");
+ return;
+ }
+ if((start < 0) || (start >= end))
+ goto error;
+ if(end > sb->st_size)
+ end = sb->st_size;
+ errno = 0;
+ if(lseek(fd, start, SEEK_SET) != start) {
+ simpleerror(1, 500, "Internal Error", "Could not seek properly to beginning of requested byte range.");
+ flog(LOG_ERR, "sendfile: could not seek properly when serving partial content: %s", strerror(errno));
+ exit(1);
+ }
+ printf("HTTP/1.1 206 Partial content\n");
+ printf("Content-Range: bytes %ji-%ji/%ji\n", (intmax_t)start, (intmax_t)(end - 1), (intmax_t)sb->st_size);
+ printf("Content-Length: %ji\n", (intmax_t)(end - start));
+ printf("Content-Type: %s\n", contype);
+ printf("Last-Modified: %s\n", fmthttpdate(sb->st_mtime));
+ printf("Date: %s\n", fmthttpdate(time(NULL)));
+ printf("\n");
+ fflush(stdout);
+ if(!head)
+ passdata(fd, 1, end - start);
+ return;
+
+error:
+ sendwhole(fd, sb, contype, head);
+}
+
static void usage(void)
{
- flog(LOG_ERR, "usage: sendfile [-c CONTENT-TYPE] METHOD URL REST");
+ flog(LOG_ERR, "usage: sendfile [-c CONTENT-TYPE] [-f FILE] METHOD URL REST");
}
int main(int argc, char **argv)
{
int c;
- char *file;
+ char *file, *hdr;
struct stat sb;
- int fd;
+ int fd, ishead, ignrest;
const char *contype;
setlocale(LC_ALL, "");
contype = NULL;
- while((c = getopt(argc, argv, "c:")) >= 0) {
+ file = NULL;
+ ignrest = 0;
+ while((c = getopt(argc, argv, "c:f:")) >= 0) {
switch(c) {
case 'c':
contype = optarg;
break;
+ case 'f':
+ file = optarg;
+ ignrest = 1;
+ break;
default:
usage();
exit(1);
usage();
exit(1);
}
- if((file = getenv("REQ_X_ASH_FILE")) == NULL) {
- flog(LOG_ERR, "sendfile: needs to be called with the X-Ash-File header");
+ if((file == NULL) && ((file = getenv("REQ_X_ASH_FILE")) == NULL)) {
+ flog(LOG_ERR, "sendfile: needs to be called with either the X-Ash-File header or the -f option");
exit(1);
}
- if(*argv[optind + 2]) {
+ if(!ignrest && *argv[optind + 2]) {
simpleerror(1, 404, "Not Found", "The requested URL has no corresponding resource.");
exit(0);
}
simpleerror(1, 500, "Internal Error", "The server could not access its own data.");
exit(1);
}
- if(contype == NULL)
- contype = getmimetype(file, &sb);
+ if(!strcasecmp(argv[optind], "get")) {
+ ishead = 0;
+ } else if(!strcasecmp(argv[optind], "head")) {
+ ishead = 1;
+ } else {
+ simpleerror(1, 405, "Method not allowed", "The requested method is not defined for this resource.");
+ return(0);
+ }
+ if(contype == NULL) {
+ if((hdr = getenv("REQ_X_ASH_CONTENT_TYPE")) != NULL)
+ contype = hdr;
+ else
+ contype = getmimetype(file, &sb);
+ }
contype = ckctype(contype);
checkcache(file, &sb);
- printf("HTTP/1.1 200 OK\n");
- printf("Content-Type: %s\n", contype);
- printf("Content-Length: %ji\n", (intmax_t)sb.st_size);
- printf("Last-Modified: %s\n", fmthttpdate(sb.st_mtime));
- printf("Date: %s\n", fmthttpdate(time(NULL)));
- printf("\n");
- fflush(stdout);
- if(strcasecmp(argv[optind], "head"))
- passdata(fd, 1);
+ if((hdr = getenv("REQ_RANGE")) != NULL)
+ sendrange(fd, &sb, contype, hdr, ishead);
+ else
+ sendwhole(fd, &sb, contype, ishead);
return(0);
}