/* * Copyright (C) 1996-2023 The Squid Software Foundation and contributors * * Squid software is distributed under GPLv2+ license and includes * contributions from numerous individuals and organizations. * Please see the COPYING and CONTRIBUTORS files for details. */ /* DEBUG: section 93 eCAP Interface */ #include "squid.h" #include "BodyPipe.h" #include "HttpReply.h" #include "HttpRequest.h" #include #include #include #include #include "adaptation/ecap/Host.h" /* for protocol constants */ #include "adaptation/ecap/MessageRep.h" #include "adaptation/ecap/XactionRep.h" #include "base/TextException.h" /* HeaderRep */ Adaptation::Ecap::HeaderRep::HeaderRep(Http::Message &aMessage): theHeader(aMessage.header), theMessage(aMessage) { } bool Adaptation::Ecap::HeaderRep::hasAny(const Name &name) const { const Http::HdrType squidId = TranslateHeaderId(name); return squidId == Http::HdrType::OTHER ? theHeader.hasNamed(name.image().c_str(), name.image().size()) : static_cast(theHeader.has(squidId)); } Adaptation::Ecap::HeaderRep::Value Adaptation::Ecap::HeaderRep::value(const Name &name) const { const Http::HdrType squidId = TranslateHeaderId(name); const String value = squidId == Http::HdrType::OTHER ? theHeader.getByName(name.image().c_str()) : theHeader.getStrOrList(squidId); return value.size() > 0 ? Value::FromTempString(value.termedBuf()) : Value(); } void Adaptation::Ecap::HeaderRep::add(const Name &name, const Value &value) { const Http::HdrType squidId = TranslateHeaderId(name); // Http::HdrType::OTHER OK HttpHeaderEntry *e = new HttpHeaderEntry(squidId, SBuf(name.image()), value.toString().c_str()); theHeader.addEntry(e); if (squidId == Http::HdrType::CONTENT_LENGTH) theMessage.content_length = theHeader.getInt64(Http::HdrType::CONTENT_LENGTH); } void Adaptation::Ecap::HeaderRep::removeAny(const Name &name) { const Http::HdrType squidId = TranslateHeaderId(name); if (squidId == Http::HdrType::OTHER) theHeader.delByName(name.image().c_str()); else theHeader.delById(squidId); if (squidId == Http::HdrType::CONTENT_LENGTH) theMessage.content_length = theHeader.getInt64(Http::HdrType::CONTENT_LENGTH); } void Adaptation::Ecap::HeaderRep::visitEach(libecap::NamedValueVisitor &visitor) const { HttpHeaderPos pos = HttpHeaderInitPos; while (HttpHeaderEntry *e = theHeader.getEntry(&pos)) { const Name name(std::string(e->name.rawContent(), e->name.length())); // optimize: find std Names name.assignHostId(e->id); visitor.visit(name, Value(e->value.rawBuf(), e->value.size())); } } libecap::Area Adaptation::Ecap::HeaderRep::image() const { MemBuf mb; mb.init(); theMessage.packInto(&mb, true); return Area::FromTempBuffer(mb.content(), mb.contentSize()); } // throws on failures void Adaptation::Ecap::HeaderRep::parse(const Area &buf) { Http::StatusCode error; Must(theMessage.parse(buf.start, buf.size, true, &error)); } Http::HdrType Adaptation::Ecap::HeaderRep::TranslateHeaderId(const Name &name) { if (name.assignedHostId()) return static_cast(name.hostId()); return Http::HdrType::OTHER; } /* FirstLineRep */ Adaptation::Ecap::FirstLineRep::FirstLineRep(Http::Message &aMessage): theMessage(aMessage) { } libecap::Version Adaptation::Ecap::FirstLineRep::version() const { return libecap::Version(theMessage.http_ver.major, theMessage.http_ver.minor); } void Adaptation::Ecap::FirstLineRep::version(const libecap::Version &aVersion) { theMessage.http_ver.major = aVersion.majr; theMessage.http_ver.minor = aVersion.minr; } libecap::Name Adaptation::Ecap::FirstLineRep::protocol() const { // TODO: optimize? switch (theMessage.http_ver.protocol) { case AnyP::PROTO_HTTP: return libecap::protocolHttp; case AnyP::PROTO_HTTPS: return libecap::protocolHttps; case AnyP::PROTO_FTP: return libecap::protocolFtp; case AnyP::PROTO_GOPHER: return libecap::protocolGopher; case AnyP::PROTO_WAIS: return libecap::protocolWais; case AnyP::PROTO_WHOIS: return libecap::protocolWhois; case AnyP::PROTO_URN: return libecap::protocolUrn; case AnyP::PROTO_ICP: return protocolIcp; #if USE_HTCP case AnyP::PROTO_HTCP: return protocolHtcp; #endif case AnyP::PROTO_CACHE_OBJECT: return protocolCacheObj; case AnyP::PROTO_ICY: return protocolIcy; case AnyP::PROTO_COAP: case AnyP::PROTO_COAPS: // use 'unknown' until libecap supports coap:// and coaps:// // other protocols defined in Squid but not libecap use 'unknown' case AnyP::PROTO_AUTHORITY_FORM: case AnyP::PROTO_SSL: case AnyP::PROTO_TLS: case AnyP::PROTO_UNKNOWN: return protocolUnknown; // until we remember the protocol image case AnyP::PROTO_NONE: return Name(); case AnyP::PROTO_MAX: break; // should not happen // no default to catch AnyP::PROTO_ additions } Must(false); // not reached return Name(); } void Adaptation::Ecap::FirstLineRep::protocol(const Name &p) { // TODO: what happens if we fail to translate some protocol? theMessage.http_ver.protocol = TranslateProtocolId(p); } AnyP::ProtocolType Adaptation::Ecap::FirstLineRep::TranslateProtocolId(const Name &name) { if (name.assignedHostId()) return static_cast(name.hostId()); return AnyP::PROTO_UNKNOWN; } /* RequestHeaderRep */ Adaptation::Ecap::RequestLineRep::RequestLineRep(HttpRequest &aMessage): FirstLineRep(aMessage), theMessage(aMessage) { } void Adaptation::Ecap::RequestLineRep::uri(const Area &aUri) { // TODO: if method is not set, AnyP::Uri::parse will assume it is not connect; // Can we change AnyP::Uri::parse API to remove the method parameter? const auto ok = theMessage.url.parse(theMessage.method, SBuf(aUri.toString())); Must(ok); } Adaptation::Ecap::RequestLineRep::Area Adaptation::Ecap::RequestLineRep::uri() const { const SBuf &fullUrl = theMessage.effectiveRequestUri(); // XXX: effectiveRequestUri() cannot return NULL or even empty string, some other problem? Must(!fullUrl.isEmpty()); // optimize: avoid copying by having an Area::Detail that locks theMessage return Area::FromTempBuffer(fullUrl.rawContent(), fullUrl.length()); } void Adaptation::Ecap::RequestLineRep::method(const Name &aMethod) { if (aMethod.assignedHostId()) { const int id = aMethod.hostId(); Must(Http::METHOD_NONE < id && id < Http::METHOD_ENUM_END); Must(id != Http::METHOD_OTHER); theMessage.method = HttpRequestMethod(static_cast(id)); } else { const std::string &image = aMethod.image(); theMessage.method.HttpRequestMethodXXX(image.c_str()); } } Adaptation::Ecap::RequestLineRep::Name Adaptation::Ecap::RequestLineRep::method() const { switch (theMessage.method.id()) { case Http::METHOD_GET: return libecap::methodGet; case Http::METHOD_POST: return libecap::methodPost; case Http::METHOD_PUT: return libecap::methodPut; case Http::METHOD_HEAD: return libecap::methodHead; case Http::METHOD_CONNECT: return libecap::methodConnect; case Http::METHOD_DELETE: return libecap::methodDelete; case Http::METHOD_TRACE: return libecap::methodTrace; default: return Name(theMessage.method.image().toStdString()); } } libecap::Version Adaptation::Ecap::RequestLineRep::version() const { return FirstLineRep::version(); } void Adaptation::Ecap::RequestLineRep::version(const libecap::Version &aVersion) { FirstLineRep::version(aVersion); } libecap::Name Adaptation::Ecap::RequestLineRep::protocol() const { return FirstLineRep::protocol(); } void Adaptation::Ecap::RequestLineRep::protocol(const Name &p) { FirstLineRep::protocol(p); } /* ReplyHeaderRep */ Adaptation::Ecap::StatusLineRep::StatusLineRep(HttpReply &aMessage): FirstLineRep(aMessage), theMessage(aMessage) { } void Adaptation::Ecap::StatusLineRep::statusCode(int code) { theMessage.sline.set(theMessage.sline.version, static_cast(code), nullptr); } int Adaptation::Ecap::StatusLineRep::statusCode() const { // TODO: remove cast when possible return static_cast(theMessage.sline.status()); } void Adaptation::Ecap::StatusLineRep::reasonPhrase(const Area &) { // Squid does not support external custom reason phrases so we have // to just reset it (in case there was a custom internal reason set) theMessage.sline.resetReason(); } Adaptation::Ecap::StatusLineRep::Area Adaptation::Ecap::StatusLineRep::reasonPhrase() const { return Area::FromTempString(std::string(theMessage.sline.reason())); } libecap::Version Adaptation::Ecap::StatusLineRep::version() const { return FirstLineRep::version(); } void Adaptation::Ecap::StatusLineRep::version(const libecap::Version &aVersion) { FirstLineRep::version(aVersion); } libecap::Name Adaptation::Ecap::StatusLineRep::protocol() const { return FirstLineRep::protocol(); } void Adaptation::Ecap::StatusLineRep::protocol(const Name &p) { FirstLineRep::protocol(p); } /* BodyRep */ Adaptation::Ecap::BodyRep::BodyRep(const BodyPipe::Pointer &aBody): theBody(aBody) { } void Adaptation::Ecap::BodyRep::tie(const BodyPipe::Pointer &aBody) { Must(!theBody); Must(aBody != NULL); theBody = aBody; } Adaptation::Ecap::BodyRep::BodySize Adaptation::Ecap::BodyRep::bodySize() const { return (theBody != nullptr && theBody->bodySizeKnown()) ? BodySize(theBody->bodySize()) : BodySize(); } /* MessageRep */ Adaptation::Ecap::MessageRep::MessageRep(Http::Message *rawHeader): theMessage(rawHeader), theFirstLineRep(NULL), theHeaderRep(NULL), theBodyRep(NULL) { Must(theMessage.header); // we do not want to represent a missing message if (HttpRequest *req = dynamic_cast(theMessage.header)) theFirstLineRep = new RequestLineRep(*req); else if (HttpReply *rep = dynamic_cast(theMessage.header)) theFirstLineRep = new StatusLineRep(*rep); else Must(false); // unknown message header type theHeaderRep = new HeaderRep(*theMessage.header); if (theMessage.body_pipe != NULL) theBodyRep = new BodyRep(theMessage.body_pipe); } Adaptation::Ecap::MessageRep::~MessageRep() { delete theBodyRep; delete theHeaderRep; delete theFirstLineRep; } libecap::shared_ptr Adaptation::Ecap::MessageRep::clone() const { Http::Message *hdr = theMessage.header->clone(); hdr->body_pipe = NULL; // if any; TODO: remove pipe cloning from ::clone? libecap::shared_ptr res(new MessageRep(hdr)); // restore indication of a body if needed, but not the pipe if (theMessage.header->body_pipe != NULL) res->addBody(); return res; } libecap::FirstLine & Adaptation::Ecap::MessageRep::firstLine() { return *theFirstLineRep; } const libecap::FirstLine & Adaptation::Ecap::MessageRep::firstLine() const { return *theFirstLineRep; } libecap::Header & Adaptation::Ecap::MessageRep::header() { return *theHeaderRep; } const libecap::Header & Adaptation::Ecap::MessageRep::header() const { return *theHeaderRep; } libecap::Body * Adaptation::Ecap::MessageRep::body() { return theBodyRep; } void Adaptation::Ecap::MessageRep::addBody() { Must(!theBodyRep); Must(!theMessage.body_pipe); // set in tieBody() theBodyRep = new BodyRep(NULL); } void Adaptation::Ecap::MessageRep::tieBody(Adaptation::Ecap::XactionRep *x) { Must(theBodyRep != NULL); // addBody must be called first Must(!theMessage.header->body_pipe); Must(!theMessage.body_pipe); theMessage.header->body_pipe = new BodyPipe(x); theMessage.body_pipe = theMessage.header->body_pipe; theBodyRep->tie(theMessage.body_pipe); } const libecap::Body *Adaptation::Ecap::MessageRep::body() const { return theBodyRep; }