/* * Consume bytes until the whole header is got (up to a "\r\n\r\n" sequence) * (Also unfold multi-line fields and strip '\r' chars from header) */ static int Cache_get_header(CacheEntry_t *entry, const char *buf, size_t buf_size) { size_t N, i; Dstr *hdr = entry->Header; /* Header finishes when N = 2 */ N = (hdr->len && hdr->str[hdr->len - 1] == '\n'); for (i = 0; i < buf_size && N < 2; ++i) { if (buf[i] == '\r' || !buf[i]) continue; if (N == 1 && (buf[i] == ' ' || buf[i] == '\t')) { /* unfold multiple-line header */ _MSG("Multiple-line header!\n"); dStr_erase(hdr, hdr->len - 1, 1); } N = (buf[i] == '\n') ? N + 1 : 0; dStr_append_c(hdr, buf[i]); } if (N == 2) { /* Got whole header */ _MSG("Header [buf_size=%d]\n%s", i, hdr->str); entry->Flags |= CA_GotHeader; dStr_fit(hdr); /* Return number of header bytes in 'buf' [1 based] */ return i; } return 0; }
/* * Decode chunked data */ static Dstr *Decode_chunked(Decode *dc, const char *instr, int inlen) { char *inputPtr, *eol; int inputRemaining; int chunkRemaining = *((int *)dc->state); Dstr *output = dStr_sized_new(inlen); dStr_append_l(dc->leftover, instr, inlen); inputPtr = dc->leftover->str; inputRemaining = dc->leftover->len; while (inputRemaining > 0) { if (chunkRemaining > 2) { /* chunk body to copy */ int copylen = MIN(chunkRemaining - 2, inputRemaining); dStr_append_l(output, inputPtr, copylen); chunkRemaining -= copylen; inputRemaining -= copylen; inputPtr += copylen; } if ((chunkRemaining == 2) && (inputRemaining > 0)) { /* CR to discard */ chunkRemaining--; inputRemaining--; inputPtr++; } if ((chunkRemaining == 1) && (inputRemaining > 0)) { /* LF to discard */ chunkRemaining--; inputRemaining--; inputPtr++; } /* * A chunk has a one-line header that begins with the chunk length * in hexadecimal. */ if (!(eol = (char *)memchr(inputPtr, '\n', inputRemaining))) { break; /* We don't have the whole line yet. */ } if (!(chunkRemaining = strtol(inputPtr, NULL, 0x10))) { break; /* A chunk length of 0 means we're done! */ } inputRemaining -= (eol - inputPtr) + 1; inputPtr = eol + 1; chunkRemaining += 2; /* CRLF at the end of every chunk */ } /* If we have a partial chunk header, save it for next time. */ dStr_erase(dc->leftover, 0, inputPtr - dc->leftover->str); *(int *)dc->state = chunkRemaining; return output; }
/* * Return a newlly allocated string with the next dpip token in the socket. * Return value: token string and length on success, NULL otherwise. * (useful for handling null characters in the data stream) */ char *a_Dpip_dsh_read_token2(Dsh *dsh, int blocking, int *DataSize) { char *p, *ret = NULL; *DataSize = 0; /* Read all available data without blocking */ Dpip_dsh_read(dsh, 0); /* switch mode upon request */ if (dsh->mode & DPIP_LAST_TAG) dsh->mode = DPIP_RAW; if (blocking) { if (dsh->mode & DPIP_TAG) { /* Only wait for data when the tag is incomplete */ if (!strstr(dsh->rdbuf->str, DPIP_TAG_END)) { do { Dpip_dsh_read(dsh, 1); p = strstr(dsh->rdbuf->str, DPIP_TAG_END); } while (!p && dsh->status == EAGAIN); } } else if (dsh->mode & DPIP_RAW) { /* Wait for data when the buffer is empty and there's no ERR/EOF */ while (dsh->rdbuf->len == 0 && dsh->status != DPIP_ERROR && dsh->status != DPIP_EOF) Dpip_dsh_read(dsh, 1); } } if (dsh->mode & DPIP_TAG) { /* return a full tag */ if ((p = strstr(dsh->rdbuf->str, DPIP_TAG_END))) { ret = dStrndup(dsh->rdbuf->str, p - dsh->rdbuf->str + 3); *DataSize = p - dsh->rdbuf->str + 3; dStr_erase(dsh->rdbuf, 0, p - dsh->rdbuf->str + 3); if (strstr(ret, DPIP_MODE_SWITCH_TAG)) dsh->mode |= DPIP_LAST_TAG; } } else { /* raw mode, return what we have "as is" */ if (dsh->rdbuf->len > 0) { ret = dStrndup(dsh->rdbuf->str, dsh->rdbuf->len); *DataSize = dsh->rdbuf->len; dStr_truncate(dsh->rdbuf, 0); } } return ret; }
/* * Return value: 0 on success or empty buffer, * 1..DataSize sent, -1 eagain, or -3 on big Error */ int a_Dpip_dsh_tryflush(Dsh *dsh) { int st; if (dsh->wrbuf->len == 0) { st = 0; } else { st = Dpip_dsh_write(dsh, 1, dsh->wrbuf->str, dsh->wrbuf->len); if (st > 0) { /* update internal buffer */ dStr_erase(dsh->wrbuf, 0, st); } } return (dsh->wrbuf->len == 0) ? 0 : st; }
/* * Translate to desired character set (UTF-8) */ static Dstr *Decode_charset(Decode *dc, const char *instr, int inlen) { inbuf_t *inPtr; char *outPtr; size_t inLeft, outRoom; Dstr *output = dStr_new(""); int rc = 0; dStr_append_l(dc->leftover, instr, inlen); inPtr = dc->leftover->str; inLeft = dc->leftover->len; while ((rc != EINVAL) && (inLeft > 0)) { outPtr = dc->buffer; outRoom = bufsize; rc = iconv((iconv_t)dc->state, &inPtr, &inLeft, &outPtr, &outRoom); // iconv() on success, number of bytes converted // -1, errno == EILSEQ illegal byte sequence found // EINVAL partial character ends source buffer // E2BIG destination buffer is full dStr_append_l(output, dc->buffer, bufsize - outRoom); if (rc == -1) rc = errno; if (rc == EILSEQ){ inPtr++; inLeft--; dStr_append_l(output, utf8_replacement_char, sizeof(utf8_replacement_char) - 1); } } dStr_erase(dc->leftover, 0, dc->leftover->len - inLeft); return output; }