+ if(cn->state == STATE_TXREQ) {
+ HTDEBUG("connected, sending request\n");
+ if(pollflags & POLLIN) {
+ close(cn->fd);
+ cn->fd = -1;
+ return(-1);
+ }
+ if(pollflags & POLLOUT) {
+ if((ret = send(cn->fd, cn->outbuf, cn->outbufdata, MSG_DONTWAIT)) < 0) {
+ if(errno != EAGAIN) {
+ close(cn->fd);
+ cn->fd = -1;
+ return(-1);
+ }
+ } else {
+ memmove(cn->outbuf, cn->outbuf + ret, cn->outbufdata -= ret);
+ if(cn->outbufdata == 0)
+ cn->state = STATE_RXRES;
+ }
+ }
+ }
+ /*
+ * All further states will do receiving
+ */
+ if(pollflags & POLLIN) {
+ if(cn->fd == -1) {
+ ret = 0;
+ } else {
+ if((ret = recv(cn->fd, rxbuf, sizeof(rxbuf), MSG_DONTWAIT)) < 0) {
+ HTDEBUG("error in recv: %s\n", strerror(errno));
+ if(errno != EAGAIN) {
+ close(cn->fd);
+ cn->fd = -1;
+ return(-1);
+ }
+ return(0);
+ } else if(ret == 0) {
+ HTDEBUG("EOF received\n");
+ close(cn->fd);
+ cn->fd = -1;
+ } else {
+ bufcat(cn->inbuf, rxbuf, ret);
+ HTDEBUG("received %i bytes of raw data, %i bytes in buffer\n", ret, cn->inbufdata);
+ }
+ }
+ }
+ /* We need to loop until all processable data has been processed,
+ * or we won't get called again */
+ do {
+ done = 1;
+ if(cn->state == STATE_RXRES) {
+ if(ret == 0) {
+ if(cn->rescode == 0) {
+ HTDEBUG("received EOF before response, flaggin EPROTO\n");
+ errno = EPROTO;
+ return(-1);
+ }
+ HTDEBUG("EOF after headers, no body\n");
+ cn->state = STATE_DONE;
+ } else {
+ /* Headers shouldn't be this long! */
+ if(cn->inbufdata >= 65536) {
+ HTDEBUG("got suspiciously long headers, flagging ENOMEM\n");
+ close(cn->fd);
+ cn->fd = -1;
+ errno = ENOMEM;
+ return(-1);
+ }
+ HTDEBUG("received some header data\n");
+ }
+ if(cn->rescode == 0) {
+ if((p = memchr(cn->inbuf, '\n', cn->inbufdata)) != NULL) {
+ HTDEBUG("received response line\n");
+ *(p++) = 0;
+ trimcr(cn->inbuf);
+ p2 = cn->inbuf;
+ if((p3 = strchr(p2, ' ')) == NULL) {
+ close(cn->fd);
+ cn->fd = -1;
+ errno = EPROTO;
+ return(-1);
+ }
+ *(p3++) = 0;
+ if(strncmp(p2, "HTTP/", 5)) {
+ close(cn->fd);
+ cn->fd = -1;
+ errno = EPROTO;
+ return(-1);
+ }
+ p2 = p3;
+ if((p3 = strchr(p2, ' ')) == NULL) {
+ close(cn->fd);
+ cn->fd = -1;
+ errno = EPROTO;
+ return(-1);
+ }
+ *(p3++) = 0;
+ cn->rescode = atoi(p2);
+ if((cn->rescode < 100) || (cn->rescode >= 1000)) {
+ close(cn->fd);
+ cn->fd = -1;
+ errno = EPROTO;
+ return(-1);
+ }
+ cn->resstr = sstrdup(p3);
+ memmove(cn->inbuf, p, cn->inbufdata -= (p - cn->inbuf));
+ HTDEBUG("parsed response line (%i, %s)\n", cn->rescode, cn->resstr);
+ }
+ }
+ if(cn->rescode != 0) {
+ HTDEBUG("parsing some headers\n");
+ if(parseheaders(cn)) {
+ HTDEBUG("all headers received\n");
+ if(((p = spfind(cn->headers, "transfer-encoding")) != NULL) && !strcasecmp(p, "chunked")) {
+ HTDEBUG("doing chunky decoding\n");
+ cn->chl = -1;
+ cn->state = STATE_RXCHLEN;
+ } else {
+ HTDEBUG("receiving normally\n");
+ cn->state = STATE_RXBODY;
+ }
+ }
+ }
+ }
+ if(cn->state == STATE_RXBODY) {
+ if(ret == 0) {
+ HTDEBUG("EOF in body, flagging as done\n");
+ cn->state = STATE_DONE;
+ } else {
+ bufcat(cn->databuf, cn->inbuf, cn->inbufdata);
+ HTDEBUG("transferred %i bytes from inbuf to databuf, %i bytes now in databuf\n", cn->inbufdata, cn->databufdata);
+ cn->rxd += cn->inbufdata;
+ cn->inbufdata = 0;
+ if((cn->tlen != -1) && (cn->rxd >= cn->tlen)) {
+ HTDEBUG("received Content-Length, flagging as done\n");
+ cn->state = STATE_DONE;
+ }
+ }
+ }
+ if(cn->state == STATE_RXCHLEN) {
+ HTDEBUG("trying to parse chunk length\n");
+ while(((p = memchr(cn->inbuf, '\n', cn->inbufdata)) != NULL) && (cn->chl == -1)) {
+ *(p++) = 0;
+ trimcr(cn->inbuf);
+ HTDEBUG("trimmed chunk line: %s\n", cn->inbuf);
+ if(!*cn->inbuf)
+ goto skip;
+ cn->chl = strtol(cn->inbuf, NULL, 16);
+ HTDEBUG("parsed chunk length: %i\n", cn->chl);
+ skip:
+ memmove(cn->inbuf, p, cn->inbufdata -= (p - cn->inbuf));
+ }
+ if(cn->chl == 0) {
+ HTDEBUG("zero chunk length, looking for CRLF\n");
+ if((cn->inbuf[0] == '\r') && (cn->inbuf[1] == '\n')) {
+ HTDEBUG("ending CRLF gotten, flagging as done\n");
+ cn->state = STATE_DONE;
+ }
+ } else {
+ HTDEBUG("will read chunk\n");
+ cn->state = STATE_RXCHUNK;
+ }
+ }
+ if(cn->state == STATE_RXCHUNK) {
+ if(cn->inbufdata >= cn->chl) {
+ bufcat(cn->databuf, cn->inbuf, cn->chl);
+ memmove(cn->inbuf, cn->inbuf + cn->chl, cn->inbufdata -= cn->chl);
+ HTDEBUG("received final %i bytes of chunk, inbuf %i bytes, databuf %i bytes\n", cn->chl, cn->inbufdata, cn->databufdata);
+ cn->rxd += cn->chl;
+ cn->chl = 0;
+ cn->state = STATE_RXCHLEN;
+ done = 0;
+ } else {
+ bufcat(cn->databuf, cn->inbuf, cn->inbufdata);
+ cn->chl -= cn->inbufdata;
+ cn->rxd += cn->inbufdata;
+ HTDEBUG("received %i bytes of chunk, %i bytes remaining, %i bytes in databuf\n", cn->inbufdata, cn->chl, cn->databufdata);
+ cn->inbufdata = 0;
+ }
+ }
+ } while(!done);
+ return((cn->state == STATE_DONE)?1:0);