sendfd.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  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. else{
  121. multir = 1;
  122. boundary = hmkmimeboundary(c);
  123. hprint(hout, "Content-Type: multipart/byteranges; boundary=%s\r\n", boundary);
  124. }
  125. if(c->head.ifrangeetag == nil){
  126. hprint(hout, "Last-Modified: %D\r\n", mtime);
  127. if(!multir)
  128. printtype(hout, type, enc);
  129. if(c->head.fresh_thresh)
  130. hintprint(c, hout, c->req.uri, c->head.fresh_thresh, c->head.fresh_have);
  131. }
  132. if(c->head.closeit)
  133. hprint(hout, "Connection: close\r\n");
  134. else if(!http11(c))
  135. hprint(hout, "Connection: Keep-Alive\r\n");
  136. hprint(hout, "\r\n");
  137. }
  138. if(strcmp(c->req.meth, "HEAD") == 0){
  139. if(c->head.range == nil)
  140. writelog(c, "Reply: 200 file 0\n");
  141. else
  142. writelog(c, "Reply: 206 file 0\n");
  143. hflush(hout);
  144. close(fd);
  145. return 1;
  146. }
  147. /*
  148. * send the file if it's a normal file
  149. */
  150. if(r == nil){
  151. hflush(hout);
  152. wrote = 0;
  153. if(n > 0)
  154. wrote = write(hout->fd, c->xferbuf, n);
  155. if(n <= 0 || wrote == n){
  156. while((n = read(fd, c->xferbuf, HBufSize)) > 0){
  157. nw = write(hout->fd, c->xferbuf, n);
  158. if(nw != n){
  159. if(nw > 0)
  160. wrote += nw;
  161. break;
  162. }
  163. wrote += nw;
  164. }
  165. }
  166. writelog(c, "Reply: 200 file %lld %lld\n", length, wrote);
  167. close(fd);
  168. if(length == wrote)
  169. return 1;
  170. return -1;
  171. }
  172. /*
  173. * for multipart/byterange messages,
  174. * it is not ok for the boundary string to appear within a message part.
  175. * however, it probably doesn't matter, since there are lengths for every part.
  176. */
  177. wrote = 0;
  178. ok = 1;
  179. for(; r != nil; r = r->next){
  180. if(multir){
  181. hprint(hout, "\r\n--%s\r\n", boundary);
  182. printtype(hout, type, enc);
  183. hprint(hout, "Content-Range: bytes %ld-%ld/%lld\r\n", r->start, r->stop, length);
  184. hprint(hout, "\r\n");
  185. }
  186. hflush(hout);
  187. if(seek(fd, r->start, 0) != r->start){
  188. ok = -1;
  189. break;
  190. }
  191. for(tr = r->stop - r->start + 1; tr; tr -= n){
  192. n = tr;
  193. if(n > HBufSize)
  194. n = HBufSize;
  195. if(read(fd, c->xferbuf, n) != n){
  196. ok = -1;
  197. goto breakout;
  198. }
  199. nw = write(hout->fd, c->xferbuf, n);
  200. if(nw != n){
  201. if(nw > 0)
  202. wrote += nw;
  203. ok = -1;
  204. goto breakout;
  205. }
  206. wrote += nw;
  207. }
  208. }
  209. breakout:;
  210. if(r == nil){
  211. if(multir){
  212. hprint(hout, "--%s--\r\n", boundary);
  213. hflush(hout);
  214. }
  215. writelog(c, "Reply: 206 partial content %lld %lld\n", length, wrote);
  216. }else
  217. writelog(c, "Reply: 206 partial content, early termination %lld %lld\n", length, wrote);
  218. close(fd);
  219. return ok;
  220. }
  221. static void
  222. printtype(Hio *hout, HContent *type, HContent *enc)
  223. {
  224. hprint(hout, "Content-Type: %s/%s", type->generic, type->specific);
  225. /*
  226. if(cistrcmp(type->generic, "text") == 0)
  227. hprint(hout, ";charset=utf-8");
  228. */
  229. hprint(hout, "\r\n");
  230. if(enc != nil)
  231. hprint(hout, "Content-Encoding: %s\r\n", enc->generic);
  232. }
  233. int
  234. etagmatch(int strong, HETag *tags, char *e)
  235. {
  236. char *s, *t;
  237. for(; tags != nil; tags = tags->next){
  238. if(strong && tags->weak)
  239. continue;
  240. s = tags->etag;
  241. if(s[0] == '*' && s[1] == '\0')
  242. return 1;
  243. t = e + 1;
  244. while(*t != '"'){
  245. if(*s != *t)
  246. break;
  247. s++;
  248. t++;
  249. }
  250. if(*s == '\0' && *t == '"')
  251. return 1;
  252. }
  253. return 0;
  254. }
  255. static char *
  256. acceptcont(char *s, char *e, HContent *ok, char *which)
  257. {
  258. char *sep;
  259. if(ok == nil)
  260. return seprint(s, e, "Your browser accepts any %s.<br>\n", which);
  261. s = seprint(s, e, "Your browser accepts %s: ", which);
  262. sep = "";
  263. for(; ok != nil; ok = ok->next){
  264. if(ok->specific)
  265. s = seprint(s, e, "%s%s/%s", sep, ok->generic, ok->specific);
  266. else
  267. s = seprint(s, e, "%s%s", sep, ok->generic);
  268. sep = ", ";
  269. }
  270. return seprint(s, e, ".<br>\n");
  271. }
  272. /*
  273. * send back a nice error message if the content is unacceptable
  274. * to get this message in ie, go to tools, internet options, advanced,
  275. * and turn off Show Friendly HTTP Error Messages under the Browsing category
  276. */
  277. static int
  278. notaccept(HConnect *c, HContent *type, HContent *enc, char *which)
  279. {
  280. Hio *hout;
  281. char *s, *e;
  282. hout = &c->hout;
  283. e = &c->xferbuf[HBufSize];
  284. s = c->xferbuf;
  285. s = seprint(s, e, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n");
  286. s = seprint(s, e, "<html>\n<title>Unacceptable %s</title>\n<body>\n", which);
  287. s = seprint(s, e, "Your browser will not accept this data, %H, because of it's %s.<br>\n", c->req.uri, which);
  288. s = seprint(s, e, "It's Content-Type is %s/%s", type->generic, type->specific);
  289. if(enc != nil)
  290. s = seprint(s, e, ", and Content-Encoding is %s", enc->generic);
  291. s = seprint(s, e, ".<br>\n\n");
  292. s = acceptcont(s, e, c->head.oktype, "Content-Type");
  293. s = acceptcont(s, e, c->head.okencode, "Content-Encoding");
  294. s = seprint(s, e, "</body>\n</html>\n");
  295. hprint(hout, "%s 406 Not Acceptable\r\n", hversion);
  296. hprint(hout, "Server: Plan9\r\n");
  297. hprint(hout, "Date: %D\r\n", time(nil));
  298. hprint(hout, "Content-Type: text/html\r\n");
  299. hprint(hout, "Content-Length: %lud\r\n", s - c->xferbuf);
  300. if(c->head.closeit)
  301. hprint(hout, "Connection: close\r\n");
  302. else if(!http11(c))
  303. hprint(hout, "Connection: Keep-Alive\r\n");
  304. hprint(hout, "\r\n");
  305. if(strcmp(c->req.meth, "HEAD") != 0)
  306. hwrite(hout, c->xferbuf, s - c->xferbuf);
  307. writelog(c, "Reply: 406 Not Acceptable\nReason: %s\n", which);
  308. return hflush(hout);
  309. }
  310. /*
  311. * check time and entity tag conditions.
  312. */
  313. int
  314. checkreq(HConnect *c, HContent *type, HContent *enc, long mtime, char *etag)
  315. {
  316. Hio *hout;
  317. int m;
  318. hout = &c->hout;
  319. if(c->req.vermaj >= 1 && c->req.vermin >= 1 && !hcheckcontent(type, c->head.oktype, "Content-Type", 0))
  320. return notaccept(c, type, enc, "Content-Type");
  321. if(c->req.vermaj >= 1 && c->req.vermin >= 1 && !hcheckcontent(enc, c->head.okencode, "Content-Encoding", 0))
  322. return notaccept(c, type, enc, "Content-Encoding");
  323. /*
  324. * can use weak match only with get or head;
  325. * this always uses strong matches
  326. */
  327. m = etagmatch(1, c->head.ifnomatch, etag);
  328. if(m && strcmp(c->req.meth, "GET") != 0 && strcmp(c->req.meth, "HEAD") != 0
  329. || c->head.ifunmodsince && c->head.ifunmodsince < mtime
  330. || c->head.ifmatch != nil && !etagmatch(1, c->head.ifmatch, etag)){
  331. hprint(hout, "%s 412 Precondition Failed\r\n", hversion);
  332. hprint(hout, "Server: Plan9\r\n");
  333. hprint(hout, "Date: %D\r\n", time(nil));
  334. hprint(hout, "Content-Type: text/html\r\n");
  335. hprint(hout, "Content-Length: %d\r\n", STRLEN(UNMATCHED));
  336. if(c->head.closeit)
  337. hprint(hout, "Connection: close\r\n");
  338. else if(!http11(c))
  339. hprint(hout, "Connection: Keep-Alive\r\n");
  340. hprint(hout, "\r\n");
  341. if(strcmp(c->req.meth, "HEAD") != 0)
  342. hprint(hout, "%s", UNMATCHED);
  343. writelog(c, "Reply: 412 Precondition Failed\n");
  344. return hflush(hout);
  345. }
  346. if(c->head.ifmodsince >= mtime
  347. && (m || c->head.ifnomatch == nil)){
  348. /*
  349. * can only send back Date, ETag, Content-Location,
  350. * Expires, Cache-Control, and Vary entity-headers
  351. */
  352. hprint(hout, "%s 304 Not Modified\r\n", hversion);
  353. hprint(hout, "Server: Plan9\r\n");
  354. hprint(hout, "Date: %D\r\n", time(nil));
  355. hprint(hout, "ETag: %s\r\n", etag);
  356. if(c->head.closeit)
  357. hprint(hout, "Connection: close\r\n");
  358. else if(!http11(c))
  359. hprint(hout, "Connection: Keep-Alive\r\n");
  360. hprint(hout, "\r\n");
  361. writelog(c, "Reply: 304 Not Modified\n");
  362. return hflush(hout);
  363. }
  364. return 1;
  365. }
  366. /*
  367. * length is the actual length of the entity requested.
  368. * discard any range requests which are invalid,
  369. * ie start after the end, or have stop before start.
  370. * rewrite suffix requests
  371. */
  372. HRange*
  373. fixrange(HRange *h, long length)
  374. {
  375. HRange *r, *rr;
  376. if(length == 0)
  377. return nil;
  378. /*
  379. * rewrite each range to reflect the actual length of the file
  380. * toss out any invalid ranges
  381. */
  382. rr = nil;
  383. for(r = h; r != nil; r = r->next){
  384. if(r->suffix){
  385. r->start = length - r->stop;
  386. if(r->start >= length)
  387. r->start = 0;
  388. r->stop = length - 1;
  389. r->suffix = 0;
  390. }
  391. if(r->stop >= length)
  392. r->stop = length - 1;
  393. if(r->start > r->stop){
  394. if(rr == nil)
  395. h = r->next;
  396. else
  397. rr->next = r->next;
  398. }else
  399. rr = r;
  400. }
  401. /*
  402. * merge consecutive overlapping or abutting ranges
  403. *
  404. * not clear from rfc2616 how much merging needs to be done.
  405. * this code merges only if a range is adjacent to a later starting,
  406. * over overlapping or abutting range. this allows a client
  407. * to request wanted data first, followed by other data.
  408. * this may be useful then fetching part of a page, then the adjacent regions.
  409. */
  410. if(h == nil)
  411. return h;
  412. r = h;
  413. for(;;){
  414. rr = r->next;
  415. if(rr == nil)
  416. break;
  417. if(r->start <= rr->start && r->stop + 1 >= rr->start){
  418. if(r->stop < rr->stop)
  419. r->stop = rr->stop;
  420. r->next = rr->next;
  421. }else
  422. r = rr;
  423. }
  424. return h;
  425. }