client.c 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. #include <u.h>
  2. #include <libc.h>
  3. #include <bio.h>
  4. #include <ip.h>
  5. #include <plumb.h>
  6. #include <thread.h>
  7. #include <fcall.h>
  8. #include <9p.h>
  9. #include "dat.h"
  10. #include "fns.h"
  11. int nclient;
  12. Client **client;
  13. static void clientthread(void*);
  14. int
  15. newclient(int plumbed)
  16. {
  17. int i;
  18. Client *c;
  19. for(i=0; i<nclient; i++)
  20. if(client[i]->ref==0)
  21. return i;
  22. c = emalloc(sizeof(Client));
  23. c->plumbed = plumbed;
  24. c->creq = chancreate(sizeof(Req*), 8);
  25. threadcreate(clientthread, c, STACK);
  26. c->io = ioproc();
  27. c->num = nclient;
  28. c->ctl = globalctl;
  29. clonectl(&c->ctl);
  30. if(nclient%16 == 0)
  31. client = erealloc(client, (nclient+16)*sizeof(client[0]));
  32. client[nclient++] = c;
  33. return nclient-1;
  34. }
  35. void
  36. closeclient(Client *c)
  37. {
  38. if(--c->ref == 0){
  39. if(c->bodyopened){
  40. if(c->url && c->url->close)
  41. (*c->url->close)(c);
  42. c->bodyopened = 0;
  43. }
  44. free(c->contenttype);
  45. c->contenttype = nil;
  46. free(c->postbody);
  47. c->postbody = nil;
  48. freeurl(c->url);
  49. c->url = nil;
  50. free(c->redirect);
  51. c->redirect = nil;
  52. free(c->authenticate);
  53. c->authenticate = nil;
  54. c->npostbody = 0;
  55. c->havepostbody = 0;
  56. c->bodyopened = 0;
  57. }
  58. }
  59. void
  60. clonectl(Ctl *c)
  61. {
  62. if(c->useragent)
  63. c->useragent = estrdup(c->useragent);
  64. }
  65. void
  66. clientbodyopen(Client *c, Req *r)
  67. {
  68. char e[ERRMAX], *next;
  69. int i, nauth;
  70. Url *u;
  71. nauth = 0;
  72. next = nil;
  73. for(i=0; i<=c->ctl.redirectlimit; i++){
  74. if(c->url == nil){
  75. werrstr("nil url");
  76. goto Error;
  77. }
  78. if(c->url->open == nil){
  79. werrstr("unsupported url type");
  80. goto Error;
  81. }
  82. if(fsdebug)
  83. fprint(2, "try %s\n", c->url->url);
  84. if(c->url->open(c, c->url) < 0){
  85. Error:
  86. if(next)
  87. fprint(2, "next %s (but for error)\n", next);
  88. free(next);
  89. rerrstr(e, sizeof e);
  90. c->iobusy = 0;
  91. if(r != nil)
  92. r->fid->omode = -1;
  93. closeclient(c); /* not opening */
  94. if(r != nil)
  95. respond(r, e);
  96. return;
  97. }
  98. if (c->authenticate && nauth++ < 1)
  99. continue;
  100. if(!c->redirect)
  101. break;
  102. next = c->redirect;
  103. c->redirect = nil;
  104. if(i==c->ctl.redirectlimit){
  105. werrstr("redirect limit reached");
  106. goto Error;
  107. }
  108. if((u = parseurl(next, c->url)) == nil)
  109. goto Error;
  110. if(urldebug)
  111. fprint(2, "parseurl %s got scheme %d\n", next, u->ischeme);
  112. if(u->ischeme == USunknown){
  113. werrstr("redirect with unknown URL scheme");
  114. goto Error;
  115. }
  116. if(u->ischeme == UScurrent){
  117. werrstr("redirect to URL relative to current document");
  118. goto Error;
  119. }
  120. freeurl(c->url);
  121. c->url = u;
  122. }
  123. free(next);
  124. c->iobusy = 0;
  125. if(r != nil)
  126. respond(r, nil);
  127. }
  128. void
  129. plumburl(char *url, char *base)
  130. {
  131. int i;
  132. Client *c;
  133. Url *ubase, *uurl;
  134. ubase = nil;
  135. if(base){
  136. ubase = parseurl(base, nil);
  137. if(ubase == nil)
  138. return;
  139. }
  140. uurl = parseurl(url, ubase);
  141. if(uurl == nil){
  142. freeurl(ubase);
  143. return;
  144. }
  145. i = newclient(1);
  146. c = client[i];
  147. c->ref++;
  148. c->baseurl = ubase;
  149. c->url = uurl;
  150. sendp(c->creq, nil);
  151. }
  152. void
  153. clientbodyread(Client *c, Req *r)
  154. {
  155. char e[ERRMAX];
  156. if(c->url->read == nil){
  157. respond(r, "unsupported url type");
  158. return;
  159. }
  160. if(c->url->read(c, r) < 0){
  161. rerrstr(e, sizeof e);
  162. c->iobusy = 0;
  163. respond(r, e);
  164. return;
  165. }
  166. c->iobusy = 0;
  167. respond(r, nil);
  168. }
  169. static void
  170. clientthread(void *a)
  171. {
  172. Client *c;
  173. Req *r;
  174. c = a;
  175. if(c->plumbed) {
  176. recvp(c->creq);
  177. if(c->url == nil){
  178. fprint(2, "bad url got plumbed\n");
  179. return;
  180. }
  181. clientbodyopen(c, nil);
  182. replumb(c);
  183. }
  184. while((r = recvp(c->creq)) != nil){
  185. if(fsdebug)
  186. fprint(2, "clientthread %F\n", &r->ifcall);
  187. switch(r->ifcall.type){
  188. case Topen:
  189. if(c->plumbed) {
  190. c->plumbed = 0;
  191. c->ref--; /* from plumburl() */
  192. respond(r, nil);
  193. }
  194. else
  195. clientbodyopen(c, r);
  196. break;
  197. case Tread:
  198. clientbodyread(c, r);
  199. break;
  200. case Tflush:
  201. respond(r, nil);
  202. }
  203. if(fsdebug)
  204. fprint(2, "clientthread finished req\n");
  205. }
  206. }
  207. enum
  208. {
  209. Bool,
  210. Int,
  211. String,
  212. XUrl,
  213. Fn,
  214. };
  215. typedef struct Ctab Ctab;
  216. struct Ctab {
  217. char *name;
  218. int type;
  219. void *offset;
  220. };
  221. Ctab ctltab[] = {
  222. "acceptcookies", Bool, (void*)offsetof(Ctl, acceptcookies),
  223. "sendcookies", Bool, (void*)offsetof(Ctl, sendcookies),
  224. "redirectlimit", Int, (void*)offsetof(Ctl, redirectlimit),
  225. "useragent", String, (void*)offsetof(Ctl, useragent),
  226. };
  227. Ctab globaltab[] = {
  228. "chatty9p", Int, &chatty9p,
  229. "fsdebug", Int, &fsdebug,
  230. "cookiedebug", Int, &cookiedebug,
  231. "urldebug", Int, &urldebug,
  232. "httpdebug", Int, &httpdebug,
  233. };
  234. Ctab clienttab[] = {
  235. "baseurl", XUrl, (void*)offsetof(Client, baseurl),
  236. "url", XUrl, (void*)offsetof(Client, url),
  237. };
  238. static Ctab*
  239. findcmd(char *cmd, Ctab *tab, int ntab)
  240. {
  241. int i;
  242. for(i=0; i<ntab; i++)
  243. if(strcmp(tab[i].name, cmd) == 0)
  244. return &tab[i];
  245. return nil;
  246. }
  247. static void
  248. parseas(Req *r, char *arg, int type, void *a)
  249. {
  250. Url *u;
  251. char e[ERRMAX];
  252. switch(type){
  253. case Bool:
  254. if(strcmp(arg, "on")==0 || strcmp(arg, "1")==0)
  255. *(int*)a = 1;
  256. else
  257. *(int*)a = 0;
  258. break;
  259. case String:
  260. free(*(char**)a);
  261. *(char**)a = estrdup(arg);
  262. break;
  263. case XUrl:
  264. u = parseurl(arg, nil);
  265. if(u == nil){
  266. snprint(e, sizeof e, "parseurl: %r");
  267. respond(r, e);
  268. return;
  269. }
  270. freeurl(*(Url**)a);
  271. *(Url**)a = u;
  272. break;
  273. case Int:
  274. if(strcmp(arg, "on")==0)
  275. *(int*)a = 1;
  276. else
  277. *(int*)a = atoi(arg);
  278. break;
  279. }
  280. respond(r, nil);
  281. }
  282. int
  283. ctlwrite(Req *r, Ctl *ctl, char *cmd, char *arg)
  284. {
  285. void *a;
  286. Ctab *t;
  287. if((t = findcmd(cmd, ctltab, nelem(ctltab))) == nil)
  288. return 0;
  289. a = (void*)((uintptr)ctl+(uintptr)t->offset);
  290. parseas(r, arg, t->type, a);
  291. return 1;
  292. }
  293. int
  294. clientctlwrite(Req *r, Client *c, char *cmd, char *arg)
  295. {
  296. void *a;
  297. Ctab *t;
  298. if((t = findcmd(cmd, clienttab, nelem(clienttab))) == nil)
  299. return 0;
  300. a = (void*)((uintptr)c+(uintptr)t->offset);
  301. parseas(r, arg, t->type, a);
  302. return 1;
  303. }
  304. int
  305. globalctlwrite(Req *r, char *cmd, char *arg)
  306. {
  307. void *a;
  308. Ctab *t;
  309. if((t = findcmd(cmd, globaltab, nelem(globaltab))) == nil)
  310. return 0;
  311. a = t->offset;
  312. parseas(r, arg, t->type, a);
  313. return 1;
  314. }
  315. static void
  316. ctlfmt(Ctl *c, char *s)
  317. {
  318. int i;
  319. void *a;
  320. char *t;
  321. for(i=0; i<nelem(ctltab); i++){
  322. a = (void*)((uintptr)c+(uintptr)ctltab[i].offset);
  323. switch(ctltab[i].type){
  324. case Bool:
  325. s += sprint(s, "%s %s\n", ctltab[i].name, *(int*)a ? "on" : "off");
  326. break;
  327. case Int:
  328. s += sprint(s, "%s %d\n", ctltab[i].name, *(int*)a);
  329. break;
  330. case String:
  331. t = *(char**)a;
  332. if(t != nil)
  333. s += sprint(s, "%s %.*s%s\n", ctltab[i].name, utfnlen(t, 100), t, strlen(t)>100 ? "..." : "");
  334. break;
  335. }
  336. }
  337. }
  338. void
  339. ctlread(Req *r, Client *c)
  340. {
  341. char buf[1024];
  342. sprint(buf, "%11d \n", c->num);
  343. ctlfmt(&c->ctl, buf+strlen(buf));
  344. readstr(r, buf);
  345. respond(r, nil);
  346. }
  347. void
  348. globalctlread(Req *r)
  349. {
  350. char buf[1024], *s;
  351. int i;
  352. s = buf;
  353. for(i=0; i<nelem(globaltab); i++)
  354. s += sprint(s, "%s %d\n", globaltab[i].name, *(int*)globaltab[i].offset);
  355. ctlfmt(&globalctl, s);
  356. readstr(r, buf);
  357. respond(r, nil);
  358. }