| /** | |
| Licensed to the Apache Software Foundation (ASF) under one | |
| or more contributor license agreements. See the NOTICE file | |
| distributed with this work for additional information | |
| regarding copyright ownership. The ASF licenses this file | |
| to you under the Apache License, Version 2.0 (the | |
| "License"); you may not use this file except in compliance | |
| with the License. You may obtain a copy of the License at | |
| http://www.apache.org/licenses/LICENSE-2.0 | |
| Unless required by applicable law or agreed to in writing, software | |
| distributed under the License is distributed on an "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| See the License for the specific language governing permissions and | |
| limitations under the License. | |
| */ | |
| /** | |
| * @file Headers.cc | |
| */ | |
| #include <memory> | |
| #include <string> | |
| #include <cstring> | |
| #include <sstream> | |
| #include <cctype> | |
| #include "ts/ts.h" | |
| #include "tscpp/api/Headers.h" | |
| #include "logging_internal.h" | |
| #include "tscpp/api/noncopyable.h" | |
| using atscppapi::header_field_iterator; | |
| using atscppapi::header_field_value_iterator; | |
| using atscppapi::HeaderField; | |
| using atscppapi::HeaderFieldName; | |
| using atscppapi::Headers; | |
| using std::string; | |
| namespace atscppapi | |
| { | |
| HeaderFieldName::HeaderFieldName(const std::string &name) | |
| { | |
| name_ = name; | |
| } | |
| HeaderFieldName::operator std::string() | |
| { | |
| return name_; | |
| } | |
| HeaderFieldName::operator const char *() | |
| { | |
| return name_.c_str(); | |
| } | |
| std::string | |
| HeaderFieldName::str() | |
| { | |
| return name_; | |
| } | |
| HeaderFieldName::size_type | |
| HeaderFieldName::length() | |
| { | |
| return name_.length(); | |
| } | |
| const char * | |
| HeaderFieldName::c_str() | |
| { | |
| return name_.c_str(); | |
| } | |
| bool | |
| HeaderFieldName::operator==(const char *field_name) | |
| { | |
| return (::strcasecmp(c_str(), field_name) == 0); | |
| } | |
| bool | |
| HeaderFieldName::operator==(const std::string &field_name) | |
| { | |
| return operator==(field_name.c_str()); | |
| } | |
| bool | |
| HeaderFieldName::operator!=(const char *field_name) | |
| { | |
| return !operator==(field_name); | |
| } | |
| bool | |
| HeaderFieldName::operator!=(const std::string &field_name) | |
| { | |
| return !operator==(field_name.c_str()); | |
| } | |
| /** | |
| * @private | |
| */ | |
| struct HeaderFieldValueIteratorState : noncopyable { | |
| TSMBuffer hdr_buf_ = nullptr; | |
| TSMLoc hdr_loc_ = nullptr; | |
| TSMLoc field_loc_ = nullptr; | |
| int index_ = 0; | |
| HeaderFieldValueIteratorState() = default; | |
| void | |
| reset(TSMBuffer bufp, TSMLoc hdr_loc, TSMLoc field_loc, int index) | |
| { | |
| hdr_buf_ = bufp; | |
| hdr_loc_ = hdr_loc; | |
| field_loc_ = field_loc; | |
| index_ = index; | |
| } | |
| }; | |
| header_field_value_iterator::header_field_value_iterator(void *bufp, void *hdr_loc, void *field_loc, int index) | |
| { | |
| state_ = std::make_unique<HeaderFieldValueIteratorState>(); | |
| state_->reset(static_cast<TSMBuffer>(bufp), static_cast<TSMLoc>(hdr_loc), static_cast<TSMLoc>(field_loc), index); | |
| } | |
| header_field_value_iterator::header_field_value_iterator(const header_field_value_iterator &it) | |
| { | |
| state_ = std::make_unique<HeaderFieldValueIteratorState>(); | |
| state_->reset(it.state_->hdr_buf_, it.state_->hdr_loc_, it.state_->field_loc_, it.state_->index_); | |
| } | |
| header_field_value_iterator::~header_field_value_iterator() {} | |
| std::string | |
| header_field_value_iterator::operator*() | |
| { | |
| if (state_->index_ >= 0) { | |
| int length = 0; | |
| const char *str = TSMimeHdrFieldValueStringGet(state_->hdr_buf_, state_->hdr_loc_, state_->field_loc_, state_->index_, &length); | |
| if (length && str) { | |
| return std::string(str, length); | |
| } | |
| } | |
| return std::string(); | |
| } | |
| header_field_value_iterator & | |
| header_field_value_iterator::operator++() | |
| { | |
| ++state_->index_; | |
| return *this; | |
| } | |
| header_field_value_iterator | |
| header_field_value_iterator::operator++(int) | |
| { | |
| header_field_value_iterator tmp(*this); | |
| operator++(); | |
| return tmp; | |
| } | |
| bool | |
| header_field_value_iterator::operator==(const header_field_value_iterator &rhs) const | |
| { | |
| return (state_->hdr_buf_ == rhs.state_->hdr_buf_) && (state_->hdr_loc_ == rhs.state_->hdr_loc_) && | |
| (state_->field_loc_ == rhs.state_->field_loc_) && (state_->index_ == rhs.state_->index_); | |
| } | |
| bool | |
| header_field_value_iterator::operator!=(const header_field_value_iterator &rhs) const | |
| { | |
| return !operator==(rhs); | |
| } | |
| /** | |
| * @private | |
| */ | |
| struct MLocContainer { | |
| TSMBuffer hdr_buf_; | |
| TSMLoc hdr_loc_; | |
| TSMLoc field_loc_; | |
| MLocContainer(TSMBuffer bufp, TSMLoc hdr_loc, TSMLoc field_loc) : hdr_buf_(bufp), hdr_loc_(hdr_loc), field_loc_(field_loc) {} | |
| ~MLocContainer() | |
| { | |
| if (field_loc_ != TS_NULL_MLOC) { | |
| TSHandleMLocRelease(hdr_buf_, hdr_loc_, field_loc_); | |
| } | |
| } | |
| }; | |
| /** | |
| * @private | |
| */ | |
| struct HeaderFieldIteratorState { | |
| std::shared_ptr<MLocContainer> mloc_container_; | |
| HeaderFieldIteratorState(TSMBuffer bufp, TSMLoc hdr_loc, TSMLoc field_loc) | |
| : mloc_container_(new MLocContainer(bufp, hdr_loc, field_loc)) | |
| { | |
| } | |
| }; | |
| HeaderField::~HeaderField() = default; | |
| HeaderField::size_type | |
| HeaderField::size() const | |
| { | |
| return TSMimeHdrFieldValuesCount(iter_.state_->mloc_container_->hdr_buf_, iter_.state_->mloc_container_->hdr_loc_, | |
| iter_.state_->mloc_container_->field_loc_); | |
| } | |
| header_field_value_iterator | |
| HeaderField::begin() | |
| { | |
| return header_field_value_iterator(iter_.state_->mloc_container_->hdr_buf_, iter_.state_->mloc_container_->hdr_loc_, | |
| iter_.state_->mloc_container_->field_loc_, 0); | |
| } | |
| header_field_value_iterator | |
| HeaderField::end() | |
| { | |
| return header_field_value_iterator(iter_.state_->mloc_container_->hdr_buf_, iter_.state_->mloc_container_->hdr_loc_, | |
| iter_.state_->mloc_container_->field_loc_, size()); | |
| } | |
| HeaderFieldName | |
| HeaderField::name() const | |
| { | |
| int length = 0; | |
| const char *str = TSMimeHdrFieldNameGet(iter_.state_->mloc_container_->hdr_buf_, iter_.state_->mloc_container_->hdr_loc_, | |
| iter_.state_->mloc_container_->field_loc_, &length); | |
| if (str && length) { | |
| return std::string(str, length); | |
| } | |
| return std::string(); | |
| } | |
| std::string | |
| HeaderField::values(const char *join) | |
| { | |
| std::string ret; | |
| for (header_field_value_iterator it = begin(); it != end(); ++it) { | |
| if (ret.size()) { | |
| ret.append(join); | |
| } | |
| ret.append(*it); | |
| } | |
| return ret; | |
| } | |
| std::string | |
| HeaderField::values(const std::string &join) | |
| { | |
| return values(join.c_str()); | |
| } | |
| std::string | |
| HeaderField::values(const char join) | |
| { | |
| return values(std::string().append(1, join)); | |
| } | |
| std::string | |
| Headers::value(const std::string &key, size_type index /* = 0 */) | |
| { | |
| header_field_iterator iter = find(key); | |
| if (iter == end()) { | |
| return string(); | |
| } | |
| if (index == 0) { // skip for loop | |
| return *((*iter).begin()); | |
| } | |
| for (; iter != end(); iter.nextDup()) { | |
| if (index < (*iter).size()) { | |
| return (*iter)[index]; | |
| } | |
| index -= (*iter).size(); | |
| } | |
| return string(); | |
| } | |
| bool | |
| HeaderField::empty() | |
| { | |
| return (begin() == end()); | |
| } | |
| bool | |
| HeaderField::clear() | |
| { | |
| return (TSMimeHdrFieldValuesClear(iter_.state_->mloc_container_->hdr_buf_, iter_.state_->mloc_container_->hdr_loc_, | |
| iter_.state_->mloc_container_->field_loc_) == TS_SUCCESS); | |
| } | |
| bool | |
| HeaderField::erase(const header_field_value_iterator &it) | |
| { | |
| return (TSMimeHdrFieldValueDelete(it.state_->hdr_buf_, it.state_->hdr_loc_, it.state_->field_loc_, it.state_->index_) == | |
| TS_SUCCESS); | |
| } | |
| bool | |
| HeaderField::append(const std::string &value) | |
| { | |
| return append(value.c_str(), value.length()); | |
| } | |
| bool | |
| HeaderField::append(const char *value, int length) | |
| { | |
| return (TSMimeHdrFieldValueStringInsert(iter_.state_->mloc_container_->hdr_buf_, iter_.state_->mloc_container_->hdr_loc_, | |
| iter_.state_->mloc_container_->field_loc_, -1, value, length) == TS_SUCCESS); | |
| } | |
| bool | |
| HeaderField::setName(const std::string &str) | |
| { | |
| return (TSMimeHdrFieldNameSet(iter_.state_->mloc_container_->hdr_buf_, iter_.state_->mloc_container_->hdr_loc_, | |
| iter_.state_->mloc_container_->field_loc_, str.c_str(), str.length()) == TS_SUCCESS); | |
| } | |
| bool | |
| HeaderField::operator==(const char *field_name) const | |
| { | |
| return (::strcasecmp(name(), field_name) == 0); | |
| } | |
| bool | |
| HeaderField::operator==(const std::string &field_name) const | |
| { | |
| return operator==(field_name.c_str()); | |
| } | |
| bool | |
| HeaderField::operator!=(const char *field_name) const | |
| { | |
| return !operator==(field_name); | |
| } | |
| bool | |
| HeaderField::operator!=(const std::string &field_name) const | |
| { | |
| return !operator==(field_name.c_str()); | |
| } | |
| bool | |
| HeaderField::operator=(const std::string &field_value) | |
| { | |
| if (!clear()) { | |
| return false; | |
| } | |
| return append(field_value); | |
| } | |
| bool | |
| HeaderField::operator=(const char *field_value) | |
| { | |
| if (!clear()) { | |
| return false; | |
| } | |
| return append(field_value); | |
| } | |
| std::string | |
| HeaderField::operator[](const int index) | |
| { | |
| return *header_field_value_iterator(iter_.state_->mloc_container_->hdr_buf_, iter_.state_->mloc_container_->hdr_loc_, | |
| iter_.state_->mloc_container_->field_loc_, index); | |
| } | |
| std::string | |
| HeaderField::str() | |
| { | |
| std::ostringstream oss; | |
| oss << (*this); | |
| return oss.str(); | |
| } | |
| std::ostream & | |
| operator<<(std::ostream &os, HeaderField &obj) | |
| { | |
| os << obj.name() << ": "; | |
| int count = obj.size(); | |
| for (HeaderField::iterator it = obj.begin(); it != obj.end(); ++it) { | |
| os << (*it); | |
| if (--count > 0) { | |
| os << ","; | |
| } | |
| } | |
| return os; | |
| } | |
| header_field_iterator::header_field_iterator(void *hdr_buf, void *hdr_loc, void *field_loc) | |
| : state_(std::make_unique<HeaderFieldIteratorState>(static_cast<TSMBuffer>(hdr_buf), static_cast<TSMLoc>(hdr_loc), | |
| static_cast<TSMLoc>(field_loc))) | |
| { | |
| } | |
| header_field_iterator::header_field_iterator(const header_field_iterator &it) | |
| : state_(std::make_unique<HeaderFieldIteratorState>(*it.state_)) | |
| { | |
| } | |
| header_field_iterator & | |
| header_field_iterator::operator=(const header_field_iterator &rhs) | |
| { | |
| if (this != &rhs) { | |
| state_ = std::make_unique<HeaderFieldIteratorState>(*rhs.state_); | |
| } | |
| return *this; | |
| } | |
| header_field_iterator::~header_field_iterator() {} | |
| // utility function to use to advance iterators using different functions | |
| std::unique_ptr<HeaderFieldIteratorState> | |
| advanceIterator(std::unique_ptr<HeaderFieldIteratorState> state, TSMLoc (*getNextField)(TSMBuffer, TSMLoc, TSMLoc)) | |
| { | |
| if (state->mloc_container_->field_loc_ != TS_NULL_MLOC) { | |
| TSMBuffer hdr_buf = state->mloc_container_->hdr_buf_; | |
| TSMLoc hdr_loc = state->mloc_container_->hdr_loc_; | |
| TSMLoc next_field_loc = getNextField(hdr_buf, hdr_loc, state->mloc_container_->field_loc_); | |
| state = std::make_unique<HeaderFieldIteratorState>(hdr_buf, hdr_loc, next_field_loc); | |
| } | |
| return state; | |
| } | |
| header_field_iterator & | |
| header_field_iterator::operator++() | |
| { | |
| state_ = advanceIterator(std::move(state_), TSMimeHdrFieldNext); | |
| return *this; | |
| } | |
| header_field_iterator | |
| header_field_iterator::operator++(int) | |
| { | |
| header_field_iterator tmp(*this); | |
| operator++(); | |
| return tmp; | |
| } | |
| header_field_iterator & | |
| header_field_iterator::nextDup() | |
| { | |
| state_ = advanceIterator(std::move(state_), TSMimeHdrFieldNextDup); | |
| return *this; | |
| } | |
| bool | |
| header_field_iterator::operator==(const header_field_iterator &rhs) const | |
| { | |
| return (state_->mloc_container_->hdr_buf_ == rhs.state_->mloc_container_->hdr_buf_) && | |
| (state_->mloc_container_->hdr_loc_ == rhs.state_->mloc_container_->hdr_loc_) && | |
| (state_->mloc_container_->field_loc_ == rhs.state_->mloc_container_->field_loc_); | |
| } | |
| bool | |
| header_field_iterator::operator!=(const header_field_iterator &rhs) const | |
| { | |
| return !operator==(rhs); | |
| } | |
| HeaderField | |
| header_field_iterator::operator*() | |
| { | |
| return HeaderField(*this); | |
| } | |
| /** | |
| * @private | |
| */ | |
| struct HeadersState : noncopyable { | |
| TSMBuffer hdr_buf_; | |
| TSMLoc hdr_loc_; | |
| bool self_created_structures_; | |
| HeadersState() | |
| { | |
| hdr_buf_ = TSMBufferCreate(); | |
| hdr_loc_ = TSHttpHdrCreate(hdr_buf_); | |
| self_created_structures_ = true; | |
| } | |
| void | |
| reset(TSMBuffer bufp, TSMLoc hdr_loc) | |
| { | |
| if (self_created_structures_) { | |
| TSHandleMLocRelease(hdr_buf_, TS_NULL_MLOC /* no parent */, hdr_loc_); | |
| TSMBufferDestroy(hdr_buf_); | |
| self_created_structures_ = false; | |
| } | |
| hdr_buf_ = bufp; | |
| hdr_loc_ = hdr_loc; | |
| } | |
| ~HeadersState() { reset(nullptr, nullptr); } | |
| }; | |
| Headers::Headers() | |
| { | |
| state_ = std::make_unique<HeadersState>(); | |
| } | |
| Headers::Headers(void *bufp, void *mloc) | |
| { | |
| state_ = std::make_unique<HeadersState>(); | |
| reset(bufp, mloc); | |
| } | |
| void | |
| Headers::reset(void *bufp, void *mloc) | |
| { | |
| state_->reset(static_cast<TSMBuffer>(bufp), static_cast<TSMLoc>(mloc)); | |
| } | |
| Headers::~Headers() {} | |
| bool | |
| Headers::isInitialized() const | |
| { | |
| return (state_->hdr_buf_ && state_->hdr_loc_); | |
| } | |
| bool | |
| Headers::empty() | |
| { | |
| return (begin() == end()); | |
| } | |
| Headers::size_type | |
| Headers::size() const | |
| { | |
| return TSMimeHdrFieldsCount(state_->hdr_buf_, state_->hdr_loc_); | |
| } | |
| Headers::size_type | |
| Headers::lengthBytes() const | |
| { | |
| return TSMimeHdrLengthGet(state_->hdr_buf_, state_->hdr_loc_); | |
| } | |
| header_field_iterator | |
| Headers::begin() | |
| { | |
| return header_field_iterator(state_->hdr_buf_, state_->hdr_loc_, TSMimeHdrFieldGet(state_->hdr_buf_, state_->hdr_loc_, 0)); | |
| } | |
| header_field_iterator | |
| Headers::end() | |
| { | |
| return header_field_iterator(state_->hdr_buf_, state_->hdr_loc_, TS_NULL_MLOC); | |
| } | |
| bool | |
| Headers::clear() | |
| { | |
| return (TSMimeHdrFieldsClear(state_->hdr_buf_, state_->hdr_loc_) == TS_SUCCESS); | |
| } | |
| bool | |
| Headers::erase(const header_field_iterator &it) | |
| { | |
| return (TSMimeHdrFieldDestroy(it.state_->mloc_container_->hdr_buf_, it.state_->mloc_container_->hdr_loc_, | |
| it.state_->mloc_container_->field_loc_) == TS_SUCCESS); | |
| } | |
| Headers::size_type | |
| Headers::erase(const std::string &key) | |
| { | |
| return erase(key.c_str(), key.length()); | |
| } | |
| Headers::size_type | |
| Headers::erase(const char *key, int length) | |
| { | |
| header_field_iterator iter = find(key, length); | |
| size_type erased_count = 0; | |
| while (iter != end()) { | |
| header_field_iterator iter_to_delete = iter; | |
| iter.nextDup(); | |
| erase(iter_to_delete); | |
| ++erased_count; | |
| } | |
| return erased_count; | |
| } | |
| Headers::size_type | |
| Headers::count(const char *key, int length) | |
| { | |
| size_type ret_count = 0; | |
| size_t len = (length < 0) ? strlen(key) : length; | |
| for (header_field_iterator it = begin(); it != end(); ++it) { | |
| HeaderFieldName nm = (*it).name(); | |
| if ((nm.length() == len) && (::strncasecmp(nm.c_str(), key, len) == 0)) { | |
| ret_count++; | |
| } | |
| } | |
| return ret_count; | |
| } | |
| Headers::size_type | |
| Headers::count(const std::string &key) | |
| { | |
| return count(key.c_str(), key.length()); | |
| } | |
| std::string | |
| Headers::values(const std::string &key, const char *join) | |
| { | |
| std::string ret; | |
| for (header_field_iterator it = find(key); it != end(); it.nextDup()) { | |
| if (ret.size()) { | |
| ret.append(join); | |
| } | |
| ret.append((*it).values(join)); | |
| } | |
| return ret; | |
| } | |
| std::string | |
| Headers::values(const std::string &key, const std::string &join) | |
| { | |
| return values(key, join.c_str()); | |
| } | |
| std::string | |
| Headers::values(const std::string &key, const char join) | |
| { | |
| return values(key, std::string().assign(1, join)); | |
| } | |
| header_field_iterator | |
| Headers::find(const std::string &key) | |
| { | |
| return find(key.c_str(), key.length()); | |
| } | |
| header_field_iterator | |
| Headers::find(const char *key, int length) | |
| { | |
| TSMLoc field_loc = TSMimeHdrFieldFind(state_->hdr_buf_, state_->hdr_loc_, key, length); | |
| if (field_loc != TS_NULL_MLOC) { | |
| return header_field_iterator(state_->hdr_buf_, state_->hdr_loc_, field_loc); | |
| } | |
| return end(); | |
| } | |
| Headers::iterator | |
| Headers::append(const std::string &key, const std::string &value) | |
| { | |
| TSMLoc field_loc = TS_NULL_MLOC; | |
| if (TSMimeHdrFieldCreate(state_->hdr_buf_, state_->hdr_loc_, &field_loc) == TS_SUCCESS) { | |
| TSMimeHdrFieldNameSet(state_->hdr_buf_, state_->hdr_loc_, field_loc, key.c_str(), key.length()); | |
| TSMimeHdrFieldAppend(state_->hdr_buf_, state_->hdr_loc_, field_loc); | |
| TSMimeHdrFieldValueStringInsert(state_->hdr_buf_, state_->hdr_loc_, field_loc, 0, value.c_str(), value.length()); | |
| return header_field_iterator(state_->hdr_buf_, state_->hdr_loc_, field_loc); | |
| } else { | |
| return end(); | |
| } | |
| } | |
| Headers::iterator | |
| Headers::set(const std::string &key, const std::string &value) | |
| { | |
| erase(key); | |
| return append(key, value); | |
| } | |
| HeaderField | |
| Headers::operator[](const std::string &key) | |
| { | |
| // In STL fashion if the key doesn't exist it will be added with an empty value. | |
| header_field_iterator it = find(key); | |
| if (it != end()) { | |
| return *it; | |
| } else { | |
| return *append(key, ""); | |
| } | |
| } | |
| std::string | |
| Headers::str() | |
| { | |
| std::ostringstream oss; | |
| oss << (*this); | |
| return oss.str(); | |
| } | |
| std::string | |
| Headers::wireStr() | |
| { | |
| string retval; | |
| for (auto &&iter : *this) { | |
| HeaderField hf = iter; | |
| retval += hf.name().str(); | |
| retval += ": "; | |
| retval += hf.values(", "); | |
| retval += "\r\n"; | |
| } | |
| return retval; | |
| } | |
| std::ostream & | |
| operator<<(std::ostream &os, atscppapi::Headers &obj) | |
| { | |
| for (header_field_iterator it = obj.begin(); it != obj.end(); ++it) { | |
| HeaderField hf = *it; | |
| os << hf << std::endl; | |
| } | |
| return os; | |
| } | |
| } // namespace atscppapi |