sendfd.c 12 KB

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