HttpHdrRange.c
上传用户:liugui
上传日期:2007-01-04
资源大小:822k
文件大小:14k
源码类别:

代理服务器

开发平台:

Unix_Linux

  1. /*
  2.  * $Id: HttpHdrRange.c,v 1.19 1999/01/19 23:16:48 wessels Exp $
  3.  *
  4.  * DEBUG: section 64    HTTP Range Header
  5.  * AUTHOR: Alex Rousskov
  6.  *
  7.  * SQUID Internet Object Cache  http://squid.nlanr.net/Squid/
  8.  * ----------------------------------------------------------
  9.  *
  10.  *  Squid is the result of efforts by numerous individuals from the
  11.  *  Internet community.  Development is led by Duane Wessels of the
  12.  *  National Laboratory for Applied Network Research and funded by the
  13.  *  National Science Foundation.  Squid is Copyrighted (C) 1998 by
  14.  *  Duane Wessels and the University of California San Diego.  Please
  15.  *  see the COPYRIGHT file for full details.  Squid incorporates
  16.  *  software developed and/or copyrighted by other sources.  Please see
  17.  *  the CREDITS file for full details.
  18.  *
  19.  *  This program is free software; you can redistribute it and/or modify
  20.  *  it under the terms of the GNU General Public License as published by
  21.  *  the Free Software Foundation; either version 2 of the License, or
  22.  *  (at your option) any later version.
  23.  *  
  24.  *  This program is distributed in the hope that it will be useful,
  25.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  26.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  27.  *  GNU General Public License for more details.
  28.  *  
  29.  *  You should have received a copy of the GNU General Public License
  30.  *  along with this program; if not, write to the Free Software
  31.  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
  32.  *
  33.  */
  34. #include "squid.h"
  35. /*
  36.  *    Currently only byte ranges are supported
  37.  *
  38.  *    Essentially, there are three types of byte ranges:
  39.  *
  40.  *      1) first-byte-pos "-" last-byte-pos  // range
  41.  *      2) first-byte-pos "-"                // trailer
  42.  *      3)                "-" suffix-length  // suffix (last length bytes)
  43.  *
  44.  *
  45.  *    When Range field is parsed, we have no clue about the content
  46.  *    length of the document. Thus, we simply code an "absent" part
  47.  *    using range_spec_unknown constant.
  48.  *
  49.  *    Note: when response length becomes known, we convert any range
  50.  *    spec into type one above. (Canonization process).
  51.  */
  52. /* local constants */
  53. #define range_spec_unknown ((size_t)-1)
  54. /* local routines */
  55. #define known_spec(s) ((s) != range_spec_unknown)
  56. #define size_min(a,b) ((a) <= (b) ? (a) : (b))
  57. #define size_diff(a,b) ((a) >= (b) ? ((a)-(b)) : 0)
  58. static HttpHdrRangeSpec *httpHdrRangeSpecDup(const HttpHdrRangeSpec * spec);
  59. static int httpHdrRangeSpecCanonize(HttpHdrRangeSpec * spec, size_t clen);
  60. static void httpHdrRangeSpecPackInto(const HttpHdrRangeSpec * spec, Packer * p);
  61. /* globals */
  62. static int RangeParsedCount = 0;
  63. /*
  64.  * Range-Spec
  65.  */
  66. static HttpHdrRangeSpec *
  67. httpHdrRangeSpecCreate()
  68. {
  69.     return memAllocate(MEM_HTTP_HDR_RANGE_SPEC);
  70. }
  71. /* parses range-spec and returns new object on success */
  72. static HttpHdrRangeSpec *
  73. httpHdrRangeSpecParseCreate(const char *field, int flen)
  74. {
  75.     HttpHdrRangeSpec spec =
  76.     {range_spec_unknown, range_spec_unknown};
  77.     const char *p;
  78.     if (flen < 2)
  79. return NULL;
  80.     /* is it a suffix-byte-range-spec ? */
  81.     if (*field == '-') {
  82. if (!httpHeaderParseSize(field + 1, &spec.length))
  83.     return NULL;
  84.     } else
  85. /* must have a '-' somewhere in _this_ field */
  86.     if (!((p = strchr(field, '-')) || (p - field >= flen))) {
  87. debug(64, 2) ("ignoring invalid (missing '-') range-spec near: '%s'n", field);
  88. return NULL;
  89.     } else {
  90. if (!httpHeaderParseSize(field, &spec.offset))
  91.     return NULL;
  92. p++;
  93. /* do we have last-pos ? */
  94. if (p - field < flen) {
  95.     size_t last_pos;
  96.     if (!httpHeaderParseSize(p, &last_pos))
  97. return NULL;
  98.     spec.length = size_diff(last_pos + 1, spec.offset);
  99. }
  100.     }
  101.     /* we managed to parse, check if the result makes sence */
  102.     if (known_spec(spec.length) && !spec.length) {
  103. debug(64, 2) ("ignoring invalid (zero length) range-spec near: '%s'n", field);
  104. return NULL;
  105.     }
  106.     return httpHdrRangeSpecDup(&spec);
  107. }
  108. static void
  109. httpHdrRangeSpecDestroy(HttpHdrRangeSpec * spec)
  110. {
  111.     memFree(spec, MEM_HTTP_HDR_RANGE_SPEC);
  112. }
  113. static HttpHdrRangeSpec *
  114. httpHdrRangeSpecDup(const HttpHdrRangeSpec * spec)
  115. {
  116.     HttpHdrRangeSpec *dup = httpHdrRangeSpecCreate();
  117.     dup->offset = spec->offset;
  118.     dup->length = spec->length;
  119.     return dup;
  120. }
  121. static void
  122. httpHdrRangeSpecPackInto(const HttpHdrRangeSpec * spec, Packer * p)
  123. {
  124.     if (!known_spec(spec->offset)) /* suffix */
  125. packerPrintf(p, "-%d", spec->length);
  126.     else if (!known_spec(spec->length)) /* trailer */
  127. packerPrintf(p, "%d-", spec->offset);
  128.     else /* range */
  129. packerPrintf(p, "%d-%d",
  130.     spec->offset, spec->offset + spec->length - 1);
  131. }
  132. /* fills "absent" positions in range specification based on response body size 
  133.  * returns true if the range is still valid
  134.  * range is valid if its intersection with [0,length-1] is not empty
  135.  */
  136. static int
  137. httpHdrRangeSpecCanonize(HttpHdrRangeSpec * spec, size_t clen)
  138. {
  139.     debug(64, 5) ("httpHdrRangeSpecCanonize: have: [%d, %d) len: %dn",
  140. spec->offset, spec->offset + spec->length, spec->length);
  141.     if (!known_spec(spec->offset)) /* suffix */
  142. spec->offset = size_diff(clen, spec->length);
  143.     else if (!known_spec(spec->length)) /* trailer */
  144. spec->length = size_diff(clen, spec->offset);
  145.     /* we have a "range" now, adjust length if needed */
  146.     assert(known_spec(spec->length));
  147.     assert(known_spec(spec->offset));
  148.     spec->length = size_min(size_diff(clen, spec->offset), spec->length);
  149.     /* check range validity */
  150.     debug(64, 5) ("httpHdrRangeSpecCanonize: done: [%d, %d) len: %dn",
  151. spec->offset, spec->offset + spec->length, spec->length);
  152.     return spec->length > 0;
  153. }
  154. /* merges recepient with donor if possible; returns true on success 
  155.  * both specs must be canonized prior to merger, of course */
  156. static int
  157. httpHdrRangeSpecMergeWith(HttpHdrRangeSpec * recep, const HttpHdrRangeSpec * donor)
  158. {
  159.     int merged = 0;
  160. #if MERGING_BREAKS_NOTHING
  161.     /* Note: this code works, but some clients may not like its effects */
  162.     size_t rhs = recep->offset + recep->length; /* no -1 ! */
  163.     const size_t donor_rhs = donor->offset + donor->length; /* no -1 ! */
  164.     assert(known_spec(recep->offset));
  165.     assert(known_spec(donor->offset));
  166.     assert(recep->length > 0);
  167.     assert(donor->length > 0);
  168.     /* do we have a left hand side overlap? */
  169.     if (donor->offset < recep->offset && recep->offset <= donor_rhs) {
  170. recep->offset = donor->offset; /* decrease left offset */
  171. merged = 1;
  172.     }
  173.     /* do we have a right hand side overlap? */
  174.     if (donor->offset <= rhs && rhs < donor_rhs) {
  175. rhs = donor_rhs; /* increase right offset */
  176. merged = 1;
  177.     }
  178.     /* adjust length if offsets have been changed */
  179.     if (merged) {
  180. assert(rhs > recep->offset);
  181. recep->length = rhs - recep->offset;
  182.     } else {
  183. /* does recepient contain donor? */
  184. merged =
  185.     recep->offset <= donor->offset && donor->offset < rhs;
  186.     }
  187. #endif
  188.     return merged;
  189. }
  190. /*
  191.  * Range
  192.  */
  193. HttpHdrRange *
  194. httpHdrRangeCreate()
  195. {
  196.     HttpHdrRange *r = memAllocate(MEM_HTTP_HDR_RANGE);
  197.     stackInit(&r->specs);
  198.     return r;
  199. }
  200. HttpHdrRange *
  201. httpHdrRangeParseCreate(const String * str)
  202. {
  203.     HttpHdrRange *r = httpHdrRangeCreate();
  204.     if (!httpHdrRangeParseInit(r, str)) {
  205. httpHdrRangeDestroy(r);
  206. r = NULL;
  207.     }
  208.     return r;
  209. }
  210. /* returns true if ranges are valid; inits HttpHdrRange */
  211. int
  212. httpHdrRangeParseInit(HttpHdrRange * range, const String * str)
  213. {
  214.     const char *item;
  215.     const char *pos = NULL;
  216.     int ilen;
  217.     int count = 0;
  218.     assert(range && str);
  219.     RangeParsedCount++;
  220.     debug(64, 8) ("parsing range field: '%s'n", strBuf(*str));
  221.     /* check range type */
  222.     if (strNCaseCmp(*str, "bytes=", 6))
  223. return 0;
  224.     /* skip "bytes="; hack! */
  225.     pos = strBuf(*str) + 5;
  226.     /* iterate through comma separated list */
  227.     while (strListGetItem(str, ',', &item, &ilen, &pos)) {
  228. HttpHdrRangeSpec *spec = httpHdrRangeSpecParseCreate(item, ilen);
  229. /*
  230.  * HTTP/1.1 draft says we must ignore the whole header field if one spec
  231.  * is invalid. However, RFC 2068 just says that we must ignore that spec.
  232.  */
  233. if (spec)
  234.     stackPush(&range->specs, spec);
  235. count++;
  236.     }
  237.     debug(64, 8) ("parsed range range count: %dn", range->specs.count);
  238.     return range->specs.count;
  239. }
  240. void
  241. httpHdrRangeDestroy(HttpHdrRange * range)
  242. {
  243.     assert(range);
  244.     while (range->specs.count)
  245. httpHdrRangeSpecDestroy(stackPop(&range->specs));
  246.     stackClean(&range->specs);
  247.     memFree(range, MEM_HTTP_HDR_RANGE);
  248. }
  249. HttpHdrRange *
  250. httpHdrRangeDup(const HttpHdrRange * range)
  251. {
  252.     HttpHdrRange *dup;
  253.     int i;
  254.     assert(range);
  255.     dup = httpHdrRangeCreate();
  256.     stackPrePush(&dup->specs, range->specs.count);
  257.     for (i = 0; i < range->specs.count; i++)
  258. stackPush(&dup->specs, httpHdrRangeSpecDup(range->specs.items[i]));
  259.     assert(range->specs.count == dup->specs.count);
  260.     return dup;
  261. }
  262. void
  263. httpHdrRangePackInto(const HttpHdrRange * range, Packer * p)
  264. {
  265.     HttpHdrRangePos pos = HttpHdrRangeInitPos;
  266.     const HttpHdrRangeSpec *spec;
  267.     assert(range);
  268.     while ((spec = httpHdrRangeGetSpec(range, &pos))) {
  269. if (pos != HttpHdrRangeInitPos)
  270.     packerAppend(p, ",", 1);
  271. httpHdrRangeSpecPackInto(spec, p);
  272.     }
  273. }
  274. /*
  275.  * canonizes all range specs within a set preserving the order
  276.  * returns true if the set is valid after canonization; 
  277.  * the set is valid if 
  278.  *   - all range specs are valid and 
  279.  *   - there is at least one range spec
  280.  */
  281. int
  282. httpHdrRangeCanonize(HttpHdrRange * range, size_t clen)
  283. {
  284.     int i;
  285.     HttpHdrRangeSpec *spec;
  286.     HttpHdrRangePos pos = HttpHdrRangeInitPos;
  287.     Stack goods;
  288.     assert(range);
  289.     assert(clen >= 0);
  290.     stackInit(&goods);
  291.     debug(64, 3) ("httpHdrRangeCanonize: started with %d specs, clen: %dn", range->specs.count, clen);
  292.     /* canonize each entry and destroy bad ones if any */
  293.     while ((spec = httpHdrRangeGetSpec(range, &pos))) {
  294. if (httpHdrRangeSpecCanonize(spec, clen))
  295.     stackPush(&goods, spec);
  296. else
  297.     httpHdrRangeSpecDestroy(spec);
  298.     }
  299.     debug(64, 3) ("httpHdrRangeCanonize: found %d bad specsn",
  300. range->specs.count - goods.count);
  301.     /* reset old array */
  302.     stackClean(&range->specs);
  303.     stackInit(&range->specs);
  304.     spec = NULL;
  305.     /* merge specs:
  306.      * take one spec from "goods" and merge it with specs from 
  307.      * "range->specs" (if any) until there is no overlap */
  308.     for (i = 0; i < goods.count;) {
  309. HttpHdrRangeSpec *prev_spec = stackTop(&range->specs);
  310. spec = goods.items[i];
  311. if (prev_spec) {
  312.     if (httpHdrRangeSpecMergeWith(spec, prev_spec)) {
  313. /* merged with current so get rid of the prev one */
  314. assert(prev_spec == stackPop(&range->specs));
  315. httpHdrRangeSpecDestroy(prev_spec);
  316. continue; /* re-iterate */
  317.     }
  318. }
  319. stackPush(&range->specs, spec);
  320. spec = NULL;
  321. i++; /* progress */
  322.     }
  323.     if (spec) /* last "merge" may not be pushed yet */
  324. stackPush(&range->specs, spec);
  325.     debug(64, 3) ("httpHdrRangeCanonize: had %d specs, merged %d specsn",
  326. goods.count, goods.count - range->specs.count);
  327.     debug(64, 3) ("httpHdrRangeCanonize: finished with %d specsn",
  328. range->specs.count);
  329.     stackClean(&goods);
  330.     return range->specs.count > 0;
  331. }
  332. /* searches for next range, returns true if found */
  333. HttpHdrRangeSpec *
  334. httpHdrRangeGetSpec(const HttpHdrRange * range, HttpHdrRangePos * pos)
  335. {
  336.     assert(range);
  337.     assert(pos && *pos >= -1 && *pos < range->specs.count);
  338.     (*pos)++;
  339.     if (*pos < range->specs.count)
  340. return (HttpHdrRangeSpec *) range->specs.items[*pos];
  341.     else
  342. return NULL;
  343. }
  344. /* hack: returns true if range specs are too "complex" for Squid to handle */
  345. /* requires that specs are "canonized" first! */
  346. int
  347. httpHdrRangeIsComplex(const HttpHdrRange * range)
  348. {
  349.     HttpHdrRangePos pos = HttpHdrRangeInitPos;
  350.     const HttpHdrRangeSpec *spec;
  351.     size_t offset = 0;
  352.     assert(range);
  353.     /* check that all rangers are in "strong" order */
  354.     while ((spec = httpHdrRangeGetSpec(range, &pos))) {
  355. if (spec->offset < offset)
  356.     return 1;
  357. offset = spec->offset + spec->length;
  358.     }
  359.     return 0;
  360. }
  361. /* hack: returns true if range specs may be too "complex" when "canonized" */
  362. /* see also: httpHdrRangeIsComplex */
  363. int
  364. httpHdrRangeWillBeComplex(const HttpHdrRange * range)
  365. {
  366.     HttpHdrRangePos pos = HttpHdrRangeInitPos;
  367.     const HttpHdrRangeSpec *spec;
  368.     size_t offset = 0;
  369.     assert(range);
  370.     /* check that all rangers are in "strong" order, */
  371.     /* as far as we can tell without the content length */
  372.     while ((spec = httpHdrRangeGetSpec(range, &pos))) {
  373. if (!known_spec(spec->offset)) /* ignore unknowns */
  374.     continue;
  375. if (spec->offset < offset)
  376.     return 1;
  377. offset = spec->offset;
  378. if (known_spec(spec->length)) /* avoid  unknowns */
  379.     offset += spec->length;
  380.     }
  381.     return 0;
  382. }
  383. /* Returns lowest known offset in range spec(s), or range_spec_unknown */
  384. /* this is used for size limiting */
  385. size_t
  386. httpHdrRangeFirstOffset(const HttpHdrRange * range)
  387. {
  388.     size_t offset = range_spec_unknown;
  389.     HttpHdrRangePos pos = HttpHdrRangeInitPos;
  390.     const HttpHdrRangeSpec *spec;
  391.     assert(range);
  392.     while ((spec = httpHdrRangeGetSpec(range, &pos))) {
  393. if (spec->offset < offset || !known_spec(offset))
  394.     offset = spec->offset;
  395.     }
  396.     return offset;
  397. }
  398. /* Returns lowest offset in range spec(s), 0 if unknown */
  399. /* This is used for finding out where we need to start if all
  400.  * ranges are combined into one, for example FTP REST.
  401.  * Use 0 for size if unknown
  402.  */
  403. size_t
  404. httpHdrRangeLowestOffset(const HttpHdrRange * range, size_t size)
  405. {
  406.     size_t offset = range_spec_unknown;
  407.     size_t current;
  408.     HttpHdrRangePos pos = HttpHdrRangeInitPos;
  409.     const HttpHdrRangeSpec *spec;
  410.     assert(range);
  411.     while ((spec = httpHdrRangeGetSpec(range, &pos))) {
  412. current = spec->offset;
  413. if (!known_spec(current)) {
  414.     if (spec->length > size || !known_spec(spec->length))
  415. return 0; /* Unknown. Assume start of file */
  416.     current = size - spec->length;
  417. }
  418. if (current < offset || !known_spec(offset))
  419.     offset = current;
  420.     }
  421.     return known_spec(offset) ? offset : 0;
  422. }
  423. /* generates a "unique" boundary string for multipart responses
  424.  * the caller is responsible for cleaning the string */
  425. String
  426. httpHdrRangeBoundaryStr(clientHttpRequest * http)
  427. {
  428.     const char *key;
  429.     String b = StringNull;
  430.     assert(http);
  431.     stringAppend(&b, full_appname_string, strlen(full_appname_string));
  432.     stringAppend(&b, ":", 1);
  433.     key = storeKeyText(http->entry->key);
  434.     stringAppend(&b, key, strlen(key));
  435.     return b;
  436. }