/*
- * Copyright (C) 1996-2014 The Squid Software Foundation and contributors
+ * Copyright (C) 1996-2017 The Squid Software Foundation and contributors
*
* Squid software is distributed under GPLv2+ license and includes
* contributions from numerous individuals and organizations.
#include "squid.h"
#include "Debug.h"
+#include "http/one/Parser.h"
+#include "HttpHdrCc.h"
#include "HttpHeaderTools.h"
#include "HttpMsg.h"
#include "MemBuf.h"
#include "profiler/Profiler.h"
#include "SquidConfig.h"
-HttpMsg::HttpMsg(http_hdr_owner_type owner): header(owner),
- cache_control(NULL), hdr_sz(0), content_length(0),
- pstate(psReadyToParseStartLine)
+HttpMsg::HttpMsg(http_hdr_owner_type owner):
+ http_ver(Http::ProtocolVersion()),
+ header(owner),
+ cache_control(NULL),
+ hdr_sz(0),
+ content_length(0),
+ pstate(psReadyToParseStartLine),
+ sources(0)
{}
HttpMsg::~HttpMsg()
assert(!body_pipe);
}
-HttpMsgParseState &operator++ (HttpMsgParseState &aState)
-{
- int tmp = (int)aState;
- aState = (HttpMsgParseState)(++tmp);
- return aState;
-}
-
-/* find end of headers */
-static int
-httpMsgIsolateHeaders(const char **parse_start, int l, const char **blk_start, const char **blk_end)
+void
+HttpMsg::putCc(const HttpHdrCc *otherCc)
{
- /*
- * parse_start points to the first line of HTTP message *headers*,
- * not including the request or status lines
- */
- size_t end = headersEnd(*parse_start, l);
- int nnl;
-
- if (end) {
- *blk_start = *parse_start;
- *blk_end = *parse_start + end - 1;
- /*
- * leave blk_end pointing to the first character after the
- * first newline which terminates the headers
- */
- assert(**blk_end == '\n');
-
- while (*(*blk_end - 1) == '\r')
- --(*blk_end);
-
- assert(*(*blk_end - 1) == '\n');
-
- *parse_start += end;
-
- return 1;
+ // get rid of the old CC, if any
+ if (cache_control) {
+ delete cache_control;
+ cache_control = nullptr;
+ if (!otherCc)
+ header.delById(Http::HdrType::CACHE_CONTROL);
+ // else it will be deleted inside putCc() below
}
- /*
- * If we didn't find the end of headers, and parse_start does
- * NOT point to a CR or NL character, then return failure
- */
- if (**parse_start != '\r' && **parse_start != '\n')
- return 0; /* failure */
-
- /*
- * If we didn't find the end of headers, and parse_start does point
- * to an empty line, then we have empty headers. Skip all CR and
- * NL characters up to the first NL. Leave parse_start pointing at
- * the first character after the first NL.
- */
- *blk_start = *parse_start;
-
- *blk_end = *blk_start;
-
- for (nnl = 0; nnl == 0; ++(*parse_start)) {
- if (**parse_start == '\r')
- (void) 0;
- else if (**parse_start == '\n')
- ++nnl;
- else
- break;
+ // add new CC, if any
+ if (otherCc) {
+ cache_control = new HttpHdrCc(*otherCc);
+ header.putCc(cache_control);
}
+}
- return 1;
+HttpMsgParseState &operator++ (HttpMsgParseState &aState)
+{
+ int tmp = (int)aState;
+ aState = (HttpMsgParseState)(++tmp);
+ return aState;
}
/* find first CRLF */
// zero return means need more data
// positive return is the size of parsed headers
bool
-HttpMsg::parse(MemBuf *buf, bool eof, Http::StatusCode *error)
+HttpMsg::parse(const char *buf, const size_t sz, bool eof, Http::StatusCode *error)
{
assert(error);
*error = Http::scNone;
- // httpMsgParseStep() and debugging require 0-termination, unfortunately
- buf->terminate(); // does not affect content size
-
// find the end of headers
- const size_t hdr_len = headersEnd(buf->content(), buf->contentSize());
+ const size_t hdr_len = headersEnd(buf, sz);
// sanity check the start line to see if this is in fact an HTTP message
if (!sanityCheckStartLine(buf, hdr_len, error)) {
return false;
}
- // TODO: move to httpReplyParseStep()
- if (hdr_len > Config.maxReplyHeaderSize || (hdr_len <= 0 && (size_t)buf->contentSize() > Config.maxReplyHeaderSize)) {
+ if (hdr_len > Config.maxReplyHeaderSize || (hdr_len <= 0 && sz > Config.maxReplyHeaderSize)) {
debugs(58, DBG_IMPORTANT, "HttpMsg::parse: Too large reply header (" << hdr_len << " > " << Config.maxReplyHeaderSize);
*error = Http::scHeaderTooLarge;
return false;
}
if (hdr_len <= 0) {
- debugs(58, 3, "HttpMsg::parse: failed to find end of headers (eof: " << eof << ") in '" << buf->content() << "'");
+ debugs(58, 3, "HttpMsg::parse: failed to find end of headers (eof: " << eof << ") in '" << buf << "'");
if (eof) // iff we have seen the end, this is an error
*error = Http::scInvalidHeader;
return false;
}
- const int res = httpMsgParseStep(buf->content(), buf->contentSize(), eof);
+ const int res = httpMsgParseStep(buf, sz, eof);
if (res < 0) { // error
- debugs(58, 3, "HttpMsg::parse: cannot parse isolated headers in '" << buf->content() << "'");
+ debugs(58, 3, "HttpMsg::parse: cannot parse isolated headers in '" << buf << "'");
*error = Http::scInvalidHeader;
return false;
}
if (res == 0) {
- debugs(58, 2, "HttpMsg::parse: strange, need more data near '" << buf->content() << "'");
+ debugs(58, 2, "HttpMsg::parse: strange, need more data near '" << buf << "'");
*error = Http::scInvalidHeader;
return false; // but this should not happen due to headersEnd() above
}
assert(res > 0);
- debugs(58, 9, "HttpMsg::parse success (" << hdr_len << " bytes) near '" << buf->content() << "'");
+ debugs(58, 9, "HttpMsg::parse success (" << hdr_len << " bytes) near '" << buf << "'");
if (hdr_sz != (int)hdr_len) {
debugs(58, DBG_IMPORTANT, "internal HttpMsg::parse vs. headersEnd error: " <<
* after headers.) Grr.
*/
if (pstate == psReadyToParseHeaders) {
- if (!httpMsgIsolateHeaders(&parse_start, parse_len, &blk_start, &blk_end)) {
- if (atEnd) {
- blk_start = parse_start;
- blk_end = blk_start + strlen(blk_start);
- } else {
- PROF_stop(HttpMsg_httpMsgParseStep);
- return 0;
- }
- }
-
- if (!header.parse(blk_start, blk_end-blk_start)) {
+ size_t hsize = 0;
+ const int parsed = header.parse(parse_start, parse_len, atEnd, hsize);
+ if (parsed <= 0) {
PROF_stop(HttpMsg_httpMsgParseStep);
- return httpMsgParseError();
+ return !parsed ? 0 : httpMsgParseError();
}
-
+ hdr_sz += hsize;
hdrCacheInit();
-
- *parse_end_ptr = parse_start;
-
- hdr_sz = *parse_end_ptr - buf;
-
++pstate;
}
return 1;
}
+bool
+HttpMsg::parseHeader(Http1::Parser &hp)
+{
+ // HTTP/1 message contains "zero or more header fields"
+ // zero does not need parsing
+ // XXX: c_str() reallocates. performance regression.
+ if (hp.headerBlockSize() && !header.parse(hp.mimeHeader().c_str(), hp.headerBlockSize())) {
+ pstate = psError;
+ return false;
+ }
+
+ // XXX: we are just parsing HTTP headers, not the whole message prefix here
+ hdr_sz = hp.messageHeaderSize();
+ pstate = psParsed;
+ hdrCacheInit();
+ return true;
+}
+
/* handy: resets and returns -1 */
int
HttpMsg::httpMsgParseError()
void
HttpMsg::setContentLength(int64_t clen)
{
- header.delById(HDR_CONTENT_LENGTH); // if any
- header.putInt64(HDR_CONTENT_LENGTH, clen);
+ header.delById(Http::HdrType::CONTENT_LENGTH); // if any
+ header.putInt64(Http::HdrType::CONTENT_LENGTH, clen);
content_length = clen;
}
}
}
-void HttpMsg::packInto(Packer *p, bool full_uri) const
+void HttpMsg::packInto(Packable *p, bool full_uri) const
{
packFirstLineInto(p, full_uri);
header.packInto(p);
- packerAppend(p, "\r\n", 2);
+ p->append("\r\n", 2);
}
void HttpMsg::hdrCacheInit()
{
- content_length = header.getInt64(HDR_CONTENT_LENGTH);
+ content_length = header.getInt64(Http::HdrType::CONTENT_LENGTH);
assert(NULL == cache_control);
cache_control = header.getCc();
}
*/
void HttpMsg::firstLineBuf(MemBuf& mb)
{
- Packer p;
- packerToMemInit(&p, &mb);
- packFirstLineInto(&p, true);
- packerClean(&p);
+ packFirstLineInto(&mb, true);
}
+