client.c 6.4 KB

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