1 /*
2 * Copyright 2022 Google LLC
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include "fcp/client/http/curl/curl_header_parser.h"
17
18 #include <string>
19 #include <utility>
20
21 #include "absl/strings/match.h"
22 #include "absl/strings/str_split.h"
23 #include "fcp/base/monitoring.h"
24 #include "fcp/client/http/http_client_util.h"
25
26 namespace fcp::client::http::curl {
27
CurlHeaderParser()28 CurlHeaderParser::CurlHeaderParser()
29 : status_code_(-1),
30 is_last_header_line_(false),
31 use_curl_encoding_(false) {}
32
ParseHeader(const std::string & header_string)33 void CurlHeaderParser::ParseHeader(const std::string& header_string) {
34 if (ParseAsStatus(header_string)) {
35 return;
36 }
37 if (ParseAsHeader(header_string)) {
38 return;
39 }
40 if (ParseAsLastLine(header_string)) {
41 return;
42 }
43 }
44
ParseAsStatus(const std::string & header_string)45 bool CurlHeaderParser::ParseAsStatus(const std::string& header_string) {
46 if (!absl::StartsWith(header_string, "HTTP/")) {
47 return false;
48 }
49
50 std::pair<std::string, std::string> split =
51 absl::StrSplit(header_string, ' ');
52 int status_code;
53 if (!absl::SimpleAtoi(split.second.substr(0, 3), &status_code)) {
54 return false;
55 }
56
57 status_code_ = status_code;
58 // It is required that we store only the final header list. So we keep the
59 // last set of headers
60 header_list_.clear();
61 return true;
62 }
63
ParseAsHeader(const std::string & header_string)64 bool CurlHeaderParser::ParseAsHeader(const std::string& header_string) {
65 if (!absl::StrContains(header_string, ':')) {
66 return false;
67 }
68
69 std::pair<std::string, std::string> split =
70 absl::StrSplit(header_string, ':');
71 std::string key = split.first;
72 std::string value = std::string(absl::StripAsciiWhitespace(split.second));
73
74 // Removes the "Content-Encoding", "Content-Length", and "Content-Length"
75 // headers from the response when the curl encoding in use because they
76 // reflect in-flight encoded values
77 if (!use_curl_encoding_ ||
78 (!absl::EqualsIgnoreCase(key, kContentEncodingHdr) &&
79 !absl::EqualsIgnoreCase(key, kContentLengthHdr) &&
80 !absl::EqualsIgnoreCase(key, kTransferEncodingHdr))) {
81 header_list_.push_back({key, value});
82 }
83 return true;
84 }
85
ParseAsLastLine(const std::string & header_string)86 bool CurlHeaderParser::ParseAsLastLine(const std::string& header_string) {
87 // In general, it is impossible to tell when curl will reach the last
88 // header because there could another one in some special cases. In
89 // particular, it happens when curl hits a redirect status code (301).
90 // In this case, we need to proceed.
91 if (std::string(header_string) == "\r\n" &&
92 status_code_ != HttpResponseCode::kHttpMovedPermanently) {
93 is_last_header_line_ = true;
94 return true;
95 }
96
97 return false;
98 }
99
UseCurlEncoding()100 void CurlHeaderParser::UseCurlEncoding() { use_curl_encoding_ = true; }
101
IsLastHeader() const102 bool CurlHeaderParser::IsLastHeader() const { return is_last_header_line_; }
103
GetStatusCode() const104 int CurlHeaderParser::GetStatusCode() const { return status_code_; }
105
GetHeaderList() const106 HeaderList CurlHeaderParser::GetHeaderList() const { return header_list_; }
107
108 } // namespace fcp::client::http::curl
109