/** * Fills @val with second part of special HTTP header containing the header * value. */ void tfw_http_msg_hdr_val(TfwStr *hdr, unsigned id, TfwStr *val) { static const size_t hdr_lens[] = { [TFW_HTTP_HDR_HOST] = sizeof("Host:") - 1, [TFW_HTTP_HDR_CONTENT_LENGTH] = sizeof("Content-Length:") - 1, [TFW_HTTP_HDR_CONTENT_TYPE] = sizeof("Content-Type:") - 1, [TFW_HTTP_HDR_CONNECTION] = sizeof("Connection:") - 1, [TFW_HTTP_HDR_X_FORWARDED_FOR] = sizeof("X-Forwarded-For:") - 1, [TFW_HTTP_HDR_USER_AGENT] = sizeof("User-Agent:") - 1, [TFW_HTTP_HDR_COOKIE] = sizeof("Cookie:") - 1, }; TfwStr *c, *end; int nlen = hdr_lens[id]; BUG_ON(TFW_STR_DUP(hdr)); BUG_ON(id >= TFW_HTTP_HDR_RAW); /* Only Host: header is allowed to be empty * If header string is plain, it is always empty header. * Not empty headers are compount strings. */ BUG_ON(id == TFW_HTTP_HDR_HOST ? (nlen > hdr->len) : (nlen >= hdr->len || TFW_STR_PLAIN(hdr))); *val = *hdr; /* Field value, if it exist, lies in the separate chunk. * So we skip several first chunks, containing field name, * to get the field value. If we have field with empty value, * we get an empty string with val->len = 0 and val->ptr from the * last name's chunk, but it is unimportant. */ TFW_STR_FOR_EACH_CHUNK(c, hdr, end) { BUG_ON(!c->len); if (nlen > 0) { nlen -= c->len; val->len -= c->len; } else if (unlikely(((char *)c->ptr)[0] == ' ' || ((char *)c->ptr)[0] == '\t')) { /* * RFC 7230: skip OWS before header field. * In most cases OWS is on the same chunk with * the header name. * Header field-value always begins at new chunk. */ val->len -= c->len; } else { break; } TFW_STR_CHUNKN_SUB(val, 1); }
/** * Fills @val with second part of specula HTTP header containing the header * value. */ void tfw_http_msg_hdr_val(TfwStr *hdr, int id, TfwStr *val) { static const size_t hdr_lens[] = { [TFW_HTTP_HDR_HOST] = sizeof("Host:") - 1, [TFW_HTTP_HDR_CONTENT_LENGTH] = sizeof("Content-Length:") - 1, [TFW_HTTP_HDR_CONNECTION] = sizeof("Connection:") - 1, [TFW_HTTP_HDR_X_FORWARDED_FOR] = sizeof("X-Forwarded-For:") - 1, }; TfwStr *c; int nlen = hdr_lens[id]; BUG_ON(TFW_STR_PLAIN(hdr)); BUG_ON(TFW_STR_DUP(hdr)); BUG_ON(nlen >= hdr->len); BUG_ON(id >= TFW_HTTP_HDR_RAW); *val = *hdr; val->len -= nlen; TFW_STR_FOR_EACH_CHUNK(c, hdr, { BUG_ON(!c->len); BUG_ON(c->len >= val->len); if (nlen > 0) { nlen -= val->len; } else if (unlikely(((char *)c->ptr)[0] == ' ' || ((char *)c->ptr)[0] == '\t')) { /* * RFC 7230: skip OWS before header field. * In most cases OWS is on the same chunk with * the header name. * Header field always begins at new chunk. */ val->len -= c->len; } else { break; } TFW_STR_CHUNKN_SUB(val, 1); });
void tfw_str_del_chunk(TfwStr *str, int id) { unsigned int cn = TFW_STR_CHUNKN(str); if (unlikely(TFW_STR_PLAIN(str))) return; BUG_ON(TFW_STR_DUP(str)); BUG_ON(id >= cn); if (TFW_STR_CHUNKN(str) == 2) { /* Just fall back to plain string. */ *str = *((TfwStr *)str->ptr + (id ^ 1)); return; } str->len -= TFW_STR_CHUNK(str, id)->len; TFW_STR_CHUNKN_SUB(str, 1); /* Move all chunks after @id. */ memmove((TfwStr *)str->ptr + id, (TfwStr *)str->ptr + id + 1, (cn - id - 1) * sizeof(TfwStr)); }
int tfw_strcpy(TfwStr *dst, const TfwStr *src) { int n1, n2, o1 = 0, o2 = 0, chunks = 0; int mode = (TFW_STR_PLAIN(src) << 1) | TFW_STR_PLAIN(dst); TfwStr *c1, *c2, *end; BUG_ON(TFW_STR_DUP(dst)); BUG_ON(TFW_STR_DUP(src)); /* After the check we don't need to control @dst chunks overrun. */ if (unlikely(src->len > dst->len)) return -E2BIG; switch (mode) { case 3: /* The both are plain. */ memcpy(dst->ptr, src->ptr, min(src->len, dst->len)); break; case 1: /* @src is compound, @dst is plain. */ n1 = TFW_STR_CHUNKN(src); end = (TfwStr *)src->ptr + n1; for (c1 = (TfwStr *)src->ptr; c1 < end; ++c1) { memcpy((char *)dst->ptr + o2, c1->ptr, c1->len); o2 += c1->len; } BUG_ON(o2 != src->len); break; case 2: /* @src is plain, @dst is compound. */ for (c2 = (TfwStr *)dst->ptr; o1 < src->len; ++c2) { /* Update length of the last chunk. */ c2->len = min(c2->len, src->len - o1); memcpy(c2->ptr, (char *)src->ptr + o1, c2->len); ++chunks; o1 += c2->len; } break; case 0: /* The both are compound. */ n1 = TFW_STR_CHUNKN(src); n2 = TFW_STR_CHUNKN(dst); c1 = (TfwStr *)src->ptr; c2 = (TfwStr *)dst->ptr; end = c1 + n1 - 1; while (1) { int _n = min(c1->len - o1, c2->len - o2); memcpy((char *)c2->ptr + o2, (char *)c1->ptr + o1, _n); if (c1 == end && _n == c1->len - o1) { /* Adjust @dst last chunk length. */ c2->len = o2 + _n; ++chunks; break; } if (c1->len - o1 == c2->len - o2) { ++c1; ++c2; ++chunks; o1 = o2 = 0; } else if (_n == c1->len - o1) { ++c1; o1 = 0; o2 += _n; } else { ++c2; ++chunks; o2 = 0; o1 += _n; } } } /* Set resulting number of chunks, forget about others. */ __TFW_STR_CHUNKN_SET(dst, chunks); dst->len = src->len; return 0; }