This makes Squid accept the ICY protocol replies and pass them on to the client.
<tag>redirector_bypass</tag>
<p>Replaced by <em>url_rewrite_bypass</em>
+ <tag>upgrade_http0.9</tag>
+ <p>ICY protocol streaming support added natively.
+
<tag>zph_local</tag>
<p>Replaced by <em>qos_flows local-hit=</em>
<tag>update_headers</tag>
<p>Not yet ported from 2.7
- <tag>upgrade_http0.9</tag>
- <p>Not yet ported from 2.7
-
<tag>zero_buffers</tag>
<p>Not yet ported from 2.7
return false;
}
+ int pos;
// catch missing or mismatched protocol identifier
- if (protoPrefix.cmp(buf->content(), protoPrefix.size()) != 0) {
- debugs(58, 3, "HttpReply::sanityCheckStartLine: missing protocol prefix (" << protoPrefix << ") in '" << buf->content() << "'");
- *error = HTTP_INVALID_HEADER;
- return false;
+ // allow special-case for ICY protocol (non-HTTP identifier) in response to faked HTTP request.
+ if (strncmp(buf->content(), "ICY", 3) == 0) {
+ protoPrefix = "ICY";
+ pos = protoPrefix.psize();
}
+ else {
- // catch missing or negative status value (negative '-' is not a digit)
- int pos = protoPrefix.psize();
+ if (protoPrefix.cmp(buf->content(), protoPrefix.size()) != 0) {
+ debugs(58, 3, "HttpReply::sanityCheckStartLine: missing protocol prefix (" << protoPrefix << ") in '" << buf->content() << "'");
+ *error = HTTP_INVALID_HEADER;
+ return false;
+ }
- // skip arbitrary number of digits and a dot in the verion portion
- while ( pos <= buf->contentSize() && (*(buf->content()+pos) == '.' || xisdigit(*(buf->content()+pos)) ) ) ++pos;
+ // catch missing or negative status value (negative '-' is not a digit)
+ pos = protoPrefix.psize();
+
+ // skip arbitrary number of digits and a dot in the verion portion
+ while ( pos <= buf->contentSize() && (*(buf->content()+pos) == '.' || xisdigit(*(buf->content()+pos)) ) ) ++pos;
- // catch missing version info
- if (pos == protoPrefix.psize()) {
- debugs(58, 3, "HttpReply::sanityCheckStartLine: missing protocol version numbers (ie. " << protoPrefix << "/1.0) in '" << buf->content() << "'");
- *error = HTTP_INVALID_HEADER;
- return false;
+ // catch missing version info
+ if (pos == protoPrefix.psize()) {
+ debugs(58, 3, "HttpReply::sanityCheckStartLine: missing protocol version numbers (ie. " << protoPrefix << "/1.0) in '" << buf->content() << "'");
+ *error = HTTP_INVALID_HEADER;
+ return false;
+ }
}
// skip arbitrary number of spaces...
/* local constants */
/* AYJ: see bug 2469 - RFC2616 confirms stating 'SP characters' plural! */
const char *HttpStatusLineFormat = "HTTP/%d.%d %3d %s\r\n";
+const char *IcyStatusLineFormat = "ICY %3d %s\r\n";
void
httpStatusLineInit(HttpStatusLine * sline)
httpStatusLineSet(HttpStatusLine * sline, HttpVersion version, http_status status, const char *reason)
{
assert(sline);
+ sline->protocol = PROTO_HTTP;
sline->version = version;
sline->status = status;
/* Note: no xstrdup for 'reason', assumes constant 'reasons' */
sline->reason = reason;
}
-/** write HTTP version and status structures into a Packer buffer for output as HTTP status line. */
+/**
+ * Write HTTP version and status structures into a Packer buffer for output as HTTP status line.
+ * Special exemption made for ICY response status lines.
+ */
void
httpStatusLinePackInto(const HttpStatusLine * sline, Packer * p)
{
assert(sline && p);
+
+ /* handle ICY protocol status line specially. Pass on the bad format. */
+ if (sline->protocol == PROTO_ICY) {
+ debugs(57, 9, "packing sline " << sline << " using " << p << ":");
+ debugs(57, 9, "FORMAT=" << IcyStatusLineFormat );
+ debugs(57, 9, "ICY " << sline->status << " " << (sline->reason ? sline->reason : httpStatusString(sline->status)) );
+ packerPrintf(p, IcyStatusLineFormat, sline->status, httpStatusLineReason(sline));
+ return;
+ }
+
debugs(57, 9, "packing sline " << sline << " using " << p << ":");
debugs(57, 9, "FORMAT=" << HttpStatusLineFormat );
debugs(57, 9, "HTTP/" << sline->version.major << "." << sline->version.minor <<
// XXX: HttpMsg::parse() has a similar check but is using
// casesensitive comparison (which is required by HTTP errata?)
- if (protoPrefix.caseCmp(start, protoPrefix.size()) != 0)
- return 0;
+ if (protoPrefix.cmp("ICY", 3) == 0) {
+ debugs(57, 3, "httpStatusLineParse: Invalid HTTP identifier. Detected ICY protocol istead.");
+ sline->protocol = PROTO_ICY;
+ start += protoPrefix.size();
+ }
+ else if (protoPrefix.caseCmp(start, protoPrefix.size()) == 0) {
- start += protoPrefix.size();
+ start += protoPrefix.size();
- if (!xisdigit(*start))
- return 0;
+ if (!xisdigit(*start))
+ return 0;
- if (sscanf(start, "%d.%d", &sline->version.major, &sline->version.minor) != 2) {
- debugs(57, 7, "httpStatusLineParse: Invalid HTTP identifier.");
+ if (sscanf(start, "%d.%d", &sline->version.major, &sline->version.minor) != 2) {
+ debugs(57, 7, "httpStatusLineParse: Invalid HTTP identifier.");
+ }
}
+ else
+ return 0;
if (!(start = strchr(start, ' ')))
return 0;
/* for SQUIDCEXTERN */
#include "config.h"
-/* for http_status */
+/* for http_status and protocol_t */
#include "enums.h"
-/* for class variables */
#include "HttpVersion.h"
+#include "SquidString.h"
+/**
+ * Holds the values parsed from an HTTP reply status line.
+ *
+ * For example: HTTP/1.1 200 Okay
+ */
class HttpStatusLine
{
-
public:
/* public, read only */
- HttpVersion version;
- const char *reason; /**< points to a _constant_ string (default or supplied), never free()d */
- http_status status;
+
+ /**
+ * By rights protocol name should be a constant "HTTP", with no need for this field to exist.
+ * However there are protocols which violate HTTP by sending their wn custom formats
+ * back with other protocol names (ICY streaming format being the current major problem)
+ */
+ protocol_t protocol;
+
+ HttpVersion version; ///< breakdown of protocol version labels: 0.9 1.0 1.1
+ http_status status; ///< status code. ie 200 404
+ const char *reason; ///< points to a _constant_ string (default or supplied), never free()d */
};
/* init/clean */
"whois",
"internal",
"https",
+ "icy",
"TOTAL"
};
-
reply = HTTPMSGLOCK(rep);
- /* enforce 1.0 reply version */
- reply->sline.version = HttpVersion(1,0);
+ if (reply->sline.protocol == PROTO_HTTP) {
+ /* enforce 1.0 reply version (but only on real HTTP traffic) */
+ reply->sline.version = HttpVersion(1,0);
+ }
/* do header conversions */
buildReplyHeader();
PROTO_WHOIS,
PROTO_INTERNAL,
PROTO_HTTPS,
+ PROTO_ICY,
PROTO_MAX
} protocol_t;
HttpReply *newrep = new HttpReply;
const bool parsed = newrep->parse(readBuf, eof, &error);
- if (!parsed && readBuf->contentSize() > 5 && strncmp(readBuf->content(), "HTTP/", 5) != 0) {
+ if (!parsed && readBuf->contentSize() > 5 && strncmp(readBuf->content(), "HTTP/", 5) != 0 && strncmp(readBuf->content(), "ICY", 3) != 0) {
MemBuf *mb;
HttpReply *tmprep = new HttpReply;
tmprep->sline.version = HttpVersion(1, 0);
error = HTTP_STATUS_NONE;
#endif
+ // valid ICY protocol status line
+ input.append("ICY 200 Okay\n\n", 18);
+ hdr_len = headersEnd(input.content(),input.contentSize());
+ CPPUNIT_ASSERT( engine.sanityCheckStartLine(&input, hdr_len, &error) );
+ CPPUNIT_ASSERT_EQUAL(error, HTTP_STATUS_NONE);
+ input.reset();
+ error = HTTP_STATUS_NONE;
+
// empty status line
input.append("\n\n", 2);
hdr_len = headersEnd(input.content(),input.contentSize());
CPPUNIT_ASSERT_EQUAL(error, HTTP_INVALID_HEADER);
input.reset();
error = HTTP_STATUS_NONE;
-
- // status line with non-HTTP protocol
- input.append("ICY/1.1 200 Okay\n\n", 18); /* real case seen */
- hdr_len = headersEnd(input.content(),input.contentSize());
- /* NP: for nw ICY is handled as a pass-thru */
- /* Squid-3 will ignore it (and mangle the headers as per HTTP). */
- CPPUNIT_ASSERT(!engine.sanityCheckStartLine(&input, hdr_len, &error) );
- CPPUNIT_ASSERT_EQUAL(error, HTTP_INVALID_HEADER);
- input.reset();
- error = HTTP_STATUS_NONE;
-
}