blob: af923a59a617f07e6dc77d4b119f8d60e5589a55 [file] [log] [blame]
Andreea Costinas90b71642020-06-12 10:18:25 +02001// Copyright 2020 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "system-proxy/http_util.h"
6
7#include <array>
8#include <string_view>
9
10#include <base/strings/stringprintf.h>
11#include <base/strings/string_split.h>
12#include <base/strings/string_tokenizer.h>
13
14namespace {
15// The elements in this array are used to identify the end of a HTTP header
16// which should be an empty line. Note: all HTTP header lines end with CRLF.
17// RFC7230, section 3.5 allow LF (without CR) as a valid end of header. HTTP
18// connect requests don't have a body so end of header is end of request.
19static const std::array<std::string, 2> kValidHttpHeaderEnd = {"\r\n\n",
20 "\r\n\r\n"};
21constexpr char kConnectMethod[] = "CONNECT";
22constexpr char kProxyAuthenticate[] = "Proxy-Authenticate:";
23const std::string_view kRealm = "realm=";
24} // namespace
25
26namespace system_proxy {
27
28bool IsEndingWithHttpEmptyLine(const base::StringPiece& http_header_line) {
29 for (const auto& header_end : kValidHttpHeaderEnd) {
30 if (http_header_line.size() > header_end.size() &&
31 std::memcmp(header_end.data(),
32 http_header_line.data() + http_header_line.size() -
33 header_end.size(),
34 header_end.size()) == 0) {
35 return true;
36 }
37 }
38 return false;
39}
40
Andreea Costinas435851b2020-05-25 14:18:41 +020041bool ExtractHTTPRequest(const std::vector<char>& input,
42 std::vector<char>* out_http_request,
43 std::vector<char>* out_remaining_data) {
44 for (const auto& header_end : kValidHttpHeaderEnd) {
45 auto it = std::search(input.begin(), input.end(), header_end.c_str(),
46 header_end.c_str() + header_end.length());
47 if (it == input.end())
48 continue;
49 it += header_end.length();
50 *out_http_request = {input.begin(), it};
51 *out_remaining_data = {it, input.end()};
52 return true;
53 }
54 return false;
55}
56
Andreea Costinas90b71642020-06-12 10:18:25 +020057std::string GetUriAuthorityFromHttpHeader(
58 const base::StringPiece& http_request) {
59 // Request-Line ends with CRLF (RFC2616, section 5.1).
Andreea Costinas435851b2020-05-25 14:18:41 +020060 size_t i = http_request.find("\r\n");
Andreea Costinas90b71642020-06-12 10:18:25 +020061 if (i == base::StringPiece::npos)
62 return std::string();
63 // Elements are delimited by non-breaking space (SP).
64 auto pieces =
65 base::SplitString(http_request.substr(0, i), " ", base::TRIM_WHITESPACE,
66 base::SPLIT_WANT_NONEMPTY);
67 // Request-Line has the format: Method SP Request-URI SP HTTP-Version CRLF.
68 if (pieces.size() < 3)
69 return std::string();
70 if (pieces[0] != kConnectMethod)
71 return std::string();
72
73 return pieces[1];
74}
75
76SchemeRealmPairList ParseAuthChallenge(const base::StringPiece& http_request) {
77 SchemeRealmPairList scheme_realm_pairs;
78 std::string scheme;
79 std::string realm;
80 std::vector<base::StringPiece> header_lines = base::SplitStringPiece(
81 http_request, "\r\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
82
83 for (const auto& line : header_lines) {
84 const std::string request_line = line.as_string();
85 base::StringTokenizer tok_challenge(request_line, " ");
86 tok_challenge.GetNext();
87 if (tok_challenge.token() != kProxyAuthenticate) {
88 continue;
89 }
90
91 tok_challenge.GetNext();
92 scheme = tok_challenge.token();
93 realm = std::string();
94 // Depending on the challenge scheme, the challenge can contain a
95 // comma-separated list of authenticatio parameters. See RFC7235,
96 // section 4.3.
97 base::StringTokenizer tok_realm(tok_challenge.token_end(),
98 request_line.end(), ",");
99 tok_realm.set_quote_chars("\"");
100 while (tok_realm.GetNext()) {
101 int pos = tok_realm.token().find(kRealm);
102 if (pos != std::string::npos) {
103 realm = tok_realm.token().substr(pos + kRealm.size());
104 }
105 }
106 scheme_realm_pairs.push_back(std::make_pair(scheme, realm));
107 }
108
109 return scheme_realm_pairs;
110}
111
112} // namespace system_proxy