1 /*
2  * Copyright (C) 2019 The Android Open Source Project
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 
17 #include <https/HTTPRequestResponse.h>
18 
19 #include <cerrno>
20 #include <iostream>
21 #include <regex>
22 
HTTPRequestResponse()23 HTTPRequestResponse::HTTPRequestResponse()
24     : mInitCheck(-ENODEV),
25       mContentLength(0) {
26 }
27 
setTo(const uint8_t * data,size_t size)28 int HTTPRequestResponse::setTo(const uint8_t *data, size_t size) {
29     mInitCheck = -EINVAL;
30 
31     bool sawEmptyLine = false;
32 
33     size_t start = 0;
34     while (start < size) {
35         size_t end = start;
36         while (end + 1 < size && memcmp(&data[end], "\r\n", 2)) {
37             ++end;
38         }
39 
40         if ((end + 1) == size) {
41             return mInitCheck;
42         }
43 
44         std::string line(
45                 reinterpret_cast<const char *>(&data[start]), end - start);
46 
47         if (start == 0) {
48             // Parse the request/response line.
49 
50             if (!parseRequestResponseLine(line)) {
51                 return mInitCheck;
52             }
53 
54         } else if (end > start) {
55             std::regex re("([a-zA-Z0-9-]+): (.*)");
56             std::smatch match;
57 
58             if (!std::regex_match(line, match, re)) {
59                 return mInitCheck;
60             }
61 
62             auto key = match[1];
63             auto value = match[2];
64             mHeaders[key] = value;
65         }
66 
67         sawEmptyLine = line.empty();
68 
69         start = end + 2;
70     }
71 
72     if (!sawEmptyLine) {
73         return mInitCheck;
74     }
75 
76     std::string stringValue;
77     if (getHeaderField("Content-Length", &stringValue)) {
78         char *end;
79         unsigned long value = strtoul(stringValue.c_str(), &end, 10);
80 
81         if (end == stringValue.c_str() || *end != '\0') {
82             return mInitCheck;
83         }
84 
85         mContentLength = value;
86     }
87 
88     mInitCheck = 0;
89     return mInitCheck;
90 }
91 
initCheck() const92 int HTTPRequestResponse::initCheck() const {
93     return mInitCheck;
94 }
95 
getHeaderField(std::string_view key,std::string * value) const96 bool HTTPRequestResponse::getHeaderField(
97         std::string_view key, std::string *value) const {
98     auto it = mHeaders.find(std::string(key));
99 
100     if (it != mHeaders.end()) {
101         *value = it->second;
102         return true;
103     }
104 
105     return false;
106 }
107 
getContentLength() const108 size_t HTTPRequestResponse::getContentLength() const {
109     return mContentLength;
110 }
111 
112 ////////////////////////////////////////////////////////////////////////////////
113 
getMethod() const114 std::string HTTPRequest::getMethod() const {
115     return mMethod;
116 }
117 
getPath() const118 std::string HTTPRequest::getPath() const {
119     return mPath;
120 }
121 
getVersion() const122 std::string HTTPRequest::getVersion() const {
123     return mVersion;
124 }
125 
parseRequestResponseLine(const std::string & line)126 bool HTTPRequest::parseRequestResponseLine(const std::string &line) {
127     std::regex re("(GET|HEAD) ([a-zA-Z_/.0-9?&=]+) (HTTP/1\\.1)");
128     std::smatch match;
129 
130     if (!std::regex_match(line, match, re)) {
131         return false;
132     }
133 
134     mMethod = match[1];
135     mPath = match[2];
136     mVersion = match[3];
137 
138     return true;
139 }
140 
141 ////////////////////////////////////////////////////////////////////////////////
142 
getVersion() const143 std::string HTTPResponse::getVersion() const {
144     return mVersion;
145 }
146 
getStatusCode() const147 int32_t HTTPResponse::getStatusCode() const {
148     return mStatusCode;
149 }
150 
getStatusMessage() const151 std::string HTTPResponse::getStatusMessage() const {
152     return mStatusMessage;
153 }
154 
parseRequestResponseLine(const std::string & line)155 bool HTTPResponse::parseRequestResponseLine(const std::string &line) {
156     std::regex re(
157             "(HTTP/1\\.1) ([1-9][0-9][0-9]) ([a-zA-Z _0-9.]+)");
158 
159     std::smatch match;
160 
161     if (!std::regex_match(line, match, re)) {
162         return false;
163     }
164 
165     mVersion = match[1];
166     std::string statusString = match[2];
167     mStatusMessage = match[3];
168 
169     mStatusCode =
170         static_cast<int32_t>(strtol(statusString.c_str(), nullptr, 10));
171 
172     return true;
173 }
174 
175