#include "FwdState.h"
#include "globals.h"
#include "http.h"
+#include "http/one/RequestParser.h"
#include "HttpHdrContRange.h"
#include "HttpHeaderTools.h"
#include "HttpReply.h"
#include "MemBuf.h"
#include "MemObject.h"
#include "mime_header.h"
+#include "parser/Tokenizer.h"
#include "profiler/Profiler.h"
#include "rfc1738.h"
#include "SquidConfig.h"
#endif
static CTCB clientLifetimeTimeout;
static ClientSocketContext *parseHttpRequestAbort(ConnStateData * conn, const char *uri);
-static ClientSocketContext *parseHttpRequest(ConnStateData *, HttpParser *, HttpRequestMethod *, Http::ProtocolVersion *);
+static ClientSocketContext *parseHttpRequest(ConnStateData *, Http1::RequestParser &);
#if USE_IDENT
static IDCB clientIdentDone;
#endif
}
static void
-prepareAcceleratedURL(ConnStateData * conn, ClientHttpRequest *http, char *url, const char *req_hdr)
+prepareAcceleratedURL(ConnStateData * conn, ClientHttpRequest *http, Http1::RequestParser &hp)
{
int vhost = conn->port->vhost;
int vport = conn->port->vport;
- char *host;
- char ipbuf[MAX_IPSTRLEN];
+ static char ipbuf[MAX_IPSTRLEN];
http->flags.accel = true;
/* BUG: Squid cannot deal with '*' URLs (RFC2616 5.1.2) */
- if (strncasecmp(url, "cache_object://", 15) == 0)
+ static const SBuf cache_object("cache_object://");
+ if (hp.requestUri().startsWith(cache_object))
return; /* already in good shape */
- if (*url != '/') {
+ // XXX: re-use proper URL parser for this
+ SBuf url = hp.requestUri(); // use full provided URI if we abort
+ do { // use a loop so we can break out of it
+ ::Parser::Tokenizer tok(url);
+ if (tok.remaining()[0] == '/')
+ break;
+
if (conn->port->vhost)
return; /* already in good shape */
- /* else we need to ignore the host name */
- url = strstr(url, "//");
+ // skip the URI scheme
+ static const CharacterSet uriScheme = CharacterSet::ALPHA + CharacterSet::DIGIT + CharacterSet(NULL,"+-.");
+ static const SBuf uriSchemeEnd("://");
+ if (!tok.skip(uriScheme))
+ break;
+ if (!tok.skip(uriSchemeEnd))
+ break;
-#if SHOULD_REJECT_UNKNOWN_URLS
+ // skip the authority segment
+ // RFC 3986 complex nested ABNF for "authority" boils down to this:
+ static const CharacterSet authority = CharacterSet("authority","-._~%:@[]!$&'()*+,;=") +
+ CharacterSet::HEXDIG + CharacterSet::ALPHA + CharacterSet::DIGIT;
+ if (!tok.skip(authority))
+ break;
- if (!url) {
- hp->request_parse_status = Http::scBadRequest;
- return parseHttpRequestAbort(conn, "error:invalid-request");
- }
-#endif
+ static const SBuf slashUri("/");
+ const SBuf t = tok.remaining();
+ if (t.isEmpty())
+ url = slashUri;
+ else if (t[0]=='/') // looks like path
+ url = t;
+ else if (t[0]=='?' || t[0]=='#') { // looks like query or fragment. fix '/'
+ url = slashUri;
+ url.append(t);
+ } // else do nothing. invalid path
- if (url)
- url = strchr(url + 2, '/');
+ } while(false);
- if (!url)
- url = (char *) "/";
+#if SHOULD_REJECT_UNKNOWN_URLS
+ // reject URI which are not well-formed even after the processing above
+ if (url[0] != '/') {
+ hp.request_parse_status = Http::scBadRequest;
+ return parseHttpRequestAbort(conn, "error:invalid-request");
}
+#endif
if (vport < 0)
vport = http->getConn()->clientConnection->local.port();
const bool switchedToHttps = conn->switchedToHttps();
const bool tryHostHeader = vhost || switchedToHttps;
- if (tryHostHeader && (host = mime_get_header(req_hdr, "Host")) != NULL) {
+ char *host = NULL;
+ if (tryHostHeader && (host = hp.getHeaderField("Host"))) {
debugs(33, 5, "ACCEL VHOST REWRITE: vhost=" << host << " + vport=" << vport);
char thost[256];
if (vport > 0) {
host = thost;
}
} // else nothing to alter port-wise.
- int url_sz = strlen(url) + 32 + Config.appendDomainLen +
- strlen(host);
+ const int url_sz = hp.requestUri().length() + 32 + Config.appendDomainLen + strlen(host);
http->uri = (char *)xcalloc(url_sz, 1);
const char *protocol = switchedToHttps ?
"https" : AnyP::UriScheme(conn->port->transport.protocol).c_str();
- snprintf(http->uri, url_sz, "%s://%s%s", protocol, host, url);
- debugs(33, 5, "ACCEL VHOST REWRITE: '" << http->uri << "'");
+ snprintf(http->uri, url_sz, "%s://%s" SQUIDSBUFPH, protocol, host, SQUIDSBUFPRINT(url));
+ debugs(33, 5, "ACCEL VHOST REWRITE: " << http->uri);
} else if (conn->port->defaultsite /* && !vhost */) {
debugs(33, 5, "ACCEL DEFAULTSITE REWRITE: defaultsite=" << conn->port->defaultsite << " + vport=" << vport);
- int url_sz = strlen(url) + 32 + Config.appendDomainLen +
+ const int url_sz = hp.requestUri().length() + 32 + Config.appendDomainLen +
strlen(conn->port->defaultsite);
http->uri = (char *)xcalloc(url_sz, 1);
char vportStr[32];
if (vport > 0) {
snprintf(vportStr, sizeof(vportStr),":%d",vport);
}
- snprintf(http->uri, url_sz, "%s://%s%s%s",
- AnyP::UriScheme(conn->port->transport.protocol).c_str(), conn->port->defaultsite, vportStr, url);
- debugs(33, 5, "ACCEL DEFAULTSITE REWRITE: '" << http->uri <<"'");
+ snprintf(http->uri, url_sz, "%s://%s%s" SQUIDSBUFPH,
+ AnyP::UriScheme(conn->port->transport.protocol).c_str(), conn->port->defaultsite, vportStr, SQUIDSBUFPRINT(url));
+ debugs(33, 5, "ACCEL DEFAULTSITE REWRITE: " << http->uri);
} else if (vport > 0 /* && (!vhost || no Host:) */) {
debugs(33, 5, "ACCEL VPORT REWRITE: http_port IP + vport=" << vport);
/* Put the local socket IP address as the hostname, with whatever vport we found */
- int url_sz = strlen(url) + 32 + Config.appendDomainLen;
+ const int url_sz = hp.requestUri().length() + 32 + Config.appendDomainLen;
http->uri = (char *)xcalloc(url_sz, 1);
http->getConn()->clientConnection->local.toHostStr(ipbuf,MAX_IPSTRLEN);
- snprintf(http->uri, url_sz, "%s://%s:%d%s",
+ snprintf(http->uri, url_sz, "%s://%s:%d" SQUIDSBUFPH,
AnyP::UriScheme(conn->port->transport.protocol).c_str(),
- ipbuf, vport, url);
- debugs(33, 5, "ACCEL VPORT REWRITE: '" << http->uri << "'");
+ ipbuf, vport, SQUIDSBUFPRINT(url));
+ debugs(33, 5, "ACCEL VPORT REWRITE: " << http->uri);
}
}
static void
-prepareTransparentURL(ConnStateData * conn, ClientHttpRequest *http, char *url, const char *req_hdr)
+prepareTransparentURL(ConnStateData * conn, ClientHttpRequest *http, Http1::RequestParser &hp)
{
- char *host;
- char ipbuf[MAX_IPSTRLEN];
+ static char ipbuf[MAX_IPSTRLEN];
- if (*url != '/')
+ // TODO Must() on URI !empty when the parser supports throw. For now avoid assert().
+ if (!hp.requestUri().isEmpty() && hp.requestUri()[0] != '/')
return; /* already in good shape */
/* BUG: Squid cannot deal with '*' URLs (RFC2616 5.1.2) */
- if ((host = mime_get_header(req_hdr, "Host")) != NULL) {
- int url_sz = strlen(url) + 32 + Config.appendDomainLen +
+ if (const char *host = hp.getHeaderField("Host")) {
+ const int url_sz = hp.requestUri().length() + 32 + Config.appendDomainLen +
strlen(host);
http->uri = (char *)xcalloc(url_sz, 1);
- snprintf(http->uri, url_sz, "%s://%s%s", AnyP::UriScheme(conn->port->transport.protocol).c_str(), host, url);
- debugs(33, 5, "TRANSPARENT HOST REWRITE: '" << http->uri <<"'");
+ snprintf(http->uri, url_sz, "%s://%s" SQUIDSBUFPH,
+ AnyP::UriScheme(conn->port->transport.protocol).c_str(), host, SQUIDSBUFPRINT(hp.requestUri()));
+ debugs(33, 5, "TRANSPARENT HOST REWRITE: " << http->uri);
} else {
/* Put the local socket IP address as the hostname. */
- int url_sz = strlen(url) + 32 + Config.appendDomainLen;
+ const int url_sz = hp.requestUri().length() + 32 + Config.appendDomainLen;
http->uri = (char *)xcalloc(url_sz, 1);
http->getConn()->clientConnection->local.toHostStr(ipbuf,MAX_IPSTRLEN);
- snprintf(http->uri, url_sz, "%s://%s:%d%s",
+ snprintf(http->uri, url_sz, "%s://%s:%d" SQUIDSBUFPH,
AnyP::UriScheme(http->getConn()->port->transport.protocol).c_str(),
- ipbuf, http->getConn()->clientConnection->local.port(), url);
- debugs(33, 5, "TRANSPARENT REWRITE: '" << http->uri << "'");
+ ipbuf, http->getConn()->clientConnection->local.port(), SQUIDSBUFPRINT(hp.requestUri()));
+ debugs(33, 5, "TRANSPARENT REWRITE: " << http->uri);
}
}
* \note Sets result->flags.parsed_ok to 0 if failed to parse the request,
* to 1 if the request was correctly parsed.
* \param[in] csd a ConnStateData. The caller must make sure it is not null
- * \param[in] hp an HttpParser
+ * \param[in] hp an Http1::RequestParser
* \param[out] mehtod_p will be set as a side-effect of the parsing.
* Pointed-to value will be set to Http::METHOD_NONE in case of
* parsing failure
* a ClientSocketContext structure on success or failure.
*/
static ClientSocketContext *
-parseHttpRequest(ConnStateData *csd, HttpParser *hp, HttpRequestMethod * method_p, Http::ProtocolVersion *http_ver)
+parseHttpRequest(ConnStateData *csd, Http1::RequestParser &hp)
{
- char *req_hdr = NULL;
- char *end;
- size_t req_sz;
- ClientHttpRequest *http;
- ClientSocketContext *result;
- StoreIOBuffer tempBuffer;
- int r;
-
- /* pre-set these values to make aborting simpler */
- *method_p = Http::METHOD_NONE;
-
- /* NP: don't be tempted to move this down or remove again.
- * It's the only DDoS protection old-String has against long URL */
- if ( hp->bufsiz <= 0) {
- debugs(33, 5, "Incomplete request, waiting for end of request line");
- return NULL;
- } else if ( (size_t)hp->bufsiz >= Config.maxRequestHeaderSize && headersEnd(hp->buf, Config.maxRequestHeaderSize) == 0) {
- debugs(33, 5, "parseHttpRequest: Too large request");
- hp->request_parse_status = Http::scHeaderTooLarge;
- return parseHttpRequestAbort(csd, "error:request-too-large");
- }
-
- /* Attempt to parse the first line; this'll define the method, url, version and header begin */
- r = HttpParserParseReqLine(hp);
-
- if (r == 0) {
- debugs(33, 5, "Incomplete request, waiting for end of request line");
- return NULL;
- }
-
- if (r == -1) {
- return parseHttpRequestAbort(csd, "error:invalid-request");
- }
+ /* Attempt to parse the first line; this will define where the method, url, version and header begin */
+ {
+ const bool parsedOk = hp.parse(csd->in.buf);
- /* Request line is valid here .. */
- *http_ver = Http::ProtocolVersion(hp->req.v_maj, hp->req.v_min);
+ // sync the buffers after parsing.
+ csd->in.buf = hp.remaining();
- /* This call scans the entire request, not just the headers */
- if (hp->req.v_maj > 0) {
- if ((req_sz = headersEnd(hp->buf, hp->bufsiz)) == 0) {
- debugs(33, 5, "Incomplete request, waiting for end of headers");
+ if (hp.needsMoreData()) {
+ debugs(33, 5, "Incomplete request, waiting for end of request line");
return NULL;
}
- } else {
- debugs(33, 3, "parseHttpRequest: Missing HTTP identifier");
- req_sz = HttpParserReqSz(hp);
- }
-
- /* We know the whole request is in hp->buf now */
-
- assert(req_sz <= (size_t) hp->bufsiz);
- /* Will the following be true with HTTP/0.9 requests? probably not .. */
- /* So the rest of the code will need to deal with '0'-byte headers (ie, none, so don't try parsing em) */
- assert(req_sz > 0);
+ if (!parsedOk) {
+ if (hp.request_parse_status == Http::scHeaderTooLarge)
+ return parseHttpRequestAbort(csd, "error:request-too-large");
- hp->hdr_end = req_sz - 1;
-
- hp->hdr_start = hp->req.end + 1;
-
- /* Enforce max_request_size */
- if (req_sz >= Config.maxRequestHeaderSize) {
- debugs(33, 5, "parseHttpRequest: Too large request");
- hp->request_parse_status = Http::scHeaderTooLarge;
- return parseHttpRequestAbort(csd, "error:request-too-large");
+ return parseHttpRequestAbort(csd, "error:invalid-request");
+ }
}
- /* Set method_p */
- *method_p = HttpRequestMethod(&hp->buf[hp->req.m_start], &hp->buf[hp->req.m_end]+1);
+ /* We know the whole request is in parser now */
+ debugs(11, 2, "HTTP Client " << csd->clientConnection);
+ debugs(11, 2, "HTTP Client REQUEST:\n---------\n" <<
+ hp.method() << " " << hp.requestUri() << " " << hp.messageProtocol() << "\n" <<
+ hp.mimeHeader() <<
+ "\n----------");
/* deny CONNECT via accelerated ports */
- if (*method_p == Http::METHOD_CONNECT && csd->port && csd->port->flags.accelSurrogate) {
+ if (hp.method() == Http::METHOD_CONNECT && csd->port && csd->port->flags.accelSurrogate) {
debugs(33, DBG_IMPORTANT, "WARNING: CONNECT method received on " << csd->port->transport.protocol << " Accelerator port " << csd->port->s.port());
- /* XXX need a way to say "this many character length string" */
- debugs(33, DBG_IMPORTANT, "WARNING: for request: " << hp->buf);
- hp->request_parse_status = Http::scMethodNotAllowed;
+ debugs(33, DBG_IMPORTANT, "WARNING: for request: " << hp.method() << " " << hp.requestUri() << " " << hp.messageProtocol());
+ hp.request_parse_status = Http::scMethodNotAllowed;
return parseHttpRequestAbort(csd, "error:method-not-allowed");
}
- if (*method_p == Http::METHOD_NONE) {
- /* XXX need a way to say "this many character length string" */
- debugs(33, DBG_IMPORTANT, "clientParseRequestMethod: Unsupported method in request '" << hp->buf << "'");
- hp->request_parse_status = Http::scMethodNotAllowed;
+ if (hp.method() == Http::METHOD_NONE) {
+ debugs(33, DBG_IMPORTANT, "WARNING: Unsupported method: " << hp.method() << " " << hp.requestUri() << " " << hp.messageProtocol());
+ hp.request_parse_status = Http::scMethodNotAllowed;
return parseHttpRequestAbort(csd, "error:unsupported-request-method");
}
- /*
- * Process headers after request line
- * TODO: Use httpRequestParse here.
- */
- /* XXX this code should be modified to take a const char * later! */
- req_hdr = (char *) hp->buf + hp->req.end + 1;
-
- debugs(33, 3, "parseHttpRequest: req_hdr = {" << req_hdr << "}");
-
- end = (char *) hp->buf + hp->hdr_end;
-
- debugs(33, 3, "parseHttpRequest: end = {" << end << "}");
-
- debugs(33, 3, "parseHttpRequest: prefix_sz = " <<
- (int) HttpParserRequestLen(hp) << ", req_line_sz = " <<
- HttpParserReqSz(hp));
+ // Process headers after request line
+ debugs(33, 3, "complete request received. " <<
+ "prefix_sz = " << hp.messageHeaderSize() <<
+ ", request-line-size=" << hp.firstLineSize() <<
+ ", mime-header-size=" << hp.headerBlockSize() <<
+ ", mime header block:\n" << hp.mimeHeader() << "\n----------");
/* Ok, all headers are received */
- http = new ClientHttpRequest(csd);
+ ClientHttpRequest *http = new ClientHttpRequest(csd);
- http->req_sz = HttpParserRequestLen(hp);
- result = new ClientSocketContext(csd->clientConnection, http);
+ http->req_sz = hp.messageHeaderSize();
+ ClientSocketContext *result = new ClientSocketContext(csd->clientConnection, http);
+
+ StoreIOBuffer tempBuffer;
tempBuffer.data = result->reqbuf;
tempBuffer.length = HTTP_REQBUF_SZ;
clientReplyStatus, newServer, clientSocketRecipient,
clientSocketDetach, newClient, tempBuffer);
- debugs(33, 5, "parseHttpRequest: Request Header is\n" <<(hp->buf) + hp->hdr_start);
-
/* set url */
- /*
- * XXX this should eventually not use a malloc'ed buffer; the transformation code
- * below needs to be modified to not expect a mutable nul-terminated string.
- */
- char *url = (char *)xmalloc(hp->req.u_end - hp->req.u_start + 16);
-
- memcpy(url, hp->buf + hp->req.u_start, hp->req.u_end - hp->req.u_start + 1);
-
- url[hp->req.u_end - hp->req.u_start + 1] = '\0';
-
-#if THIS_VIOLATES_HTTP_SPECS_ON_URL_TRANSFORMATION
-
- if ((t = strchr(url, '#'))) /* remove HTML anchors */
- *t = '\0';
-
-#endif
+ // XXX: c_str() does re-allocate but here replaces explicit malloc/free.
+ // when internalCheck() accepts SBuf removing this will be a net gain for performance.
+ const char *url = SBuf(hp.requestUri()).c_str();
debugs(33,5, HERE << "repare absolute URL from " <<
(csd->transparent()?"intercept":(csd->port->flags.accelSurrogate ? "accel":"")));
*/
if (csd->transparent()) {
/* intercept or transparent mode, properly working with no failures */
- prepareTransparentURL(csd, http, url, req_hdr);
+ prepareTransparentURL(csd, http, hp);
} else if (internalCheck(url)) {
/* internal URL mode */
} else if (csd->port->flags.accelSurrogate || csd->switchedToHttps()) {
/* accelerator mode */
- prepareAcceleratedURL(csd, http, url, req_hdr);
+ prepareAcceleratedURL(csd, http, hp);
}
if (!http->uri) {
/* No special rewrites have been applied above, use the
* requested url. may be rewritten later, so make extra room */
- int url_sz = strlen(url) + Config.appendDomainLen + 5;
+ int url_sz = hp.requestUri().length() + Config.appendDomainLen + 5;
http->uri = (char *)xcalloc(url_sz, 1);
strcpy(http->uri, url);
}
- debugs(33, 5, "parseHttpRequest: Complete request received");
-
- // XXX: crop this dump at the end of headers. No need for extras
- debugs(11, 2, "HTTP Client " << csd->clientConnection);
- debugs(11, 2, "HTTP Client REQUEST:\n---------\n" << (hp->buf) + hp->req.m_start << "\n----------");
-
result->flags.parsed_ok = 1;
- xfree(url);
return result;
}
debugs(33, 5, "conn->in.buf has " << conn->in.buf.length() << " bytes unused.");
}
-/// respond with ERR_TOO_BIG if request header exceeds request_header_max_size
-void
-ConnStateData::checkHeaderLimits()
-{
- if (in.buf.length() < Config.maxRequestHeaderSize)
- return; // can accumulte more header data
-
- debugs(33, 3, "Request header is too large (" << in.buf.length() << " > " <<
- Config.maxRequestHeaderSize << " bytes)");
-
- ClientSocketContext *context = parseHttpRequestAbort(this, "error:request-too-large");
- clientStreamNode *node = context->getClientReplyContext();
- clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
- assert (repContext);
- repContext->setReplyToError(ERR_TOO_BIG,
- Http::scBadRequest, Http::METHOD_NONE, NULL,
- clientConnection->remote, NULL, NULL, NULL);
- context->registerWithConn();
- context->pullData();
-}
-
void
ConnStateData::clientAfterReadingRequests()
{
#endif // USE_OPENSSL
static void
-clientProcessRequest(ConnStateData *conn, HttpParser *hp, ClientSocketContext *context, const HttpRequestMethod& method, Http::ProtocolVersion http_ver)
+clientProcessRequest(ConnStateData *conn, Http1::RequestParser &hp, ClientSocketContext *context)
{
ClientHttpRequest *http = context->http;
HttpRequest::Pointer request;
- bool notedUseOfBuffer = false;
bool chunked = false;
bool mustReplyToOptions = false;
bool unsupportedTe = false;
bool expectBody = false;
+ const AnyP::ProtocolVersion &http_ver = hp.messageProtocol();
+ const HttpRequestMethod &method = hp.method();
/* We have an initial client stream in place should it be needed */
/* setup our private context */
if (context->flags.parsed_ok == 0) {
clientStreamNode *node = context->getClientReplyContext();
- debugs(33, 2, "clientProcessRequest: Invalid Request");
+ debugs(33, 2, "Invalid Request");
conn->quitAfterError(NULL);
// setLogUri should called before repContext->setReplyToError
setLogUri(http, http->uri, true);
clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
assert (repContext);
- switch (hp->request_parse_status) {
+ switch (hp.request_parse_status) {
case Http::scHeaderTooLarge:
- repContext->setReplyToError(ERR_TOO_BIG, Http::scBadRequest, method, http->uri, conn->clientConnection->remote, NULL, conn->in.buf.c_str(), NULL);
+ repContext->setReplyToError(ERR_TOO_BIG, Http::scBadRequest, method, http->uri,
+ conn->clientConnection->remote, NULL, conn->in.buf.c_str(), NULL);
break;
case Http::scMethodNotAllowed:
repContext->setReplyToError(ERR_UNSUP_REQ, Http::scMethodNotAllowed, method, http->uri,
conn->clientConnection->remote, NULL, conn->in.buf.c_str(), NULL);
break;
+ case Http::scHttpVersionNotSupported:
+ repContext->setReplyToError(ERR_UNSUP_HTTPVERSION, Http::scHttpVersionNotSupported, method, http->uri,
+ conn->clientConnection->remote, NULL, conn->in.buf.c_str(), NULL);
+ break;
default:
- repContext->setReplyToError(ERR_INVALID_REQ, hp->request_parse_status, method, http->uri,
+ repContext->setReplyToError(ERR_INVALID_REQ, hp.request_parse_status, method, http->uri,
conn->clientConnection->remote, NULL, conn->in.buf.c_str(), NULL);
}
assert(context->http->out.offset == 0);
goto finish;
}
- /* RFC 2616 section 10.5.6 : handle unsupported HTTP major versions cleanly. */
- /* We currently only support 0.9, 1.0, 1.1 properly */
- if ( (http_ver.major == 0 && http_ver.minor != 9) ||
- (http_ver.major > 1) ) {
-
- clientStreamNode *node = context->getClientReplyContext();
- debugs(33, 5, "Unsupported HTTP version discovered. :\n" << HttpParserHdrBuf(hp));
- conn->quitAfterError(request.getRaw());
- // setLogUri should called before repContext->setReplyToError
- setLogUri(http, http->uri, true);
- clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
- assert (repContext);
- repContext->setReplyToError(ERR_UNSUP_HTTPVERSION, Http::scHttpVersionNotSupported, method, http->uri,
- conn->clientConnection->remote, NULL, HttpParserHdrBuf(hp), NULL);
- assert(context->http->out.offset == 0);
- context->pullData();
- goto finish;
- }
-
/* compile headers */
- /* we should skip request line! */
- /* XXX should actually know the damned buffer size here */
- if (http_ver.major >= 1 && !request->parseHeader(HttpParserHdrBuf(hp), HttpParserHdrSz(hp))) {
+ if (http_ver.major >= 1 && !request->parseHeader(hp)) {
clientStreamNode *node = context->getClientReplyContext();
- debugs(33, 5, "Failed to parse request headers:\n" << HttpParserHdrBuf(hp));
+ debugs(33, 5, "Failed to parse request headers:\n" << hp.mimeHeader());
conn->quitAfterError(request.getRaw());
// setLogUri should called before repContext->setReplyToError
setLogUri(http, http->uri, true);
#endif /* FOLLOW_X_FORWARDED_FOR */
request->my_addr = conn->clientConnection->local;
request->myportname = conn->port->name;
- request->http_ver = http_ver;
+ // XXX: for non-HTTP messages instantiate a different HttpMsg child type
+ // for now Squid only supports HTTP requests
+ assert(request->http_ver.protocol == http_ver.protocol);
+ request->http_ver.major = http_ver.major;
+ request->http_ver.minor = http_ver.minor;
// Link this HttpRequest to ConnStateData relatively early so the following complex handling can use it
// TODO: this effectively obsoletes a lot of conn->FOO copying. That needs cleaning up later.
if (http->request->method == Http::METHOD_CONNECT) {
context->mayUseConnection(true);
conn->flags.readMore = false;
-
- // consume header early so that tunnel gets just the body
- connNoteUseOfBuffer(conn, http->req_sz);
- notedUseOfBuffer = true;
}
#if USE_OPENSSL
request->body_pipe = conn->expectRequestBody(
chunked ? -1 : request->content_length);
- // consume header early so that body pipe gets just the body
- connNoteUseOfBuffer(conn, http->req_sz);
- notedUseOfBuffer = true;
-
/* Is it too large? */
if (!chunked && // if chunked, we will check as we accumulate
clientIsRequestBodyTooLargeForPolicy(request->content_length)) {
http->doCallouts();
finish:
- if (!notedUseOfBuffer)
- connNoteUseOfBuffer(conn, http->req_sz);
-
/*
* DPW 2007-05-18
* Moved the TCP_RESET feature from clientReplyContext::sendMoreData
}
}
-static void
-connStripBufferWhitespace (ConnStateData * conn)
-{
- // XXX: kill this whole function.
- while (!conn->in.buf.isEmpty() && xisspace(conn->in.buf.at(0))) {
- conn->in.buf.consume(1);
- }
-}
-
/**
* Limit the number of concurrent requests.
* \return true when there are available position(s) in the pipeline queue for another request.
bool
ConnStateData::clientParseRequests()
{
- HttpRequestMethod method;
bool parsed_req = false;
debugs(33, 5, HERE << clientConnection << ": attempting to parse");
// Loop while we have read bytes that are not needed for producing the body
// On errors, bodyPipe may become nil, but readMore will be cleared
while (!in.buf.isEmpty() && !bodyPipe && flags.readMore) {
- connStripBufferWhitespace(this);
/* Don't try to parse if the buffer is empty */
if (in.buf.isEmpty())
/* Begin the parsing */
PROF_start(parseHttpRequest);
- HttpParserInit(&parser_, in.buf.c_str(), in.buf.length());
+
+ // parser is incremental. Generate new parser state if we,
+ // a) dont have one already
+ // b) have completed the previous request parsing already
+ if (!parser_ || !parser_->needsMoreData())
+ parser_ = new Http1::RequestParser();
/* Process request */
- Http::ProtocolVersion http_ver;
- ClientSocketContext *context = parseHttpRequest(this, &parser_, &method, &http_ver);
+ ClientSocketContext *context = parseHttpRequest(this, *parser_);
PROF_stop(parseHttpRequest);
- /* partial or incomplete request */
- if (!context) {
- // TODO: why parseHttpRequest can just return parseHttpRequestAbort
- // (which becomes context) but checkHeaderLimits cannot?
- checkHeaderLimits();
- break;
- }
-
/* status -1 or 1 */
if (context) {
debugs(33, 5, HERE << clientConnection << ": parsed a request");
CommTimeoutCbPtrFun(clientLifetimeTimeout, context->http));
commSetConnTimeout(clientConnection, Config.Timeout.lifetime, timeoutCall);
- clientProcessRequest(this, &parser_, context, method, http_ver);
+ clientProcessRequest(this, *parser_, context);
parsed_req = true; // XXX: do we really need to parse everything right NOW ?
break;
}
}
+ else // incomplete parse, wait for more data
+ break;
}
/* XXX where to 'finish' the parsing pass? */