| // Copyright 2011 Google Inc. |
| // |
| // Licensed 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. |
| |
| // Author: jmarantz@google.com (Joshua Marantz) |
| |
| #include "pagespeed/kernel/http/request_headers.h" |
| |
| #include <map> |
| #include <utility> |
| |
| #include "base/logging.h" |
| #include "pagespeed/kernel/base/string.h" |
| #include "pagespeed/kernel/base/string_util.h" |
| #include "pagespeed/kernel/base/string_writer.h" |
| #include "pagespeed/kernel/base/writer.h" |
| #include "pagespeed/kernel/http/headers.h" |
| #include "pagespeed/kernel/http/http.pb.h" |
| #include "pagespeed/kernel/http/http_names.h" |
| |
| namespace net_instaweb { |
| |
| class MessageHandler; |
| |
| RequestHeaders::RequestHeaders() { |
| Headers<HttpRequestHeaders>::SetProto(new HttpRequestHeaders); |
| } |
| |
| void RequestHeaders::Clear() { |
| Headers<HttpRequestHeaders>::Clear(); |
| mutable_proto()->Clear(); |
| } |
| |
| void RequestHeaders::CopyFromProto(const HttpRequestHeaders& p) { |
| Headers<HttpRequestHeaders>::Clear(); |
| Headers<HttpRequestHeaders>::CopyProto(p); |
| } |
| |
| void RequestHeaders::CopyFrom(const RequestHeaders& other) { |
| CopyFromProto(*other.proto()); |
| } |
| |
| GoogleString RequestHeaders::ToString() const { |
| GoogleString str; |
| StringWriter writer(&str); |
| WriteAsHttp("", &writer, NULL); |
| return str; |
| } |
| |
| // To avoid having every transitive dependency pull in the generated |
| // protobuf header file during compilation, we have a distinct enum |
| // for the RequestHeaders interface class. We translate with switch |
| // statements rather than array lookups just so we don't have to bother |
| // initializing the array. |
| void RequestHeaders::set_method(Method method) { |
| HttpRequestHeaders* proto = mutable_proto(); |
| switch (method) { |
| case kOptions: proto->set_method(HttpRequestHeaders::OPTIONS); break; |
| case kGet: proto->set_method(HttpRequestHeaders::GET); break; |
| case kHead: proto->set_method(HttpRequestHeaders::HEAD); break; |
| case kPost: proto->set_method(HttpRequestHeaders::POST); break; |
| case kPut: proto->set_method(HttpRequestHeaders::PUT); break; |
| case kDelete: proto->set_method(HttpRequestHeaders::DELETE); break; |
| case kTrace: proto->set_method(HttpRequestHeaders::TRACE); break; |
| case kConnect: proto->set_method(HttpRequestHeaders::CONNECT); break; |
| case kPatch: proto->set_method(HttpRequestHeaders::PATCH); break; |
| case kPurge: proto->set_method(HttpRequestHeaders::PURGE); break; |
| case kError: proto->set_method(HttpRequestHeaders::INVALID); break; |
| } |
| } |
| |
| RequestHeaders::Method RequestHeaders::method() const { |
| switch (proto()->method()) { |
| case HttpRequestHeaders::OPTIONS: return kOptions; |
| case HttpRequestHeaders::GET: return kGet; |
| case HttpRequestHeaders::HEAD: return kHead; |
| case HttpRequestHeaders::POST: return kPost; |
| case HttpRequestHeaders::PUT: return kPut; |
| case HttpRequestHeaders::DELETE: return kDelete; |
| case HttpRequestHeaders::TRACE: return kTrace; |
| case HttpRequestHeaders::CONNECT: return kConnect; |
| case HttpRequestHeaders::PATCH: return kPatch; |
| case HttpRequestHeaders::PURGE: return kPurge; |
| case HttpRequestHeaders::INVALID: return kError; |
| } |
| LOG(DFATAL) << "Invalid method"; |
| return kGet; |
| } |
| |
| const char* RequestHeaders::method_string() const { |
| switch (proto()->method()) { |
| case HttpRequestHeaders::OPTIONS: return "OPTIONS"; |
| case HttpRequestHeaders::GET: return "GET"; |
| case HttpRequestHeaders::HEAD: return "HEAD"; |
| case HttpRequestHeaders::POST: return "POST"; |
| case HttpRequestHeaders::PUT: return "PUT"; |
| case HttpRequestHeaders::DELETE: return "DELETE"; |
| case HttpRequestHeaders::TRACE: return "TRACE"; |
| case HttpRequestHeaders::CONNECT: return "CONNECT"; |
| case HttpRequestHeaders::PATCH: return "PATCH"; |
| case HttpRequestHeaders::PURGE: return "PURGE"; |
| case HttpRequestHeaders::INVALID: return "ERROR"; |
| } |
| LOG(DFATAL) << "Invalid method"; |
| return NULL; |
| } |
| |
| const GoogleString& RequestHeaders::message_body() const { |
| return proto()->message_body(); |
| } |
| |
| void RequestHeaders::set_message_body(const GoogleString& data) { |
| mutable_proto()->set_message_body(data); |
| } |
| |
| // Serialize meta-data to a binary stream. |
| bool RequestHeaders::WriteAsHttp( |
| const StringPiece& url, Writer* writer, MessageHandler* handler) const { |
| bool ret = true; |
| GoogleString buf = StringPrintf("%s %s HTTP/%d.%d\r\n", |
| method_string(), url.as_string().c_str(), |
| major_version(), minor_version()); |
| ret &= writer->Write(buf, handler); |
| ret &= Headers<HttpRequestHeaders>::WriteAsHttp(writer, handler); |
| return ret; |
| } |
| |
| bool RequestHeaders::AcceptsGzip() const { |
| ConstStringStarVector v; |
| if (Lookup(HttpAttributes::kAcceptEncoding, &v)) { |
| for (int i = 0, nv = v.size(); i < nv; ++i) { |
| StringPieceVector encodings; |
| SplitStringPieceToVector(*(v[i]), ",", &encodings, true); |
| for (int j = 0, nencodings = encodings.size(); j < nencodings; ++j) { |
| if (StringCaseEqual(encodings[j], HttpAttributes::kGzip)) { |
| return true; |
| } |
| } |
| } |
| } |
| return false; |
| } |
| |
| bool RequestHeaders::IsXmlHttpRequest() const { |
| // Check if kXRequestedWith header is present to determine whether it is |
| // XmlHttpRequest or not. |
| // Note: Not every ajax request sends this header but many libraries like |
| // jquery, prototype and mootools etc. send this header. Google closure and |
| // custom ajax hacks will not set this header. |
| // It is not guaranteed that javascript present in the html loaded via |
| // ajax request will execute. |
| const char* x_requested_with = Lookup1(HttpAttributes::kXRequestedWith); |
| if (x_requested_with != NULL && |
| StringCaseEqual(x_requested_with, HttpAttributes::kXmlHttpRequest)) { |
| return true; |
| } |
| return false; |
| } |
| |
| const Headers<HttpRequestHeaders>::CookieMultimap& |
| RequestHeaders::GetAllCookies() const { |
| return *PopulateCookieMap(HttpAttributes::kCookie); |
| } |
| |
| RequestHeaders::Properties RequestHeaders::GetProperties() const { |
| Properties properties(Has(HttpAttributes::kCookie), |
| Has(HttpAttributes::kCookie2), |
| Has(HttpAttributes::kAuthorization)); |
| return properties; |
| } |
| |
| bool RequestHeaders::HasCookie(StringPiece cookie_name) const { |
| const CookieMultimap& cookies = GetAllCookies(); |
| return cookies.find(cookie_name) != cookies.end(); |
| } |
| |
| bool RequestHeaders::HasCookieValue(StringPiece cookie_name, |
| StringPiece cookie_value) const { |
| const CookieMultimap& cookies = GetAllCookies(); |
| typedef CookieMultimap::const_iterator Iter; |
| std::pair<Iter, Iter> range = cookies.equal_range(cookie_name); |
| for (Iter p = range.first; p != range.second; ++p) { |
| const ValueAndAttributes& value_attr = p->second; |
| if (value_attr.first == cookie_value) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| } // namespace net_instaweb |