httpfile.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658
  1. /* contributed by 20h@r-36.net, September 2005 */
  2. #include <u.h>
  3. #include <libc.h>
  4. #include <bio.h>
  5. #include <ndb.h>
  6. #include <thread.h>
  7. #include <fcall.h>
  8. #include <9p.h>
  9. #include <mp.h>
  10. #include <libsec.h>
  11. enum
  12. {
  13. Blocksize = 64*1024,
  14. Stacksize = 8192,
  15. };
  16. char *host;
  17. char *file;
  18. char *port;
  19. char *url;
  20. char *get;
  21. char *user;
  22. char *net = "net";
  23. vlong size;
  24. int usetls;
  25. int debug;
  26. int ncache;
  27. int mcache;
  28. void
  29. usage(void)
  30. {
  31. fprint(2, "usage: httpfile [-Dd] [-b count] [-f file] [-m mtpt] [-s srvname] [-x net] url\n");
  32. exits("usage");
  33. }
  34. enum
  35. {
  36. Qroot,
  37. Qfile,
  38. };
  39. #define PATH(type, n) ((type)|((n)<<8))
  40. #define TYPE(path) ((int)(path) & 0xFF)
  41. #define NUM(path) ((uint)(path)>>8)
  42. Channel *reqchan;
  43. Channel *httpchan;
  44. Channel *finishchan;
  45. ulong time0;
  46. typedef struct Block Block;
  47. struct Block
  48. {
  49. uchar *p;
  50. vlong off;
  51. vlong len;
  52. Block *link;
  53. long lastuse;
  54. Req *rq;
  55. Req **erq;
  56. };
  57. typedef struct Blocklist Blocklist;
  58. struct Blocklist
  59. {
  60. Block *first;
  61. Block **end;
  62. };
  63. Blocklist cache;
  64. Blocklist inprogress;
  65. void
  66. queuereq(Block *b, Req *r)
  67. {
  68. if(b->rq==nil)
  69. b->erq = &b->rq;
  70. *b->erq = r;
  71. r->aux = nil;
  72. b->erq = (Req**)&r->aux;
  73. }
  74. void
  75. addblock(Blocklist *l, Block *b)
  76. {
  77. if(debug)
  78. print("adding: %p %lld\n", b, b->off);
  79. if(l->first == nil)
  80. l->end = &l->first;
  81. *l->end = b;
  82. b->link = nil;
  83. l->end = &b->link;
  84. b->lastuse = time(0);
  85. }
  86. void
  87. delreq(Block *b, Req *r)
  88. {
  89. Req **l;
  90. for(l = &b->rq; *l; l = (Req**)&(*l)->aux){
  91. if(*l == r){
  92. *l = r->aux;
  93. if(*l == nil)
  94. b->erq = l;
  95. free(r);
  96. return;
  97. }
  98. }
  99. }
  100. void
  101. evictblock(Blocklist *cache)
  102. {
  103. Block **l, **oldest, *b;
  104. if(cache->first == nil)
  105. return;
  106. oldest = nil;
  107. for(l=&cache->first; *l; l=&(*l)->link)
  108. if(oldest == nil || (*oldest)->lastuse > (*l)->lastuse)
  109. oldest = l;
  110. b = *oldest;
  111. *oldest = (*oldest)->link;
  112. if(*oldest == nil)
  113. cache->end = oldest;
  114. free(b->p);
  115. free(b);
  116. ncache--;
  117. }
  118. Block *
  119. findblock(Blocklist *s, vlong off)
  120. {
  121. Block *b;
  122. for(b = s->first; b != nil; b = b->link){
  123. if(b->off <= off && off < b->off + Blocksize){
  124. if(debug)
  125. print("found: %lld -> %lld\n", off, b->off);
  126. b->lastuse = time(0);
  127. return b;
  128. }
  129. }
  130. return nil;
  131. }
  132. void
  133. readfrom(Req *r, Block *b)
  134. {
  135. int d, n;
  136. b->lastuse = time(0);
  137. n = r->ifcall.count;
  138. d = r->ifcall.offset - b->off;
  139. if(b->off + d + n > b->off + b->len)
  140. n = b->len - d;
  141. if(debug)
  142. print("Reading from: %p %d %d\n", b->p, d, n);
  143. memmove(r->ofcall.data, b->p + d, n);
  144. r->ofcall.count = n;
  145. respond(r, nil);
  146. }
  147. void
  148. hangupclient(Srv*)
  149. {
  150. if(debug)
  151. print("Hangup.\n");
  152. threadexitsall("done");
  153. }
  154. int
  155. dotls(int fd)
  156. {
  157. TLSconn conn;
  158. if((fd=tlsClient(fd, &conn)) < 0)
  159. sysfatal("tlsclient: %r");
  160. if(conn.cert != nil)
  161. free(conn.cert);
  162. return fd;
  163. }
  164. char*
  165. nocr(char *s)
  166. {
  167. char *r, *w;
  168. for(r=w=s; *r; r++)
  169. if(*r != '\r')
  170. *w++ = *r;
  171. *w = 0;
  172. return s;
  173. }
  174. char*
  175. readhttphdr(Biobuf *netbio, vlong *size)
  176. {
  177. char *s, *stat;
  178. stat = nil;
  179. while((s = Brdstr(netbio, '\n', 1)) != nil && s[0] != '\r'
  180. && s[0] != '\0'){
  181. if(stat == nil)
  182. stat = estrdup9p(s);
  183. if(strncmp(s, "Content-Length: ", 16) == 0 && size != nil)
  184. *size = atoll(s + 16);
  185. free(s);
  186. }
  187. if(stat)
  188. nocr(stat);
  189. return stat;
  190. }
  191. int
  192. dialhttp(Biobuf *netbio)
  193. {
  194. int netfd;
  195. netfd = dial(netmkaddr(host, net, port), 0, 0, 0);
  196. if(netfd < 0)
  197. sysfatal("dial: %r");
  198. if(usetls)
  199. netfd = dotls(netfd);
  200. Binit(netbio, netfd, OREAD);
  201. return netfd;
  202. }
  203. uchar*
  204. getrange(Block *b)
  205. {
  206. uchar *data;
  207. char *status;
  208. int netfd;
  209. static Biobuf netbio;
  210. b->len = Blocksize;
  211. if(b->off + b->len > size)
  212. b->len = size - b->off;
  213. if(debug)
  214. print("getrange: %lld %lld\n", b->off, b->len);
  215. netfd = dialhttp(&netbio);
  216. fprint(netfd,
  217. "GET %s HTTP/1.1\r\n"
  218. "Host: %s\r\n"
  219. "Accept-Encoding:\r\n"
  220. "Range: bytes=%lld-%lld\r\n"
  221. "\r\n",
  222. get, host, b->off, b->off+b->len);
  223. Bflush(&netbio);
  224. status = readhttphdr(&netbio, nil);
  225. if(status == nil)
  226. return nil;
  227. /*
  228. * Some servers (e.g., www.google.com) return 200 OK
  229. * when you ask for the entire page in one range.
  230. */
  231. if(strstr(status, "206 Partial Content")==nil
  232. && (b->off!=0 || b->len!=size || strstr(status, "200 OK")==nil)){
  233. free(status);
  234. close(netfd);
  235. werrstr("did not get requested range");
  236. return nil;
  237. }
  238. free(status);
  239. data = emalloc9p(b->len);
  240. if(Bread(&netbio, data, b->len) != b->len){
  241. free(data);
  242. close(netfd);
  243. werrstr("not enough bytes read");
  244. return nil;
  245. }
  246. b->p = data;
  247. close(netfd);
  248. return data;
  249. }
  250. void
  251. httpfilereadproc(void*)
  252. {
  253. Block *b;
  254. threadsetname("httpfilereadproc");
  255. for(;;){
  256. b = recvp(httpchan);
  257. if(b == nil)
  258. continue;
  259. if(getrange(b) == nil)
  260. sysfatal("getrange: %r");
  261. sendp(finishchan, b);
  262. }
  263. }
  264. typedef struct Tab Tab;
  265. struct Tab
  266. {
  267. char *name;
  268. ulong mode;
  269. };
  270. Tab tab[] =
  271. {
  272. "/", DMDIR|0555,
  273. nil, 0444,
  274. };
  275. static void
  276. fillstat(Dir *d, uvlong path)
  277. {
  278. Tab *t;
  279. memset(d, 0, sizeof(*d));
  280. d->uid = estrdup9p(user);
  281. d->gid = estrdup9p(user);
  282. d->qid.path = path;
  283. d->atime = d->mtime = time0;
  284. t = &tab[TYPE(path)];
  285. d->name = estrdup9p(t->name);
  286. d->length = size;
  287. d->qid.type = t->mode>>24;
  288. d->mode = t->mode;
  289. }
  290. static void
  291. fsattach(Req *r)
  292. {
  293. if(r->ifcall.aname && r->ifcall.aname[0]){
  294. respond(r, "invalid attach specifier");
  295. return;
  296. }
  297. r->fid->qid.path = PATH(Qroot, 0);
  298. r->fid->qid.type = QTDIR;
  299. r->fid->qid.vers = 0;
  300. r->ofcall.qid = r->fid->qid;
  301. respond(r, nil);
  302. }
  303. static void
  304. fsstat(Req *r)
  305. {
  306. fillstat(&r->d, r->fid->qid.path);
  307. respond(r, nil);
  308. }
  309. static int
  310. rootgen(int i, Dir *d, void*)
  311. {
  312. i += Qroot + 1;
  313. if(i <= Qfile){
  314. fillstat(d, i);
  315. return 0;
  316. }
  317. return -1;
  318. }
  319. static char*
  320. fswalk1(Fid *fid, char *name, Qid *qid)
  321. {
  322. int i;
  323. ulong path;
  324. path = fid->qid.path;
  325. if(!(fid->qid.type & QTDIR))
  326. return "walk in non-directory";
  327. if(strcmp(name, "..") == 0){
  328. switch(TYPE(path)){
  329. case Qroot:
  330. return nil;
  331. default:
  332. return "bug in fswalk1";
  333. }
  334. }
  335. i = TYPE(path) + 1;
  336. while(i < nelem(tab)){
  337. if(strcmp(name, tab[i].name) == 0){
  338. qid->path = PATH(i, NUM(path));
  339. qid->type = tab[i].mode>>24;
  340. return nil;
  341. }
  342. if(tab[i].mode & DMDIR)
  343. break;
  344. i++;
  345. }
  346. return "directory entry not found";
  347. }
  348. vlong
  349. getfilesize(void)
  350. {
  351. char *status;
  352. vlong size;
  353. int netfd;
  354. static Biobuf netbio;
  355. netfd = dialhttp(&netbio);
  356. fprint(netfd,
  357. "HEAD %s HTTP/1.1\r\n"
  358. "Host: %s\r\n"
  359. "Accept-Encoding:\r\n"
  360. "\r\n",
  361. get, host);
  362. status = readhttphdr(&netbio, &size);
  363. if(strstr(status, "200 OK") == nil){
  364. werrstr("%s", status);
  365. size = -1;
  366. }
  367. free(status);
  368. close(netfd);
  369. return size;
  370. }
  371. void
  372. fileread(Req *r)
  373. {
  374. Block *b;
  375. if(r->ifcall.offset > size){
  376. respond(r, nil);
  377. return;
  378. }
  379. if((b = findblock(&cache, r->ifcall.offset)) != nil){
  380. readfrom(r, b);
  381. return;
  382. }
  383. if((b = findblock(&inprogress, r->ifcall.offset)) == nil){
  384. b = emalloc9p(sizeof(Block));
  385. b->off = r->ifcall.offset - (r->ifcall.offset % Blocksize);
  386. addblock(&inprogress, b);
  387. if(inprogress.first == b)
  388. sendp(httpchan, b);
  389. }
  390. queuereq(b, r);
  391. }
  392. static void
  393. fsopen(Req *r)
  394. {
  395. if(r->ifcall.mode != OREAD){
  396. respond(r, "permission denied");
  397. return;
  398. }
  399. respond(r, nil);
  400. }
  401. void
  402. finishthread(void*)
  403. {
  404. Block *b;
  405. Req *r, *nextr;
  406. threadsetname("finishthread");
  407. for(;;){
  408. b = recvp(finishchan);
  409. assert(b == inprogress.first);
  410. inprogress.first = b->link;
  411. ncache++;
  412. if(ncache >= mcache)
  413. evictblock(&cache);
  414. addblock(&cache, b);
  415. for(r=b->rq; r; r=nextr){
  416. nextr = r->aux;
  417. readfrom(r, b);
  418. }
  419. b->rq = nil;
  420. if(inprogress.first)
  421. sendp(httpchan, inprogress.first);
  422. }
  423. }
  424. void
  425. fsnetproc(void*)
  426. {
  427. Req *r;
  428. Block *b;
  429. threadcreate(finishthread, nil, 8192);
  430. threadsetname("fsnetproc");
  431. for(;;){
  432. r = recvp(reqchan);
  433. switch(r->ifcall.type){
  434. case Tflush:
  435. b = findblock(&inprogress, r->ifcall.offset);
  436. delreq(b, r->oldreq);
  437. respond(r->oldreq, "interrupted");
  438. respond(r, nil);
  439. break;
  440. case Tread:
  441. fileread(r);
  442. break;
  443. default:
  444. respond(r, "bug in fsthread");
  445. break;
  446. }
  447. }
  448. }
  449. static void
  450. fsflush(Req *r)
  451. {
  452. sendp(reqchan, r);
  453. }
  454. static void
  455. fsread(Req *r)
  456. {
  457. char e[ERRMAX];
  458. ulong path;
  459. path = r->fid->qid.path;
  460. switch(TYPE(path)){
  461. case Qroot:
  462. dirread9p(r, rootgen, nil);
  463. respond(r, nil);
  464. break;
  465. case Qfile:
  466. sendp(reqchan, r);
  467. break;
  468. default:
  469. snprint(e, sizeof(e), "bug in fsread path=%lux", path);
  470. respond(r, e);
  471. break;
  472. }
  473. }
  474. Srv fs =
  475. {
  476. .attach= fsattach,
  477. .walk1= fswalk1,
  478. .open= fsopen,
  479. .read= fsread,
  480. .stat= fsstat,
  481. .flush= fsflush,
  482. .end= hangupclient,
  483. };
  484. void
  485. threadmain(int argc, char **argv)
  486. {
  487. char *defport, *mtpt, *srvname, *p;
  488. mtpt = nil;
  489. srvname = nil;
  490. ARGBEGIN{
  491. case 'D':
  492. chatty9p++;
  493. break;
  494. case 'd':
  495. debug++;
  496. break;
  497. case 's':
  498. srvname = EARGF(usage());
  499. break;
  500. case 'm':
  501. mtpt = EARGF(usage());
  502. break;
  503. case 'c':
  504. mcache = atoi(EARGF(usage()));
  505. break;
  506. case 'f':
  507. file = EARGF(usage());
  508. break;
  509. case 'x':
  510. net = smprint("%s/net", EARGF(usage()));
  511. break;
  512. default:
  513. usage();
  514. }ARGEND;
  515. if(srvname == nil && mtpt == nil)
  516. mtpt = ".";
  517. if(argc < 1)
  518. usage();
  519. if(mcache <= 0)
  520. mcache = 32;
  521. time0 = time(0);
  522. host = url = estrdup9p(argv[0]);
  523. defport = nil;
  524. if(!cistrncmp(url, "https://", 8)){
  525. host += 8;
  526. usetls = 1;
  527. defport = "https";
  528. }else if(!cistrncmp(url, "http://", 7)){
  529. host += 7;
  530. defport = "http";
  531. }else
  532. sysfatal("unsupported url: %s", url);
  533. if((p = strchr(host, '/')) != nil){
  534. get = estrdup9p(p);
  535. *p = '\0';
  536. }else
  537. get = "/";
  538. port = strchr(host, ':');
  539. if(port != nil)
  540. *port++ = '\0';
  541. else
  542. port = defport;
  543. if(file == nil){
  544. file = strrchr(get, '/')+1;
  545. if(*file == 0)
  546. file = "index";
  547. }
  548. tab[Qfile].name = file;
  549. user = getuser();
  550. size = getfilesize();
  551. if(size < 0)
  552. sysfatal("getfilesize: %r");
  553. reqchan = chancreate(sizeof(Req*), 0);
  554. httpchan = chancreate(sizeof(Block*), 0);
  555. finishchan = chancreate(sizeof(Block*), 0);
  556. procrfork(fsnetproc, nil, Stacksize, RFNAMEG|RFNOTEG);
  557. procrfork(httpfilereadproc, nil, Stacksize, RFNAMEG|RFNOTEG);
  558. threadpostmountsrv(&fs, srvname, mtpt, MBEFORE);
  559. threadexits(0);
  560. }