sendfd.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. #include <u.h>
  2. #include <libc.h>
  3. #include <auth.h>
  4. #include "httpd.h"
  5. #include "httpsrv.h"
  6. static void printtype(Hio *hout, HContent *type, HContent *enc);
  7. /*
  8. * these should be done better; see the reponse codes in /lib/rfc/rfc2616 for
  9. * more info on what should be included.
  10. */
  11. #define UNAUTHED "You are not authorized to see this area.\n"
  12. #define NOCONTENT "No acceptable type of data is available.\n"
  13. #define NOENCODE "No acceptable encoding of the contents is available.\n"
  14. #define UNMATCHED "The entity requested does not match the existing entity.\n"
  15. #define BADRANGE "No bytes are avaible for the range you requested.\n"
  16. /*
  17. * fd references a file which has been authorized & checked for relocations.
  18. * send back the headers & it's contents.
  19. * includes checks for conditional requests & ranges.
  20. */
  21. int
  22. sendfd(HConnect *c, int fd, Dir *dir, HContent *type, HContent *enc)
  23. {
  24. Qid qid;
  25. HRange *r;
  26. HContents conts;
  27. Hio *hout;
  28. char *boundary, etag[32];
  29. long mtime;
  30. ulong tr;
  31. int n, nw, multir, ok;
  32. vlong wrote, length;
  33. hout = &c->hout;
  34. length = dir->length;
  35. mtime = dir->mtime;
  36. qid = dir->qid;
  37. free(dir);
  38. /*
  39. * figure out the type of file and send headers
  40. */
  41. n = -1;
  42. r = nil;
  43. multir = 0;
  44. boundary = nil;
  45. if(c->req.vermaj){
  46. if(type == nil && enc == nil){
  47. conts = uriclass(c, c->req.uri);
  48. type = conts.type;
  49. enc = conts.encoding;
  50. if(type == nil && enc == nil){
  51. n = read(fd, c->xferbuf, HBufSize-1);
  52. if(n > 0){
  53. c->xferbuf[n] = '\0';
  54. conts = dataclass(c, c->xferbuf, n);
  55. type = conts.type;
  56. enc = conts.encoding;
  57. }
  58. }
  59. }
  60. if(type == nil)
  61. type = hmkcontent(c, "application", "octet-stream", nil);
  62. snprint(etag, sizeof(etag), "\"%lluxv%lux\"", qid.path, qid.vers);
  63. ok = checkreq(c, type, enc, mtime, etag);
  64. if(ok <= 0){
  65. close(fd);
  66. return ok;
  67. }
  68. /*
  69. * check for if-range requests
  70. */
  71. if(c->head.range == nil
  72. || c->head.ifrangeetag != nil && !etagmatch(1, c->head.ifrangeetag, etag)
  73. || c->head.ifrangedate != 0 && c->head.ifrangedate != mtime){
  74. c->head.range = nil;
  75. c->head.ifrangeetag = nil;
  76. c->head.ifrangedate = 0;
  77. }
  78. if(c->head.range != nil){
  79. c->head.range = fixrange(c->head.range, length);
  80. if(c->head.range == nil){
  81. if(c->head.ifrangeetag == nil && c->head.ifrangedate == 0){
  82. hprint(hout, "%s 416 Request range not satisfiable\r\n", hversion);
  83. hprint(hout, "Date: %D\r\n", time(nil));
  84. hprint(hout, "Server: Plan9\r\n");
  85. hprint(hout, "Content-Range: bytes */%lld\r\n", length);
  86. hprint(hout, "Content-Length: %d\r\n", STRLEN(BADRANGE));
  87. hprint(hout, "Content-Type: text/html\r\n");
  88. if(c->head.closeit)
  89. hprint(hout, "Connection: close\r\n");
  90. else if(!http11(c))
  91. hprint(hout, "Connection: Keep-Alive\r\n");
  92. hprint(hout, "\r\n");
  93. if(strcmp(c->req.meth, "HEAD") != 0)
  94. hprint(hout, "%s", BADRANGE);
  95. hflush(hout);
  96. writelog(c, "Reply: 416 Request range not satisfiable\n");
  97. close(fd);
  98. return 1;
  99. }
  100. c->head.ifrangeetag = nil;
  101. c->head.ifrangedate = 0;
  102. }
  103. }
  104. if(c->head.range == nil)
  105. hprint(hout, "%s 200 OK\r\n", hversion);
  106. else
  107. hprint(hout, "%s 206 Partial Content\r\n", hversion);
  108. hprint(hout, "Server: Plan9\r\n");
  109. hprint(hout, "Date: %D\r\n", time(nil));
  110. hprint(hout, "ETag: %s\r\n", etag);
  111. /*
  112. * can't send some entity headers if partially responding
  113. * to an if-range: etag request
  114. */
  115. r = c->head.range;
  116. if(r == nil)
  117. hprint(hout, "Content-Length: %lld\r\n", length);
  118. else if(r->next == nil){
  119. hprint(hout, "Content-Range: bytes %ld-%ld/%lld\r\n", r->start, r->stop, length);
  120. hprint(hout, "Content-Length: %ld\r\n", r->stop - r->start);
  121. }else{
  122. multir = 1;
  123. boundary = hmkmimeboundary(c);
  124. hprint(hout, "Content-Type: multipart/byteranges; boundary=%s\r\n", boundary);
  125. }
  126. if(c->head.ifrangeetag == nil){
  127. hprint(hout, "Last-Modified: %D\r\n", mtime);
  128. if(!multir)
  129. printtype(hout, type, enc);
  130. if(c->head.fresh_thresh)
  131. hintprint(c, hout, c->req.uri, c->head.fresh_thresh, c->head.fresh_have);
  132. }
  133. if(c->head.closeit)
  134. hprint(hout, "Connection: close\r\n");
  135. else if(!http11(c))
  136. hprint(hout, "Connection: Keep-Alive\r\n");
  137. hprint(hout, "\r\n");
  138. }
  139. if(strcmp(c->req.meth, "HEAD") == 0){
  140. if(c->head.range == nil)
  141. writelog(c, "Reply: 200 file 0\n");
  142. else
  143. writelog(c, "Reply: 206 file 0\n");
  144. hflush(hout);
  145. close(fd);
  146. return 1;
  147. }
  148. /*
  149. * send the file if it's a normal file
  150. */
  151. if(r == nil){
  152. hflush(hout);
  153. wrote = 0;
  154. if(n > 0)
  155. wrote = write(hout->fd, c->xferbuf, n);
  156. if(n <= 0 || wrote == n){
  157. while((n = read(fd, c->xferbuf, HBufSize)) > 0){
  158. nw = write(hout->fd, c->xferbuf, n);
  159. if(nw != n){
  160. if(nw > 0)
  161. wrote += nw;
  162. break;
  163. }
  164. wrote += nw;
  165. }
  166. }
  167. writelog(c, "Reply: 200 file %lld %lld\n", length, wrote);
  168. close(fd);
  169. if(length == wrote)
  170. return 1;
  171. return -1;
  172. }
  173. /*
  174. * for multipart/byterange messages,
  175. * it is not ok for the boundary string to appear within a message part.
  176. * however, it probably doesn't matter, since there are lengths for every part.
  177. */
  178. wrote = 0;
  179. ok = 1;
  180. for(; r != nil; r = r->next){
  181. if(multir){
  182. hprint(hout, "\r\n--%s\r\n", boundary);
  183. printtype(hout, type, enc);
  184. hprint(hout, "Content-Range: bytes %ld-%ld/%lld\r\n", r->start, r->stop, length);
  185. hprint(hout, "Content-Length: %ld\r\n", r->stop - r->start);
  186. hprint(hout, "\r\n");
  187. }
  188. hflush(hout);
  189. if(seek(fd, r->start, 0) != r->start){
  190. ok = -1;
  191. break;
  192. }
  193. for(tr = r->stop - r->start + 1; tr; tr -= n){
  194. n = tr;
  195. if(n > HBufSize)
  196. n = HBufSize;
  197. if(read(fd, c->xferbuf, n) != n){
  198. ok = -1;
  199. goto breakout;
  200. }
  201. nw = write(hout->fd, c->xferbuf, n);
  202. if(nw != n){
  203. if(nw > 0)
  204. wrote += nw;
  205. ok = -1;
  206. goto breakout;
  207. }
  208. wrote += nw;
  209. }
  210. }
  211. breakout:;
  212. if(r == nil){
  213. if(multir){
  214. hprint(hout, "--%s--\r\n", boundary);
  215. hflush(hout);
  216. }
  217. writelog(c, "Reply: 206 partial content %lld %lld\n", length, wrote);
  218. }else
  219. writelog(c, "Reply: 206 partial content, early termination %lld %lld\n", length, wrote);
  220. close(fd);
  221. return ok;
  222. }
  223. static void
  224. printtype(Hio *hout, HContent *type, HContent *enc)
  225. {
  226. hprint(hout, "Content-Type: %s/%s", type->generic, type->specific);
  227. /*
  228. if(cistrcmp(type->generic, "text") == 0)
  229. hprint(hout, ";charset=utf-8");
  230. */
  231. hprint(hout, "\r\n");
  232. if(enc != nil)
  233. hprint(hout, "Content-Encoding: %s\r\n", enc->generic);
  234. }
  235. int
  236. etagmatch(int strong, HETag *tags, char *e)
  237. {
  238. char *s, *t;
  239. for(; tags != nil; tags = tags->next){
  240. if(strong && tags->weak)
  241. continue;
  242. s = tags->etag;
  243. if(s[0] == '*' && s[1] == '\0')
  244. return 1;
  245. t = e + 1;
  246. while(*t != '"'){
  247. if(*s != *t)
  248. break;
  249. s++;
  250. t++;
  251. }
  252. if(*s == '\0' && *t == '"')
  253. return 1;
  254. }
  255. return 0;
  256. }
  257. static char *
  258. acceptcont(char *s, char *e, HContent *ok, char *which)
  259. {
  260. char *sep;
  261. if(ok == nil)
  262. return seprint(s, e, "Your browser accepts any %s.<br>\n", which);
  263. s = seprint(s, e, "Your browser accepts %s: ", which);
  264. sep = "";
  265. for(; ok != nil; ok = ok->next){
  266. if(ok->specific)
  267. s = seprint(s, e, "%s%s/%s", sep, ok->generic, ok->specific);
  268. else
  269. s = seprint(s, e, "%s%s", sep, ok->generic);
  270. sep = ", ";
  271. }
  272. return seprint(s, e, ".<br>\n");
  273. }
  274. /*
  275. * send back a nice error message if the content is unacceptable
  276. * to get this message in ie, go to tools, internet options, advanced,
  277. * and turn off Show Friendly HTTP Error Messages under the Browsing category
  278. */
  279. static int
  280. notaccept(HConnect *c, HContent *type, HContent *enc, char *which)
  281. {
  282. Hio *hout;
  283. char *s, *e;
  284. hout = &c->hout;
  285. e = &c->xferbuf[HBufSize];
  286. s = c->xferbuf;
  287. s = seprint(s, e, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n");
  288. s = seprint(s, e, "<html>\n<title>Unacceptable %s</title>\n<body>\n", which);
  289. s = seprint(s, e, "Your browser will not accept this data, %H, because of it's %s.<br>\n", c->req.uri, which);
  290. s = seprint(s, e, "It's Content-Type is %s/%s", type->generic, type->specific);
  291. if(enc != nil)
  292. s = seprint(s, e, ", and Content-Encoding is %s", enc->generic);
  293. s = seprint(s, e, ".<br>\n\n");
  294. s = acceptcont(s, e, c->head.oktype, "Content-Type");
  295. s = acceptcont(s, e, c->head.okencode, "Content-Encoding");
  296. s = seprint(s, e, "</body>\n</html>\n");
  297. hprint(hout, "%s 406 Not Acceptable\r\n", hversion);
  298. hprint(hout, "Server: Plan9\r\n");
  299. hprint(hout, "Date: %D\r\n", time(nil));
  300. hprint(hout, "Content-Type: text/html\r\n");
  301. hprint(hout, "Content-Length: %lud\r\n", s - c->xferbuf);
  302. if(c->head.closeit)
  303. hprint(hout, "Connection: close\r\n");
  304. else if(!http11(c))
  305. hprint(hout, "Connection: Keep-Alive\r\n");
  306. hprint(hout, "\r\n");
  307. if(strcmp(c->req.meth, "HEAD") != 0)
  308. hwrite(hout, c->xferbuf, s - c->xferbuf);
  309. writelog(c, "Reply: 406 Not Acceptable\nReason: %s\n", which);
  310. return hflush(hout);
  311. }
  312. /*
  313. * check time and entity tag conditions.
  314. */
  315. int
  316. checkreq(HConnect *c, HContent *type, HContent *enc, long mtime, char *etag)
  317. {
  318. Hio *hout;
  319. int m;
  320. hout = &c->hout;
  321. if(c->req.vermaj >= 1 && c->req.vermin >= 1 && !hcheckcontent(type, c->head.oktype, "Content-Type", 0))
  322. return notaccept(c, type, enc, "Content-Type");
  323. if(c->req.vermaj >= 1 && c->req.vermin >= 1 && !hcheckcontent(enc, c->head.okencode, "Content-Encoding", 0))
  324. return notaccept(c, type, enc, "Content-Encoding");
  325. /*
  326. * can use weak match only with get or head;
  327. * this always uses strong matches
  328. */
  329. m = etagmatch(1, c->head.ifnomatch, etag);
  330. if(m && strcmp(c->req.meth, "GET") != 0 && strcmp(c->req.meth, "HEAD") != 0
  331. || c->head.ifunmodsince && c->head.ifunmodsince < mtime
  332. || c->head.ifmatch != nil && !etagmatch(1, c->head.ifmatch, etag)){
  333. hprint(hout, "%s 412 Precondition Failed\r\n", hversion);
  334. hprint(hout, "Server: Plan9\r\n");
  335. hprint(hout, "Date: %D\r\n", time(nil));
  336. hprint(hout, "Content-Type: text/html\r\n");
  337. hprint(hout, "Content-Length: %d\r\n", STRLEN(UNMATCHED));
  338. if(c->head.closeit)
  339. hprint(hout, "Connection: close\r\n");
  340. else if(!http11(c))
  341. hprint(hout, "Connection: Keep-Alive\r\n");
  342. hprint(hout, "\r\n");
  343. if(strcmp(c->req.meth, "HEAD") != 0)
  344. hprint(hout, "%s", UNMATCHED);
  345. writelog(c, "Reply: 412 Precondition Failed\n");
  346. return hflush(hout);
  347. }
  348. if(c->head.ifmodsince >= mtime
  349. && (m || c->head.ifnomatch == nil)){
  350. /*
  351. * can only send back Date, ETag, Content-Location,
  352. * Expires, Cache-Control, and Vary entity-headers
  353. */
  354. hprint(hout, "%s 304 Not Modified\r\n", hversion);
  355. hprint(hout, "Server: Plan9\r\n");
  356. hprint(hout, "Date: %D\r\n", time(nil));
  357. hprint(hout, "ETag: %s\r\n", etag);
  358. if(c->head.closeit)
  359. hprint(hout, "Connection: close\r\n");
  360. else if(!http11(c))
  361. hprint(hout, "Connection: Keep-Alive\r\n");
  362. hprint(hout, "\r\n");
  363. writelog(c, "Reply: 304 Not Modified\n");
  364. return hflush(hout);
  365. }
  366. return 1;
  367. }
  368. /*
  369. * length is the actual length of the entity requested.
  370. * discard any range requests which are invalid,
  371. * ie start after the end, or have stop before start.
  372. * rewrite suffix requests
  373. */
  374. HRange*
  375. fixrange(HRange *h, long length)
  376. {
  377. HRange *r, *rr;
  378. if(length == 0)
  379. return nil;
  380. /*
  381. * rewrite each range to reflect the actual length of the file
  382. * toss out any invalid ranges
  383. */
  384. rr = nil;
  385. for(r = h; r != nil; r = r->next){
  386. if(r->suffix){
  387. r->start = length - r->stop;
  388. if(r->start >= length)
  389. r->start = 0;
  390. r->stop = length - 1;
  391. r->suffix = 0;
  392. }
  393. if(r->stop >= length)
  394. r->stop = length - 1;
  395. if(r->start > r->stop){
  396. if(rr == nil)
  397. h = r->next;
  398. else
  399. rr->next = r->next;
  400. }else
  401. rr = r;
  402. }
  403. /*
  404. * merge consecutive overlapping or abutting ranges
  405. *
  406. * not clear from rfc2616 how much merging needs to be done.
  407. * this code merges only if a range is adjacent to a later starting,
  408. * over overlapping or abutting range. this allows a client
  409. * to request wanted data first, followed by other data.
  410. * this may be useful then fetching part of a page, then the adjacent regions.
  411. */
  412. if(h == nil)
  413. return h;
  414. r = h;
  415. for(;;){
  416. rr = r->next;
  417. if(rr == nil)
  418. break;
  419. if(r->start <= rr->start && r->stop + 1 >= rr->start){
  420. if(r->stop < rr->stop)
  421. r->stop = rr->stop;
  422. r->next = rr->next;
  423. }else
  424. r = rr;
  425. }
  426. return h;
  427. }