client.c 6.7 KB

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